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