Adds support to ipu-dc to dynamically create new display interface
pixel mappings.

The mappings are formally defined by a struct ipu_dc_if_map, which is
passed to ipu_dc_init_sync().

The ipu-dc maintains a list of the currently programmed mappings.
Some mappings are pre-loaded at probe time (RGB24, BGR24, GBR24,
RGB565, BGR666, and "LVDS666").

If the map pointer to ipu_dc_init_sync() is not NULL, a new mapping
is created, otherwise previously loaded mappings will be searched based
on the passed pixel_fmt.

Signed-off-by: Steve Longerbeam <steve_longerbeam at mentor.com>
---
 drivers/gpu/ipu-v3/ipu-dc.c          |  248 ++++++++++++++++++++++------------
 drivers/staging/imx-drm/ipuv3-crtc.c |    2 +-
 include/video/imx-ipu-v3.h           |   23 +++-
 3 files changed, 184 insertions(+), 89 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
index 2326c75..36c7c57 100644
--- a/drivers/gpu/ipu-v3/ipu-dc.c
+++ b/drivers/gpu/ipu-v3/ipu-dc.c
@@ -26,6 +26,7 @@

 #define DC_MAP_CONF_PTR(n)     (0x108 + ((n) & ~0x1) * 2)
 #define DC_MAP_CONF_VAL(n)     (0x144 + ((n) & ~0x1) * 2)
+#define DC_MAX_MAPS            24

 #define DC_EVT_NF              0
 #define DC_EVT_NL              1
@@ -86,13 +87,40 @@

 struct ipu_dc_priv;

-enum ipu_dc_map {
-       IPU_DC_MAP_RGB24,
-       IPU_DC_MAP_RGB565,
-       IPU_DC_MAP_GBR24, /* TVEv2 */
-       IPU_DC_MAP_BGR666,
-       IPU_DC_MAP_LVDS666,
-       IPU_DC_MAP_BGR24,
+/* some pre-defined maps */
+static struct ipu_dc_if_map predef_maps[] = {
+       {
+               .src_mask = {0xff, 0xff, 0xff},
+               .dest_msb = {7, 15, 23},
+               .v4l2_fmt = V4L2_PIX_FMT_RGB24,
+       }, {
+               .src_mask = {0xf8, 0xfc, 0xf8},
+               .dest_msb = {4, 10, 15},
+               .v4l2_fmt = V4L2_PIX_FMT_RGB565,
+       }, {
+               /* V4L2_PIX_FMT_GBR24, for TVEv2 */
+               .src_mask = {0xff, 0xff, 0xff},
+               .dest_msb = {23, 7, 15},
+               .v4l2_fmt = v4l2_fourcc('G', 'B', 'R', '3'),
+       }, {
+               .src_mask = {0xfc, 0xfc, 0xfc},
+               .dest_msb = {17, 11, 5},
+               .v4l2_fmt = V4L2_PIX_FMT_BGR666,
+       }, {
+               .src_mask = {0xfc, 0xfc, 0xfc},
+               .dest_msb = {21, 13, 5},
+               .v4l2_fmt = v4l2_fourcc('L', 'V', 'D', '6'),
+       }, {
+               .src_mask = {0xff, 0xff, 0xff},
+               .dest_msb = {23, 15, 7},
+               .v4l2_fmt = V4L2_PIX_FMT_BGR24,
+       },
+};
+
+struct dc_if_map_priv {
+       struct ipu_dc_if_map map;
+       int mapnr;
+       struct list_head list;
 };

 struct ipu_dc {
@@ -110,12 +138,20 @@ struct ipu_dc_priv {
        struct ipu_soc          *ipu;
        struct device           *dev;
        struct ipu_dc           channels[IPU_DC_NUM_CHANNELS];
+       struct list_head        map_list;
+       int                     next_map;
        struct mutex            mutex;
        struct completion       comp;
        int                     dc_irq;
        int                     dp_irq;
 };

+/* forward references */
+static void ipu_dc_map_config(struct ipu_dc_priv *priv,
+                             struct dc_if_map_priv *map);
+static void ipu_dc_map_clear(struct ipu_dc_priv *priv,
+                            struct dc_if_map_priv *map);
+
 static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority)
 {
        u32 reg;
@@ -146,39 +182,91 @@ static void dc_write_tmpl(struct ipu_dc *dc, int word, 
u32 opcode, u32 operand,
        writel(reg2, priv->dc_tmpl_reg + word * 8 + 4);
 }

-static int ipu_pixfmt_to_map(u32 fmt)
+static inline bool identical_mapping(struct ipu_dc_if_map *map1,
+                                    struct ipu_dc_if_map *map2)
 {
-       switch (fmt) {
-       case V4L2_PIX_FMT_RGB24:
-               return IPU_DC_MAP_RGB24;
-       case V4L2_PIX_FMT_RGB565:
-               return IPU_DC_MAP_RGB565;
-       case IPU_PIX_FMT_GBR24:
-               return IPU_DC_MAP_GBR24;
-       case V4L2_PIX_FMT_BGR666:
-               return IPU_DC_MAP_BGR666;
-       case v4l2_fourcc('L', 'V', 'D', '6'):
-               return IPU_DC_MAP_LVDS666;
-       case V4L2_PIX_FMT_BGR24:
-               return IPU_DC_MAP_BGR24;
-       default:
-               return -EINVAL;
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               if (map1->src_mask[i] != map2->src_mask[i] ||
+                   map1->dest_msb[i] != map2->dest_msb[i])
+                       return false;
+       }
+
+       return true;
+}
+
+/* priv->mutex held when calling */
+static struct dc_if_map_priv *ipu_dc_new_map(struct ipu_dc_priv *priv,
+                                            struct ipu_dc_if_map *new_map)
+{
+       struct dc_if_map_priv *entry;
+
+       /* first search for an existing map that matches */
+       list_for_each_entry(entry, &priv->map_list, list) {
+               if (identical_mapping(&entry->map, new_map))
+                       return entry;
+       }
+
+       if (priv->next_map >= DC_MAX_MAPS) {
+               dev_err(priv->dev, "IPU_DISP: No map space left\n");
+               return ERR_PTR(-ENOSPC);
+       }
+
+       entry = devm_kzalloc(priv->dev, sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return ERR_PTR(-ENOMEM);
+
+       /* Copy new map */
+       entry->map = *new_map;
+       entry->mapnr = priv->next_map++;
+       list_add_tail(&entry->list, &priv->map_list);
+
+       ipu_dc_map_clear(priv, entry);
+       ipu_dc_map_config(priv, entry);
+
+       return entry;
+}
+
+static struct dc_if_map_priv *ipu_dc_get_map(struct ipu_dc_priv *priv,
+                                            struct ipu_dc_if_map *new_map,
+                                            u32 v4l2_fmt)
+{
+       struct dc_if_map_priv *entry = ERR_PTR(-EINVAL);
+
+       mutex_lock(&priv->mutex);
+
+       if (new_map) {
+               /* create a new map */
+               entry = ipu_dc_new_map(priv, new_map);
+       } else if (v4l2_fmt) {
+               /* otherwise search for an existing map */
+               list_for_each_entry(entry, &priv->map_list, list) {
+                       if (entry->map.v4l2_fmt == v4l2_fmt)
+                               goto unlock;
+               }
+
+               entry = ERR_PTR(-EINVAL);
        }
+
+unlock:
+       mutex_unlock(&priv->mutex);
+       return entry;
 }

 int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
-               u32 pixel_fmt, u32 width)
+                    u32 pixel_fmt, struct ipu_dc_if_map *new_map, u32 width)
 {
        struct ipu_dc_priv *priv = dc->priv;
+       struct dc_if_map_priv *map;
        u32 reg = 0;
-       int map;

        dc->di = ipu_di_get_num(di);

-       map = ipu_pixfmt_to_map(pixel_fmt);
-       if (map < 0) {
+       map = ipu_dc_get_map(priv, new_map, pixel_fmt);
+       if (IS_ERR(map)) {
                dev_dbg(priv->dev, "IPU_DISP: No MAP\n");
-               return map;
+               return PTR_ERR(map);
        }

        if (interlaced) {
@@ -187,26 +275,35 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di 
*di, bool interlaced,
                dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);

                /* Init template microcode */
-               dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
+               dc_write_tmpl(dc, 0, WROD(0), 0, map->mapnr,
+                             SYNC_WAVE, 0, 8, 1);
        } else {
                if (dc->di) {
                        dc_link_event(dc, DC_EVT_NL, 2, 3);
                        dc_link_event(dc, DC_EVT_EOL, 3, 2);
                        dc_link_event(dc, DC_EVT_NEW_DATA, 1, 1);
                        /* Init template microcode */
-                       dc_write_tmpl(dc, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 
1);
-                       dc_write_tmpl(dc, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5, 
0);
-                       dc_write_tmpl(dc, 4, WRG, 0, map, NULL_WAVE, 0, 0, 1);
-                       dc_write_tmpl(dc, 1, WROD(0), 0, map, SYNC_WAVE, 0, 5, 
1);
+                       dc_write_tmpl(dc, 2, WROD(0), 0, map->mapnr,
+                                     SYNC_WAVE, 8, 5, 1);
+                       dc_write_tmpl(dc, 3, WROD(0), 0, map->mapnr,
+                                     SYNC_WAVE, 4, 5, 0);
+                       dc_write_tmpl(dc, 4, WRG, 0, map->mapnr,
+                                     NULL_WAVE, 0, 0, 1);
+                       dc_write_tmpl(dc, 1, WROD(0), 0, map->mapnr,
+                                     SYNC_WAVE, 0, 5, 1);
                } else {
                        dc_link_event(dc, DC_EVT_NL, 5, 3);
                        dc_link_event(dc, DC_EVT_EOL, 6, 2);
                        dc_link_event(dc, DC_EVT_NEW_DATA, 8, 1);
                        /* Init template microcode */
-                       dc_write_tmpl(dc, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 
1);
-                       dc_write_tmpl(dc, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5, 
0);
-                       dc_write_tmpl(dc, 7, WRG, 0, map, NULL_WAVE, 0, 0, 1);
-                       dc_write_tmpl(dc, 8, WROD(0), 0, map, SYNC_WAVE, 0, 5, 
1);
+                       dc_write_tmpl(dc, 5, WROD(0), 0, map->mapnr,
+                                     SYNC_WAVE, 8, 5, 1);
+                       dc_write_tmpl(dc, 6, WROD(0), 0, map->mapnr,
+                                     SYNC_WAVE, 4, 5, 0);
+                       dc_write_tmpl(dc, 7, WRG, 0, map->mapnr,
+                                     NULL_WAVE, 0, 0, 1);
+                       dc_write_tmpl(dc, 8, WROD(0), 0, map->mapnr,
+                                     SYNC_WAVE, 0, 5, 1);
                }
        }
        dc_link_event(dc, DC_EVT_NF, 0, 0);
@@ -298,29 +395,35 @@ void ipu_dc_disable(struct ipu_soc *ipu)
 }
 EXPORT_SYMBOL_GPL(ipu_dc_disable);

-static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map,
-               int byte_num, int offset, int mask)
+static void ipu_dc_map_config(struct ipu_dc_priv *priv,
+                             struct dc_if_map_priv *map)
 {
-       int ptr = map * 3 + byte_num;
+       int i, ptr;
        u32 reg;

-       reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr));
-       reg &= ~(0xffff << (16 * (ptr & 0x1)));
-       reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
-       writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+       for (i = 0; i < 3; i++) {
+               ptr = map->mapnr * 3 + i;

-       reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
-       reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
-       reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
-       writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map));
+               reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+               reg &= ~(0xffff << (16 * (ptr & 0x1)));
+               reg |= (((map->map.dest_msb[i] << 8) |
+                        map->map.src_mask[i]) << (16 * (ptr & 0x1)));
+               writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+
+               reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map->mapnr));
+               reg &= ~(0x1f << ((16 * (map->mapnr & 0x1)) + (5 * i)));
+               reg |= (ptr << ((16 * (map->mapnr & 0x1)) + (5 * i)));
+               writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map->mapnr));
+       }
 }

