From d1b117998749535957c0c085b902435821f23229 Mon Sep 17 00:00:00 2001 From: Satish Kumar Nagireddy Date: Mon, 22 Oct 2018 20:30:49 -0700 Subject: [PATCH] xilinx: v4l: scd: Initial version of streaming based driver This patch adds support for streaming scene change detection driver. Signed-off-by: Satish Kumar Nagireddy Reviewed-by: Hyun Kwon Signed-off-by: Michal Simek --- .../xilinx/xilinx-scenechange-channel.c | 152 +++++++++++++++--- .../platform/xilinx/xilinx-scenechange-dma.c | 106 ++++++------ .../platform/xilinx/xilinx-scenechange.c | 49 +++--- .../platform/xilinx/xilinx-scenechange.h | 10 +- 4 files changed, 218 insertions(+), 99 deletions(-) diff --git a/drivers/media/platform/xilinx/xilinx-scenechange-channel.c b/drivers/media/platform/xilinx/xilinx-scenechange-channel.c index 8897d8390eab..1bfd9fad6a09 100644 --- a/drivers/media/platform/xilinx/xilinx-scenechange-channel.c +++ b/drivers/media/platform/xilinx/xilinx-scenechange-channel.c @@ -4,7 +4,8 @@ * * Copyright (C) 2018 Xilinx, Inc. * - * Author: Anand Ashok Dumbre + * Authors: Anand Ashok Dumbre + * Satish Kumar Nagireddy */ #include @@ -26,13 +27,34 @@ #include #include #include + #include "xilinx-scenechange.h" +#include "xilinx-vip.h" #define XSCD_MAX_WIDTH 3840 #define XSCD_MAX_HEIGHT 2160 #define XSCD_MIN_WIDTH 640 #define XSCD_MIN_HEIGHT 480 +#define XSCD_WIDTH_OFFSET 0x10 +#define XSCD_HEIGHT_OFFSET 0x18 +#define XSCD_STRIDE_OFFSET 0x20 +#define XSCD_VID_FMT_OFFSET 0x28 +#define XSCD_SUBSAMPLE_OFFSET 0x30 + +/* Hardware video formats for memory based IP */ +#define XSCD_COLOR_FMT_Y8 24 +#define XSCD_COLOR_FMT_Y10 25 + +/* Hardware video formats for streaming based IP */ +#define XSCD_COLOR_FMT_RGB 0 +#define XSCD_COLOR_FMT_YUV_444 1 +#define XSCD_COLOR_FMT_YUV_422 2 +#define XSCD_COLOR_FMT_YUV_420 4 + +#define XSCD_V_SUBSAMPLING 16 +#define XSCD_BYTE_ALIGN 16 + /* ----------------------------------------------------------------------------- * V4L2 Subdevice Pad Operations */ @@ -94,6 +116,70 @@ static int xscd_set_format(struct v4l2_subdev *subdev, return 0; } +static int xscd_chan_get_vid_fmt(u32 media_bus_fmt, bool memory_based) +{ + /* + * FIXME: We have same media bus codes for both 8bit and 10bit pixel + * formats. So, there is no way to differentiate between 8bit and 10bit + * formats based on media bus code. This will be fixed when we have + * dedicated media bus code for each format. + */ + if (memory_based) + return XSCD_COLOR_FMT_Y8; + + switch (media_bus_fmt) { + case MEDIA_BUS_FMT_VYYUYY8_1X24: + return XSCD_COLOR_FMT_YUV_420; + case MEDIA_BUS_FMT_UYVY8_1X16: + return XSCD_COLOR_FMT_YUV_422; + case MEDIA_BUS_FMT_VUY8_1X24: + return XSCD_COLOR_FMT_YUV_444; + case MEDIA_BUS_FMT_RBG888_1X24: + return XSCD_COLOR_FMT_RGB; + default: + return XSCD_COLOR_FMT_YUV_420; + } +} + +/** + * xscd_chan_configure_params - Program parameters to HW registers + * @chan: Driver specific channel struct pointer + * @shared_data: Shared data + * @chan_offset: Register offset for a channel + */ +void xscd_chan_configure_params(struct xscd_chan *chan, + struct xscd_shared_data *shared_data, + u32 chan_offset) +{ + u32 vid_fmt, stride; + + xscd_write(chan->iomem, XSCD_WIDTH_OFFSET + chan_offset, + chan->format.width); + + /* Stride is required only for memory based IP, not for streaming IP */ + if (shared_data->memory_based) { + stride = roundup(chan->format.width, XSCD_BYTE_ALIGN); + xscd_write(chan->iomem, XSCD_STRIDE_OFFSET + chan_offset, + stride); + } + + xscd_write(chan->iomem, XSCD_HEIGHT_OFFSET + chan_offset, + chan->format.height); + + /* Hardware video format */ + vid_fmt = xscd_chan_get_vid_fmt(chan->format.code, + shared_data->memory_based); + xscd_write(chan->iomem, XSCD_VID_FMT_OFFSET + chan_offset, vid_fmt); + + /* + * This is the vertical subsampling factor of the input image. Instead + * of sampling every line to calculate the histogram, IP uses this + * register value to sample only specific lines of the frame. + */ + xscd_write(chan->iomem, XSCD_SUBSAMPLE_OFFSET + chan_offset, + XSCD_V_SUBSAMPLING); +} + /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ @@ -102,6 +188,7 @@ static int xscd_s_stream(struct v4l2_subdev *subdev, int enable) struct xscd_chan *chan = to_chan(subdev); struct xscd_shared_data *shared_data; unsigned long flags; + u32 chan_offset; /* TODO: Re-organise shared data in a better way */ shared_data = (struct xscd_shared_data *)chan->dev->parent->driver_data; @@ -109,19 +196,34 @@ static int xscd_s_stream(struct v4l2_subdev *subdev, int enable) spin_lock_irqsave(&chan->dmachan.lock, flags); - if (enable) { - if (!shared_data->active_streams) { - chan->dmachan.valid_interrupt = true; - shared_data->active_streams++; - xscd_dma_start_transfer(&chan->dmachan); + if (shared_data->memory_based) { + chan_offset = chan->id * XILINX_XSCD_CHAN_OFFSET; + xscd_chan_configure_params(chan, shared_data, chan_offset); + if (enable) { + if (!shared_data->active_streams) { + chan->dmachan.valid_interrupt = true; + shared_data->active_streams++; + xscd_dma_start_transfer(&chan->dmachan); + xscd_dma_reset(&chan->dmachan); + xscd_dma_chan_enable(&chan->dmachan, + BIT(chan->id)); + xscd_dma_start(&chan->dmachan); + } else { + shared_data->active_streams++; + } + } else { + shared_data->active_streams--; + } + } else { + /* Streaming based */ + if (enable) { + xscd_chan_configure_params(chan, shared_data, chan->id); xscd_dma_reset(&chan->dmachan); xscd_dma_chan_enable(&chan->dmachan, BIT(chan->id)); xscd_dma_start(&chan->dmachan); } else { - shared_data->active_streams++; + xscd_dma_halt(&chan->dmachan); } - } else { - shared_data->active_streams--; } spin_unlock_irqrestore(&chan->dmachan.lock, flags); @@ -273,9 +375,11 @@ static int xscd_chan_probe(struct platform_device *pdev) { struct xscd_chan *chan; struct v4l2_subdev *subdev; - struct v4l2_mbus_framefmt *default_format; + struct xscd_shared_data *shared_data; int ret; + u32 num_pads; + shared_data = (struct xscd_shared_data *)pdev->dev.parent->driver_data; chan = devm_kzalloc(&pdev->dev, sizeof(*chan), GFP_KERNEL); if (!chan) return -ENOMEM; @@ -297,20 +401,28 @@ static int xscd_chan_probe(struct platform_device *pdev) subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; /* Initialize default format */ - default_format = &chan->format; - default_format->code = MEDIA_BUS_FMT_VYYUYY8_1X24; - default_format->field = V4L2_FIELD_NONE; - default_format->colorspace = V4L2_COLORSPACE_SRGB; - default_format->width = XSCD_MAX_WIDTH; - default_format->height = XSCD_MAX_HEIGHT; - chan->format = *default_format; - chan->pad.flags = MEDIA_PAD_FL_SINK; - subdev->entity.ops = &xscd_media_ops; + chan->format.code = MEDIA_BUS_FMT_VYYUYY8_1X24; + chan->format.field = V4L2_FIELD_NONE; + chan->format.width = XSCD_MAX_WIDTH; + chan->format.height = XSCD_MAX_HEIGHT; + + /* Initialize media pads */ + num_pads = shared_data->memory_based ? 1 : 2; + chan->pad = devm_kzalloc(&pdev->dev, + sizeof(struct media_pad) * num_pads, + GFP_KERNEL); + if (!chan->pad) + return -ENOMEM; + + chan->pad[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + if (!shared_data->memory_based) + chan->pad[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&subdev->entity, 1, &chan->pad); + ret = media_entity_pads_init(&subdev->entity, num_pads, chan->pad); if (ret < 0) goto error; + subdev->entity.ops = &xscd_media_ops; ret = v4l2_async_register_subdev(subdev); if (ret < 0) { dev_err(&pdev->dev, "failed to register subdev\n"); diff --git a/drivers/media/platform/xilinx/xilinx-scenechange-dma.c b/drivers/media/platform/xilinx/xilinx-scenechange-dma.c index 086e2682b307..69edf7fcbc65 100644 --- a/drivers/media/platform/xilinx/xilinx-scenechange-dma.c +++ b/drivers/media/platform/xilinx/xilinx-scenechange-dma.c @@ -4,7 +4,8 @@ * * Copyright (C) 2018 Xilinx, Inc. * - * Author: Anand Ashok Dumbre + * Authors: Anand Ashok Dumbre + * Satish Kumar Nagireddy */ #include @@ -30,25 +31,17 @@ #define XILINX_XSCD_CTRL_OFFSET 0x00 #define XILINX_XSCD_GIE_OFFSET 0x04 #define XILINX_XSCD_IE_OFFSET 0x08 -#define XILINX_XSCD_WIDTH_OFFSET 0x10 -#define XILINX_XSCD_HEIGHT_OFFSET 0x18 -#define XILINX_XSCD_STRIDE_OFFSET 0x20 -#define XILINX_XSCD_FMT_OFFSET 0x28 -#define XILINX_XSCD_SUBSAMPLE_OFFSET 0x30 #define XILINX_XSCD_ADDR_OFFSET 0x40 #define XILINX_XSCD_CHAN_EN_OFFSET 0x780 -#define XILINX_XSCD_FMT_Y8 24 -#define XILINX_XSCD_FMT_Y10 25 /* Control Registers */ #define XILINX_XSCD_CTRL_AP_START BIT(0) #define XILINX_XSCD_CTRL_AP_DONE BIT(1) #define XILINX_XSCD_CTRL_AP_IDLE BIT(2) #define XILINX_XSCD_CTRL_AP_READY BIT(3) +#define XILINX_XSCD_CTRL_AUTO_RESTART BIT(7) #define XILINX_XSCD_GIE_EN BIT(0) -#define XSCD_V_SUBSAMPLING 16 - /** * struct xscd_dma_device - Scene Change DMA device * @regs: I/O mapped base address @@ -56,6 +49,7 @@ * @common: DMA device structure * @chan: Driver specific DMA channel * @numchannels: Total number of channels + * @memory_based: Memory based or streaming based */ struct xscd_dma_device { void __iomem *regs; @@ -63,6 +57,7 @@ struct xscd_dma_device { struct dma_device common; struct xscd_dma_chan **chan; u32 numchannels; + bool memory_based; }; /** @@ -76,33 +71,36 @@ static irqreturn_t xscd_dma_irq_handler(int irq, void *data) { struct xscd_dma_device *dev = data; struct xscd_dma_chan *chan; - u32 chan_en = 0, id; - for (id = 0; id < dev->numchannels; id++) { - chan = dev->chan[id]; - spin_lock(&chan->lock); - chan->idle = true; + if (dev->memory_based) { + u32 chan_en = 0, id; - if (chan->en && (!list_empty(&chan->pending_list))) { - chan_en |= 1 << chan->id; - chan->valid_interrupt = true; - } else { - chan->valid_interrupt = false; - } + for (id = 0; id < dev->numchannels; id++) { + chan = dev->chan[id]; + spin_lock(&chan->lock); + chan->idle = true; - xscd_dma_start_transfer(chan); - spin_unlock(&chan->lock); - } + if (chan->en && (!list_empty(&chan->pending_list))) { + chan_en |= 1 << chan->id; + chan->valid_interrupt = true; + } else { + chan->valid_interrupt = false; + } - if (chan_en) { - xscd_dma_reset(chan); - xscd_dma_chan_enable(chan, chan_en); - xscd_dma_start(chan); - } + xscd_dma_start_transfer(chan); + spin_unlock(&chan->lock); + } - for (id = 0; id < dev->numchannels; id++) { - chan = dev->chan[id]; - tasklet_schedule(&chan->tasklet); + if (chan_en) { + xscd_dma_reset(chan); + xscd_dma_chan_enable(chan, chan_en); + xscd_dma_start(chan); + } + + for (id = 0; id < dev->numchannels; id++) { + chan = dev->chan[id]; + tasklet_schedule(&chan->tasklet); + } } return IRQ_HANDLED; @@ -210,20 +208,6 @@ void xscd_dma_start_transfer(struct xscd_dma_chan *chan) xscd_write(chan->iomem, XILINX_XSCD_ADDR_OFFSET + chanoffset, desc->sw.luma_plane_addr); - /* HW expects these parameters to be same for one transaction */ - xscd_write(chan->iomem, (XILINX_XSCD_WIDTH_OFFSET + chanoffset), - desc->sw.hsize); - xscd_write(chan->iomem, (XILINX_XSCD_STRIDE_OFFSET + chanoffset), - desc->sw.stride); - xscd_write(chan->iomem, (XILINX_XSCD_HEIGHT_OFFSET + chanoffset), - desc->sw.vsize); - xscd_write(chan->iomem, (XILINX_XSCD_FMT_OFFSET + chanoffset), - XILINX_XSCD_FMT_Y8); - - /* Number of times subsampled */ - xscd_write(chan->iomem, (XILINX_XSCD_SUBSAMPLE_OFFSET + chanoffset), - XSCD_V_SUBSAMPLING); - list_del(&desc->node); chan->staged_desc = desc; } @@ -371,8 +355,7 @@ static void xscd_dma_issue_pending(struct dma_chan *dchan) { struct xscd_dma_chan *chan = to_xilinx_chan(dchan); struct xscd_dma_device *dev = chan->xdev; - u32 chan_en = 0; - u32 id; + u32 chan_en = 0, id; for (id = 0; id < dev->numchannels; id++) { chan = dev->chan[id]; @@ -410,8 +393,17 @@ static enum dma_status xscd_dma_tx_status(struct dma_chan *dchan, */ void xscd_dma_halt(struct xscd_dma_chan *chan) { - xscd_clr(chan->iomem, XILINX_XSCD_CTRL_OFFSET, - XILINX_XSCD_CTRL_AP_START); + struct xscd_dma_device *xdev = chan->xdev; + + if (xdev->memory_based) + xscd_clr(chan->iomem, XILINX_XSCD_CTRL_OFFSET, + XILINX_XSCD_CTRL_AP_START); + else + /* Streaming based */ + xscd_clr(chan->iomem, XILINX_XSCD_CTRL_OFFSET, + XILINX_XSCD_CTRL_AP_START | + XILINX_XSCD_CTRL_AUTO_RESTART); + chan->idle = true; } @@ -421,8 +413,17 @@ void xscd_dma_halt(struct xscd_dma_chan *chan) */ void xscd_dma_start(struct xscd_dma_chan *chan) { - xscd_set(chan->iomem, XILINX_XSCD_CTRL_OFFSET, - XILINX_XSCD_CTRL_AP_START); + struct xscd_dma_device *xdev = chan->xdev; + + if (xdev->memory_based) + xscd_set(chan->iomem, XILINX_XSCD_CTRL_OFFSET, + XILINX_XSCD_CTRL_AP_START); + else + /* Streaming based */ + xscd_set(chan->iomem, XILINX_XSCD_CTRL_OFFSET, + XILINX_XSCD_CTRL_AP_START | + XILINX_XSCD_CTRL_AUTO_RESTART); + chan->idle = false; } @@ -541,6 +542,7 @@ static int xscd_dma_probe(struct platform_device *pdev) shared_data = (struct xscd_shared_data *)pdev->dev.parent->driver_data; xdev->regs = shared_data->iomem; xdev->chan = shared_data->dma_chan_list; + xdev->memory_based = shared_data->memory_based; dma_set_mask(xdev->dev, DMA_BIT_MASK(32)); /* Initialize the DMA engine */ diff --git a/drivers/media/platform/xilinx/xilinx-scenechange.c b/drivers/media/platform/xilinx/xilinx-scenechange.c index f6fb0da1e5bf..c32ea3d88a31 100644 --- a/drivers/media/platform/xilinx/xilinx-scenechange.c +++ b/drivers/media/platform/xilinx/xilinx-scenechange.c @@ -4,7 +4,8 @@ * * Copyright (C) 2018 Xilinx, Inc. * - * Author: Anand Ashok Dumbre + * Authors: Anand Ashok Dumbre + * Satish Kumar Nagireddy */ #include "xilinx-scenechange.h" @@ -56,8 +57,8 @@ static void xscd_chan_remove(struct platform_device *dev) } static -struct platform_device *xlnx_scdma_device_init(struct platform_device *pdev, - struct device_node *node) +struct platform_device *xilinx_scdma_device_init(struct platform_device *pdev, + struct device_node *node) { struct platform_device *dma; int ret; @@ -107,7 +108,8 @@ static int xscd_parse_of(struct xscd_device *xscd) struct device_node *node = xscd->dev->of_node; int ret; - xscd->memorybased = of_property_read_bool(node, "xlnx,memorybased"); + xscd->shared_data.memory_based = + of_property_read_bool(node, "xlnx,memorybased"); xscd->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(xscd->rst_gpio)) { if (PTR_ERR(xscd->rst_gpio) != -EPROBE_DEFER) @@ -140,11 +142,6 @@ static int xscd_probe(struct platform_device *pdev) if (!xscd) return -ENOMEM; - /* - * Memory based is enabled by default, this can be used for streaming - * based driver - */ - xscd->memorybased = true; xscd->dev = &pdev->dev; node = pdev->dev.of_node; @@ -162,7 +159,21 @@ static int xscd_probe(struct platform_device *pdev) xscd->shared_data.iomem = xscd->iomem; platform_set_drvdata(pdev, (void *)&xscd->shared_data); - for_each_child_of_node(node, subdev_node) { + if (xscd->shared_data.memory_based) { + for_each_child_of_node(node, subdev_node) { + subdev = xscd_chan_alloc(pdev, subdev_node, id); + if (IS_ERR(subdev)) { + dev_err(&pdev->dev, + "Failed to initialize subdev@%d\n", id); + ret = PTR_ERR(subdev); + goto cleanup; + } + xscd->subdevs[id] = subdev; + id++; + } + } else { + /* Streaming based */ + subdev_node = of_get_next_child(node, NULL); subdev = xscd_chan_alloc(pdev, subdev_node, id); if (IS_ERR(subdev)) { dev_err(&pdev->dev, @@ -171,17 +182,11 @@ static int xscd_probe(struct platform_device *pdev) goto cleanup; } xscd->subdevs[id] = subdev; - id++; } - if (xscd->memorybased) { - xscd->dma_device = xlnx_scdma_device_init(pdev, xscd->dma_node); - if (IS_ERR(xscd->dma_node)) { - ret = IS_ERR(xscd->dma_node); - dev_err(&pdev->dev, "Failed to initialize the DMA\n"); - goto cleanup; - } - } + xscd->dma_device = xilinx_scdma_device_init(pdev, xscd->dma_node); + if (IS_ERR(xscd->dma_node)) + dev_err(&pdev->dev, "Failed to initialize the DMA\n"); dev_info(xscd->dev, "scene change detect device found!\n"); return 0; @@ -200,10 +205,8 @@ static int xscd_remove(struct platform_device *pdev) struct xscd_device *xscd = platform_get_drvdata(pdev); u32 i; - if (xscd->memorybased) { - xilinx_scdma_device_exit(xscd->dma_device); - xscd->dma_node = NULL; - } + xilinx_scdma_device_exit(xscd->dma_device); + xscd->dma_node = NULL; for (i = 0; i < xscd->numstreams; i++) xscd_chan_remove(xscd->subdevs[i]); diff --git a/drivers/media/platform/xilinx/xilinx-scenechange.h b/drivers/media/platform/xilinx/xilinx-scenechange.h index 6faeafa5a3a4..618b9d42ffa3 100644 --- a/drivers/media/platform/xilinx/xilinx-scenechange.h +++ b/drivers/media/platform/xilinx/xilinx-scenechange.h @@ -4,7 +4,8 @@ * * Copyright (C) 2018 Xilinx, Inc. * - * Author: Anand Ashok Dumbre + * Authors: Anand Ashok Dumbre + * Satish Kumar Nagireddy */ #ifndef _XILINX_SCENECHANGE_H_ @@ -50,17 +51,18 @@ * @iomem: device I/O register space remapped to kernel virtual memory * @dma_chan_list: List of DMA channels available * @active_streams: Number of active streams + * @memory_based: Flag to identify memory based mode */ struct xscd_shared_data { void __iomem *iomem; struct xscd_dma_chan *dma_chan_list[XSCD_MAX_CHANNELS]; u8 active_streams; + bool memory_based; }; /** * struct xscd_device - Xilinx Scene Change Detection device structure * @iomem: device I/O register space remapped to kernel virtual memory - * @memorybased: Flag to identify memory based mode * @numstreams: Number of streams in the design * @irq: Device IRQ * @dev: (OF) device @@ -73,7 +75,6 @@ struct xscd_shared_data { */ struct xscd_device { void __iomem *iomem; - bool memorybased; int numstreams; int irq; struct device *dev; @@ -132,6 +133,7 @@ struct xscd_dma_tx_descriptor { struct xscd_dma_chan { struct xscd_dma_device *xdev; void __iomem *iomem; + /* Descriptor operation Lock */ spinlock_t lock; struct list_head chan_node; @@ -167,7 +169,7 @@ struct xscd_chan { void __iomem *iomem; struct device *dev; struct v4l2_subdev subdev; - struct media_pad pad; + struct media_pad *pad; struct v4l2_mbus_framefmt format; struct v4l2_event event; struct xscd_dma_chan dmachan; -- 2.39.2