]> rtime.felk.cvut.cz Git - zynq/linux.git/blob - drivers/media/platform/xilinx/xilinx-scenechange-channel.c
8897d8390eab9da727108e558af2890a5da7c883
[zynq/linux.git] / drivers / media / platform / xilinx / xilinx-scenechange-channel.c
1 //SPDX-License-Identifier: GPL-2.0
2 /*
3  * Xilinx Scene Change Detection driver
4  *
5  * Copyright (C) 2018 Xilinx, Inc.
6  *
7  * Author: Anand Ashok Dumbre <anand.ashok.dumbre@xilinx.com>
8  */
9
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>
18 #include <linux/of.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"
30
31 #define XSCD_MAX_WIDTH          3840
32 #define XSCD_MAX_HEIGHT         2160
33 #define XSCD_MIN_WIDTH          640
34 #define XSCD_MIN_HEIGHT         480
35
36 /* -----------------------------------------------------------------------------
37  * V4L2 Subdevice Pad Operations
38  */
39
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)
43 {
44         return 0;
45 }
46
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)
50 {
51         return 0;
52 }
53
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)
58 {
59         switch (which) {
60         case V4L2_SUBDEV_FORMAT_TRY:
61                 return v4l2_subdev_get_try_format(&chan->subdev, cfg, pad);
62         case V4L2_SUBDEV_FORMAT_ACTIVE:
63                 return &chan->format;
64         default:
65                 return NULL;
66         }
67         return NULL;
68 }
69
70 static int xscd_get_format(struct v4l2_subdev *subdev,
71                            struct v4l2_subdev_pad_config *cfg,
72                            struct v4l2_subdev_format *fmt)
73 {
74         struct xscd_chan *chan = to_chan(subdev);
75
76         fmt->format = *__xscd_get_pad_format(chan, cfg, fmt->pad, fmt->which);
77         return 0;
78 }
79
80 static int xscd_set_format(struct v4l2_subdev *subdev,
81                            struct v4l2_subdev_pad_config *cfg,
82                            struct v4l2_subdev_format *fmt)
83 {
84         struct xscd_chan *chan = to_chan(subdev);
85         struct v4l2_mbus_framefmt *format;
86
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;
93
94         return 0;
95 }
96
97 /* -----------------------------------------------------------------------------
98  * V4L2 Subdevice Operations
99  */
100 static int xscd_s_stream(struct v4l2_subdev *subdev, int enable)
101 {
102         struct xscd_chan *chan = to_chan(subdev);
103         struct xscd_shared_data *shared_data;
104         unsigned long flags;
105
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;
109
110         spin_lock_irqsave(&chan->dmachan.lock, flags);
111
112         if (enable) {
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);
120                 } else {
121                         shared_data->active_streams++;
122                 }
123         } else {
124                 shared_data->active_streams--;
125         }
126
127         spin_unlock_irqrestore(&chan->dmachan.lock, flags);
128         return 0;
129 }
130
131 static int xscd_subscribe_event(struct v4l2_subdev *sd,
132                                 struct v4l2_fh *fh,
133                                 struct v4l2_event_subscription *sub)
134 {
135         int ret;
136         struct xscd_chan *chan = to_chan(sd);
137
138         mutex_lock(&chan->lock);
139
140         switch (sub->type) {
141         case V4L2_EVENT_XLNXSCD:
142                 ret = v4l2_event_subscribe(fh, sub, 1, NULL);
143                 break;
144         default:
145                 ret = -EINVAL;
146         }
147
148         mutex_unlock(&chan->lock);
149
150         return ret;
151 }
152
153 static int xscd_unsubscribe_event(struct v4l2_subdev *sd,
154                                   struct v4l2_fh *fh,
155                                   struct v4l2_event_subscription *sub)
156 {
157         int ret;
158         struct xscd_chan *chan = to_chan(sd);
159
160         mutex_lock(&chan->lock);
161         ret = v4l2_event_unsubscribe(fh, sub);
162         mutex_unlock(&chan->lock);
163
164         return ret;
165 }
166
167 static int xscd_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
168 {
169         return 0;
170 }
171
172 static int xscd_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
173 {
174         return 0;
175 }
176
177 static const struct v4l2_subdev_core_ops xscd_core_ops = {
178         .subscribe_event = xscd_subscribe_event,
179         .unsubscribe_event = xscd_unsubscribe_event
180 };
181
182 static struct v4l2_subdev_video_ops xscd_video_ops = {
183         .s_stream = xscd_s_stream,
184 };
185
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,
191 };
192
193 static struct v4l2_subdev_ops xscd_ops = {
194         .core = &xscd_core_ops,
195         .video = &xscd_video_ops,
196         .pad = &xscd_pad_ops,
197 };
198
199 static const struct v4l2_subdev_internal_ops xscd_internal_ops = {
200         .open = xscd_open,
201         .close = xscd_close,
202 };
203
204 /* -----------------------------------------------------------------------------
205  * Media Operations
206  */
207
208 static const struct media_entity_operations xscd_media_ops = {
209         .link_validate = v4l2_subdev_link_validate,
210 };
211
212 static irqreturn_t xscd_chan_irq_handler(int irq, void *data)
213 {
214         struct xscd_chan *chan = (struct xscd_chan *)data;
215         u32 sad;
216         u32 *eventdata;
217
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;
226
227                 if (sad >= 1)
228                         eventdata[0] = 1;
229                 else
230                         eventdata[0] = 0;
231
232                 chan->event.type = V4L2_EVENT_XLNXSCD;
233                 v4l2_subdev_notify(&chan->subdev, V4L2_DEVICE_NOTIFY_EVENT,
234                                    &chan->event);
235                 return IRQ_HANDLED;
236         }
237
238         spin_unlock(&chan->dmachan.lock);
239         return IRQ_NONE;
240 }
241
242 static int xscd_chan_parse_of(struct xscd_chan *chan)
243 {
244         struct device_node *parent_node;
245         struct xscd_shared_data *shared_data;
246         int err;
247
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);
255         if (err) {
256                 dev_err(chan->dev, "unable to request IRQ %d\n", chan->irq);
257                 return err;
258         }
259
260         chan->dmachan.iomem = shared_data->iomem;
261         chan->dmachan.id = chan->id;
262
263         return 0;
264 }
265
266 /**
267  * xscd_chan_probe - Driver probe function
268  * @pdev: Pointer to the device structure
269  *
270  * Return: '0' on success and failure value on error
271  */
272 static int xscd_chan_probe(struct platform_device *pdev)
273 {
274         struct xscd_chan *chan;
275         struct v4l2_subdev *subdev;
276         struct v4l2_mbus_framefmt *default_format;
277         int ret;
278
279         chan = devm_kzalloc(&pdev->dev, sizeof(*chan), GFP_KERNEL);
280         if (!chan)
281                 return -ENOMEM;
282
283         mutex_init(&chan->lock);
284         chan->dev = &pdev->dev;
285         chan->id = pdev->id;
286         ret = xscd_chan_parse_of(chan);
287         if (ret < 0)
288                 return ret;
289
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;
298
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;
309
310         ret = media_entity_pads_init(&subdev->entity, 1, &chan->pad);
311         if (ret < 0)
312                 goto error;
313
314         ret = v4l2_async_register_subdev(subdev);
315         if (ret < 0) {
316                 dev_err(&pdev->dev, "failed to register subdev\n");
317                 goto error;
318         }
319
320         dev_info(chan->dev, "Scene change detection channel found!\n");
321         return 0;
322
323 error:
324         media_entity_cleanup(&subdev->entity);
325         return ret;
326 }
327
328 static int xscd_chan_remove(struct platform_device *pdev)
329 {
330         return 0;
331 }
332
333 static struct platform_driver xscd_chan_driver = {
334         .probe          = xscd_chan_probe,
335         .remove         = xscd_chan_remove,
336         .driver         = {
337                 .name   = "xlnx-scdchan",
338         },
339 };
340
341 static int __init xscd_chan_init(void)
342 {
343         platform_driver_register(&xscd_chan_driver);
344         return 0;
345 }
346
347 static void __exit xscd_chan_exit(void)
348 {
349         platform_driver_unregister(&xscd_chan_driver);
350 }
351
352 module_init(xscd_chan_init);
353 module_exit(xscd_chan_exit);
354
355 MODULE_AUTHOR("Xilinx Inc.");
356 MODULE_DESCRIPTION("Xilinx Scene Change Detection");
357 MODULE_LICENSE("GPL v2");