|
@@ -100,14 +100,38 @@ static unsigned long 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 {
|
|
|
- void __iomem *base;
|
|
|
- struct mutex lock;
|
|
|
- unsigned long used_icb;
|
|
|
- unsigned int used_meram_cache_regions;
|
|
|
- unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM];
|
|
|
- unsigned long cmn_saved_regs[CMN_REGS_SIZE];
|
|
|
- unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM];
|
|
|
+ void __iomem *base;
|
|
|
+ unsigned long regs[CMN_REGS_SIZE];
|
|
|
+
|
|
|
+ struct mutex lock;
|
|
|
+ unsigned long used_icb;
|
|
|
+ struct sh_mobile_meram_icb icbs[SH_MOBILE_MERAM_ICB_NUM];
|
|
|
};
|
|
|
|
|
|
/* 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,
|
|
|
- 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 i;
|
|
@@ -167,17 +191,20 @@ static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
|
|
|
return 1;
|
|
|
|
|
|
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;
|
|
|
|
|
|
- for (i = 0; i < priv->used_meram_cache_regions; i++) {
|
|
|
- used_start = MERAM_CACHE_START(priv->used_meram_cache[i]);
|
|
|
- used_end = MERAM_CACHE_END(priv->used_meram_cache[i]);
|
|
|
+ for (i = 0; i < SH_MOBILE_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))
|
|
|
+ (meram_end > used_start && meram_end < used_end))
|
|
|
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,
|
|
|
- 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->cache_icb, &priv->used_icb);
|
|
|
|
|
|
- n = priv->used_meram_cache_regions;
|
|
|
-
|
|
|
- priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset,
|
|
|
- new->meram_size);
|
|
|
-
|
|
|
- priv->used_meram_cache_regions++;
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -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,
|
|
|
- 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->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)
|
|
|
{
|
|
|
if (cspace == SH_MOBILE_MERAM_PF_NV ||
|
|
|
- cspace == SH_MOBILE_MERAM_PF_NV24)
|
|
|
+ cspace == SH_MOBILE_MERAM_PF_NV24)
|
|
|
return 1;
|
|
|
return 0;
|
|
|
}
|
|
@@ -253,46 +256,51 @@ static inline int is_nvcolor(int cspace)
|
|
|
* set the next address to fetch
|
|
|
*/
|
|
|
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_c)
|
|
|
{
|
|
|
+ struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
|
|
|
unsigned long target;
|
|
|
|
|
|
- cfg->current_reg ^= 1;
|
|
|
- target = cfg->current_reg ? MExxSARB : MExxSARA;
|
|
|
+ icb->current_reg ^= 1;
|
|
|
+ target = icb->current_reg ? MExxSARB : MExxSARA;
|
|
|
|
|
|
/* 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);
|
|
|
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,
|
|
|
base_addr_c);
|
|
|
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
|
|
|
*/
|
|
|
-static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
|
|
|
- struct sh_mobile_meram_cfg *cfg,
|
|
|
- unsigned long *icb_addr_y,
|
|
|
- unsigned long *icb_addr_c)
|
|
|
+static inline void
|
|
|
+meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
|
|
|
+ const struct sh_mobile_meram_cfg *cfg,
|
|
|
+ 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;
|
|
|
|
|
|
if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
|
|
|
- icb_offset = 0x80000000 | (cfg->current_reg << 29);
|
|
|
+ icb_offset = 0x80000000 | (icb->current_reg << 29);
|
|
|
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);
|
|
|
- if (is_nvcolor(cfg->pixelformat))
|
|
|
+ if (is_nvcolor(icb->pixelformat))
|
|
|
*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,
|
|
|
- struct sh_mobile_meram_icb_cfg *icb,
|
|
|
+ const struct sh_mobile_meram_icb_cfg *icb,
|
|
|
unsigned int xres, unsigned int yres,
|
|
|
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);
|
|
|
|
|
|
/* 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
|
|
@@ -374,14 +383,16 @@ static int meram_init(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 */
|
|
|
meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
|
|
|
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
|
|
|
meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
|
|
|
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,
|
|
|
- struct sh_mobile_meram_cfg *cfg,
|
|
|
+ const struct sh_mobile_meram_cfg *cfg,
|
|
|
unsigned int xres, unsigned int yres,
|
|
|
unsigned int pixelformat,
|
|
|
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);
|
|
|
|
|
|
- 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 */
|
|
|
if (meram_check_overlap(priv, &cfg->icb[0])) {
|
|
|
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 */
|
|
|
- cfg->pixelformat = pixelformat;
|
|
|
- meram_mark(priv, &cfg->icb[0]);
|
|
|
+ meram_mark(priv, &cfg->icb[0], pixelformat);
|
|
|
if (is_nvcolor(pixelformat))
|
|
|
- meram_mark(priv, &cfg->icb[1]);
|
|
|
+ meram_mark(priv, &cfg->icb[1], pixelformat);
|
|
|
|
|
|
/* initialize MERAM */
|
|
|
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,
|
|
|
&out_pitch);
|
|
|
|
|
|
- cfg->current_reg = 1;
|
|
|
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);
|
|
|
|
|
@@ -492,19 +495,21 @@ err:
|
|
|
}
|
|
|
|
|
|
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_icb *icb;
|
|
|
|
|
|
if (!pdata || !pdata->priv || !cfg)
|
|
|
return -EINVAL;
|
|
|
|
|
|
priv = pdata->priv;
|
|
|
+ icb = &priv->icbs[cfg->icb[0].marker_icb];
|
|
|
|
|
|
mutex_lock(&priv->lock);
|
|
|
|
|
|
/* deinit & unmark */
|
|
|
- if (is_nvcolor(cfg->pixelformat)) {
|
|
|
+ if (is_nvcolor(icb->pixelformat)) {
|
|
|
meram_deinit(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,
|
|
|
- struct sh_mobile_meram_cfg *cfg,
|
|
|
+ const struct sh_mobile_meram_cfg *cfg,
|
|
|
unsigned long base_addr_y,
|
|
|
unsigned long base_addr_c,
|
|
|
unsigned long *icb_addr_y,
|
|
@@ -547,18 +552,17 @@ static int sh_mobile_meram_runtime_suspend(struct device *dev)
|
|
|
unsigned int i, j;
|
|
|
|
|
|
for (i = 0; i < CMN_REGS_SIZE; i++)
|
|
|
- priv->cmn_saved_regs[i] = meram_read_reg(priv->base,
|
|
|
- common_regs[i]);
|
|
|
+ priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
|
|
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
if (!test_bit(i, &priv->used_icb))
|
|
|
continue;
|
|
|
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]);
|
|
|
/* Reset ICB on resume */
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
@@ -574,15 +578,13 @@ static int sh_mobile_meram_runtime_resume(struct device *dev)
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
if (!test_bit(i, &priv->used_icb))
|
|
|
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],
|
|
|
- priv->icb_saved_regs[i * ICB_REGS_SIZE + j]);
|
|
|
- }
|
|
|
+ priv->icbs[i].regs[j]);
|
|
|
}
|
|
|
|
|
|
for (i = 0; i < CMN_REGS_SIZE; i++)
|
|
|
- meram_write_reg(priv->base, common_regs[i],
|
|
|
- priv->cmn_saved_regs[i]);
|
|
|
+ meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
|
|
|
return 0;
|
|
|
}
|
|
|
|