Merge branches 'sh/urgent', 'sh/core', 'sh/clockevents', 'sh/asm-generic' and 'sh...
[platform/kernel/linux-arm64.git] / drivers / video / sh_mobile_meram.c
index f45d83e..82ba830 100644 (file)
@@ -9,16 +9,22 @@
  * for more details.
  */
 
  * for more details.
  */
 
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/genalloc.h>
+#include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_runtime.h>
-#include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
-#include <linux/platform_device.h>
+
 #include <video/sh_mobile_meram.h>
 
 #include <video/sh_mobile_meram.h>
 
-/* meram registers */
+/* -----------------------------------------------------------------------------
+ * MERAM registers
+ */
+
 #define MEVCR1                 0x4
 #define MEVCR1_RST             (1 << 31)
 #define MEVCR1_WD              (1 << 30)
 #define MEVCR1                 0x4
 #define MEVCR1_RST             (1 << 31)
 #define MEVCR1_WD              (1 << 30)
         ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
         ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
 
         ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
         ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
 
-#define SH_MOBILE_MERAM_ICB_NUM                32
-
-static unsigned long common_regs[] = {
+static const unsigned long common_regs[] = {
        MEVCR1,
        MEQSEL1,
        MEQSEL2,
 };
        MEVCR1,
        MEQSEL1,
        MEQSEL2,
 };
-#define CMN_REGS_SIZE ARRAY_SIZE(common_regs)
+#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
 
 
-static unsigned long icb_regs[] = {
+static const unsigned long icb_regs[] = {
        MExxCTL,
        MExxBSIZE,
        MExxMNCF,
        MExxCTL,
        MExxBSIZE,
        MExxMNCF,
@@ -100,216 +104,269 @@ 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
+ * @index: ICB index
+ * @offset: MERAM block offset
+ * @size: MERAM block size in KiB
+ * @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 int index;
+       unsigned long offset;
+       unsigned int size;
+
+       unsigned int cache_unit;
+       unsigned int pixelformat;
+       unsigned int current_reg;
+};
+
+#define MERAM_ICB_NUM                  32
+
+struct sh_mobile_meram_fb_plane {
+       struct sh_mobile_meram_icb *marker;
+       struct sh_mobile_meram_icb *cache;
+};
+
+struct sh_mobile_meram_fb_cache {
+       unsigned int nplanes;
+       struct sh_mobile_meram_fb_plane planes[2];
+};
+
+/*
+ * 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 {
 struct sh_mobile_meram_priv {
-       void __iomem    *base;
-       struct mutex    lock;
-       unsigned long   used_icb;
-       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 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 */
 };
 
 /* settings */
-#define MERAM_SEC_LINE 15
-#define MERAM_LINE_WIDTH 2048
+#define MERAM_GRANULARITY              1024
+#define MERAM_SEC_LINE                 15
+#define MERAM_LINE_WIDTH               2048
 
 
-/*
- * MERAM/ICB access functions
+/* -----------------------------------------------------------------------------
+ * Registers access
  */
 
 #define MERAM_ICB_OFFSET(base, idx, off)       ((base) + (off) + (idx) * 0x20)
 
  */
 
 #define MERAM_ICB_OFFSET(base, idx, off)       ((base) + (off) + (idx) * 0x20)
 
-static inline void meram_write_icb(void __iomem *base, int idx, int off,
-       unsigned long val)
+static inline void meram_write_icb(void __iomem *base, unsigned int idx,
+                                  unsigned int off, unsigned long val)
 {
        iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
 }
 
 {
        iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
 }
 
-static inline unsigned long meram_read_icb(void __iomem *base, int idx, int off)
+static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx,
+                                          unsigned int off)
 {
        return ioread32(MERAM_ICB_OFFSET(base, idx, off));
 }
 
 {
        return ioread32(MERAM_ICB_OFFSET(base, idx, off));
 }
 
-static inline void meram_write_reg(void __iomem *base, int off,
-               unsigned long val)
+static inline void meram_write_reg(void __iomem *base, unsigned int off,
+                                  unsigned long val)
 {
        iowrite32(val, base + off);
 }
 
 {
        iowrite32(val, base + off);
 }
 
