@@ -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;
}
@@ -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",
@@ -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);
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(-)