Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 974d250b authored by Laurent Pinchart's avatar Laurent Pinchart
Browse files

fbdev: sh_mobile_meram: Use genalloc to manage MERAM allocation



Instead of requiring the users to hardcode MERAM allocation in platform
data, allocate blocks at runtime using genalloc.

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
parent 7554340c
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -583,13 +583,11 @@ static struct sh_mobile_meram_cfg lcd_meram_cfg = {
	.icb[0] = {
		.marker_icb     = 28,
		.cache_icb      = 24,
		.meram_offset   = 0x0,
		.meram_size     = 0x40,
	},
	.icb[1] = {
		.marker_icb     = 29,
		.cache_icb      = 25,
		.meram_offset   = 0x40,
		.meram_size     = 0x40,
	},
};
@@ -866,13 +864,11 @@ static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
	.icb[0] = {
		.marker_icb     = 30,
		.cache_icb      = 26,
		.meram_offset   = 0x80,
		.meram_size     = 0x100,
	},
	.icb[1] = {
		.marker_icb     = 31,
		.cache_icb      = 27,
		.meram_offset   = 0x180,
		.meram_size     = 0x100,
	},
};
+0 −4
Original line number Diff line number Diff line
@@ -373,13 +373,11 @@ static struct sh_mobile_meram_cfg lcd_meram_cfg = {
	.icb[0] = {
		.marker_icb     = 28,
		.cache_icb      = 24,
		.meram_offset   = 0x0,
		.meram_size     = 0x40,
	},
	.icb[1] = {
		.marker_icb     = 29,
		.cache_icb      = 25,
		.meram_offset   = 0x40,
		.meram_size     = 0x40,
	},
};
@@ -465,13 +463,11 @@ static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
	.icb[0] = {
		.marker_icb     = 30,
		.cache_icb      = 26,
		.meram_offset   = 0x80,
		.meram_size     = 0x100,
	},
	.icb[1] = {
		.marker_icb     = 31,
		.cache_icb      = 27,
		.meram_offset   = 0x180,
		.meram_size     = 0x100,
	},
};
+1 −0
Original line number Diff line number Diff line
@@ -2016,6 +2016,7 @@ config FB_SH_MOBILE_HDMI
config FB_SH_MOBILE_MERAM
	tristate "SuperH Mobile MERAM read ahead support for LCDC"
	depends on FB_SH_MOBILE_LCDC
	select GENERIC_ALLOCATOR
	default y
	---help---
	  Enable MERAM support for the SH-Mobile LCD controller.
+70 −42
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
 */

#include <linux/device.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -105,15 +106,17 @@ static const unsigned long icb_regs[] = {
/*
 * sh_mobile_meram_icb - MERAM ICB information
 * @regs: Registers cache
 * @region: Start and end addresses of the MERAM region
 * @offset: MERAM block offset
 * @size: MERAM block size in bytes
 * @cache_unit: Bytes to cache per ICB
 * @pixelformat: Video pixel format of the data stored in the ICB
 * @current_reg: Which of Start Address Register A (0) or B (1) is in use
 */
struct sh_mobile_meram_icb {
	unsigned long regs[ICB_REGS_SIZE];
	unsigned long offset;
	unsigned int size;

	unsigned long region;
	unsigned int cache_unit;
	unsigned int pixelformat;
	unsigned int current_reg;
@@ -124,21 +127,27 @@ struct sh_mobile_meram_icb {
/*
 * sh_mobile_meram_priv - MERAM device
 * @base: Registers base address
 * @meram: MERAM physical address
 * @regs: Registers cache
 * @lock: Protects used_icb and icbs
 * @used_icb: Bitmask of used ICBs
 * @icbs: ICBs
 * @pool: Allocation pool to manage the MERAM
 */
struct sh_mobile_meram_priv {
	void __iomem *base;
	unsigned long meram;
	unsigned long regs[MERAM_REGS_SIZE];

	struct mutex lock;
	unsigned long used_icb;
	struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];

	struct gen_pool *pool;
};

/* settings */
#define MERAM_GRANULARITY		1024
#define MERAM_SEC_LINE			15
#define MERAM_LINE_WIDTH		2048

@@ -175,18 +184,10 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
 * Allocation
 */

#define MERAM_CACHE_START(p)	 ((p) >> 16)
#define MERAM_CACHE_END(p)	 ((p) & 0xffff)
#define MERAM_CACHE_SET(o, s)	 ((((o) & 0xffff) << 16) | \
				  (((o) + (s) - 1) & 0xffff))

/* Check if there's no overlaps in MERAM allocation. */
static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
			       const struct sh_mobile_meram_icb_cfg *new)
{
	unsigned int used_start, used_end, meram_start, meram_end;
	unsigned int i;

	/* valid ICB? */
	if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f)
		return 1;
@@ -195,43 +196,40 @@ static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
	    test_bit(new->cache_icb,  &priv->used_icb))
		return  1;

	for (i = 0; i < MERAM_ICB_NUM; i++) {
		if (!test_bit(i, &priv->used_icb))
			continue;

		used_start = MERAM_CACHE_START(priv->icbs[i].region);
		used_end   = MERAM_CACHE_END(priv->icbs[i].region);
		meram_start = new->meram_offset;
		meram_end   = new->meram_offset + new->meram_size;

		if ((meram_start >= used_start && meram_start < used_end) ||
		    (meram_end > used_start && meram_end < used_end))
			return 1;
	}

	return 0;
}