-static inline unsigned long meram_read_reg(void __iomem *base, int off)
+static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
 {
        return ioread32(base + off);
 }
 
 {
        return ioread32(base + off);
 }
 
-/*
- * register ICB
- */
-
-#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.
+/* -----------------------------------------------------------------------------
+ * Allocation
  */
 
  */
 
-static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
-                                     struct sh_mobile_meram_icb *new)
+/* Allocate ICBs and MERAM for a plane. */
+static int __meram_alloc(struct sh_mobile_meram_priv *priv,
+                        struct sh_mobile_meram_fb_plane *plane,
+                        size_t size)
 {
 {
-       int i;
-       int used_start, used_end, meram_start, meram_end;
+       unsigned long mem;
+       unsigned long idx;
 
 
-       /* valid ICB? */
-       if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f)
-               return 1;
+       idx = find_first_zero_bit(&priv->used_icb, 28);
+       if (idx == 28)
+               return -ENOMEM;
+       plane->cache = &priv->icbs[idx];
 
 
-       if (test_bit(new->marker_icb, &priv->used_icb) ||
-                       test_bit(new->cache_icb,  &priv->used_icb))
-               return  1;
+       idx = find_next_zero_bit(&priv->used_icb, 32, 28);
+       if (idx == 32)
+               return -ENOMEM;
+       plane->marker = &priv->icbs[idx];
 
 
-       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]);
-               meram_start = new->meram_offset;
-               meram_end   = new->meram_offset + new->meram_size;
+       mem = gen_pool_alloc(priv->pool, size * 1024);
+       if (mem == 0)
+               return -ENOMEM;
 
 
-               if ((meram_start >= used_start && meram_start < used_end) ||
-                       (meram_end > used_start && meram_end < used_end))
-                       return 1;
-       }
+       __set_bit(plane->marker->index, &priv->used_icb);
+       __set_bit(plane->cache->index, &priv->used_icb);
+
+       plane->marker->offset = mem - priv->meram;
+       plane->marker->size = size;
 
        return 0;
 }
 
 
        return 0;
 }
 
-/*
- * mark the specified ICB as used
- */
+/* Free ICBs and MERAM for a plane. */
+static void __meram_free(struct sh_mobile_meram_priv *priv,
+                        struct sh_mobile_meram_fb_plane *plane)
+{
+       gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
+                     plane->marker->size * 1024);
 
 
-static inline void meram_mark(struct sh_mobile_meram_priv *priv,
-                             struct sh_mobile_meram_icb *new)
+       __clear_bit(plane->marker->index, &priv->used_icb);
+       __clear_bit(plane->cache->index, &priv->used_icb);
+}
+
+/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
+static int is_nvcolor(int cspace)
 {
 {
-       int n;
+       if (cspace == SH_MOBILE_MERAM_PF_NV ||
+           cspace == SH_MOBILE_MERAM_PF_NV24)
+               return 1;
+       return 0;
+}
 
 
-       if (new->marker_icb < 0 || new->cache_icb < 0)
-               return;
+/* Allocate memory for the ICBs and mark them as used. */
+static struct sh_mobile_meram_fb_cache *
+meram_alloc(struct sh_mobile_meram_priv *priv,
+           const struct sh_mobile_meram_cfg *cfg,
+           int pixelformat)
+{
+       struct sh_mobile_meram_fb_cache *cache;
+       unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
+       int ret;
 
 
-       __set_bit(new->marker_icb, &priv->used_icb);
-       __set_bit(new->cache_icb, &priv->used_icb);
+       if (cfg->icb[0].meram_size == 0)
+               return ERR_PTR(-EINVAL);
 
 
-       n = priv->used_meram_cache_regions;
+       if (nplanes == 2 && cfg->icb[1].meram_size == 0)
+               return ERR_PTR(-EINVAL);
 
 
-       priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset,
-                                                   new->meram_size);
+       cache = kzalloc(sizeof(*cache), GFP_KERNEL);
+       if (cache == NULL)
+               return ERR_PTR(-ENOMEM);
 
 
-       priv->used_meram_cache_regions++;
-}
+       cache->nplanes = nplanes;
 
 
-/*
- * unmark the specified ICB as used
- */
+       ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
+       if (ret < 0)
+               goto error;
 
 
-static inline void meram_unmark(struct sh_mobile_meram_priv *priv,
-                               struct sh_mobile_meram_icb *icb)
-{
-       int i;
-       unsigned long pattern;
-
-       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;
-               }
+       cache->planes[0].marker->current_reg = 1;
+       cache->planes[0].marker->pixelformat = pixelformat;
+
+       if (cache->nplanes == 1)
+               return cache;
+
+       ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
+       if (ret < 0) {
+               __meram_free(priv, &cache->planes[0]);
+               goto error;
        }
        }
