1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include <linux/device.h>
21#include <linux/module.h>
22#include <linux/of.h>
23#include <linux/platform_device.h>
24#include <linux/xilinx-v4l2-controls.h>
25
26#include <media/v4l2-async.h>
27#include <media/v4l2-ctrls.h>
28#include <media/v4l2-subdev.h>
29
30#include "xilinx-vip.h"
31
32#define XRGB2YUV_YMAX 0x100
33#define XRGB2YUV_YMIN 0x104
34#define XRGB2YUV_CBMAX 0x108
35#define XRGB2YUV_CBMIN 0x10c
36#define XRGB2YUV_CRMAX 0x110
37#define XRGB2YUV_CRMIN 0x114
38#define XRGB2YUV_YOFFSET 0x118
39#define XRGB2YUV_CBOFFSET 0x11c
40#define XRGB2YUV_CROFFSET 0x120
41#define XRGB2YUV_ACOEF 0x124
42#define XRGB2YUV_BCOEF 0x128
43#define XRGB2YUV_CCOEF 0x12c
44#define XRGB2YUV_DCOEF 0x130
45
46
47
48
49
50
51
52
53
54
55struct xrgb2yuv_device {
56 struct xvip_device xvip;
57
58 struct media_pad pads[2];
59
60 struct v4l2_mbus_framefmt formats[2];
61 struct v4l2_mbus_framefmt default_formats[2];
62 const struct xvip_video_format *vip_formats[2];
63
64 struct v4l2_ctrl_handler ctrl_handler;
65};
66
67static inline struct xrgb2yuv_device *to_rgb2yuv(struct v4l2_subdev *subdev)
68{
69 return container_of(subdev, struct xrgb2yuv_device, xvip.subdev);
70}
71
72
73
74
75
76static int xrgb2yuv_s_stream(struct v4l2_subdev *subdev, int enable)
77{
78 struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev);
79
80 if (!enable) {
81 xvip_stop(&xrgb2yuv->xvip);
82 return 0;
83 }
84
85 xvip_set_frame_size(&xrgb2yuv->xvip, &xrgb2yuv->formats[XVIP_PAD_SINK]);
86
87 xvip_start(&xrgb2yuv->xvip);
88
89 return 0;
90}
91
92
93
94
95
96static struct v4l2_mbus_framefmt *
97__xrgb2yuv_get_pad_format(struct xrgb2yuv_device *xrgb2yuv,
98 struct v4l2_subdev_state *sd_state,
99 unsigned int pad, u32 which)
100{
101 struct v4l2_mbus_framefmt *format;
102
103 switch (which) {
104 case V4L2_SUBDEV_FORMAT_TRY:
105 format = v4l2_subdev_get_try_format(&xrgb2yuv->xvip.subdev,
106 sd_state, pad);
107 break;
108 case V4L2_SUBDEV_FORMAT_ACTIVE:
109 format = &xrgb2yuv->formats[pad];
110 break;
111 default:
112 format = NULL;
113 break;
114 }
115
116 return format;
117}
118
119static int xrgb2yuv_get_format(struct v4l2_subdev *subdev,
120 struct v4l2_subdev_state *sd_state,
121 struct v4l2_subdev_format *fmt)
122{
123 struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev);
124 struct v4l2_mbus_framefmt *format;
125
126 format = __xrgb2yuv_get_pad_format(xrgb2yuv, sd_state, fmt->pad,
127 fmt->which);
128 if (!format)
129 return -EINVAL;
130
131 fmt->format = *format;
132
133 return 0;
134}
135
136static int xrgb2yuv_set_format(struct v4l2_subdev *subdev,
137 struct v4l2_subdev_state *sd_state,
138 struct v4l2_subdev_format *fmt)
139{
140 struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev);
141 struct v4l2_mbus_framefmt *format;
142
143 format = __xrgb2yuv_get_pad_format(xrgb2yuv, sd_state, fmt->pad,
144 fmt->which);
145 if (!format)
146 return -EINVAL;
147
148 if (fmt->pad == XVIP_PAD_SOURCE) {
149 fmt->format = *format;
150 return 0;
151 }
152
153 xvip_set_format_size(format, fmt);
154
155 fmt->format = *format;
156
157
158 format = __xrgb2yuv_get_pad_format(xrgb2yuv, sd_state, XVIP_PAD_SOURCE,
159 fmt->which);
160
161 xvip_set_format_size(format, fmt);
162
163 return 0;
164}
165
166
167
168
169
170static int xrgb2yuv_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
171{
172 struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev);
173 struct v4l2_mbus_framefmt *format;
174
175
176 format = v4l2_subdev_get_try_format(subdev, fh->state, XVIP_PAD_SINK);
177 *format = xrgb2yuv->default_formats[XVIP_PAD_SINK];
178
179 format = v4l2_subdev_get_try_format(subdev, fh->state, XVIP_PAD_SOURCE);
180 *format = xrgb2yuv->default_formats[XVIP_PAD_SOURCE];
181
182 return 0;
183}
184
185static int xrgb2yuv_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
186{
187 return 0;
188}
189
190static int xrgb2yuv_s_ctrl(struct v4l2_ctrl *ctrl)
191{
192 struct xrgb2yuv_device *xrgb2yuv =
193 container_of(ctrl->handler, struct xrgb2yuv_device,
194 ctrl_handler);
195
196 switch (ctrl->id) {
197 case V4L2_CID_XILINX_RGB2YUV_YMAX:
198 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_YMAX, ctrl->val);
199 return 0;
200 case V4L2_CID_XILINX_RGB2YUV_YMIN:
201 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_YMIN, ctrl->val);
202 return 0;
203 case V4L2_CID_XILINX_RGB2YUV_CBMAX:
204 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CBMAX, ctrl->val);
205 return 0;
206 case V4L2_CID_XILINX_RGB2YUV_CBMIN:
207 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CBMIN, ctrl->val);
208 return 0;
209 case V4L2_CID_XILINX_RGB2YUV_CRMAX:
210 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CRMAX, ctrl->val);
211 return 0;
212 case V4L2_CID_XILINX_RGB2YUV_CRMIN:
213 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CRMIN, ctrl->val);
214 return 0;
215 case V4L2_CID_XILINX_RGB2YUV_YOFFSET:
216 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_YOFFSET, ctrl->val);
217 return 0;
218 case V4L2_CID_XILINX_RGB2YUV_CBOFFSET:
219 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CBOFFSET, ctrl->val);
220 return 0;
221 case V4L2_CID_XILINX_RGB2YUV_CROFFSET:
222 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CROFFSET, ctrl->val);
223 return 0;
224 case V4L2_CID_XILINX_RGB2YUV_ACOEF:
225 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_ACOEF, ctrl->val);
226 return 0;
227 case V4L2_CID_XILINX_RGB2YUV_BCOEF:
228 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_BCOEF, ctrl->val);
229 return 0;
230 case V4L2_CID_XILINX_RGB2YUV_CCOEF:
231 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CCOEF, ctrl->val);
232 return 0;
233 case V4L2_CID_XILINX_RGB2YUV_DCOEF:
234 xvip_write(&xrgb2yuv->xvip, XRGB2YUV_DCOEF, ctrl->val);
235 return 0;
236 }
237
238 return -EINVAL;
239}
240
241static const struct v4l2_ctrl_ops xrgb2yuv_ctrl_ops = {
242 .s_ctrl = xrgb2yuv_s_ctrl,
243};
244
245static struct v4l2_subdev_video_ops xrgb2yuv_video_ops = {
246 .s_stream = xrgb2yuv_s_stream,
247};
248
249static struct v4l2_subdev_pad_ops xrgb2yuv_pad_ops = {
250 .enum_mbus_code = xvip_enum_mbus_code,
251 .enum_frame_size = xvip_enum_frame_size,
252 .get_fmt = xrgb2yuv_get_format,
253 .set_fmt = xrgb2yuv_set_format,
254};
255
256static struct v4l2_subdev_ops xrgb2yuv_ops = {
257 .video = &xrgb2yuv_video_ops,
258 .pad = &xrgb2yuv_pad_ops,
259};
260
261static const struct v4l2_subdev_internal_ops xrgb2yuv_internal_ops = {
262 .open = xrgb2yuv_open,
263 .close = xrgb2yuv_close,
264};
265
266
267
268
269
270static struct v4l2_ctrl_config xrgb2yuv_ctrls[] = {
271 {
272 .ops = &xrgb2yuv_ctrl_ops,
273 .id = V4L2_CID_XILINX_RGB2YUV_YMAX,
274 .name = "RGB to YUV: Maximum Y value",
275 .type = V4L2_CTRL_TYPE_INTEGER,
276 .min = 0,
277 .max = (1 << 16) - 1,
278 .step = 1,
279 }, {
280 .ops = &xrgb2yuv_ctrl_ops,
281 .id = V4L2_CID_XILINX_RGB2YUV_YMIN,
282 .name = "RGB to YUV: Minimum Y value",
283 .type = V4L2_CTRL_TYPE_INTEGER,
284 .min = 0,
285 .max = (1 << 16) - 1,
286 .step = 1,
287 }, {
288 .ops = &xrgb2yuv_ctrl_ops,
289 .id = V4L2_CID_XILINX_RGB2YUV_CBMAX,
290 .name = "RGB to YUV: Maximum Cb value",
291 .type = V4L2_CTRL_TYPE_INTEGER,
292 .min = 0,
293 .max = (1 << 16) - 1,
294 .step = 1,
295 }, {
296 .ops = &xrgb2yuv_ctrl_ops,
297 .id = V4L2_CID_XILINX_RGB2YUV_CBMIN,
298 .name = "RGB to YUV: Minimum Cb value",
299 .type = V4L2_CTRL_TYPE_INTEGER,
300 .min = 0,
301 .max = (1 << 16) - 1,
302 .step = 1,
303 }, {
304 .ops = &xrgb2yuv_ctrl_ops,
305 .id = V4L2_CID_XILINX_RGB2YUV_CRMAX,
306 .name = "RGB to YUV: Maximum Cr value",
307 .type = V4L2_CTRL_TYPE_INTEGER,
308 .min = 0,
309 .max = (1 << 16) - 1,
310 .step = 1,
311 }, {
312 .ops = &xrgb2yuv_ctrl_ops,
313 .id = V4L2_CID_XILINX_RGB2YUV_CRMIN,
314 .name = "RGB to YUV: Minimum Cr value",
315 .type = V4L2_CTRL_TYPE_INTEGER,
316 .min = 0,
317 .max = (1 << 16) - 1,
318 .step = 1,
319 }, {
320 .ops = &xrgb2yuv_ctrl_ops,
321 .id = V4L2_CID_XILINX_RGB2YUV_YOFFSET,
322 .name = "RGB to YUV: Luma offset",
323 .type = V4L2_CTRL_TYPE_INTEGER,
324 .min = 0,
325 .max = (1 << 17) - 1,
326 .step = 1,
327 }, {
328 .ops = &xrgb2yuv_ctrl_ops,
329 .id = V4L2_CID_XILINX_RGB2YUV_CBOFFSET,
330 .name = "RGB to YUV: Chroma Cb offset",
331 .type = V4L2_CTRL_TYPE_INTEGER,
332 .min = 0,
333 .max = (1 << 17) - 1,
334 .step = 1,
335 }, {
336 .ops = &xrgb2yuv_ctrl_ops,
337 .id = V4L2_CID_XILINX_RGB2YUV_CROFFSET,
338 .name = "RGB to YUV: Chroma Cr offset",
339 .type = V4L2_CTRL_TYPE_INTEGER,
340 .min = 0,
341 .max = (1 << 17) - 1,
342 .step = 1,
343 }, {
344 .ops = &xrgb2yuv_ctrl_ops,
345 .id = V4L2_CID_XILINX_RGB2YUV_ACOEF,
346 .name = "RGB to YUV: CA coefficient",
347 .type = V4L2_CTRL_TYPE_INTEGER,
348 .min = -((1 << 17) - 1),
349 .max = (1 << 17) - 1,
350 .step = 1,
351 }, {
352 .ops = &xrgb2yuv_ctrl_ops,
353 .id = V4L2_CID_XILINX_RGB2YUV_BCOEF,
354 .name = "RGB to YUV: CB coefficient",
355 .type = V4L2_CTRL_TYPE_INTEGER,
356 .min = -((1 << 17) - 1),
357 .max = (1 << 17) - 1,
358 .step = 1,
359 }, {
360 .ops = &xrgb2yuv_ctrl_ops,
361 .id = V4L2_CID_XILINX_RGB2YUV_CCOEF,
362 .name = "RGB to YUV: CC coefficient",
363 .type = V4L2_CTRL_TYPE_INTEGER,
364 .min = -((1 << 17) - 1),
365 .max = (1 << 17) - 1,
366 .step = 1,
367 }, {
368 .ops = &xrgb2yuv_ctrl_ops,
369 .id = V4L2_CID_XILINX_RGB2YUV_DCOEF,
370 .name = "RGB to YUV: CD coefficient",
371 .type = V4L2_CTRL_TYPE_INTEGER,
372 .min = -((1 << 17) - 1),
373 .max = (1 << 17) - 1,
374 .step = 1,
375 },
376};
377
378
379
380
381
382static const struct media_entity_operations xrgb2yuv_media_ops = {
383 .link_validate = v4l2_subdev_link_validate,
384};
385
386
387
388
389
390static int __maybe_unused xrgb2yuv_pm_suspend(struct device *dev)
391{
392 struct xrgb2yuv_device *xrgb2yuv = dev_get_drvdata(dev);
393
394 xvip_suspend(&xrgb2yuv->xvip);
395
396 return 0;
397}
398
399static int __maybe_unused xrgb2yuv_pm_resume(struct device *dev)
400{
401 struct xrgb2yuv_device *xrgb2yuv = dev_get_drvdata(dev);
402
403 xvip_resume(&xrgb2yuv->xvip);
404
405 return 0;
406}
407
408
409
410
411
412static int xrgb2yuv_parse_of(struct xrgb2yuv_device *xrgb2yuv)
413{
414 struct device *dev = xrgb2yuv->xvip.dev;
415 struct device_node *node = xrgb2yuv->xvip.dev->of_node;
416 struct device_node *ports;
417 struct device_node *port;
418 u32 port_id;
419 int ret;
420
421 ports = of_get_child_by_name(node, "ports");
422 if (ports == NULL)
423 ports = node;
424
425
426 for_each_child_of_node(ports, port) {
427 if (port->name && (of_node_cmp(port->name, "port") == 0)) {
428 const struct xvip_video_format *vip_format;
429
430 vip_format = xvip_of_get_format(port);
431 if (IS_ERR(vip_format)) {
432 dev_err(dev, "invalid format in DT");
433 return PTR_ERR(vip_format);
434 }
435
436 ret = of_property_read_u32(port, "reg", &port_id);
437 if (ret < 0) {
438 dev_err(dev, "no reg in DT");
439 return ret;
440 }
441
442 if (port_id != 0 && port_id != 1) {
443 dev_err(dev, "invalid reg in DT");
444 return -EINVAL;
445 }
446
447 xrgb2yuv->vip_formats[port_id] = vip_format;
448 }
449 }
450
451 return 0;
452}
453
454static int xrgb2yuv_probe(struct platform_device *pdev)
455{
456 struct xrgb2yuv_device *xrgb2yuv;
457 struct v4l2_subdev *subdev;
458 struct v4l2_mbus_framefmt *default_format;
459 unsigned int i;
460 int ret;
461
462 xrgb2yuv = devm_kzalloc(&pdev->dev, sizeof(*xrgb2yuv), GFP_KERNEL);
463 if (!xrgb2yuv)
464 return -ENOMEM;
465
466 xrgb2yuv->xvip.dev = &pdev->dev;
467
468 ret = xrgb2yuv_parse_of(xrgb2yuv);
469 if (ret < 0)
470 return ret;
471
472 ret = xvip_init_resources(&xrgb2yuv->xvip);
473 if (ret < 0)
474 return ret;
475
476
477 xvip_reset(&xrgb2yuv->xvip);
478
479
480 subdev = &xrgb2yuv->xvip.subdev;
481 v4l2_subdev_init(subdev, &xrgb2yuv_ops);
482 subdev->dev = &pdev->dev;
483 subdev->internal_ops = &xrgb2yuv_internal_ops;
484 strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
485 v4l2_set_subdevdata(subdev, xrgb2yuv);
486 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
487
488
489 default_format = &xrgb2yuv->default_formats[XVIP_PAD_SINK];
490 default_format->code = xrgb2yuv->vip_formats[XVIP_PAD_SINK]->code;
491 default_format->field = V4L2_FIELD_NONE;
492 default_format->colorspace = V4L2_COLORSPACE_SRGB;
493 xvip_get_frame_size(&xrgb2yuv->xvip, default_format);
494
495 xrgb2yuv->formats[XVIP_PAD_SINK] = *default_format;
496
497 default_format = &xrgb2yuv->default_formats[XVIP_PAD_SOURCE];
498 *default_format = xrgb2yuv->default_formats[XVIP_PAD_SINK];
499 default_format->code = xrgb2yuv->vip_formats[XVIP_PAD_SOURCE]->code;
500
501 xrgb2yuv->formats[XVIP_PAD_SOURCE] = *default_format;
502
503 xrgb2yuv->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
504 xrgb2yuv->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
505 subdev->entity.ops = &xrgb2yuv_media_ops;
506 ret = media_entity_pads_init(&subdev->entity, 2, xrgb2yuv->pads);
507 if (ret < 0)
508 goto error;
509
510 v4l2_ctrl_handler_init(&xrgb2yuv->ctrl_handler, 13);
511
512 for (i = 0; i < ARRAY_SIZE(xrgb2yuv_ctrls); i++) {
513 xrgb2yuv_ctrls[i].def = xvip_read(&xrgb2yuv->xvip,
514 XRGB2YUV_YMAX + i * 4);
515 v4l2_ctrl_new_custom(&xrgb2yuv->ctrl_handler,
516 &xrgb2yuv_ctrls[i], NULL);
517 }
518
519 if (xrgb2yuv->ctrl_handler.error) {
520 dev_err(&pdev->dev, "failed to add controls\n");
521 ret = xrgb2yuv->ctrl_handler.error;
522 goto error;
523 }
524 subdev->ctrl_handler = &xrgb2yuv->ctrl_handler;
525
526 platform_set_drvdata(pdev, xrgb2yuv);
527
528 xvip_print_version(&xrgb2yuv->xvip);
529
530 ret = v4l2_async_register_subdev(subdev);
531 if (ret < 0) {
532 dev_err(&pdev->dev, "failed to register subdev\n");
533 goto error;
534 }
535
536 return 0;
537
538error:
539 v4l2_ctrl_handler_free(&xrgb2yuv->ctrl_handler);
540 media_entity_cleanup(&subdev->entity);
541 xvip_cleanup_resources(&xrgb2yuv->xvip);
542 return ret;
543}
544
545static int xrgb2yuv_remove(struct platform_device *pdev)
546{
547 struct xrgb2yuv_device *xrgb2yuv = platform_get_drvdata(pdev);
548 struct v4l2_subdev *subdev = &xrgb2yuv->xvip.subdev;
549
550 v4l2_async_unregister_subdev(subdev);
551 v4l2_ctrl_handler_free(&xrgb2yuv->ctrl_handler);
552 media_entity_cleanup(&subdev->entity);
553
554 xvip_cleanup_resources(&xrgb2yuv->xvip);
555
556 return 0;
557}
558
559static SIMPLE_DEV_PM_OPS(xrgb2yuv_pm_ops, xrgb2yuv_pm_suspend,
560 xrgb2yuv_pm_resume);
561
562static const struct of_device_id xrgb2yuv_of_id_table[] = {
563 { .compatible = "xlnx,v-rgb2yuv-7.1" },
564 { }
565};
566MODULE_DEVICE_TABLE(of, xrgb2yuv_of_id_table);
567
568static struct platform_driver xrgb2yuv_driver = {
569 .driver = {
570 .name = "xilinx-rgb2yuv",
571 .pm = &xrgb2yuv_pm_ops,
572 .of_match_table = xrgb2yuv_of_id_table,
573 },
574 .probe = xrgb2yuv_probe,
575 .remove = xrgb2yuv_remove,
576};
577
578module_platform_driver(xrgb2yuv_driver);
579
580MODULE_DESCRIPTION("Xilinx RGB to YUV Converter Driver");
581MODULE_LICENSE("GPL v2");
582