1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/clk.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/platform_device.h>
19#include <linux/slab.h>
20
21#include <media/v4l2-async.h>
22#include <media/v4l2-subdev.h>
23
24#include "xilinx-vip.h"
25
26#define XREMAP_MIN_WIDTH 1
27#define XREMAP_DEF_WIDTH 1920
28#define XREMAP_MAX_WIDTH 65535
29#define XREMAP_MIN_HEIGHT 1
30#define XREMAP_DEF_HEIGHT 1080
31#define XREMAP_MAX_HEIGHT 65535
32
33#define XREMAP_PAD_SINK 0
34#define XREMAP_PAD_SOURCE 1
35
36
37
38
39
40
41
42struct xremap_mapping_output {
43 u32 code;
44 unsigned int num_components;
45 unsigned int component_maps[4];
46};
47
48
49
50
51
52
53
54
55struct xremap_mapping {
56 u32 code;
57 unsigned int width;
58 unsigned int num_components;
59 const struct xremap_mapping_output *outputs;
60};
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75struct xremap_device {
76 struct xvip_device xvip;
77 struct media_pad pads[2];
78 struct v4l2_mbus_framefmt formats[2];
79
80 struct {
81 unsigned int width;
82 unsigned int num_s_components;
83 unsigned int num_m_components;
84 unsigned int component_maps[4];
85 } config;
86
87 const struct xremap_mapping *default_mapping;
88 const struct xremap_mapping_output *default_output;
89};
90
91static inline struct xremap_device *to_remap(struct v4l2_subdev *subdev)
92{
93 return container_of(subdev, struct xremap_device, xvip.subdev);
94}
95
96
97
98
99
100static const struct xremap_mapping xremap_mappings[] = {
101 {
102 .code = MEDIA_BUS_FMT_RBG888_1X24,
103 .width = 8,
104 .num_components = 3,
105 .outputs = (const struct xremap_mapping_output[]) {
106 { MEDIA_BUS_FMT_RGB888_1X32_PADHI, 4, { 1, 0, 2, 4 } },
107 { },
108 },
109 },
110};
111
112static const struct xremap_mapping_output *
113xremap_match_mapping(struct xremap_device *xremap,
114 const struct xremap_mapping *mapping)
115{
116 const struct xremap_mapping_output *output;
117
118 if (mapping->width != xremap->config.width ||
119 mapping->num_components != xremap->config.num_s_components)
120 return NULL;
121
122 for (output = mapping->outputs; output->code; ++output) {
123 unsigned int i;
124
125 if (output->num_components != xremap->config.num_m_components)
126 continue;
127
128 for (i = 0; i < output->num_components; ++i) {
129 if (output->component_maps[i] !=
130 xremap->config.component_maps[i])
131 break;
132 }
133
134 if (i == output->num_components)
135 return output;
136 }
137
138 return NULL;
139}
140
141
142
143
144
145static int xremap_enum_mbus_code(struct v4l2_subdev *subdev,
146 struct v4l2_subdev_pad_config *cfg,
147 struct v4l2_subdev_mbus_code_enum *code)
148{
149 struct xremap_device *xremap = to_remap(subdev);
150 struct v4l2_mbus_framefmt *format;
151
152 if (code->pad == XREMAP_PAD_SINK) {
153 const struct xremap_mapping *mapping = NULL;
154 unsigned int index = code->index + 1;
155 unsigned int i;
156
157
158
159
160
161 for (i = 0; i < ARRAY_SIZE(xremap_mappings) && index; ++i) {
162 mapping = &xremap_mappings[i];
163
164 if (xremap_match_mapping(xremap, mapping))
165 index--;
166 }
167
168
169
170
171 if (index > 0)
172 return -EINVAL;
173
174 code->code = mapping->code;
175 } else {
176 if (code->index)
177 return -EINVAL;
178
179 format = v4l2_subdev_get_try_format(subdev, cfg, code->pad);
180 code->code = format->code;
181 }
182
183 return 0;
184}
185
186static int xremap_enum_frame_size(struct v4l2_subdev *subdev,
187 struct v4l2_subdev_pad_config *cfg,
188 struct v4l2_subdev_frame_size_enum *fse)
189{
190 struct v4l2_mbus_framefmt *format;
191
192 format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad);
193
194 if (fse->index || fse->code != format->code)
195 return -EINVAL;
196
197 if (fse->pad == XREMAP_PAD_SINK) {
198
199 fse->min_width = XREMAP_MIN_WIDTH;
200 fse->max_width = XREMAP_MAX_WIDTH;
201 fse->min_height = XREMAP_MIN_HEIGHT;
202 fse->max_height = XREMAP_MAX_HEIGHT;
203 } else {
204
205
206
207 fse->min_width = format->width;
208 fse->max_width = format->width;
209 fse->min_height = format->height;
210 fse->max_height = format->height;
211 }
212
213 return 0;
214}
215
216static struct v4l2_mbus_framefmt *
217xremap_get_pad_format(struct xremap_device *xremap,
218 struct v4l2_subdev_pad_config *cfg,
219 unsigned int pad, u32 which)
220{
221 switch (which) {
222 case V4L2_SUBDEV_FORMAT_TRY:
223 return v4l2_subdev_get_try_format(&xremap->xvip.subdev, cfg,
224 pad);
225 case V4L2_SUBDEV_FORMAT_ACTIVE:
226 return &xremap->formats[pad];
227 default:
228 return NULL;
229 }
230}
231
232static int xremap_get_format(struct v4l2_subdev *subdev,
233 struct v4l2_subdev_pad_config *cfg,
234 struct v4l2_subdev_format *fmt)
235{
236 struct xremap_device *xremap = to_remap(subdev);
237
238 fmt->format = *xremap_get_pad_format(xremap, cfg, fmt->pad, fmt->which);
239
240 return 0;
241}
242
243static int xremap_set_format(struct v4l2_subdev *subdev,
244 struct v4l2_subdev_pad_config *cfg,
245 struct v4l2_subdev_format *fmt)
246{
247 struct xremap_device *xremap = to_remap(subdev);
248 const struct xremap_mapping_output *output;
249 const struct xremap_mapping *mapping;
250 struct v4l2_mbus_framefmt *format;
251 unsigned int i;
252
253 format = xremap_get_pad_format(xremap, cfg, fmt->pad, fmt->which);
254
255 if (fmt->pad == XREMAP_PAD_SOURCE) {
256 fmt->format = *format;
257 return 0;
258 }
259
260
261
262
263 for (i = 0; i < ARRAY_SIZE(xremap_mappings); ++i) {
264 mapping = &xremap_mappings[i];
265 if (mapping->code != fmt->format.code)
266 continue;
267
268 output = xremap_match_mapping(xremap, mapping);
269 if (output)
270 break;
271 }
272
273 if (!output) {
274 mapping = xremap->default_mapping;
275 output = xremap->default_output;
276 }
277
278 format->code = mapping->code;
279 format->width = clamp_t(unsigned int, fmt->format.width,
280 XREMAP_MIN_WIDTH, XREMAP_MAX_WIDTH);
281 format->height = clamp_t(unsigned int, fmt->format.height,
282 XREMAP_MIN_HEIGHT, XREMAP_MAX_HEIGHT);
283 format->field = V4L2_FIELD_NONE;
284 format->colorspace = V4L2_COLORSPACE_SRGB;
285
286 fmt->format = *format;
287
288
289 format = xremap_get_pad_format(xremap, cfg, XREMAP_PAD_SOURCE,
290 fmt->which);
291 *format = fmt->format;
292 format->code = output->code;
293
294 return 0;
295}
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310static void xremap_init_formats(struct v4l2_subdev *subdev,
311 struct v4l2_subdev_fh *fh)
312{
313 struct xremap_device *xremap = to_remap(subdev);
314 struct v4l2_subdev_format format;
315
316 memset(&format, 0, sizeof(format));
317
318 format.pad = XREMAP_PAD_SINK;
319 format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
320 format.format.code = xremap->default_mapping->code;
321 format.format.width = XREMAP_DEF_WIDTH;
322 format.format.height = XREMAP_DEF_HEIGHT;
323
324 xremap_set_format(subdev, fh ? fh->pad : NULL, &format);
325}
326
327static int xremap_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
328{
329 xremap_init_formats(subdev, fh);
330
331 return 0;
332}
333
334static int xremap_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
335{
336 return 0;
337}
338
339static struct v4l2_subdev_core_ops xremap_core_ops = {
340};
341
342static struct v4l2_subdev_video_ops xremap_video_ops = {
343};
344
345static struct v4l2_subdev_pad_ops xremap_pad_ops = {
346 .enum_mbus_code = xremap_enum_mbus_code,
347 .enum_frame_size = xremap_enum_frame_size,
348 .get_fmt = xremap_get_format,
349 .set_fmt = xremap_set_format,
350};
351
352static struct v4l2_subdev_ops xremap_ops = {
353 .core = &xremap_core_ops,
354 .video = &xremap_video_ops,
355 .pad = &xremap_pad_ops,
356};
357
358static const struct v4l2_subdev_internal_ops xremap_internal_ops = {
359 .open = xremap_open,
360 .close = xremap_close,
361};
362
363
364
365
366
367static const struct media_entity_operations xremap_media_ops = {
368 .link_validate = v4l2_subdev_link_validate,
369};
370
371
372
373
374
375static int xremap_parse_of(struct xremap_device *xremap)
376{
377 struct device_node *node = xremap->xvip.dev->of_node;
378 unsigned int i;
379 int ret;
380
381
382 ret = of_property_read_u32(node, "xlnx,video-width",
383 &xremap->config.width);
384 if (ret < 0) {
385 dev_dbg(xremap->xvip.dev, "unable to parse %s property\n",
386 "xlnx,video-width");
387 return -EINVAL;
388 }
389
390 ret = of_property_read_u32(node, "#xlnx,s-components",
391 &xremap->config.num_s_components);
392 if (ret < 0) {
393 dev_dbg(xremap->xvip.dev, "unable to parse %s property\n",
394 "#xlnx,s-components");
395 return -EINVAL;
396 }
397
398 ret = of_property_read_u32(node, "#xlnx,m-components",
399 &xremap->config.num_m_components);
400 if (ret < 0) {
401 dev_dbg(xremap->xvip.dev, "unable to parse %s property\n",
402 "#xlnx,m-components");
403 return -EINVAL;
404 }
405
406 ret = of_property_read_u32_array(node, "xlnx,component-maps",
407 xremap->config.component_maps,
408 xremap->config.num_m_components);
409 if (ret < 0) {
410 dev_dbg(xremap->xvip.dev, "unable to parse %s property\n",
411 "xlnx,component-maps");
412 return -EINVAL;
413 }
414
415
416 if (xremap->config.num_s_components > 4 ||
417 xremap->config.num_m_components > 4) {
418 dev_dbg(xremap->xvip.dev,
419 "invalid number of components (s %u m %u)\n",
420 xremap->config.num_s_components,
421 xremap->config.num_m_components);
422 return -EINVAL;
423 }
424
425 for (i = 0; i < xremap->config.num_m_components; ++i) {
426 if (xremap->config.component_maps[i] > 4) {
427 dev_dbg(xremap->xvip.dev, "invalid map %u @%u\n",
428 xremap->config.component_maps[i], i);
429 return -EINVAL;
430 }
431 }
432
433
434
435
436 for (i = 0; i < ARRAY_SIZE(xremap_mappings); ++i) {
437 const struct xremap_mapping_output *output;
438 const struct xremap_mapping *mapping;
439
440 mapping = &xremap_mappings[i];
441 output = xremap_match_mapping(xremap, mapping);
442
443 if (output) {
444 xremap->default_mapping = mapping;
445 xremap->default_output = output;
446 return 0;
447 }
448 }
449
450 dev_err(xremap->xvip.dev,
451 "No format compatible with device configuration\n");
452
453 return -EINVAL;
454}
455
456static int xremap_probe(struct platform_device *pdev)
457{
458 struct xremap_device *xremap;
459 struct v4l2_subdev *subdev;
460 int ret;
461
462 xremap = devm_kzalloc(&pdev->dev, sizeof(*xremap), GFP_KERNEL);
463 if (!xremap)
464 return -ENOMEM;
465
466 xremap->xvip.dev = &pdev->dev;
467
468 ret = xremap_parse_of(xremap);
469 if (ret < 0)
470 return ret;
471
472 xremap->xvip.clk = devm_clk_get(xremap->xvip.dev, NULL);
473 if (IS_ERR(xremap->xvip.clk))
474 return PTR_ERR(xremap->xvip.clk);
475
476 clk_prepare_enable(xremap->xvip.clk);
477
478
479 subdev = &xremap->xvip.subdev;
480 v4l2_subdev_init(subdev, &xremap_ops);
481 subdev->dev = &pdev->dev;
482 subdev->internal_ops = &xremap_internal_ops;
483 strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
484 v4l2_set_subdevdata(subdev, xremap);
485 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
486
487 xremap_init_formats(subdev, NULL);
488
489 xremap->pads[XREMAP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
490 xremap->pads[XREMAP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
491 subdev->entity.ops = &xremap_media_ops;
492 ret = media_entity_pads_init(&subdev->entity, 2, xremap->pads);
493 if (ret < 0)
494 goto error;
495
496 platform_set_drvdata(pdev, xremap);
497
498 ret = v4l2_async_register_subdev(subdev);
499 if (ret < 0) {
500 dev_err(&pdev->dev, "failed to register subdev\n");
501 goto error;
502 }
503
504 dev_info(&pdev->dev, "device registered\n");
505
506 return 0;
507
508error:
509 media_entity_cleanup(&subdev->entity);
510 clk_disable_unprepare(xremap->xvip.clk);
511 return ret;
512}
513
514static int xremap_remove(struct platform_device *pdev)
515{
516 struct xremap_device *xremap = platform_get_drvdata(pdev);
517 struct v4l2_subdev *subdev = &xremap->xvip.subdev;
518
519 v4l2_async_unregister_subdev(subdev);
520 media_entity_cleanup(&subdev->entity);
521
522 clk_disable_unprepare(xremap->xvip.clk);
523
524 return 0;
525}
526
527static const struct of_device_id xremap_of_id_table[] = {
528 { .compatible = "xlnx,v-remapper" },
529 { }
530};
531MODULE_DEVICE_TABLE(of, xremap_of_id_table);
532
533static struct platform_driver xremap_driver = {
534 .driver = {
535 .name = "xilinx-remapper",
536 .of_match_table = xremap_of_id_table,
537 },
538 .probe = xremap_probe,
539 .remove = xremap_remove,
540};
541
542module_platform_driver(xremap_driver);
543
544MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
545MODULE_DESCRIPTION("Xilinx Video Remapper Driver");
546MODULE_LICENSE("GPL v2");
547