+
+       return cache;
+
+error:
+       kfree(cache);
+       return ERR_PTR(-ENOMEM);
 }
 
 }
 
-/*
- * is this a YCbCr(NV12, NV16 or NV24) colorspace
- */
-static inline int is_nvcolor(int cspace)
+/* Unmark the specified ICB as used. */
+static void meram_free(struct sh_mobile_meram_priv *priv,
+                      struct sh_mobile_meram_fb_cache *cache)
 {
 {
-       if (cspace == SH_MOBILE_MERAM_PF_NV ||
-                       cspace == SH_MOBILE_MERAM_PF_NV24)
-               return 1;
-       return 0;
+       __meram_free(priv, &cache->planes[0]);
+       if (cache->nplanes == 2)
+               __meram_free(priv, &cache->planes[1]);
+
+       kfree(cache);
 }
 
 }
 
-/*
- * 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,
-                                      unsigned long base_addr_y,
-                                      unsigned long base_addr_c)
+/* Set the next address to fetch. */
+static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
+                               struct sh_mobile_meram_fb_cache *cache,
+                               unsigned long base_addr_y,
+                               unsigned long base_addr_c)
 {
 {
+       struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
        unsigned long target;
 
        unsigned long target;
 
-       target = (cfg->current_reg) ? MExxSARA : MExxSARB;
-       cfg->current_reg ^= 1;
+       icb->current_reg ^= 1;
+       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, cache->planes[0].cache->index, target,
                        base_addr_y);
                        base_addr_y);
-       meram_write_icb(priv->base, cfg->icb[0].marker_icb, target,
-                       base_addr_y + cfg->icb[0].cache_unit);
-
-       if (is_nvcolor(cfg->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);
+       meram_write_icb(priv->base, cache->planes[0].marker->index, target,
+                       base_addr_y + cache->planes[0].marker->cache_unit);
+
+       if (cache->nplanes == 2) {
+               meram_write_icb(priv->base, cache->planes[1].cache->index,
+                               target, base_addr_c);
+               meram_write_icb(priv->base, cache->planes[1].marker->index,
+                               target, base_addr_c +
+                               cache->planes[1].marker->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)
+/* Get the next ICB address. */
+static void
+meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
+                       struct sh_mobile_meram_fb_cache *cache,
+                       unsigned long *icb_addr_y, unsigned long *icb_addr_c)
 {
 {
+       struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
        unsigned long icb_offset;
 
        if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
        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
        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))
-               *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24);
+       *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
+       if (cache->nplanes == 2)
+               *icb_addr_c = icb_offset
+                           | (cache->planes[1].marker->index << 24);
 }
 
 #define MERAM_CALC_BYTECOUNT(x, y) \
        (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
 
 }
 
 #define MERAM_CALC_BYTECOUNT(x, y) \
        (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
 
-/*
- * initialize MERAM
- */
-
+/* Initialize MERAM. */
 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 *icb,
-                     int xres, int yres, int *out_pitch)
+                     struct sh_mobile_meram_fb_plane *plane,
+                     unsigned int xres, unsigned int yres,
+                     unsigned int *out_pitch)
 {
 {
+       struct sh_mobile_meram_icb *marker = plane->marker;
        unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
        unsigned long bnm;
        unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
        unsigned long bnm;
-       int lcdc_pitch, xpitch, line_cnt;
-       int save_lines;
+       unsigned int lcdc_pitch;
+       unsigned int xpitch;
+       unsigned int line_cnt;
+       unsigned int save_lines;
 
        /* adjust pitch to 1024, 2048, 4096 or 8192 */
        lcdc_pitch = (xres - 1) | 1023;
 
        /* adjust pitch to 1024, 2048, 4096 or 8192 */
        lcdc_pitch = (xres - 1) | 1023;
@@ -322,13 +379,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
                lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
                line_cnt = total_byte_count >> 11;
                *out_pitch = xres;
                lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
                line_cnt = total_byte_count >> 11;
                *out_pitch = xres;
-               save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE);
+               save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
                save_lines *= MERAM_SEC_LINE;
        } else {
                xpitch = xres;
                line_cnt = yres;
                *out_pitch = lcdc_pitch;
                save_lines *= MERAM_SEC_LINE;
        } else {
                xpitch = xres;
                line_cnt = yres;
                *out_pitch = lcdc_pitch;
-               save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2;
+               save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
                save_lines &= 0xff;
        }
        bnm = (save_lines - 1) << 16;
                save_lines &= 0xff;
        }
        bnm = (save_lines - 1) << 16;
