]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
Bug fix to ensure only video formats enabled in IP are in driver
authorJeffrey Mouroux <jeff.mouroux@xilinx.com>
Mon, 28 Aug 2017 21:56:54 +0000 (14:56 -0700)
committerMichal Simek <michal.simek@xilinx.com>
Tue, 29 Aug 2017 06:15:00 +0000 (08:15 +0200)
The driver used to assume that all IP supported video formats were
legal choices for configuration.  However, the IP can be configured
to support all or only some (or only one) of the many possible formats.
This patch adds the needed mechanism via device-tree to communicate to
the driver which video formats are actually supported in the IP.
Additional changes are required to ensure that DMA client requests
for video formats that are NOT supported by the device instance are
rejected.

Signed-off-by: Jeffrey Mouroux <jmouroux@xilinx.com>
Reviewed-by: Hyun Kwon <hyun.kwon@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/dma/xilinx/xilinx_frmbuf.c

index 79171243da5eb0c28ecdb77dc18fb0a75f2b4708..e1f13dad38ee88b16bd2af04dcb6f35231a0ac0d 100644 (file)
 #define XILINX_FRMBUF_FMT_YUV8                 21
 #define XILINX_FRMBUF_FMT_Y8                   24
 
-/**
- * struct xilinx_dma_config - dma channel video format config
- * @fourcc: DRM or V4L2 fourcc code for video memory format
- * @type: Indicates type of fourcc code (DRM or V4L2)
- */
-struct xilinx_xdma_config {
-       u32 fourcc;
-       enum vid_frmwork_type type;
-};
-
 /**
  * struct xilinx_frmbuf_desc_hw - Hardware Descriptor
  * @luma_plane_addr: Luma or packed plane buffer address
@@ -137,7 +127,6 @@ struct xilinx_frmbuf_tx_descriptor {
  * @idle: Channel idle state
  * @tasklet: Cleanup work after irq
  * @vid_fmt: Reference to currently assigned video format description
- * @chan_config: Video configuration set by DMA client
  */
 struct xilinx_frmbuf_chan {
        struct xilinx_frmbuf_device *xdev;
@@ -157,7 +146,6 @@ struct xilinx_frmbuf_chan {
        bool idle;
        struct tasklet_struct tasklet;
        const struct xilinx_frmbuf_format_desc *vid_fmt;
-       struct xilinx_xdma_config chan_config;
 };
 
 /**
@@ -167,6 +155,7 @@ struct xilinx_frmbuf_chan {
  * @common: DMA device structure
  * @chan: Driver specific dma channel
  * @rst_gpio: GPIO reset
+ * @enabled_vid_fmts: Bitmask of video formats enabled in hardware
  */
 struct xilinx_frmbuf_device {
        void __iomem *regs;
@@ -174,22 +163,28 @@ struct xilinx_frmbuf_device {
        struct dma_device common;
        struct xilinx_frmbuf_chan chan;
        struct gpio_desc *rst_gpio;
+       u32 enabled_vid_fmts;
 };
 
 /**
  * struct xilinx_frmbuf_format_desc - lookup table to match fourcc to format
+ * @dts_name: Device tree name for this entry.
  * @id: Format ID
  * @bpp: Bytes per pixel
  * @num_planes: Expected number of plane buffers in framebuffer for this format
  * @drm_fmt: DRM video framework equivalent fourcc code
  * @v4l2_fmt: Video 4 Linux framework equivalent fourcc code
+ * @fmt_bitmask: Flag identifying this format in device-specific "enabled"
+ *     bitmap
  */
 struct xilinx_frmbuf_format_desc {
+       const char *dts_name;
        u32 id;
        u32 bpp;
        u32 num_planes;
        u32 drm_fmt;
        u32 v4l2_fmt;
+       u32 fmt_bitmask;
 };
 
 static LIST_HEAD(frmbuf_chan_list);
@@ -197,91 +192,79 @@ static DEFINE_MUTEX(frmbuf_chan_list_lock);
 
 static const struct xilinx_frmbuf_format_desc xilinx_frmbuf_formats[] = {
        {
+               .dts_name = "xbgr8888",
                .id = XILINX_FRMBUF_FMT_RGBX8,
                .bpp = 4,
                .num_planes = 1,
                .drm_fmt = DRM_FORMAT_XBGR8888,
-               .v4l2_fmt = 0
+               .v4l2_fmt = 0,
+               .fmt_bitmask = BIT(0),
        },
        {
+               .dts_name = "unsupported",
                .id = XILINX_FRMBUF_FMT_YUVX8,
                .bpp = 4,
                .num_planes = 1,
                .drm_fmt = 0,
-               .v4l2_fmt = 0
+               .v4l2_fmt = 0,
+               .fmt_bitmask = BIT(1),
        },
        {
+               .dts_name = "yuyv",
                .id = XILINX_FRMBUF_FMT_YUYV8,
                .bpp = 2,
                .num_planes = 1,
                .drm_fmt = DRM_FORMAT_YUYV,
-               .v4l2_fmt = V4L2_PIX_FMT_YUYV
+               .v4l2_fmt = V4L2_PIX_FMT_YUYV,
+               .fmt_bitmask = BIT(2),
        },
        {
+               .dts_name = "nv16",
                .id = XILINX_FRMBUF_FMT_Y_UV8,
                .bpp = 1,
                .num_planes = 2,
                .drm_fmt = DRM_FORMAT_NV16,
-               .v4l2_fmt = V4L2_PIX_FMT_NV16
+               .v4l2_fmt = V4L2_PIX_FMT_NV16,
+               .fmt_bitmask = BIT(3),
        },
        {
+               .dts_name = "nv12",
                .id = XILINX_FRMBUF_FMT_Y_UV8_420,
                .bpp = 1,
                .num_planes = 2,
                .drm_fmt = DRM_FORMAT_NV12,
-               .v4l2_fmt = V4L2_PIX_FMT_NV12
+               .v4l2_fmt = V4L2_PIX_FMT_NV12,
+               .fmt_bitmask = BIT(4),
        },
        {
+               .dts_name = "bgr888",
                .id = XILINX_FRMBUF_FMT_RGB8,
                .bpp = 3,
                .num_planes = 1,
                .drm_fmt = DRM_FORMAT_BGR888,
-               .v4l2_fmt = V4L2_PIX_FMT_RGB24
+               .v4l2_fmt = V4L2_PIX_FMT_RGB24,
+               .fmt_bitmask = BIT(5),
        },
        {
+               .dts_name = "unsupported",
                .id = XILINX_FRMBUF_FMT_YUV8,
                .bpp = 3,
                .num_planes = 1,
                .drm_fmt = 0,
-               .v4l2_fmt = 0
+               .v4l2_fmt = 0,
+               .fmt_bitmask = BIT(6),
        },
        {
+               .dts_name = "y8",
                .id = XILINX_FRMBUF_FMT_Y8,
                .bpp = 1,
                .num_planes = 1,
                .drm_fmt = 0,
-               .v4l2_fmt = V4L2_PIX_FMT_GREY
+               .v4l2_fmt = V4L2_PIX_FMT_GREY,
+               .fmt_bitmask = BIT(7),
        },
 };
 
-static void xilinx_frmbuf_set_vid_fmt(struct xilinx_frmbuf_chan *chan)
-{
-       struct xilinx_xdma_config *config;
-       u32 i;
-       u32 fourcc;
-       enum vid_frmwork_type type;
-       struct device *dev = chan->xdev->dev;
-
-       config = chan->common.private;
-       type = config->type;
-       fourcc = config->fourcc;
-
-       for (i = 0; i < ARRAY_SIZE(xilinx_frmbuf_formats); i++) {
-               if (type == XDMA_DRM &&
-                   fourcc == xilinx_frmbuf_formats[i].drm_fmt) {
-                       chan->vid_fmt = &xilinx_frmbuf_formats[i];
-                       return;
-               } else if (type == XDMA_V4L2 &&
-                       fourcc == xilinx_frmbuf_formats[i].v4l2_fmt) {
-                       chan->vid_fmt = &xilinx_frmbuf_formats[i];
-                       return;
-               }
-       }
-
-       dev_err(dev, "No matching video format for fourcc code = %u\n",
-               config->fourcc);
-}
-
 static const struct of_device_id xilinx_frmbuf_of_ids[] = {
        { .compatible = "xlnx,axi-frmbuf-wr-v2",
                .data = (void *)DMA_DEV_TO_MEM},
@@ -338,19 +321,56 @@ static inline void frmbuf_set(struct xilinx_frmbuf_chan *chan, u32 reg,
        frmbuf_write(chan, reg, frmbuf_read(chan, reg) | set);
 }
 
+static int frmbuf_verify_format(struct dma_chan *chan, u32 fourcc, u32 type)
+{
+       struct xilinx_frmbuf_chan *xil_chan = to_xilinx_chan(chan);
+       u32 i, sz = ARRAY_SIZE(xilinx_frmbuf_formats);
+
+       for (i = 0; i < sz; i++) {
+               if ((type == XDMA_DRM &&
+                    fourcc != xilinx_frmbuf_formats[i].drm_fmt) ||
+                  (type == XDMA_V4L2 &&
+                   fourcc != xilinx_frmbuf_formats[i].v4l2_fmt))
+                       continue;
+
+               if (!(xilinx_frmbuf_formats[i].fmt_bitmask &
+                     xil_chan->xdev->enabled_vid_fmts))
+                       return -EINVAL;
+
+               xil_chan->vid_fmt = &xilinx_frmbuf_formats[i];
+               return 0;
+       }
+       return -EINVAL;
+}
+
 static void xilinx_xdma_set_config(struct dma_chan *chan, u32 fourcc, u32 type)
 {
        struct xilinx_frmbuf_chan *xil_chan;
+       bool found_xchan = false;
+       int ret;
 
        mutex_lock(&frmbuf_chan_list_lock);
        list_for_each_entry(xil_chan, &frmbuf_chan_list, chan_node) {
                if (chan == &xil_chan->common) {
-                       xil_chan->chan_config.fourcc = fourcc;
-                       xil_chan->chan_config.type = type;
-                       xilinx_frmbuf_set_vid_fmt(xil_chan);
+                       found_xchan = true;
+                       break;
                }
        }
        mutex_unlock(&frmbuf_chan_list_lock);
+
+       if (!found_xchan) {
+               dev_dbg(chan->device->dev,
+                       "dma chan not a Video Framebuffer channel instance\n");
+               return;
+       }
+
+       ret = frmbuf_verify_format(chan, fourcc, type);
+       if (ret == -EINVAL) {
+               dev_err(chan->device->dev,
+                       "Framebuffer not configured for fourcc 0x%x\n",
+                       fourcc);
+               return;
+       }
 }
 
 void xilinx_xdma_drm_config(struct dma_chan *chan, u32 drm_fourcc)
@@ -714,7 +734,7 @@ xilinx_frmbuf_dma_prep_interleaved(struct dma_chan *dchan,
        struct xilinx_frmbuf_tx_descriptor *desc;
        struct xilinx_frmbuf_desc_hw *hw;
 
-       if (chan->direction != xt->dir || !chan->vid_fmt->bpp)
+       if (chan->direction != xt->dir || !chan->vid_fmt)
                goto error;
 
        if (!xt->numf || !xt->sgl[0].size)
@@ -831,7 +851,6 @@ static int xilinx_frmbuf_chan_probe(struct xilinx_frmbuf_device *xdev,
        chan->dev = xdev->dev;
        chan->xdev = xdev;
        chan->idle = true;
-       chan->common.private = &chan->chan_config;
 
        err = of_property_read_u32(node, "xlnx,dma-addr-width",
                                   &dma_addr_size);
@@ -895,6 +914,9 @@ static int xilinx_frmbuf_probe(struct platform_device *pdev)
        enum dma_transfer_direction dma_dir;
        const struct of_device_id *match;
        int err;
+       u32 i, j;
+       int hw_vid_fmt_cnt;
+       const char *vid_fmts[ARRAY_SIZE(xilinx_frmbuf_formats)];
 
        xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
        if (!xdev)
@@ -953,6 +975,32 @@ static int xilinx_frmbuf_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
+       /* read supported video formats and update internal table */
+       hw_vid_fmt_cnt = of_property_count_strings(node, "xlnx,vid-formats");
+
+       err = of_property_read_string_array(node, "xlnx,vid-formats",
+                                           vid_fmts, hw_vid_fmt_cnt);
+       if (err < 0) {
+               dev_err(&pdev->dev,
+                       "Missing or invalid xlnx,vid-formats dts prop\n");
+               return err;
+       }
+
+       for (i = 0; i < hw_vid_fmt_cnt; i++) {
+               const char *vid_fmt_name = vid_fmts[i];
+
+               for (j = 0; j < ARRAY_SIZE(xilinx_frmbuf_formats); j++) {
+                       const char *dts_name =
+                               xilinx_frmbuf_formats[j].dts_name;
+
+                       if (strcmp(vid_fmt_name, dts_name))
+                               continue;
+
+                       xdev->enabled_vid_fmts |=
+                               xilinx_frmbuf_formats[j].fmt_bitmask;
+               }
+       }
+
        xdev->common.device_alloc_chan_resources =
                                xilinx_frmbuf_alloc_chan_resources;
        xdev->common.device_free_chan_resources =