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, 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(tryfmt->code,
476					       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(mbus->code,
528					       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, buf->len, buf->virt, 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, buf->len, &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(sd_name, sz, "ipu%d_csi%d", ipu_id + 1, id);
610		break;
611	case IMX_MEDIA_GRP_ID_IPU_VDIC:
612		snprintf(sd_name, sz, "ipu%d_vdic", ipu_id + 1);
613		break;
614	case IMX_MEDIA_GRP_ID_IPU_IC_PRP:
615		snprintf(sd_name, sz, "ipu%d_ic_prp", ipu_id + 1);
616		break;
617	case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC:
618		snprintf(sd_name, sz, "ipu%d_ic_prpenc", ipu_id + 1);
619		break;
620	case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF:
621		snprintf(sd_name, sz, "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(&vdev->list, &imxmd->vdev_list);
639
640	mutex_unlock(&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(spad);
672		if (!pad)
673			continue;
674
675		if (grp_id) {
676			if (is_media_entity_v4l2_subdev(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(pad->entity, grp_id,
683						      buftype, upstream);
684		} else if (buftype) {
685			if (is_media_entity_v4l2_video_device(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(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(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(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_entity, grp_id, 0, upstream);
741	if (!me)
742		return ERR_PTR(-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(entity->pads, &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(entity->pads);
771	} else {
772		v4l2_subdev_call(sd, video, s_stream, 0);
773		if (media_pad_pipeline(entity->pads))
774			__media_pipeline_stop(entity->pads);
775	}
776
777out:
778	mutex_unlock(&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