@@ -336,19 +393,20 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
        /* TODO: we better to check if we have enough MERAM buffer size */
 
        /* set up ICB */
        /* TODO: we better to check if we have enough MERAM buffer size */
 
        /* set up ICB */
-       meram_write_icb(priv->base, icb->cache_icb,  MExxBSIZE,
+       meram_write_icb(priv->base, plane->cache->index,  MExxBSIZE,
                        MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
                        MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
-       meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE,
+       meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
                        MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
 
                        MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
 
-       meram_write_icb(priv->base, icb->cache_icb,  MExxMNCF, bnm);
-       meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm);
+       meram_write_icb(priv->base, plane->cache->index,  MExxMNCF, bnm);
+       meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
 
 
-       meram_write_icb(priv->base, icb->cache_icb,  MExxSBSIZE, xpitch);
-       meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch);
+       meram_write_icb(priv->base, plane->cache->index,  MExxSBSIZE, xpitch);
+       meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
 
        /* save a cache unit size */
 
        /* save a cache unit size */
-       icb->cache_unit = xres * save_lines;
+       plane->cache->cache_unit = xres * save_lines;
+       plane->marker->cache_unit = xres * save_lines;
 
        /*
         * Set MERAM for framebuffer
 
        /*
         * Set MERAM for framebuffer
@@ -356,13 +414,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
         * we also chain the cache_icb and the marker_icb.
         * we also split the allocated MERAM buffer between two ICBs.
         */
         * we also chain the cache_icb and the marker_icb.
         * 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) |
-                       MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
+       meram_write_icb(priv->base, plane->cache->index, MExxCTL,
+                       MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
+                       MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
                        MExxCTL_MD_FB);
                        MExxCTL_MD_FB);
-       meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
-                       MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset +
-                                         icb->meram_size / 2) |
+       meram_write_icb(priv->base, plane->marker->index, MExxCTL,
+                       MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
+                                         plane->marker->size / 2) |
                        MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
                        MExxCTL_MD_FB);
 
                        MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
                        MExxCTL_MD_FB);
 
@@ -370,239 +428,175 @@ 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 *icb)
+                        struct sh_mobile_meram_fb_plane *plane)
 {
        /* disable ICB */
 {
        /* disable ICB */
-       meram_write_icb(priv->base, icb->cache_icb,  MExxCTL,
+       meram_write_icb(priv->base, plane->cache->index,  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, plane->marker->index, MExxCTL,
                        MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
                        MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
-       icb->cache_unit = 0;
+
+       plane->cache->cache_unit = 0;
+       plane->marker->cache_unit = 0;
 }
 
 }
 
-/*
- * register the ICB
+/* -----------------------------------------------------------------------------
+ * Registration/unregistration
  */
 
  */
 
-static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
-                                   struct sh_mobile_meram_cfg *cfg,
-                                   int xres, int yres, int pixelformat,
-                                   unsigned long base_addr_y,
-                                   unsigned long base_addr_c,
-                                   unsigned long *icb_addr_y,
-                                   unsigned long *icb_addr_c,
-                                   int *pitch)
+static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
+                                     const struct sh_mobile_meram_cfg *cfg,
+                                     unsigned int xres, unsigned int yres,
+                                     unsigned int pixelformat,
+                                     unsigned int *pitch)
 {
 {
-       struct platform_device *pdev;
-       struct sh_mobile_meram_priv *priv;
-       int n, out_pitch;
-       int error = 0;
-
-       if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
-               return -EINVAL;
+       struct sh_mobile_meram_fb_cache *cache;
+       struct sh_mobile_meram_priv *priv = pdata->priv;
+       struct platform_device *pdev = pdata->pdev;
+       unsigned int out_pitch;
 
        if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
            pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
            pixelformat != SH_MOBILE_MERAM_PF_RGB)
 
        if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
            pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
            pixelformat != SH_MOBILE_MERAM_PF_RGB)