-static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map)
+static void ipu_dc_map_clear(struct ipu_dc_priv *priv,
+                            struct dc_if_map_priv *map)
 {
-       u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
+       u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map->mapnr));

-       writel(reg & ~(0xffff << (16 * (map & 0x1))),
-                    priv->dc_reg + DC_MAP_CONF_PTR(map));
+       writel(reg & ~(0xffff << (16 * (map->mapnr & 0x1))),
+                    priv->dc_reg + DC_MAP_CONF_PTR(map->mapnr));
 }

 struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel)
@@ -416,41 +519,12 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
        dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n",
                        base, template_base);

-       /* rgb24 */
-       ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24);
-       ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */
-       ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */
-       ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */
-
-       /* rgb565 */
-       ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565);
-       ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */
-       ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */
-       ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */
-
-       /* gbr24 */
-       ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24);
-       ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */
-       ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */
-       ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */
-
-       /* bgr666 */
-       ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666);
-       ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */
-       ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */
-       ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */
-
-       /* lvds666 */
-       ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666);
-       ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */
-       ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */
-       ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */
-
-       /* bgr24 */
-       ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24);
-       ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */
-       ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */
-       ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */
+       /* add the pre-defined maps */
+       mutex_lock(&priv->mutex);
+       INIT_LIST_HEAD(&priv->map_list);
+       for (i = 0; i < ARRAY_SIZE(predef_maps); i++)
+               ipu_dc_new_map(priv, &predef_maps[i]);
+       mutex_unlock(&priv->mutex);

        return 0;
 }
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c 
b/drivers/staging/imx-drm/ipuv3-crtc.c
index 11e84a2..25c0231 100644
--- a/drivers/staging/imx-drm/ipuv3-crtc.c
+++ b/drivers/staging/imx-drm/ipuv3-crtc.c
@@ -186,7 +186,7 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc,
        sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin;

        ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced,
