Skip to content

Commit

Permalink
fbdev: sh_mobile_meram: Use genalloc to manage MERAM allocation
Browse files Browse the repository at this point in the history
Instead of requiring the users to hardcode MERAM allocation in platform
data, allocate blocks at runtime using genalloc.

Signed-off-by: Laurent Pinchart <[email protected]>
  • Loading branch information
pinchartl committed Mar 12, 2012
1 parent 7554340 commit 974d250
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 51 deletions.
4 changes: 0 additions & 4 deletions arch/arm/mach-shmobile/board-ap4evb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
};
Expand Down Expand Up @@ -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,
},
};
Expand Down
4 changes: 0 additions & 4 deletions arch/arm/mach-shmobile/board-mackerel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
};
Expand Down Expand Up @@ -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,
},
};
Expand Down
1 change: 1 addition & 0 deletions drivers/video/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
112 changes: 70 additions & 42 deletions drivers/video/sh_mobile_meram.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/

#include <linux/device.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
Expand Down Expand Up @@ -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;
Expand All @@ -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

Expand Down Expand Up @@ -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;
Expand All @@ -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,
const struct sh_mobile_meram_icb_cfg *icb)
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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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:
Expand All @@ -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));
Expand Down
1 change: 0 additions & 1 deletion include/video/sh_mobile_meram.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
};

Expand Down

0 comments on commit 974d250

Please sign in to comment.