-               return -EINVAL;
-
-       priv = pdata->priv;
-       pdev = pdata->pdev;
+               return ERR_PTR(-EINVAL);
 
 
-       dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)",
-               xres, yres, (!pixelformat) ? "yuv" : "rgb",
-               base_addr_y, base_addr_c);
+       dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
+               !pixelformat ? "yuv" : "rgb");
 
        /* we can't handle wider than 8192px */
        if (xres > 8192) {
                dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
 
        /* we can't handle wider than 8192px */
        if (xres > 8192) {
                dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
-               return -EINVAL;
-       }
-
-       /* do we have at least one ICB config? */
-       if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
-               dev_err(&pdev->dev, "at least one ICB is required.");
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
        }
 
        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 */
-       if (meram_check_overlap(priv, &cfg->icb[0])) {
-               dev_err(&pdev->dev, "conflicting config detected.");
-               error = -EINVAL;
+       /* We now register the ICBs and allocate the MERAM regions. */
+       cache = meram_alloc(priv, cfg, pixelformat);
+       if (IS_ERR(cache)) {
+               dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
+                       PTR_ERR(cache));
                goto err;
        }
                goto err;
        }
-       n = 1;
-
-       /* do the same if we have the second ICB set */
-       if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) {
-               if (meram_check_overlap(priv, &cfg->icb[1])) {
-                       dev_err(&pdev->dev, "conflicting config detected.");
-                       error = -EINVAL;
-                       goto err;
-               }
-               n = 2;
-       }
-
-       if (is_nvcolor(pixelformat) && n != 2) {
-               dev_err(&pdev->dev, "requires two ICB sets for planar Y/C.");
-               error =  -EINVAL;
-               goto err;
-       }
-
-       /* we now register the ICB */
-       cfg->pixelformat = pixelformat;
-       meram_mark(priv, &cfg->icb[0]);
-       if (is_nvcolor(pixelformat))
-               meram_mark(priv, &cfg->icb[1]);
 
        /* initialize MERAM */
 
        /* initialize MERAM */
-       meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch);
+       meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
        *pitch = out_pitch;
        if (pixelformat == SH_MOBILE_MERAM_PF_NV)
        *pitch = out_pitch;
        if (pixelformat == SH_MOBILE_MERAM_PF_NV)
-               meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2,
+               meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
                        &out_pitch);
        else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
                        &out_pitch);
        else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
-               meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2,
+               meram_init(priv, &cache->planes[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_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
-
-       dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
-               *icb_addr_y, *icb_addr_c);
-
 err:
        mutex_unlock(&priv->lock);
 err:
        mutex_unlock(&priv->lock);
-       return error;
+       return cache;
 }
 
 }
 
-static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
-                                     struct sh_mobile_meram_cfg *cfg)
+static void
+sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
 {
 {
-       struct sh_mobile_meram_priv *priv;
-
-       if (!pdata || !pdata->priv || !cfg)
-               return -EINVAL;
-
-       priv = pdata->priv;
+       struct sh_mobile_meram_fb_cache *cache = data;
+       struct sh_mobile_meram_priv *priv = pdata->priv;
 
        mutex_lock(&priv->lock);
 
 
        mutex_lock(&priv->lock);
 
-       /* deinit & unmark */
-       if (is_nvcolor(cfg->pixelformat)) {
-               meram_deinit(priv, &cfg->icb[1]);
-               meram_unmark(priv, &cfg->icb[1]);
-       }
-       meram_deinit(priv, &cfg->icb[0]);
-       meram_unmark(priv, &cfg->icb[0]);
+       /* deinit & free */
+       meram_deinit(priv, &cache->planes[0]);
+       if (cache->nplanes == 2)
+               meram_deinit(priv, &cache->planes[1]);
 
 
-       mutex_unlock(&priv->lock);
+       meram_free(priv, cache);
 
 
-       return 0;
+       mutex_unlock(&priv->lock);
 }
 
 }
 
