1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
4 *
5 * Copyright (c) 2016 Mentor Graphics Inc.
6 */
7#include <linux/module.h>
8#include "imx-media.h"
9
10#define IMX_BUS_FMTS(fmt...) ((const u32[]) {fmt, 0})
11
12/*
13 * List of supported pixel formats for the subdevs.
14 */
15static const struct imx_media_pixfmt pixel_formats[] = {
16 /*** YUV formats start here ***/
17 {
18 .fourcc = V4L2_PIX_FMT_UYVY,
19 .codes = IMX_BUS_FMTS(
20 MEDIA_BUS_FMT_UYVY8_2X8,
21 MEDIA_BUS_FMT_UYVY8_1X16
22 ),
23 .cs = IPUV3_COLORSPACE_YUV,
24 .bpp = 16,
25 }, {
26 .fourcc = V4L2_PIX_FMT_YUYV,
27 .codes = IMX_BUS_FMTS(
28 MEDIA_BUS_FMT_YUYV8_2X8,
29 MEDIA_BUS_FMT_YUYV8_1X16
30 ),
31 .cs = IPUV3_COLORSPACE_YUV,
32 .bpp = 16,
33 }, {
34 .fourcc = V4L2_PIX_FMT_YUV420,
35 .cs = IPUV3_COLORSPACE_YUV,
36 .bpp = 12,
37 .planar = true,
38 }, {
39 .fourcc = V4L2_PIX_FMT_YVU420,
40 .cs = IPUV3_COLORSPACE_YUV,
41 .bpp = 12,
42 .planar = true,
43 }, {
44 .fourcc = V4L2_PIX_FMT_YUV422P,
45 .cs = IPUV3_COLORSPACE_YUV,
46 .bpp = 16,
47 .planar = true,
48 }, {
49 .fourcc = V4L2_PIX_FMT_NV12,
50 .cs = IPUV3_COLORSPACE_YUV,
51 .bpp = 12,
52 .planar = true,
53 }, {
54 .fourcc = V4L2_PIX_FMT_NV16,
55 .cs = IPUV3_COLORSPACE_YUV,
56 .bpp = 16,
57 .planar = true,
58 }, {
59 .fourcc = V4L2_PIX_FMT_YUV32,
60 .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_AYUV8_1X32),
61 .cs = IPUV3_COLORSPACE_YUV,
62 .bpp = 32,
63 .ipufmt = true,
64 },
65 /*** RGB formats start here ***/
66 {
67 .fourcc = V4L2_PIX_FMT_RGB565,
68 .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_RGB565_2X8_LE),
69 .cs = IPUV3_COLORSPACE_RGB,
70 .bpp = 16,
71 .cycles = 2,
72 }, {
73 .fourcc = V4L2_PIX_FMT_RGB24,
74 .codes = IMX_BUS_FMTS(
75 MEDIA_BUS_FMT_RGB888_1X24,
76 MEDIA_BUS_FMT_RGB888_2X12_LE
77 ),
78 .cs = IPUV3_COLORSPACE_RGB,
79 .bpp = 24,
80 }, {
81 .fourcc = V4L2_PIX_FMT_BGR24,
82 .cs = IPUV3_COLORSPACE_RGB,
83 .bpp = 24,
84 }, {
85 .fourcc = V4L2_PIX_FMT_XRGB32,
86 .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_ARGB8888_1X32),
87 .cs = IPUV3_COLORSPACE_RGB,
88 .bpp = 32,
89 }, {
90 .fourcc = V4L2_PIX_FMT_XRGB32,
91 .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_ARGB8888_1X32),
92 .cs = IPUV3_COLORSPACE_RGB,
93 .bpp = 32,
94 .ipufmt = true,
95 }, {
96 .fourcc = V4L2_PIX_FMT_XBGR32,
97 .cs = IPUV3_COLORSPACE_RGB,
98 .bpp = 32,
99 }, {
100 .fourcc = V4L2_PIX_FMT_BGRX32,
101 .cs = IPUV3_COLORSPACE_RGB,
102 .bpp = 32,
103 }, {
104 .fourcc = V4L2_PIX_FMT_RGBX32,
105 .cs = IPUV3_COLORSPACE_RGB,
106 .bpp = 32,
107 },
108 /*** raw bayer and grayscale formats start here ***/
109 {
110 .fourcc = V4L2_PIX_FMT_SBGGR8,
111 .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR8_1X8),
112 .cs = IPUV3_COLORSPACE_RGB,
113 .bpp = 8,
114 .bayer = true,
115 }, {
116 .fourcc = V4L2_PIX_FMT_SGBRG8,
117 .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG8_1X8),
118 .cs = IPUV3_COLORSPACE_RGB,
119 .bpp = 8,
120 .bayer = true,
121 }, {
122 .fourcc = V4L2_PIX_FMT_SGRBG8,
123 .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG8_1X8),
124 .cs = IPUV3_COLORSPACE_RGB,
125 .bpp = 8,
126 .bayer = true,
127 }, {
128 .fourcc = V4L2_PIX_FMT_SRGGB8,
129 .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB8_1X8),
130 .cs = IPUV3_COLORSPACE_RGB,
131 .bpp = 8,
132 .bayer = true,
133 }, {
134 .fourcc = V4L2_PIX_FMT_SBGGR16,
135 .codes = IMX_BUS_FMTS(
136 MEDIA_BUS_FMT_SBGGR10_1X10,
137 MEDIA_BUS_FMT_SBGGR12_1X12,
138 MEDIA_BUS_FMT_SBGGR14_1X14,
139 MEDIA_BUS_FMT_SBGGR16_1X16
140 ),
141 .cs = IPUV3_COLORSPACE_RGB,
142 .bpp = 16,
143 .bayer = true,
144 }, {
145 .fourcc = V4L2_PIX_FMT_SGBRG16,
146 .codes = IMX_BUS_FMTS(
147 MEDIA_BUS_FMT_SGBRG10_1X10,
148 MEDIA_BUS_FMT_SGBRG12_1X12,
149 MEDIA_BUS_FMT_SGBRG14_1X14,
150 MEDIA_BUS_FMT_SGBRG16_1X16
151 ),
152 .cs = IPUV3_COLORSPACE_RGB,
153 .bpp = 16,
154 .bayer = true,
155 }, {
156 .fourcc = V4L2_PIX_FMT_SGRBG16,
157 .codes = IMX_BUS_FMTS(
158 MEDIA_BUS_FMT_SGRBG10_1X10,
159 MEDIA_BUS_FMT_SGRBG12_1X12,
160 MEDIA_BUS_FMT_SGRBG14_1X14,
161 MEDIA_BUS_FMT_SGRBG16_1X16
162 ),
163 .cs = IPUV3_COLORSPACE_RGB,
164 .bpp = 16,
165 .bayer = true,
166 }, {
167 .fourcc = V4L2_PIX_FMT_SRGGB16,
168 .codes = IMX_BUS_FMTS(
169 MEDIA_BUS_FMT_SRGGB10_1X10,
170 MEDIA_BUS_FMT_SRGGB12_1X12,
171 MEDIA_BUS_FMT_SRGGB14_1X14,
172 MEDIA_BUS_FMT_SRGGB16_1X16
173 ),
174 .cs = IPUV3_COLORSPACE_RGB,
175 .bpp = 16,
176 .bayer = true,
177 }, {
178 .fourcc = V4L2_PIX_FMT_GREY,
179 .codes = IMX_BUS_FMTS(
180 MEDIA_BUS_FMT_Y8_1X8,
181 MEDIA_BUS_FMT_Y10_1X10,
182 MEDIA_BUS_FMT_Y12_1X12
183 ),
184 .cs = IPUV3_COLORSPACE_RGB,
185 .bpp = 8,
186 .bayer = true,
187 }, {
188 .fourcc = V4L2_PIX_FMT_Y10,
189 .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y10_1X10),
190 .cs = IPUV3_COLORSPACE_RGB,
191 .bpp = 16,
192 .bayer = true,
193 }, {
194 .fourcc = V4L2_PIX_FMT_Y12,
195 .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y12_1X12),
196 .cs = IPUV3_COLORSPACE_RGB,
197 .bpp = 16,
198 .bayer = true,
199 },
200};
201
202/*
203 * Search in the pixel_formats[] array for an entry with the given fourcc
204 * that matches the requested selection criteria and return it.
205 *
206 * @fourcc: Search for an entry with the given fourcc pixel format.
207 * @fmt_sel: Allow entries only with the given selection criteria.
208 */
209const struct imx_media_pixfmt *
210imx_media_find_pixel_format(u32 fourcc, enum imx_pixfmt_sel fmt_sel)
211{
212 bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
213 unsigned int i;
214
215 fmt_sel &= ~PIXFMT_SEL_IPU;
216
217 for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
218 const struct imx_media_pixfmt *fmt = &pixel_formats[i];
219 enum imx_pixfmt_sel sel;
220
221 if (sel_ipu != fmt->ipufmt)
222 continue;
223
224 sel = fmt->bayer ? PIXFMT_SEL_BAYER :
225 ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
226 PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
227
228 if ((fmt_sel & sel) && fmt->fourcc == fourcc)
229 return fmt;
230 }
231
232 return NULL;
233}
234EXPORT_SYMBOL_GPL(imx_media_find_pixel_format);
235
236/*
237 * Search in the pixel_formats[] array for an entry with the given media
238 * bus code that matches the requested selection criteria and return it.
239 *
240 * @code: Search for an entry with the given media-bus code.
241 * @fmt_sel: Allow entries only with the given selection criteria.
242 */
243const struct imx_media_pixfmt *
244imx_media_find_mbus_format(u32 code, enum imx_pixfmt_sel fmt_sel)
245{
246 bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
247 unsigned int i;
248
249 fmt_sel &= ~PIXFMT_SEL_IPU;
250
251 for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
252 const struct imx_media_pixfmt *fmt = &pixel_formats[i];
253 enum imx_pixfmt_sel sel;
254 unsigned int j;
255
256 if (sel_ipu != fmt->ipufmt)
257 continue;
258
259 sel = fmt->bayer ? PIXFMT_SEL_BAYER :
260 ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
261 PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
262
263 if (!(fmt_sel & sel) || !fmt->codes)
264 continue;
265
266 for (j = 0; fmt->codes[j]; j++) {
267 if (code == fmt->codes[j])
268 return fmt;
269 }
270 }
271
272 return NULL;
273}
274EXPORT_SYMBOL_GPL(imx_media_find_mbus_format);
275
276/*
277 * Enumerate entries in the pixel_formats[] array that match the
278 * requested selection criteria. Return the fourcc that matches the
279 * selection criteria at the requested match index.
280 *
281 * @fourcc: The returned fourcc that matches the search criteria at
282 * the requested match index.
283 * @index: The requested match index.
284 * @fmt_sel: Include in the enumeration entries with the given selection
285 * criteria.
286 * @code: If non-zero, only include in the enumeration entries matching this
287 * media bus code.
288 */
289int imx_media_enum_pixel_formats(u32 *fourcc, u32 index,
290 enum imx_pixfmt_sel fmt_sel, u32 code)
291{
292 bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
293 unsigned int i;
294
295 fmt_sel &= ~PIXFMT_SEL_IPU;
296
297 for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
298 const struct imx_media_pixfmt *fmt = &pixel_formats[i];
299 enum imx_pixfmt_sel sel;
300
301 if (sel_ipu != fmt->ipufmt)
302 continue;
303
304 sel = fmt->bayer ? PIXFMT_SEL_BAYER :
305 ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
306 PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
307
308 if (!(fmt_sel & sel))
309 continue;
310
311 /*
312 * If a media bus code is specified, only consider formats that
313 * match it.
314 */
315 if (code) {
316 unsigned int j;
317
318 if (!fmt->codes)
319 continue;
320
321 for (j = 0; fmt->codes[j]; j++) {
322 if (code == fmt->codes[j])
323 break;
324 }
325
326 if (!fmt->codes[j])
327 continue;
328 }
329
330 if (index == 0) {
331 *fourcc = fmt->fourcc;
332 return 0;
333 }
334
335 index--;
336 }
337
338 return -EINVAL;
339}
340EXPORT_SYMBOL_GPL(imx_media_enum_pixel_formats);
341
342/*
343 * Enumerate entries in the pixel_formats[] array that match the
344 * requested search criteria. Return the media-bus code that matches
345 * the search criteria at the requested match index.
346 *
347 * @code: The returned media-bus code that matches the search criteria at
348 * the requested match index.
349 * @index: The requested match index.
350 * @fmt_sel: Include in the enumeration entries with the given selection
351 * criteria.
352 */
353int imx_media_enum_mbus_formats(u32 *code, u32 index,
354 enum imx_pixfmt_sel fmt_sel)
355{
356 bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
357 unsigned int i;
358
359 fmt_sel &= ~PIXFMT_SEL_IPU;
360
361 for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
362 const struct imx_media_pixfmt *fmt = &pixel_formats[i];
363 enum imx_pixfmt_sel sel;
364 unsigned int j;
365
366 if (sel_ipu != fmt->ipufmt)
367 continue;
368
369 sel = fmt->bayer ? PIXFMT_SEL_BAYER :
370 ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
371 PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
372
373 if (!(fmt_sel & sel) || !fmt->codes)
374 continue;
375
376 for (j = 0; fmt->codes[j]; j++) {
377 if (index == 0) {
378 *code = fmt->codes[j];
379 return 0;
380 }
381
382 index--;
383 }
384 }
385
386 return -EINVAL;
387}
388EXPORT_SYMBOL_GPL(imx_media_enum_mbus_formats);
389
390int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
391 u32 width, u32 height, u32 code, u32 field,
392 const struct imx_media_pixfmt **cc)
393{
394 const struct imx_media_pixfmt *lcc;
395
396 mbus->width = width;
397 mbus->height = height;
398 mbus->field = field;
399
400 if (code == 0)
401 imx_media_enum_mbus_formats(&code, 0, PIXFMT_SEL_YUV);
402
403 lcc = imx_media_find_mbus_format(code, PIXFMT_SEL_ANY);
404 if (!lcc) {
405 lcc = imx_media_find_ipu_format(code, fmt_sel: PIXFMT_SEL_YUV_RGB);
406 if (!lcc)
407 return -EINVAL;
408 }
409
410 mbus->code = code;
411
412 mbus->colorspace = V4L2_COLORSPACE_SRGB;
413 mbus->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(mbus->colorspace);
414 mbus->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mbus->colorspace);
415 mbus->quantization =
416 V4L2_MAP_QUANTIZATION_DEFAULT(lcc->cs == IPUV3_COLORSPACE_RGB,
417 mbus->colorspace,
418 mbus->ycbcr_enc);
419
420 if (cc)
421 *cc = lcc;
422
423 return 0;
424}
425EXPORT_SYMBOL_GPL(imx_media_init_mbus_fmt);
426
427/*
428 * Initializes the TRY format to the ACTIVE format on all pads
429 * of a subdev. Can be used as the .init_state internal operation.
430 */
431int imx_media_init_state(struct v4l2_subdev *sd,
432 struct v4l2_subdev_state *sd_state)
433{
434 struct v4l2_mbus_framefmt *mf_try;
435 unsigned int pad;
436 int ret;
437
438 for (pad = 0; pad < sd->entity.num_pads; pad++) {
439 struct v4l2_subdev_format format = {
440 .pad = pad,
441 .which = V4L2_SUBDEV_FORMAT_ACTIVE,
442 };
443
444 ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &format);
445 if (ret)
446 continue;
447
448 mf_try = v4l2_subdev_state_get_format(sd_state, pad);
449 *mf_try = format.format;
450 }
451
452 return 0;
453}
454EXPORT_SYMBOL_GPL(imx_media_init_state);
455
456/*
457 * Default the colorspace in tryfmt to SRGB if set to an unsupported
458 * colorspace or not initialized. Then set the remaining colorimetry
459 * parameters based on the colorspace if they are uninitialized.
460 *
461 * tryfmt->code must be set on entry.
462 *
463 * If this format is destined to be routed through the Image Converter,
464 * Y`CbCr encoding must be fixed. The IC supports only BT.601 Y`CbCr
465 * or Rec.709 Y`CbCr encoding.
466 */
467void imx_media_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt,
468 bool ic_route)
469{
470 const struct imx_media_pixfmt *cc;
471 bool is_rgb = false;
472
473 cc = imx_media_find_mbus_format(tryfmt->code, PIXFMT_SEL_ANY);
474 if (!cc)
475 cc = imx_media_find_ipu_format(code: tryfmt->code,
476 fmt_sel: PIXFMT_SEL_YUV_RGB);
477
478 if (cc && cc->cs == IPUV3_COLORSPACE_RGB)
479 is_rgb = true;
480
481 switch (tryfmt->colorspace) {
482 case V4L2_COLORSPACE_SMPTE170M:
483 case V4L2_COLORSPACE_REC709:
484 case V4L2_COLORSPACE_JPEG:
485 case V4L2_COLORSPACE_SRGB:
486 case V4L2_COLORSPACE_BT2020:
487 case V4L2_COLORSPACE_OPRGB:
488 case V4L2_COLORSPACE_DCI_P3:
489 case V4L2_COLORSPACE_RAW:
490 break;
491 default:
492 tryfmt->colorspace = V4L2_COLORSPACE_SRGB;
493 break;
494 }
495
496 if (tryfmt->xfer_func == V4L2_XFER_FUNC_DEFAULT)
497 tryfmt->xfer_func =
498 V4L2_MAP_XFER_FUNC_DEFAULT(tryfmt->colorspace);
499
500 if (ic_route) {
501 if (tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_601 &&
502 tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_709)
503 tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
504 } else {
505 if (tryfmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
506 tryfmt->ycbcr_enc =
507 V4L2_MAP_YCBCR_ENC_DEFAULT(tryfmt->colorspace);
508 }
509 }
510
511 if (tryfmt->quantization == V4L2_QUANTIZATION_DEFAULT)
512 tryfmt->quantization =
513 V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb,
514 tryfmt->colorspace,
515 tryfmt->ycbcr_enc);
516}
517EXPORT_SYMBOL_GPL(imx_media_try_colorimetry);
518
519int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
520 const struct v4l2_mbus_framefmt *mbus,
521 const struct imx_media_pixfmt *cc)
522{
523 u32 width;
524 u32 stride;
525
526 if (!cc) {
527 cc = imx_media_find_ipu_format(code: mbus->code,
528 fmt_sel: PIXFMT_SEL_YUV_RGB);
529 if (!cc)
530 cc = imx_media_find_mbus_format(mbus->code,
531 PIXFMT_SEL_ANY);
532 if (!cc)
533 return -EINVAL;
534 }
535
536 /*
537 * TODO: the IPU currently does not support the AYUV32 format,
538 * so until it does convert to a supported YUV format.
539 */
540 if (cc->ipufmt && cc->cs == IPUV3_COLORSPACE_YUV) {
541 u32 code;
542
543 imx_media_enum_mbus_formats(&code, 0, PIXFMT_SEL_YUV);
544 cc = imx_media_find_mbus_format(code, PIXFMT_SEL_YUV);
545 }
546
547 /* Round up width for minimum burst size */
548 width = round_up(mbus->width, 8);
549
550 /* Round up stride for IDMAC line start address alignment */
551 if (cc->planar)
552 stride = round_up(width, 16);
553 else
554 stride = round_up((width * cc->bpp) >> 3, 8);
555
556 pix->width = width;
557 pix->height = mbus->height;
558 pix->pixelformat = cc->fourcc;
559 pix->colorspace = mbus->colorspace;
560 pix->xfer_func = mbus->xfer_func;
561 pix->ycbcr_enc = mbus->ycbcr_enc;
562 pix->quantization = mbus->quantization;
563 pix->field = mbus->field;
564 pix->bytesperline = stride;
565 pix->sizeimage = cc->planar ? ((stride * pix->height * cc->bpp) >> 3) :
566 stride * pix->height;
567
568 return 0;
569}
570EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_pix_fmt);
571
572void imx_media_free_dma_buf(struct device *dev,
573 struct imx_media_dma_buf *buf)
574{
575 if (buf->virt)
576 dma_free_coherent(dev, size: buf->len, cpu_addr: buf->virt, dma_handle: buf->phys);
577
578 buf->virt = NULL;
579 buf->phys = 0;
580}
581EXPORT_SYMBOL_GPL(imx_media_free_dma_buf);
582
583int imx_media_alloc_dma_buf(struct device *dev,
584 struct imx_media_dma_buf *buf,
585 int size)
586{
587 imx_media_free_dma_buf(dev, buf);
588
589 buf->len = PAGE_ALIGN(size);
590 buf->virt = dma_alloc_coherent(dev, size: buf->len, dma_handle: &buf->phys,
591 GFP_DMA | GFP_KERNEL);
592 if (!buf->virt) {
593 dev_err(dev, "%s: failed\n", __func__);
594 return -ENOMEM;
595 }
596
597 return 0;
598}
599EXPORT_SYMBOL_GPL(imx_media_alloc_dma_buf);
600
601/* form a subdev name given a group id and ipu id */
602void imx_media_grp_id_to_sd_name(char *sd_name, int sz, u32 grp_id, int ipu_id)
603{
604 int id;
605
606 switch (grp_id) {
607 case IMX_MEDIA_GRP_ID_IPU_CSI0...IMX_MEDIA_GRP_ID_IPU_CSI1:
608 id = (grp_id >> IMX_MEDIA_GRP_ID_IPU_CSI_BIT) - 1;
609 snprintf(buf: sd_name, size: sz, fmt: "ipu%d_csi%d", ipu_id + 1, id);
610 break;
611 case IMX_MEDIA_GRP_ID_IPU_VDIC:
612 snprintf(buf: sd_name, size: sz, fmt: "ipu%d_vdic", ipu_id + 1);
613 break;
614 case IMX_MEDIA_GRP_ID_IPU_IC_PRP:
615 snprintf(buf: sd_name, size: sz, fmt: "ipu%d_ic_prp", ipu_id + 1);
616 break;
617 case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC:
618 snprintf(buf: sd_name, size: sz, fmt: "ipu%d_ic_prpenc", ipu_id + 1);
619 break;
620 case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF:
621 snprintf(buf: sd_name, size: sz, fmt: "ipu%d_ic_prpvf", ipu_id + 1);
622 break;
623 default:
624 break;
625 }
626}
627EXPORT_SYMBOL_GPL(imx_media_grp_id_to_sd_name);
628
629/*
630 * Adds a video device to the master video device list. This is called
631 * when a video device is registered.
632 */
633void imx_media_add_video_device(struct imx_media_dev *imxmd,
634 struct imx_media_video_dev *vdev)
635{
636 mutex_lock(&imxmd->mutex);
637
638 list_add_tail(new: &vdev->list, head: &imxmd->vdev_list);
639
640 mutex_unlock(lock: &imxmd->mutex);
641}
642EXPORT_SYMBOL_GPL(imx_media_add_video_device);
643
644/*
645 * Search upstream/downstream for a subdevice or video device pad in the
646 * current pipeline, starting from start_entity. Returns the device's
647 * source/sink pad that it was reached from. Must be called with
648 * mdev->graph_mutex held.
649 *
650 * If grp_id != 0, finds a subdevice's pad of given grp_id.
651 * Else If buftype != 0, finds a video device's pad of given buffer type.
652 * Else, returns the nearest source/sink pad to start_entity.
653 */
654struct media_pad *
655imx_media_pipeline_pad(struct media_entity *start_entity, u32 grp_id,
656 enum v4l2_buf_type buftype, bool upstream)
657{
658 struct media_entity *me = start_entity;
659 struct media_pad *pad = NULL;
660 struct video_device *vfd;
661 struct v4l2_subdev *sd;
662 int i;
663
664 for (i = 0; i < me->num_pads; i++) {
665 struct media_pad *spad = &me->pads[i];
666
667 if ((upstream && !(spad->flags & MEDIA_PAD_FL_SINK)) ||
668 (!upstream && !(spad->flags & MEDIA_PAD_FL_SOURCE)))
669 continue;
670
671 pad = media_pad_remote_pad_first(pad: spad);
672 if (!pad)
673 continue;
674
675 if (grp_id) {
676 if (is_media_entity_v4l2_subdev(entity: pad->entity)) {
677 sd = media_entity_to_v4l2_subdev(pad->entity);
678 if (sd->grp_id & grp_id)
679 return pad;
680 }
681
682 return imx_media_pipeline_pad(start_entity: pad->entity, grp_id,
683 buftype, upstream);
684 } else if (buftype) {
685 if (is_media_entity_v4l2_video_device(entity: pad->entity)) {
686 vfd = media_entity_to_video_device(pad->entity);
687 if (buftype == vfd->queue->type)
688 return pad;
689 }
690
691 return imx_media_pipeline_pad(start_entity: pad->entity, grp_id,
692 buftype, upstream);
693 } else {
694 return pad;
695 }
696 }
697
698 return NULL;
699}
700EXPORT_SYMBOL_GPL(imx_media_pipeline_pad);
701
702/*
703 * Search upstream/downstream for a subdev or video device in the current
704 * pipeline. Must be called with mdev->graph_mutex held.
705 */
706static struct media_entity *
707find_pipeline_entity(struct media_entity *start, u32 grp_id,
708 enum v4l2_buf_type buftype, bool upstream)
709{
710 struct media_pad *pad = NULL;
711 struct video_device *vfd;
712 struct v4l2_subdev *sd;
713
714 if (grp_id && is_media_entity_v4l2_subdev(entity: start)) {
715 sd = media_entity_to_v4l2_subdev(start);
716 if (sd->grp_id & grp_id)
717 return &sd->entity;
718 } else if (buftype && is_media_entity_v4l2_video_device(entity: start)) {
719 vfd = media_entity_to_video_device(start);
720 if (buftype == vfd->queue->type)
721 return &vfd->entity;
722 }
723
724 pad = imx_media_pipeline_pad(start, grp_id, buftype, upstream);
725
726 return pad ? pad->entity : NULL;
727}
728
729/*
730 * Find a subdev reached upstream from the given start entity in
731 * the current pipeline.
732 * Must be called with mdev->graph_mutex held.
733 */
734struct v4l2_subdev *
735imx_media_pipeline_subdev(struct media_entity *start_entity, u32 grp_id,
736 bool upstream)
737{
738 struct media_entity *me;
739
740 me = find_pipeline_entity(start: start_entity, grp_id, buftype: 0, upstream);
741 if (!me)
742 return ERR_PTR(error: -ENODEV);
743
744 return media_entity_to_v4l2_subdev(me);
745}
746EXPORT_SYMBOL_GPL(imx_media_pipeline_subdev);
747
748/*
749 * Turn current pipeline streaming on/off starting from entity.
750 */
751int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd,
752 struct media_entity *entity,
753 bool on)
754{
755 struct v4l2_subdev *sd;
756 int ret = 0;
757
758 if (!is_media_entity_v4l2_subdev(entity))
759 return -EINVAL;
760 sd = media_entity_to_v4l2_subdev(entity);
761
762 mutex_lock(&imxmd->md.graph_mutex);
763
764 if (on) {
765 ret = __media_pipeline_start(pad: entity->pads, pipe: &imxmd->pipe);
766 if (ret)
767 goto out;
768 ret = v4l2_subdev_call(sd, video, s_stream, 1);
769 if (ret)
770 __media_pipeline_stop(pad: entity->pads);
771 } else {
772 v4l2_subdev_call(sd, video, s_stream, 0);
773 if (media_pad_pipeline(pad: entity->pads))
774 __media_pipeline_stop(pad: entity->pads);
775 }
776
777out:
778 mutex_unlock(lock: &imxmd->md.graph_mutex);
779 return ret;
780}
781EXPORT_SYMBOL_GPL(imx_media_pipeline_set_stream);
782
783MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver");
784MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
785MODULE_LICENSE("GPL");
786

source code of linux/drivers/staging/media/imx/imx-media-utils.c