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 | */ |
15 | static 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 | */ |
209 | const struct imx_media_pixfmt * |
210 | imx_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 | } |
234 | EXPORT_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 | */ |
243 | const struct imx_media_pixfmt * |
244 | imx_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 | } |
274 | EXPORT_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 | */ |
289 | int 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 | } |
340 | EXPORT_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 | */ |
353 | int 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 | } |
388 | EXPORT_SYMBOL_GPL(imx_media_enum_mbus_formats); |
389 | |
390 | int 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 | } |
425 | EXPORT_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 | */ |
431 | int 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 | } |
454 | EXPORT_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 | */ |
467 | void 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 | } |
517 | EXPORT_SYMBOL_GPL(imx_media_try_colorimetry); |
518 | |
519 | int 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 | } |
570 | EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_pix_fmt); |
571 | |
572 | void 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 | } |
581 | EXPORT_SYMBOL_GPL(imx_media_free_dma_buf); |
582 | |
583 | int 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 | } |
599 | EXPORT_SYMBOL_GPL(imx_media_alloc_dma_buf); |
600 | |
601 | /* form a subdev name given a group id and ipu id */ |
602 | void 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 | } |
627 | EXPORT_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 | */ |
633 | void 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 | } |
642 | EXPORT_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 | */ |
654 | struct media_pad * |
655 | imx_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 | } |
700 | EXPORT_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 | */ |
706 | static struct media_entity * |
707 | find_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 | */ |
734 | struct v4l2_subdev * |
735 | imx_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 | } |
746 | EXPORT_SYMBOL_GPL(imx_media_pipeline_subdev); |
747 | |
748 | /* |
749 | * Turn current pipeline streaming on/off starting from entity. |
750 | */ |
751 | int 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 | |
777 | out: |
778 | mutex_unlock(lock: &imxmd->md.graph_mutex); |
779 | return ret; |
780 | } |
781 | EXPORT_SYMBOL_GPL(imx_media_pipeline_set_stream); |
782 | |
783 | MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver" ); |
784 | MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>" ); |
785 | MODULE_LICENSE("GPL" ); |
786 | |