-static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
-                                 struct sh_mobile_meram_cfg *cfg,
-                                 unsigned long base_addr_y,
-                                 unsigned long base_addr_c,
-                                 unsigned long *icb_addr_y,
-                                 unsigned long *icb_addr_c)
+static void
+sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
+                      unsigned long base_addr_y, unsigned long base_addr_c,
+                      unsigned long *icb_addr_y, unsigned long *icb_addr_c)
 {
 {
-       struct sh_mobile_meram_priv *priv;
-
-       if (!pdata || !pdata->priv || !cfg)
-               return -EINVAL;
-
-       priv = pdata->priv;
+       struct sh_mobile_meram_fb_cache *cache = data;
+       struct sh_mobile_meram_priv *priv = pdata->priv;
 
        mutex_lock(&priv->lock);
 
 
        mutex_lock(&priv->lock);
 
-       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_set_next_addr(priv, cache, base_addr_y, base_addr_c);
+       meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
 
        mutex_unlock(&priv->lock);
 
        mutex_unlock(&priv->lock);
-
-       return 0;
 }
 
 }
 
-static int sh_mobile_meram_runtime_suspend(struct device *dev)
+static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
+       .module                 = THIS_MODULE,
+       .meram_register         = sh_mobile_meram_register,
+       .meram_unregister       = sh_mobile_meram_unregister,
+       .meram_update           = sh_mobile_meram_update,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+static int sh_mobile_meram_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
-       int k, j;
+       unsigned int i, j;
 
 
-       for (k = 0; k < CMN_REGS_SIZE; k++)
-               priv->cmn_saved_regs[k] = meram_read_reg(priv->base,
-                       common_regs[k]);
+       for (i = 0; i < MERAM_REGS_SIZE; i++)
+               priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
 
 
-       for (j = 0; j < 32; j++) {
-               if (!test_bit(j, &priv->used_icb))
+       for (i = 0; i < 32; i++) {
+               if (!test_bit(i, &priv->used_icb))
                        continue;
                        continue;
-               for (k = 0; k < ICB_REGS_SIZE; k++) {
-                       priv->icb_saved_regs[j * ICB_REGS_SIZE + k] =
-                               meram_read_icb(priv->base, j, icb_regs[k]);
+               for (j = 0; j < ICB_REGS_SIZE; j++) {
+                       priv->icbs[i].regs[j] =
+                               meram_read_icb(priv->base, i, icb_regs[j]);
                        /* Reset ICB on resume */
                        /* Reset ICB on resume */
-                       if (icb_regs[k] == MExxCTL)
-                               priv->icb_saved_regs[j * ICB_REGS_SIZE + k] |=
+                       if (icb_regs[j] == MExxCTL)
+                               priv->icbs[i].regs[j] |=
                                        MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
                }
        }
        return 0;
 }
 
                                        MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
                }
        }
        return 0;
 }
 
-static int sh_mobile_meram_runtime_resume(struct device *dev)
+static int sh_mobile_meram_resume(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
-       int k, j;
+       unsigned int i, j;
 
 
-       for (j = 0; j < 32; j++) {
-               if (!test_bit(j, &priv->used_icb))
+       for (i = 0; i < 32; i++) {
+               if (!test_bit(i, &priv->used_icb))
                        continue;
                        continue;
-               for (k = 0; k < ICB_REGS_SIZE; k++) {
-                       meram_write_icb(priv->base, j, icb_regs[k],
-                       priv->icb_saved_regs[j * ICB_REGS_SIZE + k]);
-               }
+               for (j = 0; j < ICB_REGS_SIZE; j++)
+                       meram_write_icb(priv->base, i, icb_regs[j],
+                                       priv->icbs[i].regs[j]);
        }
 
        }
 
-       for (k = 0; k < CMN_REGS_SIZE; k++)
-               meram_write_reg(priv->base, common_regs[k],
-                       priv->cmn_saved_regs[k]);
+       for (i = 0; i < MERAM_REGS_SIZE; i++)
+               meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
        return 0;
 }
 
        return 0;
 }
 
