fbdev: sh_mobile_meram: Use genalloc to manage MERAM allocation
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Mon, 19 Sep 2011 09:40:31 +0000 (11:40 +0200)
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Mon, 12 Mar 2012 21:41:10 +0000 (22:41 +0100)
Instead of requiring the users to hardcode MERAM allocation in platform
data, allocate blocks at runtime using genalloc.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

arch/arm/mach-shmobile/board-ap4evb.c
arch/arm/mach-shmobile/board-mackerel.c
drivers/video/Kconfig
drivers/video/sh_mobile_meram.c
include/video/sh_mobile_meram.h

index 63498fb..7b90227 100644 (file)
@@ -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,
        },
 };
index 17ba6bc..0c0fd4f 100644 (file)
@@ -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,
        },
 };
index 8951cbd..a435942 100644 (file)
@@ -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.
index 30a3305..92dc9bd 100644 (file)
@@ -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,
-                        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);
 }
@@ -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));
index 05ca3f9..f7700fc 100644 (file)
@@ -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 */
 };