fbdev: sh_mobile_meram: Add struct sh_mobile_meram_icb

The new structure stores ICB parameters for ICBs.

Instead of modifying the struct sh_mobile_meram_cfg instances passed by
callers, store the ICB parameters internally and make the public API
take const pointers to sh_mobile_meram_cfg.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
Laurent Pinchart 2011-09-19 11:40:31 +02:00
parent eb4f2304ba
commit 2a618e0333
2 changed files with 94 additions and 96 deletions

View File

@ -100,14 +100,38 @@ static unsigned long icb_regs[] = {
}; };
#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs) #define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
/*
* sh_mobile_meram_icb - MERAM ICB information
* @regs: Registers cache
* @region: Start and end addresses of the MERAM region
* @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 region;
unsigned int cache_unit;
unsigned int pixelformat;
unsigned int current_reg;
};
/*
* sh_mobile_meram_priv - MERAM device
* @base: Registers base address
* @regs: Registers cache
* @lock: Protects used_icb and icbs
* @used_icb: Bitmask of used ICBs
* @icbs: ICBs
*/
struct sh_mobile_meram_priv { struct sh_mobile_meram_priv {
void __iomem *base; void __iomem *base;
struct mutex lock; unsigned long regs[CMN_REGS_SIZE];
unsigned long used_icb;
unsigned int used_meram_cache_regions; struct mutex lock;
unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM]; unsigned long used_icb;
unsigned long cmn_saved_regs[CMN_REGS_SIZE]; struct sh_mobile_meram_icb icbs[SH_MOBILE_MERAM_ICB_NUM];
unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM];
}; };
/* settings */ /* settings */
@ -157,7 +181,7 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
*/ */
static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv, static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
struct sh_mobile_meram_icb_cfg *new) const struct sh_mobile_meram_icb_cfg *new)
{ {
unsigned int used_start, used_end, meram_start, meram_end; unsigned int used_start, used_end, meram_start, meram_end;
unsigned int i; unsigned int i;
@ -167,17 +191,20 @@ static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
return 1; return 1;
if (test_bit(new->marker_icb, &priv->used_icb) || if (test_bit(new->marker_icb, &priv->used_icb) ||
test_bit(new->cache_icb, &priv->used_icb)) test_bit(new->cache_icb, &priv->used_icb))
return 1; return 1;
for (i = 0; i < priv->used_meram_cache_regions; i++) { for (i = 0; i < SH_MOBILE_MERAM_ICB_NUM; i++) {
used_start = MERAM_CACHE_START(priv->used_meram_cache[i]); if (!test_bit(i, &priv->used_icb))
used_end = MERAM_CACHE_END(priv->used_meram_cache[i]); 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_start = new->meram_offset;
meram_end = new->meram_offset + new->meram_size; meram_end = new->meram_offset + new->meram_size;
if ((meram_start >= used_start && meram_start < used_end) || if ((meram_start >= used_start && meram_start < used_end) ||
(meram_end > used_start && meram_end < used_end)) (meram_end > used_start && meram_end < used_end))
return 1; return 1;
} }
@ -189,22 +216,18 @@ static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
*/ */
static inline void meram_mark(struct sh_mobile_meram_priv *priv, static inline void meram_mark(struct sh_mobile_meram_priv *priv,
struct sh_mobile_meram_icb_cfg *new) const struct sh_mobile_meram_icb_cfg *new,
int pixelformat)
{ {
unsigned int n;
if (new->marker_icb < 0 || new->cache_icb < 0)
return;
__set_bit(new->marker_icb, &priv->used_icb); __set_bit(new->marker_icb, &priv->used_icb);
__set_bit(new->cache_icb, &priv->used_icb); __set_bit(new->cache_icb, &priv->used_icb);
n = priv->used_meram_cache_regions; priv->icbs[new->marker_icb].region = MERAM_CACHE_SET(new->meram_offset,
new->meram_size);
priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset, priv->icbs[new->cache_icb].region = MERAM_CACHE_SET(new->meram_offset,
new->meram_size); new->meram_size);
priv->icbs[new->marker_icb].current_reg = 1;
priv->used_meram_cache_regions++; priv->icbs[new->marker_icb].pixelformat = pixelformat;
} }
/* /*
@ -212,30 +235,10 @@ static inline void meram_mark(struct sh_mobile_meram_priv *priv,
*/ */
static inline void meram_unmark(struct sh_mobile_meram_priv *priv, static inline void meram_unmark(struct sh_mobile_meram_priv *priv,
struct sh_mobile_meram_icb_cfg *icb) const struct sh_mobile_meram_icb_cfg *icb)
{ {
unsigned long pattern;
unsigned int i;
if (icb->marker_icb < 0 || icb->cache_icb < 0)
return;
__clear_bit(icb->marker_icb, &priv->used_icb); __clear_bit(icb->marker_icb, &priv->used_icb);
__clear_bit(icb->cache_icb, &priv->used_icb); __clear_bit(icb->cache_icb, &priv->used_icb);
pattern = MERAM_CACHE_SET(icb->meram_offset, icb->meram_size);
for (i = 0; i < priv->used_meram_cache_regions; i++) {
if (priv->used_meram_cache[i] == pattern) {
while (i < priv->used_meram_cache_regions - 1) {
priv->used_meram_cache[i] =
priv->used_meram_cache[i + 1] ;
i++;
}
priv->used_meram_cache[i] = 0;
priv->used_meram_cache_regions--;
break;
}
}
} }
/* /*
@ -244,7 +247,7 @@ static inline void meram_unmark(struct sh_mobile_meram_priv *priv,
static inline int is_nvcolor(int cspace) static inline int is_nvcolor(int cspace)
{ {
if (cspace == SH_MOBILE_MERAM_PF_NV || if (cspace == SH_MOBILE_MERAM_PF_NV ||
cspace == SH_MOBILE_MERAM_PF_NV24) cspace == SH_MOBILE_MERAM_PF_NV24)
return 1; return 1;
return 0; return 0;
} }
@ -253,46 +256,51 @@ static inline int is_nvcolor(int cspace)
* set the next address to fetch * set the next address to fetch
*/ */
static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv, static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
struct sh_mobile_meram_cfg *cfg, const struct sh_mobile_meram_cfg *cfg,
unsigned long base_addr_y, unsigned long base_addr_y,
unsigned long base_addr_c) unsigned long base_addr_c)
{ {
struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
unsigned long target; unsigned long target;
cfg->current_reg ^= 1; icb->current_reg ^= 1;
target = cfg->current_reg ? MExxSARB : MExxSARA; target = icb->current_reg ? MExxSARB : MExxSARA;
/* set the next address to fetch */ /* set the next address to fetch */
meram_write_icb(priv->base, cfg->icb[0].cache_icb, target, meram_write_icb(priv->base, cfg->icb[0].cache_icb, target,
base_addr_y); base_addr_y);
meram_write_icb(priv->base, cfg->icb[0].marker_icb, target, meram_write_icb(priv->base, cfg->icb[0].marker_icb, target,
base_addr_y + cfg->icb[0].cache_unit); base_addr_y +
priv->icbs[cfg->icb[0].marker_icb].cache_unit);
if (is_nvcolor(cfg->pixelformat)) { if (is_nvcolor(icb->pixelformat)) {
meram_write_icb(priv->base, cfg->icb[1].cache_icb, target, meram_write_icb(priv->base, cfg->icb[1].cache_icb, target,
base_addr_c); base_addr_c);
meram_write_icb(priv->base, cfg->icb[1].marker_icb, target, meram_write_icb(priv->base, cfg->icb[1].marker_icb, target,
base_addr_c + cfg->icb[1].cache_unit); base_addr_c +
priv->icbs[cfg->icb[1].marker_icb].cache_unit);
} }
} }
/* /*
* get the next ICB address * get the next ICB address
*/ */
static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata, static inline void
struct sh_mobile_meram_cfg *cfg, meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
unsigned long *icb_addr_y, const struct sh_mobile_meram_cfg *cfg,
unsigned long *icb_addr_c) unsigned long *icb_addr_y, unsigned long *icb_addr_c)
{ {
struct sh_mobile_meram_priv *priv = pdata->priv;
struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
unsigned long icb_offset; unsigned long icb_offset;
if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0) if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
icb_offset = 0x80000000 | (cfg->current_reg << 29); icb_offset = 0x80000000 | (icb->current_reg << 29);
else else
icb_offset = 0xc0000000 | (cfg->current_reg << 23); icb_offset = 0xc0000000 | (icb->current_reg << 23);
*icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24); *icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24);
if (is_nvcolor(cfg->pixelformat)) if (is_nvcolor(icb->pixelformat))
*icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24); *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24);
} }
@ -304,7 +312,7 @@ static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
*/ */
static int meram_init(struct sh_mobile_meram_priv *priv, static int meram_init(struct sh_mobile_meram_priv *priv,
struct sh_mobile_meram_icb_cfg *icb, const struct sh_mobile_meram_icb_cfg *icb,
unsigned int xres, unsigned int yres, unsigned int xres, unsigned int yres,
unsigned int *out_pitch) unsigned int *out_pitch)
{ {
@ -352,7 +360,8 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch); meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch);
/* save a cache unit size */ /* save a cache unit size */
icb->cache_unit = xres * save_lines; priv->icbs[icb->cache_icb].cache_unit = xres * save_lines;
priv->icbs[icb->marker_icb].cache_unit = xres * save_lines;
/* /*
* Set MERAM for framebuffer * Set MERAM for framebuffer
@ -374,14 +383,16 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
} }
static void meram_deinit(struct sh_mobile_meram_priv *priv, static void meram_deinit(struct sh_mobile_meram_priv *priv,
struct sh_mobile_meram_icb_cfg *icb) const struct sh_mobile_meram_icb_cfg *icb)
{ {
/* disable ICB */ /* disable ICB */
meram_write_icb(priv->base, icb->cache_icb, MExxCTL, meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
meram_write_icb(priv->base, icb->marker_icb, MExxCTL, meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
icb->cache_unit = 0;
priv->icbs[icb->cache_icb].cache_unit = 0;
priv->icbs[icb->marker_icb].cache_unit = 0;
} }
/* /*
@ -389,7 +400,7 @@ static void meram_deinit(struct sh_mobile_meram_priv *priv,
*/ */
static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
struct sh_mobile_meram_cfg *cfg, const struct sh_mobile_meram_cfg *cfg,
unsigned int xres, unsigned int yres, unsigned int xres, unsigned int yres,
unsigned int pixelformat, unsigned int pixelformat,
unsigned long base_addr_y, unsigned long base_addr_y,
@ -433,12 +444,6 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
mutex_lock(&priv->lock); mutex_lock(&priv->lock);
if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) {
dev_err(&pdev->dev, "no more ICB available.");
error = -EINVAL;
goto err;
}
/* make sure that there's no overlaps */ /* make sure that there's no overlaps */
if (meram_check_overlap(priv, &cfg->icb[0])) { if (meram_check_overlap(priv, &cfg->icb[0])) {
dev_err(&pdev->dev, "conflicting config detected."); dev_err(&pdev->dev, "conflicting config detected.");
@ -464,10 +469,9 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
} }
/* we now register the ICB */ /* we now register the ICB */
cfg->pixelformat = pixelformat; meram_mark(priv, &cfg->icb[0], pixelformat);
meram_mark(priv, &cfg->icb[0]);
if (is_nvcolor(pixelformat)) if (is_nvcolor(pixelformat))
meram_mark(priv, &cfg->icb[1]); meram_mark(priv, &cfg->icb[1], pixelformat);
/* initialize MERAM */ /* initialize MERAM */
meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch); meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch);
@ -479,7 +483,6 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2, meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2,
&out_pitch); &out_pitch);
cfg->current_reg = 1;
meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
@ -492,19 +495,21 @@ err:
} }
static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
struct sh_mobile_meram_cfg *cfg) const struct sh_mobile_meram_cfg *cfg)
{ {
struct sh_mobile_meram_priv *priv; struct sh_mobile_meram_priv *priv;
struct sh_mobile_meram_icb *icb;
if (!pdata || !pdata->priv || !cfg) if (!pdata || !pdata->priv || !cfg)
return -EINVAL; return -EINVAL;
priv = pdata->priv; priv = pdata->priv;
icb = &priv->icbs[cfg->icb[0].marker_icb];
mutex_lock(&priv->lock); mutex_lock(&priv->lock);
/* deinit & unmark */ /* deinit & unmark */
if (is_nvcolor(cfg->pixelformat)) { if (is_nvcolor(icb->pixelformat)) {
meram_deinit(priv, &cfg->icb[1]); meram_deinit(priv, &cfg->icb[1]);
meram_unmark(priv, &cfg->icb[1]); meram_unmark(priv, &cfg->icb[1]);
} }
@ -517,7 +522,7 @@ static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
} }
static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
struct sh_mobile_meram_cfg *cfg, const struct sh_mobile_meram_cfg *cfg,
unsigned long base_addr_y, unsigned long base_addr_y,
unsigned long base_addr_c, unsigned long base_addr_c,
unsigned long *icb_addr_y, unsigned long *icb_addr_y,
@ -547,18 +552,17 @@ static int sh_mobile_meram_runtime_suspend(struct device *dev)
unsigned int i, j; unsigned int i, j;
for (i = 0; i < CMN_REGS_SIZE; i++) for (i = 0; i < CMN_REGS_SIZE; i++)
priv->cmn_saved_regs[i] = meram_read_reg(priv->base, priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
common_regs[i]);
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
if (!test_bit(i, &priv->used_icb)) if (!test_bit(i, &priv->used_icb))
continue; continue;
for (j = 0; j < ICB_REGS_SIZE; j++) { for (j = 0; j < ICB_REGS_SIZE; j++) {
priv->icb_saved_regs[i * ICB_REGS_SIZE + j] = priv->icbs[i].regs[j] =
meram_read_icb(priv->base, i, icb_regs[j]); meram_read_icb(priv->base, i, icb_regs[j]);
/* Reset ICB on resume */ /* Reset ICB on resume */
if (icb_regs[j] == MExxCTL) if (icb_regs[j] == MExxCTL)
priv->icb_saved_regs[i * ICB_REGS_SIZE + j] |= priv->icbs[i].regs[j] |=
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF; MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
} }
} }
@ -574,15 +578,13 @@ static int sh_mobile_meram_runtime_resume(struct device *dev)
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
if (!test_bit(i, &priv->used_icb)) if (!test_bit(i, &priv->used_icb))
continue; continue;
for (j = 0; j < ICB_REGS_SIZE; j++) { for (j = 0; j < ICB_REGS_SIZE; j++)
meram_write_icb(priv->base, i, icb_regs[j], meram_write_icb(priv->base, i, icb_regs[j],
priv->icb_saved_regs[i * ICB_REGS_SIZE + j]); priv->icbs[i].regs[j]);
}
} }
for (i = 0; i < CMN_REGS_SIZE; i++) for (i = 0; i < CMN_REGS_SIZE; i++)
meram_write_reg(priv->base, common_regs[i], meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
priv->cmn_saved_regs[i]);
return 0; return 0;
} }

