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