1 //SPDX-License-Identifier: GPL-2.0
3 * Xilinx Scene Change Detection driver
5 * Copyright (C) 2018 Xilinx, Inc.
7 * Authors: Anand Ashok Dumbre <anand.ashok.dumbre@xilinx.com>
8 * Satish Kumar Nagireddy <satish.nagireddy.nagireddy@xilinx.com>
11 #include <linux/clk.h>
12 #include <linux/delay.h>
13 #include <linux/dmaengine.h>
14 #include <linux/module.h>
15 #include <linux/gpio/consumer.h>
16 #include <linux/interrupt.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
20 #include <linux/of_irq.h>
21 #include <linux/platform_device.h>
22 #include <linux/slab.h>
23 #include <linux/xilinx-v4l2-controls.h>
24 #include <linux/xilinx-v4l2-events.h>
25 #include <media/v4l2-async.h>
26 #include <media/v4l2-ctrls.h>
27 #include <media/v4l2-device.h>
28 #include <media/v4l2-event.h>
29 #include <media/v4l2-subdev.h>
31 #include "xilinx-scenechange.h"
32 #include "xilinx-vip.h"
34 #define XSCD_MAX_WIDTH 3840
35 #define XSCD_MAX_HEIGHT 2160
36 #define XSCD_MIN_WIDTH 640
37 #define XSCD_MIN_HEIGHT 480
39 #define XSCD_WIDTH_OFFSET 0x10
40 #define XSCD_HEIGHT_OFFSET 0x18
41 #define XSCD_STRIDE_OFFSET 0x20
42 #define XSCD_VID_FMT_OFFSET 0x28
43 #define XSCD_SUBSAMPLE_OFFSET 0x30
45 /* Hardware video formats for memory based IP */
46 #define XSCD_COLOR_FMT_Y8 24
47 #define XSCD_COLOR_FMT_Y10 25
49 /* Hardware video formats for streaming based IP */
50 #define XSCD_COLOR_FMT_RGB 0
51 #define XSCD_COLOR_FMT_YUV_444 1
52 #define XSCD_COLOR_FMT_YUV_422 2
53 #define XSCD_COLOR_FMT_YUV_420 4
55 #define XSCD_V_SUBSAMPLING 16
56 #define XSCD_BYTE_ALIGN 16
58 /* -----------------------------------------------------------------------------
59 * V4L2 Subdevice Pad Operations
62 static int xscd_enum_mbus_code(struct v4l2_subdev *subdev,
63 struct v4l2_subdev_pad_config *cfg,
64 struct v4l2_subdev_mbus_code_enum *code)
69 static int xscd_enum_frame_size(struct v4l2_subdev *subdev,
70 struct v4l2_subdev_pad_config *cfg,
71 struct v4l2_subdev_frame_size_enum *fse)
76 static struct v4l2_mbus_framefmt *
77 __xscd_get_pad_format(struct xscd_chan *chan,
78 struct v4l2_subdev_pad_config *cfg,
79 unsigned int pad, u32 which)
82 case V4L2_SUBDEV_FORMAT_TRY:
83 return v4l2_subdev_get_try_format(&chan->subdev, cfg, pad);
84 case V4L2_SUBDEV_FORMAT_ACTIVE:
92 static int xscd_get_format(struct v4l2_subdev *subdev,
93 struct v4l2_subdev_pad_config *cfg,
94 struct v4l2_subdev_format *fmt)
96 struct xscd_chan *chan = to_chan(subdev);
98 fmt->format = *__xscd_get_pad_format(chan, cfg, fmt->pad, fmt->which);
102 static int xscd_set_format(struct v4l2_subdev *subdev,
103 struct v4l2_subdev_pad_config *cfg,
104 struct v4l2_subdev_format *fmt)
106 struct xscd_chan *chan = to_chan(subdev);
107 struct v4l2_mbus_framefmt *format;
109 format = __xscd_get_pad_format(chan, cfg, fmt->pad, fmt->which);
110 format->width = clamp_t(unsigned int, fmt->format.width,
111 XSCD_MIN_WIDTH, XSCD_MAX_WIDTH);
112 format->height = clamp_t(unsigned int, fmt->format.height,
113 XSCD_MIN_HEIGHT, XSCD_MAX_HEIGHT);
114 fmt->format = *format;
119 static int xscd_chan_get_vid_fmt(u32 media_bus_fmt, bool memory_based)
122 * FIXME: We have same media bus codes for both 8bit and 10bit pixel
123 * formats. So, there is no way to differentiate between 8bit and 10bit
124 * formats based on media bus code. This will be fixed when we have
125 * dedicated media bus code for each format.
128 return XSCD_COLOR_FMT_Y8;
130 switch (media_bus_fmt) {
131 case MEDIA_BUS_FMT_VYYUYY8_1X24:
132 return XSCD_COLOR_FMT_YUV_420;
133 case MEDIA_BUS_FMT_UYVY8_1X16:
134 return XSCD_COLOR_FMT_YUV_422;
135 case MEDIA_BUS_FMT_VUY8_1X24:
136 return XSCD_COLOR_FMT_YUV_444;
137 case MEDIA_BUS_FMT_RBG888_1X24:
138 return XSCD_COLOR_FMT_RGB;
140 return XSCD_COLOR_FMT_YUV_420;
145 * xscd_chan_configure_params - Program parameters to HW registers
146 * @chan: Driver specific channel struct pointer
147 * @shared_data: Shared data
148 * @chan_offset: Register offset for a channel
150 void xscd_chan_configure_params(struct xscd_chan *chan,
151 struct xscd_shared_data *shared_data,
156 xscd_write(chan->iomem, XSCD_WIDTH_OFFSET + chan_offset,
159 /* Stride is required only for memory based IP, not for streaming IP */
160 if (shared_data->memory_based) {
161 stride = roundup(chan->format.width, XSCD_BYTE_ALIGN);
162 xscd_write(chan->iomem, XSCD_STRIDE_OFFSET + chan_offset,
166 xscd_write(chan->iomem, XSCD_HEIGHT_OFFSET + chan_offset,
167 chan->format.height);
169 /* Hardware video format */
170 vid_fmt = xscd_chan_get_vid_fmt(chan->format.code,
171 shared_data->memory_based);
172 xscd_write(chan->iomem, XSCD_VID_FMT_OFFSET + chan_offset, vid_fmt);
175 * This is the vertical subsampling factor of the input image. Instead
176 * of sampling every line to calculate the histogram, IP uses this
177 * register value to sample only specific lines of the frame.
179 xscd_write(chan->iomem, XSCD_SUBSAMPLE_OFFSET + chan_offset,
183 /* -----------------------------------------------------------------------------
184 * V4L2 Subdevice Operations
186 static int xscd_s_stream(struct v4l2_subdev *subdev, int enable)
188 struct xscd_chan *chan = to_chan(subdev);
189 struct xscd_shared_data *shared_data;
193 /* TODO: Re-organise shared data in a better way */
194 shared_data = (struct xscd_shared_data *)chan->dev->parent->driver_data;
195 chan->dmachan.en = enable;
197 spin_lock_irqsave(&chan->dmachan.lock, flags);
199 if (shared_data->memory_based) {
200 chan_offset = chan->id * XILINX_XSCD_CHAN_OFFSET;
201 xscd_chan_configure_params(chan, shared_data, chan_offset);
203 if (!shared_data->active_streams) {
204 chan->dmachan.valid_interrupt = true;
205 shared_data->active_streams++;
206 xscd_dma_start_transfer(&chan->dmachan);
207 xscd_dma_reset(&chan->dmachan);
208 xscd_dma_chan_enable(&chan->dmachan,
210 xscd_dma_start(&chan->dmachan);
212 shared_data->active_streams++;
215 shared_data->active_streams--;
218 /* Streaming based */
220 xscd_chan_configure_params(chan, shared_data, chan->id);
221 xscd_dma_reset(&chan->dmachan);
222 xscd_dma_chan_enable(&chan->dmachan, BIT(chan->id));
223 xscd_dma_start(&chan->dmachan);
225 xscd_dma_halt(&chan->dmachan);
229 spin_unlock_irqrestore(&chan->dmachan.lock, flags);
233 static int xscd_subscribe_event(struct v4l2_subdev *sd,
235 struct v4l2_event_subscription *sub)
238 struct xscd_chan *chan = to_chan(sd);
240 mutex_lock(&chan->lock);
243 case V4L2_EVENT_XLNXSCD:
244 ret = v4l2_event_subscribe(fh, sub, 1, NULL);
250 mutex_unlock(&chan->lock);
255 static int xscd_unsubscribe_event(struct v4l2_subdev *sd,
257 struct v4l2_event_subscription *sub)
260 struct xscd_chan *chan = to_chan(sd);
262 mutex_lock(&chan->lock);
263 ret = v4l2_event_unsubscribe(fh, sub);
264 mutex_unlock(&chan->lock);
269 static int xscd_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
274 static int xscd_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
279 static const struct v4l2_subdev_core_ops xscd_core_ops = {
280 .subscribe_event = xscd_subscribe_event,
281 .unsubscribe_event = xscd_unsubscribe_event
284 static struct v4l2_subdev_video_ops xscd_video_ops = {
285 .s_stream = xscd_s_stream,
288 static struct v4l2_subdev_pad_ops xscd_pad_ops = {
289 .enum_mbus_code = xscd_enum_mbus_code,
290 .enum_frame_size = xscd_enum_frame_size,
291 .get_fmt = xscd_get_format,
292 .set_fmt = xscd_set_format,
295 static struct v4l2_subdev_ops xscd_ops = {
296 .core = &xscd_core_ops,
297 .video = &xscd_video_ops,
298 .pad = &xscd_pad_ops,
301 static const struct v4l2_subdev_internal_ops xscd_internal_ops = {
306 /* -----------------------------------------------------------------------------
310 static const struct media_entity_operations xscd_media_ops = {
311 .link_validate = v4l2_subdev_link_validate,
314 static irqreturn_t xscd_chan_irq_handler(int irq, void *data)
316 struct xscd_chan *chan = (struct xscd_chan *)data;
320 spin_lock(&chan->dmachan.lock);
321 if (chan->dmachan.valid_interrupt) {
322 spin_unlock(&chan->dmachan.lock);
323 sad = xscd_read(chan->iomem, XILINX_XSCD_SAD_OFFSET +
324 (chan->id * XILINX_XSCD_CHAN_OFFSET));
325 sad = (sad * 16) / (chan->format.width * chan->format.height);
326 memset(&chan->event, 0, sizeof(chan->event));
327 eventdata = (u32 *)&chan->event.u.data;
334 chan->event.type = V4L2_EVENT_XLNXSCD;
335 v4l2_subdev_notify(&chan->subdev, V4L2_DEVICE_NOTIFY_EVENT,
340 spin_unlock(&chan->dmachan.lock);
344 static int xscd_chan_parse_of(struct xscd_chan *chan)
346 struct device_node *parent_node;
347 struct xscd_shared_data *shared_data;
350 parent_node = chan->dev->parent->of_node;
351 shared_data = (struct xscd_shared_data *)chan->dev->parent->driver_data;
352 shared_data->dma_chan_list[chan->id] = &chan->dmachan;
353 chan->iomem = shared_data->iomem;
354 chan->irq = irq_of_parse_and_map(parent_node, 0);
355 err = devm_request_irq(chan->dev, chan->irq, xscd_chan_irq_handler,
356 IRQF_SHARED, dev_name(chan->dev), chan);
358 dev_err(chan->dev, "unable to request IRQ %d\n", chan->irq);
362 chan->dmachan.iomem = shared_data->iomem;
363 chan->dmachan.id = chan->id;
369 * xscd_chan_probe - Driver probe function
370 * @pdev: Pointer to the device structure
372 * Return: '0' on success and failure value on error
374 static int xscd_chan_probe(struct platform_device *pdev)
376 struct xscd_chan *chan;
377 struct v4l2_subdev *subdev;
378 struct xscd_shared_data *shared_data;
382 shared_data = (struct xscd_shared_data *)pdev->dev.parent->driver_data;
383 chan = devm_kzalloc(&pdev->dev, sizeof(*chan), GFP_KERNEL);
387 mutex_init(&chan->lock);
388 chan->dev = &pdev->dev;
390 ret = xscd_chan_parse_of(chan);
394 /* Initialize V4L2 subdevice and media entity */
395 subdev = &chan->subdev;
396 v4l2_subdev_init(subdev, &xscd_ops);
397 subdev->dev = &pdev->dev;
398 subdev->internal_ops = &xscd_internal_ops;
399 strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
400 v4l2_set_subdevdata(subdev, chan);
401 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
403 /* Initialize default format */
404 chan->format.code = MEDIA_BUS_FMT_VYYUYY8_1X24;
405 chan->format.field = V4L2_FIELD_NONE;
406 chan->format.width = XSCD_MAX_WIDTH;
407 chan->format.height = XSCD_MAX_HEIGHT;
409 /* Initialize media pads */
410 num_pads = shared_data->memory_based ? 1 : 2;
411 chan->pad = devm_kzalloc(&pdev->dev,
412 sizeof(struct media_pad) * num_pads,
417 chan->pad[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
418 if (!shared_data->memory_based)
419 chan->pad[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
421 ret = media_entity_pads_init(&subdev->entity, num_pads, chan->pad);
425 subdev->entity.ops = &xscd_media_ops;
426 ret = v4l2_async_register_subdev(subdev);
428 dev_err(&pdev->dev, "failed to register subdev\n");
432 dev_info(chan->dev, "Scene change detection channel found!\n");
436 media_entity_cleanup(&subdev->entity);
440 static int xscd_chan_remove(struct platform_device *pdev)
445 static struct platform_driver xscd_chan_driver = {
446 .probe = xscd_chan_probe,
447 .remove = xscd_chan_remove,
449 .name = "xlnx-scdchan",
453 static int __init xscd_chan_init(void)
455 platform_driver_register(&xscd_chan_driver);
459 static void __exit xscd_chan_exit(void)
461 platform_driver_unregister(&xscd_chan_driver);
464 module_init(xscd_chan_init);
465 module_exit(xscd_chan_exit);
467 MODULE_AUTHOR("Xilinx Inc.");
468 MODULE_DESCRIPTION("Xilinx Scene Change Detection");
469 MODULE_LICENSE("GPL v2");