View File

@ -30,14 +30,10 @@ struct sh_mobile_meram_icb_cfg {
unsigned int cache_icb; /* ICB # for Cache ICB */ unsigned int cache_icb; /* ICB # for Cache ICB */
unsigned int meram_offset; /* MERAM Buffer Offset to use */ unsigned int meram_offset; /* MERAM Buffer Offset to use */
unsigned int meram_size; /* MERAM Buffer Size to use */ unsigned int meram_size; /* MERAM Buffer Size to use */
unsigned int cache_unit; /* bytes to cache per ICB */
}; };
struct sh_mobile_meram_cfg { struct sh_mobile_meram_cfg {
struct sh_mobile_meram_icb_cfg icb[2]; struct sh_mobile_meram_icb_cfg icb[2];
int pixelformat;
int current_reg;
}; };
struct module; struct module;
@ -45,7 +41,7 @@ struct sh_mobile_meram_ops {
struct module *module; struct module *module;
/* register usage of meram */ /* register usage of meram */
int (*meram_register)(struct sh_mobile_meram_info *meram_dev, int (*meram_register)(struct sh_mobile_meram_info *meram_dev,
struct sh_mobile_meram_cfg *cfg, const struct sh_mobile_meram_cfg *cfg,
unsigned int xres, unsigned int yres, unsigned int xres, unsigned int yres,
unsigned int pixelformat, unsigned int pixelformat,
unsigned long base_addr_y, unsigned long base_addr_y,
@ -56,11 +52,11 @@ struct sh_mobile_meram_ops {
/* unregister usage of meram */ /* unregister usage of meram */
int (*meram_unregister)(struct sh_mobile_meram_info *meram_dev, int (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
struct sh_mobile_meram_cfg *cfg); const struct sh_mobile_meram_cfg *cfg);
/* update meram settings */ /* update meram settings */
int (*meram_update)(struct sh_mobile_meram_info *meram_dev, int (*meram_update)(struct sh_mobile_meram_info *meram_dev,
struct sh_mobile_meram_cfg *cfg, const struct sh_mobile_meram_cfg *cfg,
unsigned long base_addr_y, unsigned long base_addr_y,
unsigned long base_addr_c, unsigned long base_addr_c,
unsigned long *icb_addr_y, unsigned long *icb_addr_y,