diff mbox

[09/72] gpu: ipu-v3: Add support for dynamic DC interface pixel maps

Message ID 1414796095-10107-10-git-send-email-steve_longerbeam@mentor.com (mailing list archive)
State New, archived
Headers show

Commit Message

Steve Longerbeam Oct. 31, 2014, 10:53 p.m. UTC
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@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 mbox

Patch

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);