1 /*
2 * V4L2 subdev interface library
3 *
4 * Copyright (C) 2010-2014 Ideas on board SPRL
5 *
6 * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published
10 * by the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <sys/ioctl.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25
26 #include <ctype.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include <linux/v4l2-subdev.h>
36
37 #include "mediactl.h"
38 #include "mediactl-priv.h"
39 #include "tools.h"
40 #include "v4l2subdev.h"
41
v4l2_subdev_open(struct media_entity * entity)42 int v4l2_subdev_open(struct media_entity *entity)
43 {
44 if (entity->fd != -1)
45 return 0;
46
47 entity->fd = open(entity->devname, O_RDWR);
48 if (entity->fd == -1) {
49 int ret = -errno;
50 media_dbg(entity->media,
51 "%s: Failed to open subdev device node %s\n", __func__,
52 entity->devname);
53 return ret;
54 }
55
56 return 0;
57 }
58
v4l2_subdev_close(struct media_entity * entity)59 void v4l2_subdev_close(struct media_entity *entity)
60 {
61 close(entity->fd);
62 entity->fd = -1;
63 }
64
v4l2_subdev_get_format(struct media_entity * entity,struct v4l2_mbus_framefmt * format,unsigned int pad,enum v4l2_subdev_format_whence which)65 int v4l2_subdev_get_format(struct media_entity *entity,
66 struct v4l2_mbus_framefmt *format, unsigned int pad,
67 enum v4l2_subdev_format_whence which)
68 {
69 struct v4l2_subdev_format fmt;
70 int ret;
71
72 ret = v4l2_subdev_open(entity);
73 if (ret < 0)
74 return ret;
75
76 memset(&fmt, 0, sizeof(fmt));
77 fmt.pad = pad;
78 fmt.which = which;
79
80 ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FMT, &fmt);
81 if (ret < 0)
82 return -errno;
83
84 *format = fmt.format;
85 return 0;
86 }
87
v4l2_subdev_set_format(struct media_entity * entity,struct v4l2_mbus_framefmt * format,unsigned int pad,enum v4l2_subdev_format_whence which)88 int v4l2_subdev_set_format(struct media_entity *entity,
89 struct v4l2_mbus_framefmt *format, unsigned int pad,
90 enum v4l2_subdev_format_whence which)
91 {
92 struct v4l2_subdev_format fmt;
93 int ret;
94
95 ret = v4l2_subdev_open(entity);
96 if (ret < 0)
97 return ret;
98
99 memset(&fmt, 0, sizeof(fmt));
100 fmt.pad = pad;
101 fmt.which = which;
102 fmt.format = *format;
103
104 ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FMT, &fmt);
105 if (ret < 0)
106 return -errno;
107
108 *format = fmt.format;
109 return 0;
110 }
111
v4l2_subdev_get_selection(struct media_entity * entity,struct v4l2_rect * rect,unsigned int pad,unsigned int target,enum v4l2_subdev_format_whence which)112 int v4l2_subdev_get_selection(struct media_entity *entity,
113 struct v4l2_rect *rect, unsigned int pad, unsigned int target,
114 enum v4l2_subdev_format_whence which)
115 {
116 union {
117 struct v4l2_subdev_selection sel;
118 struct v4l2_subdev_crop crop;
119 } u;
120 int ret;
121
122 ret = v4l2_subdev_open(entity);
123 if (ret < 0)
124 return ret;
125
126 memset(&u.sel, 0, sizeof(u.sel));
127 u.sel.pad = pad;
128 u.sel.target = target;
129 u.sel.which = which;
130
131 ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_SELECTION, &u.sel);
132 if (ret >= 0) {
133 *rect = u.sel.r;
134 return 0;
135 }
136 if (errno != ENOTTY || target != V4L2_SEL_TGT_CROP)
137 return -errno;
138
139 memset(&u.crop, 0, sizeof(u.crop));
140 u.crop.pad = pad;
141 u.crop.which = which;
142
143 ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_CROP, &u.crop);
144 if (ret < 0)
145 return -errno;
146
147 *rect = u.crop.rect;
148 return 0;
149 }
150
v4l2_subdev_set_selection(struct media_entity * entity,struct v4l2_rect * rect,unsigned int pad,unsigned int target,enum v4l2_subdev_format_whence which)151 int v4l2_subdev_set_selection(struct media_entity *entity,
152 struct v4l2_rect *rect, unsigned int pad, unsigned int target,
153 enum v4l2_subdev_format_whence which)
154 {
155 union {
156 struct v4l2_subdev_selection sel;
157 struct v4l2_subdev_crop crop;
158 } u;
159 int ret;
160
161 ret = v4l2_subdev_open(entity);
162 if (ret < 0)
163 return ret;
164
165 memset(&u.sel, 0, sizeof(u.sel));
166 u.sel.pad = pad;
167 u.sel.target = target;
168 u.sel.which = which;
169 u.sel.r = *rect;
170
171 ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_SELECTION, &u.sel);
172 if (ret >= 0) {
173 *rect = u.sel.r;
174 return 0;
175 }
176 if (errno != ENOTTY || target != V4L2_SEL_TGT_CROP)
177 return -errno;
178
179 memset(&u.crop, 0, sizeof(u.crop));
180 u.crop.pad = pad;
181 u.crop.which = which;
182 u.crop.rect = *rect;
183
184 ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_CROP, &u.crop);
185 if (ret < 0)
186 return -errno;
187
188 *rect = u.crop.rect;
189 return 0;
190 }
191
v4l2_subdev_get_dv_timings_caps(struct media_entity * entity,struct v4l2_dv_timings_cap * caps)192 int v4l2_subdev_get_dv_timings_caps(struct media_entity *entity,
193 struct v4l2_dv_timings_cap *caps)
194 {
195 unsigned int pad = caps->pad;
196 int ret;
197
198 ret = v4l2_subdev_open(entity);
199 if (ret < 0)
200 return ret;
201
202 memset(caps, 0, sizeof(*caps));
203 caps->pad = pad;
204
205 ret = ioctl(entity->fd, VIDIOC_SUBDEV_DV_TIMINGS_CAP, caps);
206 if (ret < 0)
207 return -errno;
208
209 return 0;
210 }
211
v4l2_subdev_query_dv_timings(struct media_entity * entity,struct v4l2_dv_timings * timings)212 int v4l2_subdev_query_dv_timings(struct media_entity *entity,
213 struct v4l2_dv_timings *timings)
214 {
215 int ret;
216
217 ret = v4l2_subdev_open(entity);
218 if (ret < 0)
219 return ret;
220
221 memset(timings, 0, sizeof(*timings));
222
223 ret = ioctl(entity->fd, VIDIOC_SUBDEV_QUERY_DV_TIMINGS, timings);
224 if (ret < 0)
225 return -errno;
226
227 return 0;
228 }
229
v4l2_subdev_get_dv_timings(struct media_entity * entity,struct v4l2_dv_timings * timings)230 int v4l2_subdev_get_dv_timings(struct media_entity *entity,
231 struct v4l2_dv_timings *timings)
232 {
233 int ret;
234
235 ret = v4l2_subdev_open(entity);
236 if (ret < 0)
237 return ret;
238
239 memset(timings, 0, sizeof(*timings));
240
241 ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_DV_TIMINGS, timings);
242 if (ret < 0)
243 return -errno;
244
245 return 0;
246 }
247
v4l2_subdev_set_dv_timings(struct media_entity * entity,struct v4l2_dv_timings * timings)248 int v4l2_subdev_set_dv_timings(struct media_entity *entity,
249 struct v4l2_dv_timings *timings)
250 {
251 int ret;
252
253 ret = v4l2_subdev_open(entity);
254 if (ret < 0)
255 return ret;
256
257 ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_DV_TIMINGS, timings);
258 if (ret < 0)
259 return -errno;
260
261 return 0;
262 }
263
v4l2_subdev_get_frame_interval(struct media_entity * entity,struct v4l2_fract * interval,unsigned int pad)264 int v4l2_subdev_get_frame_interval(struct media_entity *entity,
265 struct v4l2_fract *interval,
266 unsigned int pad)
267 {
268 struct v4l2_subdev_frame_interval ival;
269 int ret;
270
271 ret = v4l2_subdev_open(entity);
272 if (ret < 0)
273 return ret;
274
275 memset(&ival, 0, sizeof(ival));
276 ival.pad = pad;
277
278 ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &ival);
279 if (ret < 0)
280 return -errno;
281
282 *interval = ival.interval;
283 return 0;
284 }
285
v4l2_subdev_set_frame_interval(struct media_entity * entity,struct v4l2_fract * interval,unsigned int pad)286 int v4l2_subdev_set_frame_interval(struct media_entity *entity,
287 struct v4l2_fract *interval,
288 unsigned int pad)
289 {
290 struct v4l2_subdev_frame_interval ival;
291 int ret;
292
293 ret = v4l2_subdev_open(entity);
294 if (ret < 0)
295 return ret;
296
297 memset(&ival, 0, sizeof(ival));
298 ival.pad = pad;
299 ival.interval = *interval;
300
301 ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &ival);
302 if (ret < 0)
303 return -errno;
304
305 *interval = ival.interval;
306 return 0;
307 }
308
v4l2_subdev_parse_format(struct media_device * media,struct v4l2_mbus_framefmt * format,const char * p,char ** endp)309 static int v4l2_subdev_parse_format(struct media_device *media,
310 struct v4l2_mbus_framefmt *format,
311 const char *p, char **endp)
312 {
313 enum v4l2_mbus_pixelcode code;
314 unsigned int width, height;
315 char *fmt;
316 char *end;
317
318 /*
319 * Compatibility with the old syntax: consider space as valid
320 * separator between the media bus pixel code and the size.
321 */
322 for (; isspace(*p); ++p);
323 for (end = (char *)p;
324 *end != '/' && *end != ' ' && *end != '\0'; ++end);
325
326 fmt = strndup(p, end - p);
327 if (!fmt)
328 return -ENOMEM;
329
330 code = v4l2_subdev_string_to_pixelcode(fmt);
331 free(fmt);
332 if (code == (enum v4l2_mbus_pixelcode)-1) {
333 media_dbg(media, "Invalid pixel code '%.*s'\n", end - p, p);
334 return -EINVAL;
335 }
336
337 p = end + 1;
338 width = strtoul(p, &end, 10);
339 if (*end != 'x') {
340 media_dbg(media, "Expected 'x'\n");
341 return -EINVAL;
342 }
343
344 p = end + 1;
345 height = strtoul(p, &end, 10);
346 *endp = end;
347
348 memset(format, 0, sizeof(*format));
349 format->width = width;
350 format->height = height;
351 format->code = code;
352
353 return 0;
354 }
355
v4l2_subdev_parse_rectangle(struct media_device * media,struct v4l2_rect * r,const char * p,char ** endp)356 static int v4l2_subdev_parse_rectangle(struct media_device *media,
357 struct v4l2_rect *r, const char *p,
358 char **endp)
359 {
360 char *end;
361
362 if (*p++ != '(') {
363 media_dbg(media, "Expected '('\n");
364 *endp = (char *)p - 1;
365 return -EINVAL;
366 }
367
368 r->left = strtoul(p, &end, 10);
369 if (*end != ',') {
370 media_dbg(media, "Expected ','\n");
371 *endp = end;
372 return -EINVAL;
373 }
374
375 p = end + 1;
376 r->top = strtoul(p, &end, 10);
377 if (*end++ != ')') {
378 media_dbg(media, "Expected ')'\n");
379 *endp = end - 1;
380 return -EINVAL;
381 }
382 if (*end != '/') {
383 media_dbg(media, "Expected '/'\n");
384 *endp = end;
385 return -EINVAL;
386 }
387
388 p = end + 1;
389 r->width = strtoul(p, &end, 10);
390 if (*end != 'x') {
391 media_dbg(media, "Expected 'x'\n");
392 *endp = end;
393 return -EINVAL;
394 }
395
396 p = end + 1;
397 r->height = strtoul(p, &end, 10);
398 *endp = end;
399
400 return 0;
401 }
402
v4l2_subdev_parse_frame_interval(struct media_device * media,struct v4l2_fract * interval,const char * p,char ** endp)403 static int v4l2_subdev_parse_frame_interval(struct media_device *media,
404 struct v4l2_fract *interval,
405 const char *p, char **endp)
406 {
407 char *end;
408
409 for (; isspace(*p); ++p);
410
411 interval->numerator = strtoul(p, &end, 10);
412
413 for (p = end; isspace(*p); ++p);
414 if (*p++ != '/') {
415 media_dbg(media, "Expected '/'\n");
416 *endp = (char *)p - 1;
417 return -EINVAL;
418 }
419
420 for (; isspace(*p); ++p);
421 interval->denominator = strtoul(p, &end, 10);
422
423 *endp = end;
424 return 0;
425 }
426
427 /*
428 * The debate over whether this function should be named icanhasstr() instead
429 * has been strong and heated. If you feel like this would be an important
430 * change, patches are welcome (or not).
431 */
strhazit(const char * str,const char ** p)432 static bool strhazit(const char *str, const char **p)
433 {
434 int len = strlen(str);
435
436 if (strncmp(str, *p, len))
437 return false;
438
439 for (*p += len; isspace(**p); ++*p);
440 return true;
441 }
442
v4l2_subdev_parse_pad_format(struct media_device * media,struct v4l2_mbus_framefmt * format,struct v4l2_rect * crop,struct v4l2_rect * compose,struct v4l2_fract * interval,const char * p,char ** endp)443 static struct media_pad *v4l2_subdev_parse_pad_format(
444 struct media_device *media, struct v4l2_mbus_framefmt *format,
445 struct v4l2_rect *crop, struct v4l2_rect *compose,
446 struct v4l2_fract *interval, const char *p, char **endp)
447 {
448 struct media_pad *pad;
449 bool first;
450 char *end;
451 int ret;
452
453 for (; isspace(*p); ++p);
454
455 pad = media_parse_pad(media, p, &end);
456 if (pad == NULL) {
457 *endp = end;
458 return NULL;
459 }
460
461 for (p = end; isspace(*p); ++p);
462 if (*p++ != '[') {
463 media_dbg(media, "Expected '['\n");
464 *endp = (char *)p - 1;
465 return NULL;
466 }
467
468 for (first = true; ; first = false) {
469 for (; isspace(*p); p++);
470
471 /*
472 * Backward compatibility: if the first property starts with an
473 * uppercase later, process it as a format description.
474 */
475 if (strhazit("fmt:", &p) || (first && isupper(*p))) {
476 ret = v4l2_subdev_parse_format(media, format, p, &end);
477 if (ret < 0) {
478 *endp = end;
479 return NULL;
480 }
481
482 p = end;
483 continue;
484 }
485
486 if (strhazit("field:", &p)) {
487 enum v4l2_field field;
488 char *strfield;
489
490 for (end = (char *)p; isalpha(*end) || *end == '-';
491 ++end);
492
493 strfield = strndup(p, end - p);
494 if (!strfield) {
495 *endp = (char *)p;
496 return NULL;
497 }
498
499 field = v4l2_subdev_string_to_field(strfield);
500 free(strfield);
501 if (field == (enum v4l2_field)-1) {
502 media_dbg(media, "Invalid field value '%*s'\n",
503 end - p, p);
504 *endp = (char *)p;
505 return NULL;
506 }
507
508 format->field = field;
509
510 p = end;
511 continue;
512 }
513
514 if (strhazit("colorspace:", &p)) {
515 enum v4l2_colorspace colorspace;
516 char *strfield;
517
518 for (end = (char *)p; isalnum(*end) || *end == '-';
519 ++end);
520
521 strfield = strndup(p, end - p);
522 if (!strfield) {
523 *endp = (char *)p;
524 return NULL;
525 }
526
527 colorspace = v4l2_subdev_string_to_colorspace(strfield);
528 free(strfield);
529 if (colorspace == (enum v4l2_colorspace)-1) {
530 media_dbg(media, "Invalid colorspace value '%*s'\n",
531 end - p, p);
532 *endp = (char *)p;
533 return NULL;
534 }
535
536 format->colorspace = colorspace;
537
538 p = end;
539 continue;
540 }
541
542 if (strhazit("xfer:", &p)) {
543 enum v4l2_xfer_func xfer_func;
544 char *strfield;
545
546 for (end = (char *)p; isalnum(*end) || *end == '-';
547 ++end);
548
549 strfield = strndup(p, end - p);
550 if (!strfield) {
551 *endp = (char *)p;
552 return NULL;
553 }
554
555 xfer_func = v4l2_subdev_string_to_xfer_func(strfield);
556 free(strfield);
557 if (xfer_func == (enum v4l2_xfer_func)-1) {
558 media_dbg(media, "Invalid transfer function value '%*s'\n",
559 end - p, p);
560 *endp = (char *)p;
561 return NULL;
562 }
563
564 format->xfer_func = xfer_func;
565
566 p = end;
567 continue;
568 }
569
570 if (strhazit("ycbcr:", &p)) {
571 enum v4l2_ycbcr_encoding ycbcr_enc;
572 char *strfield;
573
574 for (end = (char *)p; isalnum(*end) || *end == '-';
575 ++end);
576
577 strfield = strndup(p, end - p);
578 if (!strfield) {
579 *endp = (char *)p;
580 return NULL;
581 }
582
583 ycbcr_enc = v4l2_subdev_string_to_ycbcr_encoding(strfield);
584 free(strfield);
585 if (ycbcr_enc == (enum v4l2_ycbcr_encoding)-1) {
586 media_dbg(media, "Invalid YCbCr encoding value '%*s'\n",
587 end - p, p);
588 *endp = (char *)p;
589 return NULL;
590 }
591
592 format->ycbcr_enc = ycbcr_enc;
593
594 p = end;
595 continue;
596 }
597
598 if (strhazit("quantization:", &p)) {
599 enum v4l2_quantization quantization;
600 char *strfield;
601
602 for (end = (char *)p; isalnum(*end) || *end == '-';
603 ++end);
604
605 strfield = strndup(p, end - p);
606 if (!strfield) {
607 *endp = (char *)p;
608 return NULL;
609 }
610
611 quantization = v4l2_subdev_string_to_quantization(strfield);
612 free(strfield);
613 if (quantization == (enum v4l2_quantization)-1) {
614 media_dbg(media, "Invalid quantization value '%*s'\n",
615 end - p, p);
616 *endp = (char *)p;
617 return NULL;
618 }
619
620 format->quantization = quantization;
621
622 p = end;
623 continue;
624 }
625
626 /*
627 * Backward compatibility: crop rectangles can be specified
628 * implicitly without the 'crop:' property name.
629 */
630 if (strhazit("crop:", &p) || *p == '(') {
631 ret = v4l2_subdev_parse_rectangle(media, crop, p, &end);
632 if (ret < 0) {
633 *endp = end;
634 return NULL;
635 }
636
637 p = end;
638 continue;
639 }
640
641 if (strhazit("compose:", &p)) {
642 ret = v4l2_subdev_parse_rectangle(media, compose, p, &end);
643 if (ret < 0) {
644 *endp = end;
645 return NULL;
646 }
647
648 for (p = end; isspace(*p); p++);
649 continue;
650 }
651
652 if (*p == '@') {
653 ret = v4l2_subdev_parse_frame_interval(media, interval, ++p, &end);
654 if (ret < 0) {
655 *endp = end;
656 return NULL;
657 }
658
659 p = end;
660 continue;
661 }
662
663 break;
664 }
665
666 if (*p != ']') {
667 media_dbg(media, "Expected ']'\n");
668 *endp = (char *)p;
669 return NULL;
670 }
671
672 *endp = (char *)p + 1;
673 return pad;
674 }
675
set_format(struct media_pad * pad,struct v4l2_mbus_framefmt * format)676 static int set_format(struct media_pad *pad,
677 struct v4l2_mbus_framefmt *format)
678 {
679 int ret;
680
681 if (format->width == 0 || format->height == 0)
682 return 0;
683
684 media_dbg(pad->entity->media,
685 "Setting up format %s %ux%u on pad %s/%u\n",
686 v4l2_subdev_pixelcode_to_string(format->code),
687 format->width, format->height,
688 pad->entity->info.name, pad->index);
689
690 ret = v4l2_subdev_set_format(pad->entity, format, pad->index,
691 V4L2_SUBDEV_FORMAT_ACTIVE);
692 if (ret < 0) {
693 media_dbg(pad->entity->media,
694 "Unable to set format: %s (%d)\n",
695 strerror(-ret), ret);
696 return ret;
697 }
698
699 media_dbg(pad->entity->media,
700 "Format set: %s %ux%u\n",
701 v4l2_subdev_pixelcode_to_string(format->code),
702 format->width, format->height);
703
704 return 0;
705 }
706
set_selection(struct media_pad * pad,unsigned int target,struct v4l2_rect * rect)707 static int set_selection(struct media_pad *pad, unsigned int target,
708 struct v4l2_rect *rect)
709 {
710 int ret;
711
712 if (rect->left == -1 || rect->top == -1)
713 return 0;
714
715 media_dbg(pad->entity->media,
716 "Setting up selection target %u rectangle (%u,%u)/%ux%u on pad %s/%u\n",
717 target, rect->left, rect->top, rect->width, rect->height,
718 pad->entity->info.name, pad->index);
719
720 ret = v4l2_subdev_set_selection(pad->entity, rect, pad->index,
721 target, V4L2_SUBDEV_FORMAT_ACTIVE);
722 if (ret < 0) {
723 media_dbg(pad->entity->media,
724 "Unable to set selection rectangle: %s (%d)\n",
725 strerror(-ret), ret);
726 return ret;
727 }
728
729 media_dbg(pad->entity->media,
730 "Selection rectangle set: (%u,%u)/%ux%u\n",
731 rect->left, rect->top, rect->width, rect->height);
732
733 return 0;
734 }
735
set_frame_interval(struct media_pad * pad,struct v4l2_fract * interval)736 static int set_frame_interval(struct media_pad *pad,
737 struct v4l2_fract *interval)
738 {
739 int ret;
740
741 if (interval->numerator == 0)
742 return 0;
743
744 media_dbg(pad->entity->media,
745 "Setting up frame interval %u/%u on pad %s/%u\n",
746 interval->numerator, interval->denominator,
747 pad->entity->info.name, pad->index);
748
749 ret = v4l2_subdev_set_frame_interval(pad->entity, interval, pad->index);
750 if (ret < 0) {
751 media_dbg(pad->entity->media,
752 "Unable to set frame interval: %s (%d)",
753 strerror(-ret), ret);
754 return ret;
755 }
756
757 media_dbg(pad->entity->media, "Frame interval set: %u/%u\n",
758 interval->numerator, interval->denominator);
759
760 return 0;
761 }
762
763
v4l2_subdev_parse_setup_format(struct media_device * media,const char * p,char ** endp)764 static int v4l2_subdev_parse_setup_format(struct media_device *media,
765 const char *p, char **endp)
766 {
767 struct v4l2_mbus_framefmt format = { 0, 0, 0 };
768 struct media_pad *pad;
769 struct v4l2_rect crop = { -1, -1, -1, -1 };
770 struct v4l2_rect compose = crop;
771 struct v4l2_fract interval = { 0, 0 };
772 unsigned int i;
773 char *end;
774 int ret;
775
776 pad = v4l2_subdev_parse_pad_format(media, &format, &crop, &compose,
777 &interval, p, &end);
778 if (pad == NULL) {
779 media_print_streampos(media, p, end);
780 media_dbg(media, "Unable to parse format\n");
781 return -EINVAL;
782 }
783
784 if (pad->flags & MEDIA_PAD_FL_SINK) {
785 ret = set_format(pad, &format);
786 if (ret < 0)
787 return ret;
788 }
789
790 ret = set_selection(pad, V4L2_SEL_TGT_CROP, &crop);
791 if (ret < 0)
792 return ret;
793
794 ret = set_selection(pad, V4L2_SEL_TGT_COMPOSE, &compose);
795 if (ret < 0)
796 return ret;
797
798 if (pad->flags & MEDIA_PAD_FL_SOURCE) {
799 ret = set_format(pad, &format);
800 if (ret < 0)
801 return ret;
802 }
803
804 ret = set_frame_interval(pad, &interval);
805 if (ret < 0)
806 return ret;
807
808
809 /* If the pad is an output pad, automatically set the same format and
810 * frame interval on the remote subdev input pads, if any.
811 */
812 if (pad->flags & MEDIA_PAD_FL_SOURCE) {
813 for (i = 0; i < pad->entity->num_links; ++i) {
814 struct media_link *link = &pad->entity->links[i];
815 struct v4l2_mbus_framefmt remote_format;
816
817 if (!(link->flags & MEDIA_LNK_FL_ENABLED))
818 continue;
819
820 if (link->source == pad &&
821 link->sink->entity->info.type == MEDIA_ENT_T_V4L2_SUBDEV) {
822 remote_format = format;
823 set_format(link->sink, &remote_format);
824
825 ret = set_frame_interval(link->sink, &interval);
826 if (ret < 0 && ret != -EINVAL && ret != -ENOTTY)
827 return ret;
828 }
829 }
830 }
831
832 *endp = end;
833 return 0;
834 }
835
v4l2_subdev_parse_setup_formats(struct media_device * media,const char * p)836 int v4l2_subdev_parse_setup_formats(struct media_device *media, const char *p)
837 {
838 char *end;
839 int ret;
840
841 do {
842 ret = v4l2_subdev_parse_setup_format(media, p, &end);
843 if (ret < 0)
844 return ret;
845
846 for (; isspace(*end); end++);
847 p = end + 1;
848 } while (*end == ',');
849
850 return *end ? -EINVAL : 0;
851 }
852
853 static const struct {
854 const char *name;
855 enum v4l2_mbus_pixelcode code;
856 } mbus_formats[] = {
857 #include "media-bus-format-names.h"
858 { "FIXED", MEDIA_BUS_FMT_FIXED },
859 { "Y8", MEDIA_BUS_FMT_Y8_1X8 },
860 { "Y10", MEDIA_BUS_FMT_Y10_1X10 },
861 { "Y12", MEDIA_BUS_FMT_Y12_1X12 },
862 { "YUYV", MEDIA_BUS_FMT_YUYV8_1X16 },
863 { "YUYV1_5X8", MEDIA_BUS_FMT_YUYV8_1_5X8 },
864 { "YUYV2X8", MEDIA_BUS_FMT_YUYV8_2X8 },
865 { "UYVY", MEDIA_BUS_FMT_UYVY8_1X16 },
866 { "UYVY1_5X8", MEDIA_BUS_FMT_UYVY8_1_5X8 },
867 { "UYVY2X8", MEDIA_BUS_FMT_UYVY8_2X8 },
868 { "VUY24", MEDIA_BUS_FMT_VUY8_1X24 },
869 { "SBGGR8", MEDIA_BUS_FMT_SBGGR8_1X8 },
870 { "SGBRG8", MEDIA_BUS_FMT_SGBRG8_1X8 },
871 { "SGRBG8", MEDIA_BUS_FMT_SGRBG8_1X8 },
872 { "SRGGB8", MEDIA_BUS_FMT_SRGGB8_1X8 },
873 { "SBGGR10", MEDIA_BUS_FMT_SBGGR10_1X10 },
874 { "SGBRG10", MEDIA_BUS_FMT_SGBRG10_1X10 },
875 { "SGRBG10", MEDIA_BUS_FMT_SGRBG10_1X10 },
876 { "SRGGB10", MEDIA_BUS_FMT_SRGGB10_1X10 },
877 { "SBGGR10_DPCM8", MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8 },
878 { "SGBRG10_DPCM8", MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8 },
879 { "SGRBG10_DPCM8", MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8 },
880 { "SRGGB10_DPCM8", MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8 },
881 { "SBGGR12", MEDIA_BUS_FMT_SBGGR12_1X12 },
882 { "SGBRG12", MEDIA_BUS_FMT_SGBRG12_1X12 },
883 { "SGRBG12", MEDIA_BUS_FMT_SGRBG12_1X12 },
884 { "SRGGB12", MEDIA_BUS_FMT_SRGGB12_1X12 },
885 { "AYUV32", MEDIA_BUS_FMT_AYUV8_1X32 },
886 { "RBG24", MEDIA_BUS_FMT_RBG888_1X24 },
887 { "RGB32", MEDIA_BUS_FMT_RGB888_1X32_PADHI },
888 { "ARGB32", MEDIA_BUS_FMT_ARGB8888_1X32 },
889 };
890
v4l2_subdev_pixelcode_to_string(enum v4l2_mbus_pixelcode code)891 const char *v4l2_subdev_pixelcode_to_string(enum v4l2_mbus_pixelcode code)
892 {
893 unsigned int i;
894
895 for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) {
896 if (mbus_formats[i].code == code)
897 return mbus_formats[i].name;
898 }
899
900 return "unknown";
901 }
902
v4l2_subdev_string_to_pixelcode(const char * string)903 enum v4l2_mbus_pixelcode v4l2_subdev_string_to_pixelcode(const char *string)
904 {
905 unsigned int i;
906
907 for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) {
908 if (strcmp(mbus_formats[i].name, string) == 0)
909 return mbus_formats[i].code;
910 }
911
912 return (enum v4l2_mbus_pixelcode)-1;
913 }
914
915 static struct {
916 const char *name;
917 enum v4l2_field field;
918 } fields[] = {
919 { "any", V4L2_FIELD_ANY },
920 { "none", V4L2_FIELD_NONE },
921 { "top", V4L2_FIELD_TOP },
922 { "bottom", V4L2_FIELD_BOTTOM },
923 { "interlaced", V4L2_FIELD_INTERLACED },
924 { "seq-tb", V4L2_FIELD_SEQ_TB },
925 { "seq-bt", V4L2_FIELD_SEQ_BT },
926 { "alternate", V4L2_FIELD_ALTERNATE },
927 { "interlaced-tb", V4L2_FIELD_INTERLACED_TB },
928 { "interlaced-bt", V4L2_FIELD_INTERLACED_BT },
929 };
930
v4l2_subdev_field_to_string(enum v4l2_field field)931 const char *v4l2_subdev_field_to_string(enum v4l2_field field)
932 {
933 unsigned int i;
934
935 for (i = 0; i < ARRAY_SIZE(fields); ++i) {
936 if (fields[i].field == field)
937 return fields[i].name;
938 }
939
940 return "unknown";
941 }
942
v4l2_subdev_string_to_field(const char * string)943 enum v4l2_field v4l2_subdev_string_to_field(const char *string)
944 {
945 unsigned int i;
946
947 for (i = 0; i < ARRAY_SIZE(fields); ++i) {
948 if (strcasecmp(fields[i].name, string) == 0)
949 return fields[i].field;
950 }
951
952 return (enum v4l2_field)-1;
953 }
954
955 static struct {
956 const char *name;
957 enum v4l2_colorspace colorspace;
958 } colorspaces[] = {
959 { "default", V4L2_COLORSPACE_DEFAULT },
960 { "smpte170m", V4L2_COLORSPACE_SMPTE170M },
961 { "smpte240m", V4L2_COLORSPACE_SMPTE240M },
962 { "rec709", V4L2_COLORSPACE_REC709 },
963 { "470m", V4L2_COLORSPACE_470_SYSTEM_M },
964 { "470bg", V4L2_COLORSPACE_470_SYSTEM_BG },
965 { "jpeg", V4L2_COLORSPACE_JPEG },
966 { "srgb", V4L2_COLORSPACE_SRGB },
967 { "oprgb", V4L2_COLORSPACE_OPRGB },
968 { "bt2020", V4L2_COLORSPACE_BT2020 },
969 { "raw", V4L2_COLORSPACE_RAW },
970 { "dcip3", V4L2_COLORSPACE_DCI_P3 },
971 };
972
v4l2_subdev_colorspace_to_string(enum v4l2_colorspace colorspace)973 const char *v4l2_subdev_colorspace_to_string(enum v4l2_colorspace colorspace)
974 {
975 unsigned int i;
976
977 for (i = 0; i < ARRAY_SIZE(colorspaces); ++i) {
978 if (colorspaces[i].colorspace == colorspace)
979 return colorspaces[i].name;
980 }
981
982 return "unknown";
983 }
984
v4l2_subdev_string_to_colorspace(const char * string)985 enum v4l2_colorspace v4l2_subdev_string_to_colorspace(const char *string)
986 {
987 unsigned int i;
988
989 for (i = 0; i < ARRAY_SIZE(colorspaces); ++i) {
990 if (strcasecmp(colorspaces[i].name, string) == 0)
991 return colorspaces[i].colorspace;
992 }
993
994 return (enum v4l2_colorspace)-1;
995 }
996
997 static struct {
998 const char *name;
999 enum v4l2_xfer_func xfer_func;
1000 } xfer_funcs[] = {
1001 { "default", V4L2_XFER_FUNC_DEFAULT },
1002 { "709", V4L2_XFER_FUNC_709 },
1003 { "srgb", V4L2_XFER_FUNC_SRGB },
1004 { "oprgb", V4L2_XFER_FUNC_OPRGB },
1005 { "smpte240m", V4L2_XFER_FUNC_SMPTE240M },
1006 { "smpte2084", V4L2_XFER_FUNC_SMPTE2084 },
1007 { "dcip3", V4L2_XFER_FUNC_DCI_P3 },
1008 { "none", V4L2_XFER_FUNC_NONE },
1009 };
1010
v4l2_subdev_xfer_func_to_string(enum v4l2_xfer_func xfer_func)1011 const char *v4l2_subdev_xfer_func_to_string(enum v4l2_xfer_func xfer_func)
1012 {
1013 unsigned int i;
1014
1015 for (i = 0; i < ARRAY_SIZE(xfer_funcs); ++i) {
1016 if (xfer_funcs[i].xfer_func == xfer_func)
1017 return xfer_funcs[i].name;
1018 }
1019
1020 return "unknown";
1021 }
1022
v4l2_subdev_string_to_xfer_func(const char * string)1023 enum v4l2_xfer_func v4l2_subdev_string_to_xfer_func(const char *string)
1024 {
1025 unsigned int i;
1026
1027 for (i = 0; i < ARRAY_SIZE(xfer_funcs); ++i) {
1028 if (strcasecmp(xfer_funcs[i].name, string) == 0)
1029 return xfer_funcs[i].xfer_func;
1030 }
1031
1032 return (enum v4l2_xfer_func)-1;
1033 }
1034
1035 static struct {
1036 const char *name;
1037 enum v4l2_ycbcr_encoding ycbcr_enc;
1038 } ycbcr_encs[] = {
1039 { "default", V4L2_YCBCR_ENC_DEFAULT },
1040 { "601", V4L2_YCBCR_ENC_601 },
1041 { "709", V4L2_YCBCR_ENC_709 },
1042 { "xv601", V4L2_YCBCR_ENC_XV601 },
1043 { "xv709", V4L2_YCBCR_ENC_XV709 },
1044 { "bt2020", V4L2_YCBCR_ENC_BT2020 },
1045 { "bt2020c", V4L2_YCBCR_ENC_BT2020_CONST_LUM },
1046 { "smpte240m", V4L2_YCBCR_ENC_SMPTE240M },
1047 };
1048
v4l2_subdev_ycbcr_encoding_to_string(enum v4l2_ycbcr_encoding ycbcr_enc)1049 const char *v4l2_subdev_ycbcr_encoding_to_string(enum v4l2_ycbcr_encoding ycbcr_enc)
1050 {
1051 unsigned int i;
1052
1053 for (i = 0; i < ARRAY_SIZE(ycbcr_encs); ++i) {
1054 if (ycbcr_encs[i].ycbcr_enc == ycbcr_enc)
1055 return ycbcr_encs[i].name;
1056 }
1057
1058 return "unknown";
1059 }
1060
v4l2_subdev_string_to_ycbcr_encoding(const char * string)1061 enum v4l2_ycbcr_encoding v4l2_subdev_string_to_ycbcr_encoding(const char *string)
1062 {
1063 unsigned int i;
1064
1065 for (i = 0; i < ARRAY_SIZE(ycbcr_encs); ++i) {
1066 if (strcasecmp(ycbcr_encs[i].name, string) == 0)
1067 return ycbcr_encs[i].ycbcr_enc;
1068 }
1069
1070 return (enum v4l2_ycbcr_encoding)-1;
1071 }
1072
1073 static struct {
1074 const char *name;
1075 enum v4l2_quantization quantization;
1076 } quantizations[] = {
1077 { "default", V4L2_QUANTIZATION_DEFAULT },
1078 { "full-range", V4L2_QUANTIZATION_FULL_RANGE },
1079 { "lim-range", V4L2_QUANTIZATION_LIM_RANGE },
1080 };
1081
v4l2_subdev_quantization_to_string(enum v4l2_quantization quantization)1082 const char *v4l2_subdev_quantization_to_string(enum v4l2_quantization quantization)
1083 {
1084 unsigned int i;
1085
1086 for (i = 0; i < ARRAY_SIZE(quantizations); ++i) {
1087 if (quantizations[i].quantization == quantization)
1088 return quantizations[i].name;
1089 }
1090
1091 return "unknown";
1092 }
1093
v4l2_subdev_string_to_quantization(const char * string)1094 enum v4l2_quantization v4l2_subdev_string_to_quantization(const char *string)
1095 {
1096 unsigned int i;
1097
1098 for (i = 0; i < ARRAY_SIZE(quantizations); ++i) {
1099 if (strcasecmp(quantizations[i].name, string) == 0)
1100 return quantizations[i].quantization;
1101 }
1102
1103 return (enum v4l2_quantization)-1;
1104 }
1105
1106 static const enum v4l2_mbus_pixelcode mbus_codes[] = {
1107 #include "media-bus-format-codes.h"
1108 };
1109
v4l2_subdev_pixelcode_list(unsigned int * length)1110 const enum v4l2_mbus_pixelcode *v4l2_subdev_pixelcode_list(unsigned int *length)
1111 {
1112 *length = ARRAY_SIZE(mbus_codes);
1113
1114 return mbus_codes;
1115 }
1116