/* Mark the specified ICB as used. */
static void meram_mark(struct sh_mobile_meram_priv *priv,
/* Allocate memory for the ICBs and mark them as used. */
static int meram_alloc(struct sh_mobile_meram_priv *priv,
		       const struct sh_mobile_meram_icb_cfg *new,
		       int pixelformat)
{
	struct sh_mobile_meram_icb *marker = &priv->icbs[new->marker_icb];
	unsigned long mem;

	mem = gen_pool_alloc(priv->pool, new->meram_size * 1024);
	if (mem == 0)
		return -ENOMEM;

	__set_bit(new->marker_icb, &priv->used_icb);
	__set_bit(new->cache_icb, &priv->used_icb);

	priv->icbs[new->marker_icb].region = MERAM_CACHE_SET(new->meram_offset,
							     new->meram_size);
	priv->icbs[new->cache_icb].region = MERAM_CACHE_SET(new->meram_offset,
							    new->meram_size);
	priv->icbs[new->marker_icb].current_reg = 1;
	priv->icbs[new->marker_icb].pixelformat = pixelformat;
	marker->offset = mem - priv->meram;
	marker->size = new->meram_size * 1024;
	marker->current_reg = 1;
	marker->pixelformat = pixelformat;

	return 0;
}

/* Unmark the specified ICB as used. */
static void meram_unmark(struct sh_mobile_meram_priv *priv,
static void meram_free(struct sh_mobile_meram_priv *priv,
		       const struct sh_mobile_meram_icb_cfg *icb)
{
	struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb];

	gen_pool_free(priv->pool, priv->meram + marker->offset, marker->size);

	__clear_bit(icb->marker_icb, &priv->used_icb);
	__clear_bit(icb->cache_icb, &priv->used_icb);
}
@@ -302,6 +300,7 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
		      unsigned int xres, unsigned int yres,
		      unsigned int *out_pitch)
{
	struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb];
	unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
	unsigned long bnm;
	unsigned int lcdc_pitch;
@@ -356,11 +355,11 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
	 * we also split the allocated MERAM buffer between two ICBs.
	 */
	meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
			MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) |
			MERAM_MExxCTL_VAL(icb->marker_icb, marker->offset) |
			MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
			MExxCTL_MD_FB);
	meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
			MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset +
			MERAM_MExxCTL_VAL(icb->cache_icb, marker->offset +
					  icb->meram_size / 2) |
			MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
			MExxCTL_MD_FB);
@@ -454,10 +453,18 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
		goto err;
	}

	/* we now register the ICB */
	meram_mark(priv, &cfg->icb[0], pixelformat);
	if (is_nvcolor(pixelformat))
		meram_mark(priv, &cfg->icb[1], pixelformat);
	/* We now register the ICBs and allocate the MERAM regions. */
	error = meram_alloc(priv, &cfg->icb[0], pixelformat);
	if (error < 0)
		goto err;

	if (is_nvcolor(pixelformat)) {
		error = meram_alloc(priv, &cfg->icb[1], pixelformat);
		if (error < 0) {
			meram_free(priv, &cfg->icb[0]);
			goto err;
		}
	}

	/* initialize MERAM */
	meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch);
@@ -497,10 +504,10 @@ static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
	/* deinit & unmark */
	if (is_nvcolor(icb->pixelformat)) {
		meram_deinit(priv, &cfg->icb[1]);
		meram_unmark(priv, &cfg->icb[1]);
		meram_free(priv, &cfg->icb[1]);
	}
	meram_deinit(priv, &cfg->icb[0]);
	meram_unmark(priv, &cfg->icb[0]);
	meram_free(priv, &cfg->icb[0]);

	mutex_unlock(&priv->lock);

@@ -626,6 +633,7 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
	pdata->priv = priv;
	pdata->pdev = pdev;

	/* Request memory regions and remap the registers. */
	if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) {
		dev_err(&pdev->dev, "MERAM registers region already claimed\n");
		error = -EBUSY;
@@ -646,6 +654,20 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
		goto err_ioremap;
	}

	priv->meram = meram->start;

	/* Create and initialize the MERAM memory pool. */
	priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1);
	if (priv->pool == NULL) {
		error = -ENOMEM;
		goto err_genpool;
	}

	error = gen_pool_add(priv->pool, meram->start, resource_size(meram),
			     -1);
	if (error < 0)
		goto err_genpool;

	/* initialize ICB addressing mode */
	if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
		meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
@@ -657,6 +679,10 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)

	return 0;

err_genpool:
	if (priv->pool)
		gen_pool_destroy(priv->pool);
	iounmap(priv->base);
err_ioremap:
	release_mem_region(meram->start, resource_size(meram));
err_req_meram:
@@ -677,6 +703,8 @@ static int sh_mobile_meram_remove(struct platform_device *pdev)

	pm_runtime_disable(&pdev->dev);

	gen_pool_destroy(priv->pool);

	iounmap(priv->base);
	release_mem_region(meram->start, resource_size(meram));
	release_mem_region(regs->start, resource_size(regs));
+0 −1
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ struct sh_mobile_meram_info {
struct sh_mobile_meram_icb_cfg {
	unsigned int marker_icb;	/* ICB # for Marker ICB */
	unsigned int cache_icb;		/* ICB # for Cache ICB */
	unsigned int meram_offset;	/* MERAM Buffer Offset to use */
	unsigned int meram_size;	/* MERAM Buffer Size to use */
};