-                       out_pixel_fmt, mode->hdisplay);
+                       out_pixel_fmt, NULL, mode->hdisplay);
        if (ret) {
                dev_err(ipu_crtc->dev,
                                "initializing display controller failed with 
%d\n",
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 6fa86c7..032948b5 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -218,12 +218,33 @@ void ipu_cpmem_dump(struct ipuv3_channel *ch);
 /*
  * IPU Display Controller (dc) functions
  */
+
+/*
+ * This structure defines how to map each color component of the incoming
+ * RGB24 or YUV444 pixels arriving at the DC onto the DI bus:
+ *
+ * src_mask[] defines which bits of each incoming 8-bit RGB24/YUV444
+ *            component are to be selected and forwarded to the DI bus
+ *            (as a bit mask).
+ *
+ * dest_msb[] defines where to place the selected bits of each component
+ *            on the DI bus (as the most-significant-bit position).
+ *
+ * v4l2_fmt   non-zero if this mapping corresponds to a standard
+ *            V4L2 pixel format.
+ */
+struct ipu_dc_if_map {
+       u32 src_mask[3];
+       u32 dest_msb[3];
+       u32 v4l2_fmt;
+};
+
 struct ipu_dc;
 struct ipu_di;
 struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel);
 void ipu_dc_put(struct ipu_dc *dc);
 int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
-               u32 pixel_fmt, u32 width);
+                    u32 pixel_fmt, struct ipu_dc_if_map *new_map, u32 width);
 void ipu_dc_enable(struct ipu_soc *ipu);
 void ipu_dc_enable_channel(struct ipu_dc *dc);
 void ipu_dc_disable_channel(struct ipu_dc *dc);
-- 
1.7.9.5

Reply via email to