-static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = {
-       .runtime_suspend = sh_mobile_meram_runtime_suspend,
-       .runtime_resume = sh_mobile_meram_runtime_resume,
-};
-
-static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
-       .module                 = THIS_MODULE,
-       .meram_register         = sh_mobile_meram_register,
-       .meram_unregister       = sh_mobile_meram_unregister,
-       .meram_update           = sh_mobile_meram_update,
-};
+static UNIVERSAL_DEV_PM_OPS(sh_mobile_meram_dev_pm_ops,
+                           sh_mobile_meram_suspend,
+                           sh_mobile_meram_resume, NULL);
 
 
-/*
- * initialize MERAM
+/* -----------------------------------------------------------------------------
+ * Probe/remove and driver init/exit
  */
 
  */
 
-static int sh_mobile_meram_remove(struct platform_device *pdev);
-
 static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
 {
        struct sh_mobile_meram_priv *priv;
        struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
 static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
 {
        struct sh_mobile_meram_priv *priv;
        struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
-       struct resource *res;
+       struct resource *regs;
+       struct resource *meram;
+       unsigned int i;
        int error;
 
        if (!pdata) {
        int error;
 
        if (!pdata) {
@@ -610,8 +604,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
                return -EINVAL;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (regs == NULL || meram == NULL) {
                dev_err(&pdev->dev, "cannot get platform resources\n");
                return -ENOENT;
        }
                dev_err(&pdev->dev, "cannot get platform resources\n");
                return -ENOENT;
        }
@@ -622,32 +617,74 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
                return -ENOMEM;
        }
 
-       platform_set_drvdata(pdev, priv);
-
-       /* initialize private data */
+       /* Initialize private data. */
        mutex_init(&priv->lock);
        mutex_init(&priv->lock);
-       priv->base = ioremap_nocache(res->start, resource_size(res));
+       priv->used_icb = pdata->reserved_icbs;
+
+       for (i = 0; i < MERAM_ICB_NUM; ++i)
+               priv->icbs[i].index = i;
+
+       pdata->ops = &sh_mobile_meram_ops;
+       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;
+               goto err_req_regs;
+       }
+
+       if (!request_mem_region(meram->start, resource_size(meram),
+                               pdev->name)) {
+               dev_err(&pdev->dev, "MERAM memory region already claimed\n");
+               error = -EBUSY;
+               goto err_req_meram;
+       }
+
+       priv->base = ioremap_nocache(regs->start, resource_size(regs));
        if (!priv->base) {
                dev_err(&pdev->dev, "ioremap failed\n");
                error = -EFAULT;
        if (!priv->base) {
                dev_err(&pdev->dev, "ioremap failed\n");
                error = -EFAULT;
-               goto err;
+               goto err_ioremap;
        }
        }
-       pdata->ops = &sh_mobile_meram_ops;
-       pdata->priv = priv;
-       pdata->pdev = pdev;
+
+       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);
 
 
        /* initialize ICB addressing mode */
        if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
                meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
 
+       platform_set_drvdata(pdev, priv);
        pm_runtime_enable(&pdev->dev);
 
        dev_info(&pdev->dev, "sh_mobile_meram initialized.");
 
        return 0;
 
        pm_runtime_enable(&pdev->dev);
 
        dev_info(&pdev->dev, "sh_mobile_meram initialized.");
 
        return 0;
 
-err:
-       sh_mobile_meram_remove(pdev);
+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:
+       release_mem_region(regs->start, resource_size(regs));
+err_req_regs:
+       mutex_destroy(&priv->lock);
+       kfree(priv);
 
        return error;
 }
 
        return error;
 }
@@ -656,11 +693,16 @@ err:
 static int sh_mobile_meram_remove(struct platform_device *pdev)
 {
        struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
 static int sh_mobile_meram_remove(struct platform_device *pdev)
 {
        struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
+       struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 
        pm_runtime_disable(&pdev->dev);
 
 
        pm_runtime_disable(&pdev->dev);
 
-       if (priv->base)
-               iounmap(priv->base);
+       gen_pool_destroy(priv->pool);
+
+       iounmap(priv->base);
+       release_mem_region(meram->start, resource_size(meram));
+       release_mem_region(regs->start, resource_size(regs));
 
        mutex_destroy(&priv->lock);
 
 
        mutex_destroy(&priv->lock);