1 /*
2 * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
3 *
4 * Copyright (c) 2016 Mentor Graphics Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11 #include <linux/module.h>
12 #include "imx-media.h"
13
14 /*
15 * List of supported pixel formats for the subdevs.
16 *
17 * In all of these tables, the non-mbus formats (with no
18 * mbus codes) must all fall at the end of the table.
19 */
20
21 static const struct imx_media_pixfmt yuv_formats[] = {
22 {
23 .fourcc = V4L2_PIX_FMT_UYVY,
24 .codes = {
25 MEDIA_BUS_FMT_UYVY8_2X8,
26 MEDIA_BUS_FMT_UYVY8_1X16
27 },
28 .cs = IPUV3_COLORSPACE_YUV,
29 .bpp = 16,
30 }, {
31 .fourcc = V4L2_PIX_FMT_YUYV,
32 .codes = {
33 MEDIA_BUS_FMT_YUYV8_2X8,
34 MEDIA_BUS_FMT_YUYV8_1X16
35 },
36 .cs = IPUV3_COLORSPACE_YUV,
37 .bpp = 16,
38 },
39 /***
40 * non-mbus YUV formats start here. NOTE! when adding non-mbus
41 * formats, NUM_NON_MBUS_YUV_FORMATS must be updated below.
42 ***/
43 {
44 .fourcc = V4L2_PIX_FMT_YUV420,
45 .cs = IPUV3_COLORSPACE_YUV,
46 .bpp = 12,
47 .planar = true,
48 }, {
49 .fourcc = V4L2_PIX_FMT_YVU420,
50 .cs = IPUV3_COLORSPACE_YUV,
51 .bpp = 12,
52 .planar = true,
53 }, {
54 .fourcc = V4L2_PIX_FMT_YUV422P,
55 .cs = IPUV3_COLORSPACE_YUV,
56 .bpp = 16,
57 .planar = true,
58 }, {
59 .fourcc = V4L2_PIX_FMT_NV12,
60 .cs = IPUV3_COLORSPACE_YUV,
61 .bpp = 12,
62 .planar = true,
63 }, {
64 .fourcc = V4L2_PIX_FMT_NV16,
65 .cs = IPUV3_COLORSPACE_YUV,
66 .bpp = 16,
67 .planar = true,
68 },
69 };
70
71 #define NUM_NON_MBUS_YUV_FORMATS 5
72 #define NUM_YUV_FORMATS ARRAY_SIZE(yuv_formats)
73 #define NUM_MBUS_YUV_FORMATS (NUM_YUV_FORMATS - NUM_NON_MBUS_YUV_FORMATS)
74
75 static const struct imx_media_pixfmt rgb_formats[] = {
76 {
77 .fourcc = V4L2_PIX_FMT_RGB565,
78 .codes = {MEDIA_BUS_FMT_RGB565_2X8_LE},
79 .cs = IPUV3_COLORSPACE_RGB,
80 .bpp = 16,
81 .cycles = 2,
82 }, {
83 .fourcc = V4L2_PIX_FMT_RGB24,
84 .codes = {
85 MEDIA_BUS_FMT_RGB888_1X24,
86 MEDIA_BUS_FMT_RGB888_2X12_LE
87 },
88 .cs = IPUV3_COLORSPACE_RGB,
89 .bpp = 24,
90 }, {
91 .fourcc = V4L2_PIX_FMT_RGB32,
92 .codes = {MEDIA_BUS_FMT_ARGB8888_1X32},
93 .cs = IPUV3_COLORSPACE_RGB,
94 .bpp = 32,
95 .ipufmt = true,
96 },
97 /*** raw bayer and grayscale formats start here ***/
98 {
99 .fourcc = V4L2_PIX_FMT_SBGGR8,
100 .codes = {MEDIA_BUS_FMT_SBGGR8_1X8},
101 .cs = IPUV3_COLORSPACE_RGB,
102 .bpp = 8,
103 .bayer = true,
104 }, {
105 .fourcc = V4L2_PIX_FMT_SGBRG8,
106 .codes = {MEDIA_BUS_FMT_SGBRG8_1X8},
107 .cs = IPUV3_COLORSPACE_RGB,
108 .bpp = 8,
109 .bayer = true,
110 }, {
111 .fourcc = V4L2_PIX_FMT_SGRBG8,
112 .codes = {MEDIA_BUS_FMT_SGRBG8_1X8},
113 .cs = IPUV3_COLORSPACE_RGB,
114 .bpp = 8,
115 .bayer = true,
116 }, {
117 .fourcc = V4L2_PIX_FMT_SRGGB8,
118 .codes = {MEDIA_BUS_FMT_SRGGB8_1X8},
119 .cs = IPUV3_COLORSPACE_RGB,
120 .bpp = 8,
121 .bayer = true,
122 }, {
123 .fourcc = V4L2_PIX_FMT_SBGGR16,
124 .codes = {
125 MEDIA_BUS_FMT_SBGGR10_1X10,
126 MEDIA_BUS_FMT_SBGGR12_1X12,
127 MEDIA_BUS_FMT_SBGGR14_1X14,
128 MEDIA_BUS_FMT_SBGGR16_1X16
129 },
130 .cs = IPUV3_COLORSPACE_RGB,
131 .bpp = 16,
132 .bayer = true,
133 }, {
134 .fourcc = V4L2_PIX_FMT_SGBRG16,
135 .codes = {
136 MEDIA_BUS_FMT_SGBRG10_1X10,
137 MEDIA_BUS_FMT_SGBRG12_1X12,
138 MEDIA_BUS_FMT_SGBRG14_1X14,
139 MEDIA_BUS_FMT_SGBRG16_1X16,
140 },
141 .cs = IPUV3_COLORSPACE_RGB,
142 .bpp = 16,
143 .bayer = true,
144 }, {
145 .fourcc = V4L2_PIX_FMT_SGRBG16,
146 .codes = {
147 MEDIA_BUS_FMT_SGRBG10_1X10,
148 MEDIA_BUS_FMT_SGRBG12_1X12,
149 MEDIA_BUS_FMT_SGRBG14_1X14,
150 MEDIA_BUS_FMT_SGRBG16_1X16,
151 },
152 .cs = IPUV3_COLORSPACE_RGB,
153 .bpp = 16,
154 .bayer = true,
155 }, {
156 .fourcc = V4L2_PIX_FMT_SRGGB16,
157 .codes = {
158 MEDIA_BUS_FMT_SRGGB10_1X10,
159 MEDIA_BUS_FMT_SRGGB12_1X12,
160 MEDIA_BUS_FMT_SRGGB14_1X14,
161 MEDIA_BUS_FMT_SRGGB16_1X16,
162 },
163 .cs = IPUV3_COLORSPACE_RGB,
164 .bpp = 16,
165 .bayer = true,
166 }, {
167 .fourcc = V4L2_PIX_FMT_GREY,
168 .codes = {MEDIA_BUS_FMT_Y8_1X8},
169 .cs = IPUV3_COLORSPACE_RGB,
170 .bpp = 8,
171 .bayer = true,
172 }, {
173 .fourcc = V4L2_PIX_FMT_Y16,
174 .codes = {
175 MEDIA_BUS_FMT_Y10_1X10,
176 MEDIA_BUS_FMT_Y12_1X12,
177 },
178 .cs = IPUV3_COLORSPACE_RGB,
179 .bpp = 16,
180 .bayer = true,
181 },
182 /***
183 * non-mbus RGB formats start here. NOTE! when adding non-mbus
184 * formats, NUM_NON_MBUS_RGB_FORMATS must be updated below.
185 ***/
186 {
187 .fourcc = V4L2_PIX_FMT_BGR24,
188 .cs = IPUV3_COLORSPACE_RGB,
189 .bpp = 24,
190 }, {
191 .fourcc = V4L2_PIX_FMT_BGR32,
192 .cs = IPUV3_COLORSPACE_RGB,
193 .bpp = 32,
194 },
195 };
196
197 #define NUM_NON_MBUS_RGB_FORMATS 2
198 #define NUM_RGB_FORMATS ARRAY_SIZE(rgb_formats)
199 #define NUM_MBUS_RGB_FORMATS (NUM_RGB_FORMATS - NUM_NON_MBUS_RGB_FORMATS)
200
201 static const struct imx_media_pixfmt ipu_yuv_formats[] = {
202 {
203 .fourcc = V4L2_PIX_FMT_YUV32,
204 .codes = {MEDIA_BUS_FMT_AYUV8_1X32},
205 .cs = IPUV3_COLORSPACE_YUV,
206 .bpp = 32,
207 .ipufmt = true,
208 },
209 };
210
211 #define NUM_IPU_YUV_FORMATS ARRAY_SIZE(ipu_yuv_formats)
212
213 static const struct imx_media_pixfmt ipu_rgb_formats[] = {
214 {
215 .fourcc = V4L2_PIX_FMT_RGB32,
216 .codes = {MEDIA_BUS_FMT_ARGB8888_1X32},
217 .cs = IPUV3_COLORSPACE_RGB,
218 .bpp = 32,
219 .ipufmt = true,
220 },
221 };
222
223 #define NUM_IPU_RGB_FORMATS ARRAY_SIZE(ipu_rgb_formats)
224
init_mbus_colorimetry(struct v4l2_mbus_framefmt * mbus,const struct imx_media_pixfmt * fmt)225 static void init_mbus_colorimetry(struct v4l2_mbus_framefmt *mbus,
226 const struct imx_media_pixfmt *fmt)
227 {
228 mbus->colorspace = (fmt->cs == IPUV3_COLORSPACE_RGB) ?
229 V4L2_COLORSPACE_SRGB : V4L2_COLORSPACE_SMPTE170M;
230 mbus->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(mbus->colorspace);
231 mbus->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mbus->colorspace);
232 mbus->quantization =
233 V4L2_MAP_QUANTIZATION_DEFAULT(fmt->cs == IPUV3_COLORSPACE_RGB,
234 mbus->colorspace,
235 mbus->ycbcr_enc);
236 }
237
238 static const
__find_format(u32 fourcc,u32 code,bool allow_non_mbus,bool allow_bayer,const struct imx_media_pixfmt * array,u32 array_size)239 struct imx_media_pixfmt *__find_format(u32 fourcc,
240 u32 code,
241 bool allow_non_mbus,
242 bool allow_bayer,
243 const struct imx_media_pixfmt *array,
244 u32 array_size)
245 {
246 const struct imx_media_pixfmt *fmt;
247 int i, j;
248
249 for (i = 0; i < array_size; i++) {
250 fmt = &array[i];
251
252 if ((!allow_non_mbus && !fmt->codes[0]) ||
253 (!allow_bayer && fmt->bayer))
254 continue;
255
256 if (fourcc && fmt->fourcc == fourcc)
257 return fmt;
258
259 if (!code)
260 continue;
261
262 for (j = 0; fmt->codes[j]; j++) {
263 if (code == fmt->codes[j])
264 return fmt;
265 }
266 }
267 return NULL;
268 }
269
find_format(u32 fourcc,u32 code,enum codespace_sel cs_sel,bool allow_non_mbus,bool allow_bayer)270 static const struct imx_media_pixfmt *find_format(u32 fourcc,
271 u32 code,
272 enum codespace_sel cs_sel,
273 bool allow_non_mbus,
274 bool allow_bayer)
275 {
276 const struct imx_media_pixfmt *ret;
277
278 switch (cs_sel) {
279 case CS_SEL_YUV:
280 return __find_format(fourcc, code, allow_non_mbus, allow_bayer,
281 yuv_formats, NUM_YUV_FORMATS);
282 case CS_SEL_RGB:
283 return __find_format(fourcc, code, allow_non_mbus, allow_bayer,
284 rgb_formats, NUM_RGB_FORMATS);
285 case CS_SEL_ANY:
286 ret = __find_format(fourcc, code, allow_non_mbus, allow_bayer,
287 yuv_formats, NUM_YUV_FORMATS);
288 if (ret)
289 return ret;
290 return __find_format(fourcc, code, allow_non_mbus, allow_bayer,
291 rgb_formats, NUM_RGB_FORMATS);
292 default:
293 return NULL;
294 }
295 }
296
enum_format(u32 * fourcc,u32 * code,u32 index,enum codespace_sel cs_sel,bool allow_non_mbus,bool allow_bayer)297 static int enum_format(u32 *fourcc, u32 *code, u32 index,
298 enum codespace_sel cs_sel,
299 bool allow_non_mbus,
300 bool allow_bayer)
301 {
302 const struct imx_media_pixfmt *fmt;
303 u32 mbus_yuv_sz = NUM_MBUS_YUV_FORMATS;
304 u32 mbus_rgb_sz = NUM_MBUS_RGB_FORMATS;
305 u32 yuv_sz = NUM_YUV_FORMATS;
306 u32 rgb_sz = NUM_RGB_FORMATS;
307
308 switch (cs_sel) {
309 case CS_SEL_YUV:
310 if (index >= yuv_sz ||
311 (!allow_non_mbus && index >= mbus_yuv_sz))
312 return -EINVAL;
313 fmt = &yuv_formats[index];
314 break;
315 case CS_SEL_RGB:
316 if (index >= rgb_sz ||
317 (!allow_non_mbus && index >= mbus_rgb_sz))
318 return -EINVAL;
319 fmt = &rgb_formats[index];
320 if (!allow_bayer && fmt->bayer)
321 return -EINVAL;
322 break;
323 case CS_SEL_ANY:
324 if (!allow_non_mbus) {
325 if (index >= mbus_yuv_sz) {
326 index -= mbus_yuv_sz;
327 if (index >= mbus_rgb_sz)
328 return -EINVAL;
329 fmt = &rgb_formats[index];
330 if (!allow_bayer && fmt->bayer)
331 return -EINVAL;
332 } else {
333 fmt = &yuv_formats[index];
334 }
335 } else {
336 if (index >= yuv_sz + rgb_sz)
337 return -EINVAL;
338 if (index >= yuv_sz) {
339 fmt = &rgb_formats[index - yuv_sz];
340 if (!allow_bayer && fmt->bayer)
341 return -EINVAL;
342 } else {
343 fmt = &yuv_formats[index];
344 }
345 }
346 break;
347 default:
348 return -EINVAL;
349 }
350
351 if (fourcc)
352 *fourcc = fmt->fourcc;
353 if (code)
354 *code = fmt->codes[0];
355
356 return 0;
357 }
358
359 const struct imx_media_pixfmt *
imx_media_find_format(u32 fourcc,enum codespace_sel cs_sel,bool allow_bayer)360 imx_media_find_format(u32 fourcc, enum codespace_sel cs_sel, bool allow_bayer)
361 {
362 return find_format(fourcc, 0, cs_sel, true, allow_bayer);
363 }
364 EXPORT_SYMBOL_GPL(imx_media_find_format);
365
imx_media_enum_format(u32 * fourcc,u32 index,enum codespace_sel cs_sel)366 int imx_media_enum_format(u32 *fourcc, u32 index, enum codespace_sel cs_sel)
367 {
368 return enum_format(fourcc, NULL, index, cs_sel, true, false);
369 }
370 EXPORT_SYMBOL_GPL(imx_media_enum_format);
371
372 const struct imx_media_pixfmt *
imx_media_find_mbus_format(u32 code,enum codespace_sel cs_sel,bool allow_bayer)373 imx_media_find_mbus_format(u32 code, enum codespace_sel cs_sel,
374 bool allow_bayer)
375 {
376 return find_format(0, code, cs_sel, false, allow_bayer);
377 }
378 EXPORT_SYMBOL_GPL(imx_media_find_mbus_format);
379
imx_media_enum_mbus_format(u32 * code,u32 index,enum codespace_sel cs_sel,bool allow_bayer)380 int imx_media_enum_mbus_format(u32 *code, u32 index, enum codespace_sel cs_sel,
381 bool allow_bayer)
382 {
383 return enum_format(NULL, code, index, cs_sel, false, allow_bayer);
384 }
385 EXPORT_SYMBOL_GPL(imx_media_enum_mbus_format);
386
387 const struct imx_media_pixfmt *
imx_media_find_ipu_format(u32 code,enum codespace_sel cs_sel)388 imx_media_find_ipu_format(u32 code, enum codespace_sel cs_sel)
389 {
390 const struct imx_media_pixfmt *array, *fmt, *ret = NULL;
391 u32 array_size;
392 int i, j;
393
394 switch (cs_sel) {
395 case CS_SEL_YUV:
396 array_size = NUM_IPU_YUV_FORMATS;
397 array = ipu_yuv_formats;
398 break;
399 case CS_SEL_RGB:
400 array_size = NUM_IPU_RGB_FORMATS;
401 array = ipu_rgb_formats;
402 break;
403 case CS_SEL_ANY:
404 array_size = NUM_IPU_YUV_FORMATS + NUM_IPU_RGB_FORMATS;
405 array = ipu_yuv_formats;
406 break;
407 default:
408 return NULL;
409 }
410
411 for (i = 0; i < array_size; i++) {
412 if (cs_sel == CS_SEL_ANY && i >= NUM_IPU_YUV_FORMATS)
413 fmt = &ipu_rgb_formats[i - NUM_IPU_YUV_FORMATS];
414 else
415 fmt = &array[i];
416
417 for (j = 0; code && fmt->codes[j]; j++) {
418 if (code == fmt->codes[j]) {
419 ret = fmt;
420 goto out;
421 }
422 }
423 }
424
425 out:
426 return ret;
427 }
428 EXPORT_SYMBOL_GPL(imx_media_find_ipu_format);
429
imx_media_enum_ipu_format(u32 * code,u32 index,enum codespace_sel cs_sel)430 int imx_media_enum_ipu_format(u32 *code, u32 index, enum codespace_sel cs_sel)
431 {
432 switch (cs_sel) {
433 case CS_SEL_YUV:
434 if (index >= NUM_IPU_YUV_FORMATS)
435 return -EINVAL;
436 *code = ipu_yuv_formats[index].codes[0];
437 break;
438 case CS_SEL_RGB:
439 if (index >= NUM_IPU_RGB_FORMATS)
440 return -EINVAL;
441 *code = ipu_rgb_formats[index].codes[0];
442 break;
443 case CS_SEL_ANY:
444 if (index >= NUM_IPU_YUV_FORMATS + NUM_IPU_RGB_FORMATS)
445 return -EINVAL;
446 if (index >= NUM_IPU_YUV_FORMATS) {
447 index -= NUM_IPU_YUV_FORMATS;
448 *code = ipu_rgb_formats[index].codes[0];
449 } else {
450 *code = ipu_yuv_formats[index].codes[0];
451 }
452 break;
453 default:
454 return -EINVAL;
455 }
456
457 return 0;
458 }
459 EXPORT_SYMBOL_GPL(imx_media_enum_ipu_format);
460
imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt * mbus,u32 width,u32 height,u32 code,u32 field,const struct imx_media_pixfmt ** cc)461 int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
462 u32 width, u32 height, u32 code, u32 field,
463 const struct imx_media_pixfmt **cc)
464 {
465 const struct imx_media_pixfmt *lcc;
466
467 mbus->width = width;
468 mbus->height = height;
469 mbus->field = field;
470 if (code == 0)
471 imx_media_enum_mbus_format(&code, 0, CS_SEL_YUV, false);
472 lcc = imx_media_find_mbus_format(code, CS_SEL_ANY, false);
473 if (!lcc) {
474 lcc = imx_media_find_ipu_format(code, CS_SEL_ANY);
475 if (!lcc)
476 return -EINVAL;
477 }
478
479 mbus->code = code;
480 init_mbus_colorimetry(mbus, lcc);
481 if (cc)
482 *cc = lcc;
483
484 return 0;
485 }
486 EXPORT_SYMBOL_GPL(imx_media_init_mbus_fmt);
487
488 /*
489 * Initializes the TRY format to the ACTIVE format on all pads
490 * of a subdev. Can be used as the .init_cfg pad operation.
491 */
imx_media_init_cfg(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg)492 int imx_media_init_cfg(struct v4l2_subdev *sd,
493 struct v4l2_subdev_pad_config *cfg)
494 {
495 struct v4l2_mbus_framefmt *mf_try;
496 struct v4l2_subdev_format format;
497 unsigned int pad;
498 int ret;
499
500 for (pad = 0; pad < sd->entity.num_pads; pad++) {
501 memset(&format, 0, sizeof(format));
502
503 format.pad = pad;
504 format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
505 ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &format);
506 if (ret)
507 continue;
508
509 mf_try = v4l2_subdev_get_try_format(sd, cfg, pad);
510 *mf_try = format.format;
511 }
512
513 return 0;
514 }
515 EXPORT_SYMBOL_GPL(imx_media_init_cfg);
516
517 /*
518 * Check whether the field and colorimetry parameters in tryfmt are
519 * uninitialized, and if so fill them with the values from fmt,
520 * or if tryfmt->colorspace has been initialized, all the default
521 * colorimetry params can be derived from tryfmt->colorspace.
522 *
523 * tryfmt->code must be set on entry.
524 *
525 * If this format is destined to be routed through the Image Converter,
526 * quantization and Y`CbCr encoding must be fixed. The IC expects and
527 * produces fixed quantization and Y`CbCr encoding at its input and output
528 * (full range for RGB, limited range for YUV, and V4L2_YCBCR_ENC_601).
529 */
imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt * tryfmt,struct v4l2_mbus_framefmt * fmt,bool ic_route)530 void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt,
531 struct v4l2_mbus_framefmt *fmt,
532 bool ic_route)
533 {
534 const struct imx_media_pixfmt *cc;
535 bool is_rgb = false;
536
537 cc = imx_media_find_mbus_format(tryfmt->code, CS_SEL_ANY, true);
538 if (!cc)
539 cc = imx_media_find_ipu_format(tryfmt->code, CS_SEL_ANY);
540 if (cc && cc->cs != IPUV3_COLORSPACE_YUV)
541 is_rgb = true;
542
543 /* fill field if necessary */
544 if (tryfmt->field == V4L2_FIELD_ANY)
545 tryfmt->field = fmt->field;
546
547 /* fill colorimetry if necessary */
548 if (tryfmt->colorspace == V4L2_COLORSPACE_DEFAULT) {
549 tryfmt->colorspace = fmt->colorspace;
550 tryfmt->xfer_func = fmt->xfer_func;
551 tryfmt->ycbcr_enc = fmt->ycbcr_enc;
552 tryfmt->quantization = fmt->quantization;
553 } else {
554 if (tryfmt->xfer_func == V4L2_XFER_FUNC_DEFAULT) {
555 tryfmt->xfer_func =
556 V4L2_MAP_XFER_FUNC_DEFAULT(tryfmt->colorspace);
557 }
558 if (tryfmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
559 tryfmt->ycbcr_enc =
560 V4L2_MAP_YCBCR_ENC_DEFAULT(tryfmt->colorspace);
561 }
562 if (tryfmt->quantization == V4L2_QUANTIZATION_DEFAULT) {
563 tryfmt->quantization =
564 V4L2_MAP_QUANTIZATION_DEFAULT(
565 is_rgb, tryfmt->colorspace,
566 tryfmt->ycbcr_enc);
567 }
568 }
569
570 if (ic_route) {
571 tryfmt->quantization = is_rgb ?
572 V4L2_QUANTIZATION_FULL_RANGE :
573 V4L2_QUANTIZATION_LIM_RANGE;
574 tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
575 }
576 }
577 EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields);
578
imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format * pix,struct v4l2_mbus_framefmt * mbus,const struct imx_media_pixfmt * cc)579 int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
580 struct v4l2_mbus_framefmt *mbus,
581 const struct imx_media_pixfmt *cc)
582 {
583 u32 stride;
584
585 if (!cc) {
586 cc = imx_media_find_ipu_format(mbus->code, CS_SEL_ANY);
587 if (!cc)
588 cc = imx_media_find_mbus_format(mbus->code, CS_SEL_ANY,
589 true);
590 if (!cc)
591 return -EINVAL;
592 }
593
594 /*
595 * TODO: the IPU currently does not support the AYUV32 format,
596 * so until it does convert to a supported YUV format.
597 */
598 if (cc->ipufmt && cc->cs == IPUV3_COLORSPACE_YUV) {
599 u32 code;
600
601 imx_media_enum_mbus_format(&code, 0, CS_SEL_YUV, false);
602 cc = imx_media_find_mbus_format(code, CS_SEL_YUV, false);
603 }
604
605 stride = cc->planar ? mbus->width : (mbus->width * cc->bpp) >> 3;
606
607 pix->width = mbus->width;
608 pix->height = mbus->height;
609 pix->pixelformat = cc->fourcc;
610 pix->colorspace = mbus->colorspace;
611 pix->xfer_func = mbus->xfer_func;
612 pix->ycbcr_enc = mbus->ycbcr_enc;
613 pix->quantization = mbus->quantization;
614 pix->field = mbus->field;
615 pix->bytesperline = stride;
616 pix->sizeimage = (pix->width * pix->height * cc->bpp) >> 3;
617
618 return 0;
619 }
620 EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_pix_fmt);
621
imx_media_mbus_fmt_to_ipu_image(struct ipu_image * image,struct v4l2_mbus_framefmt * mbus)622 int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image,
623 struct v4l2_mbus_framefmt *mbus)
624 {
625 int ret;
626
627 memset(image, 0, sizeof(*image));
628
629 ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, mbus, NULL);
630 if (ret)
631 return ret;
632
633 image->rect.width = mbus->width;
634 image->rect.height = mbus->height;
635
636 return 0;
637 }
638 EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_ipu_image);
639
imx_media_ipu_image_to_mbus_fmt(struct v4l2_mbus_framefmt * mbus,struct ipu_image * image)640 int imx_media_ipu_image_to_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
641 struct ipu_image *image)
642 {
643 const struct imx_media_pixfmt *fmt;
644
645 fmt = imx_media_find_format(image->pix.pixelformat, CS_SEL_ANY, true);
646 if (!fmt)
647 return -EINVAL;
648
649 memset(mbus, 0, sizeof(*mbus));
650 mbus->width = image->pix.width;
651 mbus->height = image->pix.height;
652 mbus->code = fmt->codes[0];
653 mbus->field = image->pix.field;
654 mbus->colorspace = image->pix.colorspace;
655 mbus->xfer_func = image->pix.xfer_func;
656 mbus->ycbcr_enc = image->pix.ycbcr_enc;
657 mbus->quantization = image->pix.quantization;
658
659 return 0;
660 }
661 EXPORT_SYMBOL_GPL(imx_media_ipu_image_to_mbus_fmt);
662
imx_media_free_dma_buf(struct imx_media_dev * imxmd,struct imx_media_dma_buf * buf)663 void imx_media_free_dma_buf(struct imx_media_dev *imxmd,
664 struct imx_media_dma_buf *buf)
665 {
666 if (buf->virt)
667 dma_free_coherent(imxmd->md.dev, buf->len,
668 buf->virt, buf->phys);
669
670 buf->virt = NULL;
671 buf->phys = 0;
672 }
673 EXPORT_SYMBOL_GPL(imx_media_free_dma_buf);
674
imx_media_alloc_dma_buf(struct imx_media_dev * imxmd,struct imx_media_dma_buf * buf,int size)675 int imx_media_alloc_dma_buf(struct imx_media_dev *imxmd,
676 struct imx_media_dma_buf *buf,
677 int size)
678 {
679 imx_media_free_dma_buf(imxmd, buf);
680
681 buf->len = PAGE_ALIGN(size);
682 buf->virt = dma_alloc_coherent(imxmd->md.dev, buf->len, &buf->phys,
683 GFP_DMA | GFP_KERNEL);
684 if (!buf->virt) {
685 dev_err(imxmd->md.dev, "failed to alloc dma buffer\n");
686 return -ENOMEM;
687 }
688
689 return 0;
690 }
691 EXPORT_SYMBOL_GPL(imx_media_alloc_dma_buf);
692
693 /* form a subdev name given a group id and ipu id */
imx_media_grp_id_to_sd_name(char * sd_name,int sz,u32 grp_id,int ipu_id)694 void imx_media_grp_id_to_sd_name(char *sd_name, int sz, u32 grp_id, int ipu_id)
695 {
696 int id;
697
698 switch (grp_id) {
699 case IMX_MEDIA_GRP_ID_CSI0...IMX_MEDIA_GRP_ID_CSI1:
700 id = (grp_id >> IMX_MEDIA_GRP_ID_CSI_BIT) - 1;
701 snprintf(sd_name, sz, "ipu%d_csi%d", ipu_id + 1, id);
702 break;
703 case IMX_MEDIA_GRP_ID_VDIC:
704 snprintf(sd_name, sz, "ipu%d_vdic", ipu_id + 1);
705 break;
706 case IMX_MEDIA_GRP_ID_IC_PRP:
707 snprintf(sd_name, sz, "ipu%d_ic_prp", ipu_id + 1);
708 break;
709 case IMX_MEDIA_GRP_ID_IC_PRPENC:
710 snprintf(sd_name, sz, "ipu%d_ic_prpenc", ipu_id + 1);
711 break;
712 case IMX_MEDIA_GRP_ID_IC_PRPVF:
713 snprintf(sd_name, sz, "ipu%d_ic_prpvf", ipu_id + 1);
714 break;
715 default:
716 break;
717 }
718 }
719 EXPORT_SYMBOL_GPL(imx_media_grp_id_to_sd_name);
720
721 struct v4l2_subdev *
imx_media_find_subdev_by_fwnode(struct imx_media_dev * imxmd,struct fwnode_handle * fwnode)722 imx_media_find_subdev_by_fwnode(struct imx_media_dev *imxmd,
723 struct fwnode_handle *fwnode)
724 {
725 struct v4l2_subdev *sd;
726
727 list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
728 if (sd->fwnode == fwnode)
729 return sd;
730 }
731
732 return NULL;
733 }
734 EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_fwnode);
735
736 struct v4l2_subdev *
imx_media_find_subdev_by_devname(struct imx_media_dev * imxmd,const char * devname)737 imx_media_find_subdev_by_devname(struct imx_media_dev *imxmd,
738 const char *devname)
739 {
740 struct v4l2_subdev *sd;
741
742 list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
743 if (!strcmp(devname, dev_name(sd->dev)))
744 return sd;
745 }
746
747 return NULL;
748 }
749 EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_devname);
750
751 /*
752 * Adds a video device to the master video device list. This is called by
753 * an async subdev that owns a video device when it is registered.
754 */
imx_media_add_video_device(struct imx_media_dev * imxmd,struct imx_media_video_dev * vdev)755 int imx_media_add_video_device(struct imx_media_dev *imxmd,
756 struct imx_media_video_dev *vdev)
757 {
758 mutex_lock(&imxmd->mutex);
759
760 list_add_tail(&vdev->list, &imxmd->vdev_list);
761
762 mutex_unlock(&imxmd->mutex);
763 return 0;
764 }
765 EXPORT_SYMBOL_GPL(imx_media_add_video_device);
766
767 /*
768 * Search upstream/downstream for a subdevice in the current pipeline
769 * with given grp_id, starting from start_entity. Returns the subdev's
770 * source/sink pad that it was reached from. If grp_id is zero, just
771 * returns the nearest source/sink pad to start_entity. Must be called
772 * with mdev->graph_mutex held.
773 */
774 static struct media_pad *
find_pipeline_pad(struct imx_media_dev * imxmd,struct media_entity * start_entity,u32 grp_id,bool upstream)775 find_pipeline_pad(struct imx_media_dev *imxmd,
776 struct media_entity *start_entity,
777 u32 grp_id, bool upstream)
778 {
779 struct media_entity *me = start_entity;
780 struct media_pad *pad = NULL;
781 struct v4l2_subdev *sd;
782 int i;
783
784 for (i = 0; i < me->num_pads; i++) {
785 struct media_pad *spad = &me->pads[i];
786
787 if ((upstream && !(spad->flags & MEDIA_PAD_FL_SINK)) ||
788 (!upstream && !(spad->flags & MEDIA_PAD_FL_SOURCE)))
789 continue;
790
791 pad = media_entity_remote_pad(spad);
792 if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
793 continue;
794
795 if (grp_id != 0) {
796 sd = media_entity_to_v4l2_subdev(pad->entity);
797 if (sd->grp_id & grp_id)
798 return pad;
799
800 return find_pipeline_pad(imxmd, pad->entity,
801 grp_id, upstream);
802 } else {
803 return pad;
804 }
805 }
806
807 return NULL;
808 }
809
810 /*
811 * Search upstream for a subdev in the current pipeline with
812 * given grp_id. Must be called with mdev->graph_mutex held.
813 */
814 static struct v4l2_subdev *
find_upstream_subdev(struct imx_media_dev * imxmd,struct media_entity * start_entity,u32 grp_id)815 find_upstream_subdev(struct imx_media_dev *imxmd,
816 struct media_entity *start_entity,
817 u32 grp_id)
818 {
819 struct v4l2_subdev *sd;
820 struct media_pad *pad;
821
822 if (is_media_entity_v4l2_subdev(start_entity)) {
823 sd = media_entity_to_v4l2_subdev(start_entity);
824 if (sd->grp_id & grp_id)
825 return sd;
826 }
827
828 pad = find_pipeline_pad(imxmd, start_entity, grp_id, true);
829
830 return pad ? media_entity_to_v4l2_subdev(pad->entity) : NULL;
831 }
832
833 /*
834 * Find the upstream mipi-csi2 virtual channel reached from the given
835 * start entity in the current pipeline.
836 * Must be called with mdev->graph_mutex held.
837 */
imx_media_find_mipi_csi2_channel(struct imx_media_dev * imxmd,struct media_entity * start_entity)838 int imx_media_find_mipi_csi2_channel(struct imx_media_dev *imxmd,
839 struct media_entity *start_entity)
840 {
841 struct media_pad *pad;
842 int ret = -EPIPE;
843
844 pad = find_pipeline_pad(imxmd, start_entity, IMX_MEDIA_GRP_ID_CSI2,
845 true);
846 if (pad) {
847 ret = pad->index - 1;
848 dev_dbg(imxmd->md.dev, "found vc%d from %s\n",
849 ret, start_entity->name);
850 }
851
852 return ret;
853 }
854 EXPORT_SYMBOL_GPL(imx_media_find_mipi_csi2_channel);
855
856 /*
857 * Find a source pad reached upstream from the given start entity in
858 * the current pipeline. Must be called with mdev->graph_mutex held.
859 */
860 struct media_pad *
imx_media_find_upstream_pad(struct imx_media_dev * imxmd,struct media_entity * start_entity,u32 grp_id)861 imx_media_find_upstream_pad(struct imx_media_dev *imxmd,
862 struct media_entity *start_entity,
863 u32 grp_id)
864 {
865 struct media_pad *pad;
866
867 pad = find_pipeline_pad(imxmd, start_entity, grp_id, true);
868 if (!pad)
869 return ERR_PTR(-ENODEV);
870
871 return pad;
872 }
873 EXPORT_SYMBOL_GPL(imx_media_find_upstream_pad);
874
875 /*
876 * Find a subdev reached upstream from the given start entity in
877 * the current pipeline.
878 * Must be called with mdev->graph_mutex held.
879 */
880 struct v4l2_subdev *
imx_media_find_upstream_subdev(struct imx_media_dev * imxmd,struct media_entity * start_entity,u32 grp_id)881 imx_media_find_upstream_subdev(struct imx_media_dev *imxmd,
882 struct media_entity *start_entity,
883 u32 grp_id)
884 {
885 struct v4l2_subdev *sd;
886
887 sd = find_upstream_subdev(imxmd, start_entity, grp_id);
888 if (!sd)
889 return ERR_PTR(-ENODEV);
890
891 return sd;
892 }
893 EXPORT_SYMBOL_GPL(imx_media_find_upstream_subdev);
894
895 /*
896 * Turn current pipeline streaming on/off starting from entity.
897 */
imx_media_pipeline_set_stream(struct imx_media_dev * imxmd,struct media_entity * entity,bool on)898 int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd,
899 struct media_entity *entity,
900 bool on)
901 {
902 struct v4l2_subdev *sd;
903 int ret = 0;
904
905 if (!is_media_entity_v4l2_subdev(entity))
906 return -EINVAL;
907 sd = media_entity_to_v4l2_subdev(entity);
908
909 mutex_lock(&imxmd->md.graph_mutex);
910
911 if (on) {
912 ret = __media_pipeline_start(entity, &imxmd->pipe);
913 if (ret)
914 goto out;
915 ret = v4l2_subdev_call(sd, video, s_stream, 1);
916 if (ret)
917 __media_pipeline_stop(entity);
918 } else {
919 v4l2_subdev_call(sd, video, s_stream, 0);
920 if (entity->pipe)
921 __media_pipeline_stop(entity);
922 }
923
924 out:
925 mutex_unlock(&imxmd->md.graph_mutex);
926 return ret;
927 }
928 EXPORT_SYMBOL_GPL(imx_media_pipeline_set_stream);
929
930 MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver");
931 MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
932 MODULE_LICENSE("GPL");
933