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@xxxxxxxxxx> --- 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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel