1 //SPDX-License-Identifier: GPL-2.0
3 * Xilinx Scene Change Detection driver
5 * Copyright (C) 2018 Xilinx, Inc.
7 * Author: Anand Ashok Dumbre <anand.ashok.dumbre@xilinx.com>
10 #include <linux/clk.h>
11 #include <linux/delay.h>
12 #include <linux/dmaengine.h>
13 #include <linux/module.h>
14 #include <linux/gpio/consumer.h>
15 #include <linux/interrupt.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
19 #include <linux/of_irq.h>
20 #include <linux/platform_device.h>
21 #include <linux/slab.h>
22 #include <linux/xilinx-v4l2-controls.h>
23 #include <linux/xilinx-v4l2-events.h>
24 #include <media/v4l2-async.h>
25 #include <media/v4l2-ctrls.h>
26 #include <media/v4l2-device.h>
27 #include <media/v4l2-event.h>
28 #include <media/v4l2-subdev.h>
29 #include "xilinx-scenechange.h"
31 #define XSCD_MAX_WIDTH 3840
32 #define XSCD_MAX_HEIGHT 2160
33 #define XSCD_MIN_WIDTH 640
34 #define XSCD_MIN_HEIGHT 480
36 /* -----------------------------------------------------------------------------
37 * V4L2 Subdevice Pad Operations
40 static int xscd_enum_mbus_code(struct v4l2_subdev *subdev,
41 struct v4l2_subdev_pad_config *cfg,
42 struct v4l2_subdev_mbus_code_enum *code)
47 static int xscd_enum_frame_size(struct v4l2_subdev *subdev,
48 struct v4l2_subdev_pad_config *cfg,
49 struct v4l2_subdev_frame_size_enum *fse)
54 static struct v4l2_mbus_framefmt *
55 __xscd_get_pad_format(struct xscd_chan *chan,
56 struct v4l2_subdev_pad_config *cfg,
57 unsigned int pad, u32 which)
60 case V4L2_SUBDEV_FORMAT_TRY:
61 return v4l2_subdev_get_try_format(&chan->subdev, cfg, pad);
62 case V4L2_SUBDEV_FORMAT_ACTIVE:
70 static int xscd_get_format(struct v4l2_subdev *subdev,
71 struct v4l2_subdev_pad_config *cfg,
72 struct v4l2_subdev_format *fmt)
74 struct xscd_chan *chan = to_chan(subdev);
76 fmt->format = *__xscd_get_pad_format(chan, cfg, fmt->pad, fmt->which);
80 static int xscd_set_format(struct v4l2_subdev *subdev,
81 struct v4l2_subdev_pad_config *cfg,
82 struct v4l2_subdev_format *fmt)
84 struct xscd_chan *chan = to_chan(subdev);
85 struct v4l2_mbus_framefmt *format;
87 format = __xscd_get_pad_format(chan, cfg, fmt->pad, fmt->which);
88 format->width = clamp_t(unsigned int, fmt->format.width,
89 XSCD_MIN_WIDTH, XSCD_MAX_WIDTH);
90 format->height = clamp_t(unsigned int, fmt->format.height,
91 XSCD_MIN_HEIGHT, XSCD_MAX_HEIGHT);
92 fmt->format = *format;
97 /* -----------------------------------------------------------------------------
98 * V4L2 Subdevice Operations
100 static int xscd_s_stream(struct v4l2_subdev *subdev, int enable)
102 struct xscd_chan *chan = to_chan(subdev);
103 struct xscd_shared_data *shared_data;
106 /* TODO: Re-organise shared data in a better way */
107 shared_data = (struct xscd_shared_data *)chan->dev->parent->driver_data;
108 chan->dmachan.en = enable;
110 spin_lock_irqsave(&chan->dmachan.lock, flags);
113 if (!shared_data->active_streams) {
114 chan->dmachan.valid_interrupt = true;
115 shared_data->active_streams++;
116 xscd_dma_start_transfer(&chan->dmachan);
117 xscd_dma_reset(&chan->dmachan);
118 xscd_dma_chan_enable(&chan->dmachan, BIT(chan->id));
119 xscd_dma_start(&chan->dmachan);
121 shared_data->active_streams++;
124 shared_data->active_streams--;
127 spin_unlock_irqrestore(&chan->dmachan.lock, flags);
131 static int xscd_subscribe_event(struct v4l2_subdev *sd,
133 struct v4l2_event_subscription *sub)
136 struct xscd_chan *chan = to_chan(sd);
138 mutex_lock(&chan->lock);
141 case V4L2_EVENT_XLNXSCD:
142 ret = v4l2_event_subscribe(fh, sub, 1, NULL);
148 mutex_unlock(&chan->lock);
153 static int xscd_unsubscribe_event(struct v4l2_subdev *sd,
155 struct v4l2_event_subscription *sub)
158 struct xscd_chan *chan = to_chan(sd);
160 mutex_lock(&chan->lock);
161 ret = v4l2_event_unsubscribe(fh, sub);
162 mutex_unlock(&chan->lock);
167 static int xscd_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
172 static int xscd_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
177 static const struct v4l2_subdev_core_ops xscd_core_ops = {
178 .subscribe_event = xscd_subscribe_event,
179 .unsubscribe_event = xscd_unsubscribe_event
182 static struct v4l2_subdev_video_ops xscd_video_ops = {
183 .s_stream = xscd_s_stream,
186 static struct v4l2_subdev_pad_ops xscd_pad_ops = {
187 .enum_mbus_code = xscd_enum_mbus_code,
188 .enum_frame_size = xscd_enum_frame_size,
189 .get_fmt = xscd_get_format,
190 .set_fmt = xscd_set_format,
193 static struct v4l2_subdev_ops xscd_ops = {
194 .core = &xscd_core_ops,
195 .video = &xscd_video_ops,
196 .pad = &xscd_pad_ops,
199 static const struct v4l2_subdev_internal_ops xscd_internal_ops = {
204 /* -----------------------------------------------------------------------------
208 static const struct media_entity_operations xscd_media_ops = {
209 .link_validate = v4l2_subdev_link_validate,
212 static irqreturn_t xscd_chan_irq_handler(int irq, void *data)
214 struct xscd_chan *chan = (struct xscd_chan *)data;
218 spin_lock(&chan->dmachan.lock);
219 if (chan->dmachan.valid_interrupt) {
220 spin_unlock(&chan->dmachan.lock);
221 sad = xscd_read(chan->iomem, XILINX_XSCD_SAD_OFFSET +
222 (chan->id * XILINX_XSCD_CHAN_OFFSET));
223 sad = (sad * 16) / (chan->format.width * chan->format.height);
224 memset(&chan->event, 0, sizeof(chan->event));
225 eventdata = (u32 *)&chan->event.u.data;
232 chan->event.type = V4L2_EVENT_XLNXSCD;
233 v4l2_subdev_notify(&chan->subdev, V4L2_DEVICE_NOTIFY_EVENT,
238 spin_unlock(&chan->dmachan.lock);
242 static int xscd_chan_parse_of(struct xscd_chan *chan)
244 struct device_node *parent_node;
245 struct xscd_shared_data *shared_data;
248 parent_node = chan->dev->parent->of_node;
249 shared_data = (struct xscd_shared_data *)chan->dev->parent->driver_data;
250 shared_data->dma_chan_list[chan->id] = &chan->dmachan;
251 chan->iomem = shared_data->iomem;
252 chan->irq = irq_of_parse_and_map(parent_node, 0);
253 err = devm_request_irq(chan->dev, chan->irq, xscd_chan_irq_handler,
254 IRQF_SHARED, dev_name(chan->dev), chan);
256 dev_err(chan->dev, "unable to request IRQ %d\n", chan->irq);
260 chan->dmachan.iomem = shared_data->iomem;
261 chan->dmachan.id = chan->id;
267 * xscd_chan_probe - Driver probe function
268 * @pdev: Pointer to the device structure
270 * Return: '0' on success and failure value on error
272 static int xscd_chan_probe(struct platform_device *pdev)
274 struct xscd_chan *chan;
275 struct v4l2_subdev *subdev;
276 struct v4l2_mbus_framefmt *default_format;
279 chan = devm_kzalloc(&pdev->dev, sizeof(*chan), GFP_KERNEL);
283 mutex_init(&chan->lock);
284 chan->dev = &pdev->dev;
286 ret = xscd_chan_parse_of(chan);
290 /* Initialize V4L2 subdevice and media entity */
291 subdev = &chan->subdev;
292 v4l2_subdev_init(subdev, &xscd_ops);
293 subdev->dev = &pdev->dev;
294 subdev->internal_ops = &xscd_internal_ops;
295 strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
296 v4l2_set_subdevdata(subdev, chan);
297 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
299 /* Initialize default format */
300 default_format = &chan->format;
301 default_format->code = MEDIA_BUS_FMT_VYYUYY8_1X24;
302 default_format->field = V4L2_FIELD_NONE;
303 default_format->colorspace = V4L2_COLORSPACE_SRGB;
304 default_format->width = XSCD_MAX_WIDTH;
305 default_format->height = XSCD_MAX_HEIGHT;
306 chan->format = *default_format;
307 chan->pad.flags = MEDIA_PAD_FL_SINK;
308 subdev->entity.ops = &xscd_media_ops;
310 ret = media_entity_pads_init(&subdev->entity, 1, &chan->pad);
314 ret = v4l2_async_register_subdev(subdev);
316 dev_err(&pdev->dev, "failed to register subdev\n");
320 dev_info(chan->dev, "Scene change detection channel found!\n");
324 media_entity_cleanup(&subdev->entity);
328 static int xscd_chan_remove(struct platform_device *pdev)
333 static struct platform_driver xscd_chan_driver = {
334 .probe = xscd_chan_probe,
335 .remove = xscd_chan_remove,
337 .name = "xlnx-scdchan",
341 static int __init xscd_chan_init(void)
343 platform_driver_register(&xscd_chan_driver);
347 static void __exit xscd_chan_exit(void)
349 platform_driver_unregister(&xscd_chan_driver);
352 module_init(xscd_chan_init);
353 module_exit(xscd_chan_exit);
355 MODULE_AUTHOR("Xilinx Inc.");
356 MODULE_DESCRIPTION("Xilinx Scene Change Detection");
357 MODULE_LICENSE("GPL v2");