Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
0004  *
0005  * Copyright (c) 2016 Mentor Graphics Inc.
0006  */
0007 #include <linux/module.h>
0008 #include "imx-media.h"
0009 
0010 #define IMX_BUS_FMTS(fmt...) (const u32[]) {fmt, 0}
0011 
0012 /*
0013  * List of supported pixel formats for the subdevs.
0014  */
0015 static const struct imx_media_pixfmt pixel_formats[] = {
0016     /*** YUV formats start here ***/
0017     {
0018         .fourcc = V4L2_PIX_FMT_UYVY,
0019         .codes  = IMX_BUS_FMTS(
0020             MEDIA_BUS_FMT_UYVY8_2X8,
0021             MEDIA_BUS_FMT_UYVY8_1X16
0022         ),
0023         .cs     = IPUV3_COLORSPACE_YUV,
0024         .bpp    = 16,
0025     }, {
0026         .fourcc = V4L2_PIX_FMT_YUYV,
0027         .codes  = IMX_BUS_FMTS(
0028             MEDIA_BUS_FMT_YUYV8_2X8,
0029             MEDIA_BUS_FMT_YUYV8_1X16
0030         ),
0031         .cs     = IPUV3_COLORSPACE_YUV,
0032         .bpp    = 16,
0033     }, {
0034         .fourcc = V4L2_PIX_FMT_YUV420,
0035         .cs     = IPUV3_COLORSPACE_YUV,
0036         .bpp    = 12,
0037         .planar = true,
0038     }, {
0039         .fourcc = V4L2_PIX_FMT_YVU420,
0040         .cs     = IPUV3_COLORSPACE_YUV,
0041         .bpp    = 12,
0042         .planar = true,
0043     }, {
0044         .fourcc = V4L2_PIX_FMT_YUV422P,
0045         .cs     = IPUV3_COLORSPACE_YUV,
0046         .bpp    = 16,
0047         .planar = true,
0048     }, {
0049         .fourcc = V4L2_PIX_FMT_NV12,
0050         .cs     = IPUV3_COLORSPACE_YUV,
0051         .bpp    = 12,
0052         .planar = true,
0053     }, {
0054         .fourcc = V4L2_PIX_FMT_NV16,
0055         .cs     = IPUV3_COLORSPACE_YUV,
0056         .bpp    = 16,
0057         .planar = true,
0058     }, {
0059         .fourcc = V4L2_PIX_FMT_YUV32,
0060         .codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_AYUV8_1X32),
0061         .cs     = IPUV3_COLORSPACE_YUV,
0062         .bpp    = 32,
0063         .ipufmt = true,
0064     },
0065     /*** RGB formats start here ***/
0066     {
0067         .fourcc = V4L2_PIX_FMT_RGB565,
0068         .codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_RGB565_2X8_LE),
0069         .cs     = IPUV3_COLORSPACE_RGB,
0070         .bpp    = 16,
0071         .cycles = 2,
0072     }, {
0073         .fourcc = V4L2_PIX_FMT_RGB24,
0074         .codes  = IMX_BUS_FMTS(
0075             MEDIA_BUS_FMT_RGB888_1X24,
0076             MEDIA_BUS_FMT_RGB888_2X12_LE
0077         ),
0078         .cs     = IPUV3_COLORSPACE_RGB,
0079         .bpp    = 24,
0080     }, {
0081         .fourcc = V4L2_PIX_FMT_BGR24,
0082         .cs     = IPUV3_COLORSPACE_RGB,
0083         .bpp    = 24,
0084     }, {
0085         .fourcc = V4L2_PIX_FMT_XRGB32,
0086         .codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_ARGB8888_1X32),
0087         .cs     = IPUV3_COLORSPACE_RGB,
0088         .bpp    = 32,
0089     }, {
0090         .fourcc = V4L2_PIX_FMT_XRGB32,
0091         .codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_ARGB8888_1X32),
0092         .cs     = IPUV3_COLORSPACE_RGB,
0093         .bpp    = 32,
0094         .ipufmt = true,
0095     }, {
0096         .fourcc = V4L2_PIX_FMT_XBGR32,
0097         .cs     = IPUV3_COLORSPACE_RGB,
0098         .bpp    = 32,
0099     }, {
0100         .fourcc = V4L2_PIX_FMT_BGRX32,
0101         .cs     = IPUV3_COLORSPACE_RGB,
0102         .bpp    = 32,
0103     }, {
0104         .fourcc = V4L2_PIX_FMT_RGBX32,
0105         .cs     = IPUV3_COLORSPACE_RGB,
0106         .bpp    = 32,
0107     },
0108     /*** raw bayer and grayscale formats start here ***/
0109     {
0110         .fourcc = V4L2_PIX_FMT_SBGGR8,
0111         .codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR8_1X8),
0112         .cs     = IPUV3_COLORSPACE_RGB,
0113         .bpp    = 8,
0114         .bayer  = true,
0115     }, {
0116         .fourcc = V4L2_PIX_FMT_SGBRG8,
0117         .codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG8_1X8),
0118         .cs     = IPUV3_COLORSPACE_RGB,
0119         .bpp    = 8,
0120         .bayer  = true,
0121     }, {
0122         .fourcc = V4L2_PIX_FMT_SGRBG8,
0123         .codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG8_1X8),
0124         .cs     = IPUV3_COLORSPACE_RGB,
0125         .bpp    = 8,
0126         .bayer  = true,
0127     }, {
0128         .fourcc = V4L2_PIX_FMT_SRGGB8,
0129         .codes  = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB8_1X8),
0130         .cs     = IPUV3_COLORSPACE_RGB,
0131         .bpp    = 8,
0132         .bayer  = true,
0133     }, {
0134         .fourcc = V4L2_PIX_FMT_SBGGR16,
0135         .codes  = IMX_BUS_FMTS(
0136             MEDIA_BUS_FMT_SBGGR10_1X10,
0137             MEDIA_BUS_FMT_SBGGR12_1X12,
0138             MEDIA_BUS_FMT_SBGGR14_1X14,
0139             MEDIA_BUS_FMT_SBGGR16_1X16
0140         ),
0141         .cs     = IPUV3_COLORSPACE_RGB,
0142         .bpp    = 16,
0143         .bayer  = true,
0144     }, {
0145         .fourcc = V4L2_PIX_FMT_SGBRG16,
0146         .codes  = IMX_BUS_FMTS(
0147             MEDIA_BUS_FMT_SGBRG10_1X10,
0148             MEDIA_BUS_FMT_SGBRG12_1X12,
0149             MEDIA_BUS_FMT_SGBRG14_1X14,
0150             MEDIA_BUS_FMT_SGBRG16_1X16
0151         ),
0152         .cs     = IPUV3_COLORSPACE_RGB,
0153         .bpp    = 16,
0154         .bayer  = true,
0155     }, {
0156         .fourcc = V4L2_PIX_FMT_SGRBG16,
0157         .codes  = IMX_BUS_FMTS(
0158             MEDIA_BUS_FMT_SGRBG10_1X10,
0159             MEDIA_BUS_FMT_SGRBG12_1X12,
0160             MEDIA_BUS_FMT_SGRBG14_1X14,
0161             MEDIA_BUS_FMT_SGRBG16_1X16
0162         ),
0163         .cs     = IPUV3_COLORSPACE_RGB,
0164         .bpp    = 16,
0165         .bayer  = true,
0166     }, {
0167         .fourcc = V4L2_PIX_FMT_SRGGB16,
0168         .codes  = IMX_BUS_FMTS(
0169             MEDIA_BUS_FMT_SRGGB10_1X10,
0170             MEDIA_BUS_FMT_SRGGB12_1X12,
0171             MEDIA_BUS_FMT_SRGGB14_1X14,
0172             MEDIA_BUS_FMT_SRGGB16_1X16
0173         ),
0174         .cs     = IPUV3_COLORSPACE_RGB,
0175         .bpp    = 16,
0176         .bayer  = true,
0177     }, {
0178         .fourcc = V4L2_PIX_FMT_GREY,
0179         .codes = IMX_BUS_FMTS(
0180             MEDIA_BUS_FMT_Y8_1X8,
0181             MEDIA_BUS_FMT_Y10_1X10,
0182             MEDIA_BUS_FMT_Y12_1X12
0183         ),
0184         .cs     = IPUV3_COLORSPACE_RGB,
0185         .bpp    = 8,
0186         .bayer  = true,
0187     }, {
0188         .fourcc = V4L2_PIX_FMT_Y10,
0189         .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y10_1X10),
0190         .cs     = IPUV3_COLORSPACE_RGB,
0191         .bpp    = 16,
0192         .bayer  = true,
0193     }, {
0194         .fourcc = V4L2_PIX_FMT_Y12,
0195         .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y12_1X12),
0196         .cs     = IPUV3_COLORSPACE_RGB,
0197         .bpp    = 16,
0198         .bayer  = true,
0199     },
0200 };
0201 
0202 /*
0203  * Search in the pixel_formats[] array for an entry with the given fourcc
0204  * that matches the requested selection criteria and return it.
0205  *
0206  * @fourcc: Search for an entry with the given fourcc pixel format.
0207  * @fmt_sel: Allow entries only with the given selection criteria.
0208  */
0209 const struct imx_media_pixfmt *
0210 imx_media_find_pixel_format(u32 fourcc, enum imx_pixfmt_sel fmt_sel)
0211 {
0212     bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
0213     unsigned int i;
0214 
0215     fmt_sel &= ~PIXFMT_SEL_IPU;
0216 
0217     for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
0218         const struct imx_media_pixfmt *fmt = &pixel_formats[i];
0219         enum imx_pixfmt_sel sel;
0220 
0221         if (sel_ipu != fmt->ipufmt)
0222             continue;
0223 
0224         sel = fmt->bayer ? PIXFMT_SEL_BAYER :
0225             ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
0226              PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
0227 
0228         if ((fmt_sel & sel) && fmt->fourcc == fourcc)
0229             return fmt;
0230     }
0231 
0232     return NULL;
0233 }
0234 EXPORT_SYMBOL_GPL(imx_media_find_pixel_format);
0235 
0236 /*
0237  * Search in the pixel_formats[] array for an entry with the given media
0238  * bus code that matches the requested selection criteria and return it.
0239  *
0240  * @code: Search for an entry with the given media-bus code.
0241  * @fmt_sel: Allow entries only with the given selection criteria.
0242  */
0243 const struct imx_media_pixfmt *
0244 imx_media_find_mbus_format(u32 code, enum imx_pixfmt_sel fmt_sel)
0245 {
0246     bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
0247     unsigned int i;
0248 
0249     fmt_sel &= ~PIXFMT_SEL_IPU;
0250 
0251     for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
0252         const struct imx_media_pixfmt *fmt = &pixel_formats[i];
0253         enum imx_pixfmt_sel sel;
0254         unsigned int j;
0255 
0256         if (sel_ipu != fmt->ipufmt)
0257             continue;
0258 
0259         sel = fmt->bayer ? PIXFMT_SEL_BAYER :
0260             ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
0261              PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
0262 
0263         if (!(fmt_sel & sel) || !fmt->codes)
0264             continue;
0265 
0266         for (j = 0; fmt->codes[j]; j++) {
0267             if (code == fmt->codes[j])
0268                 return fmt;
0269         }
0270     }
0271 
0272     return NULL;
0273 }
0274 EXPORT_SYMBOL_GPL(imx_media_find_mbus_format);
0275 
0276 /*
0277  * Enumerate entries in the pixel_formats[] array that match the
0278  * requested selection criteria. Return the fourcc that matches the
0279  * selection criteria at the requested match index.
0280  *
0281  * @fourcc: The returned fourcc that matches the search criteria at
0282  *          the requested match index.
0283  * @index: The requested match index.
0284  * @fmt_sel: Include in the enumeration entries with the given selection
0285  *           criteria.
0286  * @code: If non-zero, only include in the enumeration entries matching this
0287  *  media bus code.
0288  */
0289 int imx_media_enum_pixel_formats(u32 *fourcc, u32 index,
0290                  enum imx_pixfmt_sel fmt_sel, u32 code)
0291 {
0292     bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
0293     unsigned int i;
0294 
0295     fmt_sel &= ~PIXFMT_SEL_IPU;
0296 
0297     for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
0298         const struct imx_media_pixfmt *fmt = &pixel_formats[i];
0299         enum imx_pixfmt_sel sel;
0300 
0301         if (sel_ipu != fmt->ipufmt)
0302             continue;
0303 
0304         sel = fmt->bayer ? PIXFMT_SEL_BAYER :
0305             ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
0306              PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
0307 
0308         if (!(fmt_sel & sel))
0309             continue;
0310 
0311         /*
0312          * If a media bus code is specified, only consider formats that
0313          * match it.
0314          */
0315         if (code) {
0316             unsigned int j;
0317 
0318             if (!fmt->codes)
0319                 continue;
0320 
0321             for (j = 0; fmt->codes[j]; j++) {
0322                 if (code == fmt->codes[j])
0323                     break;
0324             }
0325 
0326             if (!fmt->codes[j])
0327                 continue;
0328         }
0329 
0330         if (index == 0) {
0331             *fourcc = fmt->fourcc;
0332             return 0;
0333         }
0334 
0335         index--;
0336     }
0337 
0338     return -EINVAL;
0339 }
0340 EXPORT_SYMBOL_GPL(imx_media_enum_pixel_formats);
0341 
0342 /*
0343  * Enumerate entries in the pixel_formats[] array that match the
0344  * requested search criteria. Return the media-bus code that matches
0345  * the search criteria at the requested match index.
0346  *
0347  * @code: The returned media-bus code that matches the search criteria at
0348  *        the requested match index.
0349  * @index: The requested match index.
0350  * @fmt_sel: Include in the enumeration entries with the given selection
0351  *           criteria.
0352  */
0353 int imx_media_enum_mbus_formats(u32 *code, u32 index,
0354                 enum imx_pixfmt_sel fmt_sel)
0355 {
0356     bool sel_ipu = fmt_sel & PIXFMT_SEL_IPU;
0357     unsigned int i;
0358 
0359     fmt_sel &= ~PIXFMT_SEL_IPU;
0360 
0361     for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) {
0362         const struct imx_media_pixfmt *fmt = &pixel_formats[i];
0363         enum imx_pixfmt_sel sel;
0364         unsigned int j;
0365 
0366         if (sel_ipu != fmt->ipufmt)
0367             continue;
0368 
0369         sel = fmt->bayer ? PIXFMT_SEL_BAYER :
0370             ((fmt->cs == IPUV3_COLORSPACE_YUV) ?
0371              PIXFMT_SEL_YUV : PIXFMT_SEL_RGB);
0372 
0373         if (!(fmt_sel & sel) || !fmt->codes)
0374             continue;
0375 
0376         for (j = 0; fmt->codes[j]; j++) {
0377             if (index == 0) {
0378                 *code = fmt->codes[j];
0379                 return 0;
0380             }
0381 
0382             index--;
0383         }
0384     }
0385 
0386     return -EINVAL;
0387 }
0388 EXPORT_SYMBOL_GPL(imx_media_enum_mbus_formats);
0389 
0390 int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
0391                 u32 width, u32 height, u32 code, u32 field,
0392                 const struct imx_media_pixfmt **cc)
0393 {
0394     const struct imx_media_pixfmt *lcc;
0395 
0396     mbus->width = width;
0397     mbus->height = height;
0398     mbus->field = field;
0399 
0400     if (code == 0)
0401         imx_media_enum_mbus_formats(&code, 0, PIXFMT_SEL_YUV);
0402 
0403     lcc = imx_media_find_mbus_format(code, PIXFMT_SEL_ANY);
0404     if (!lcc) {
0405         lcc = imx_media_find_ipu_format(code, PIXFMT_SEL_YUV_RGB);
0406         if (!lcc)
0407             return -EINVAL;
0408     }
0409 
0410     mbus->code = code;
0411 
0412     mbus->colorspace = V4L2_COLORSPACE_SRGB;
0413     mbus->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(mbus->colorspace);
0414     mbus->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mbus->colorspace);
0415     mbus->quantization =
0416         V4L2_MAP_QUANTIZATION_DEFAULT(lcc->cs == IPUV3_COLORSPACE_RGB,
0417                           mbus->colorspace,
0418                           mbus->ycbcr_enc);
0419 
0420     if (cc)
0421         *cc = lcc;
0422 
0423     return 0;
0424 }
0425 EXPORT_SYMBOL_GPL(imx_media_init_mbus_fmt);
0426 
0427 /*
0428  * Initializes the TRY format to the ACTIVE format on all pads
0429  * of a subdev. Can be used as the .init_cfg pad operation.
0430  */
0431 int imx_media_init_cfg(struct v4l2_subdev *sd,
0432                struct v4l2_subdev_state *sd_state)
0433 {
0434     struct v4l2_mbus_framefmt *mf_try;
0435     struct v4l2_subdev_format format;
0436     unsigned int pad;
0437     int ret;
0438 
0439     for (pad = 0; pad < sd->entity.num_pads; pad++) {
0440         memset(&format, 0, sizeof(format));
0441 
0442         format.pad = pad;
0443         format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
0444         ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &format);
0445         if (ret)
0446             continue;
0447 
0448         mf_try = v4l2_subdev_get_try_format(sd, sd_state, pad);
0449         *mf_try = format.format;
0450     }
0451 
0452     return 0;
0453 }
0454 EXPORT_SYMBOL_GPL(imx_media_init_cfg);
0455 
0456 /*
0457  * Default the colorspace in tryfmt to SRGB if set to an unsupported
0458  * colorspace or not initialized. Then set the remaining colorimetry
0459  * parameters based on the colorspace if they are uninitialized.
0460  *
0461  * tryfmt->code must be set on entry.
0462  *
0463  * If this format is destined to be routed through the Image Converter,
0464  * Y`CbCr encoding must be fixed. The IC supports only BT.601 Y`CbCr
0465  * or Rec.709 Y`CbCr encoding.
0466  */
0467 void imx_media_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt,
0468                    bool ic_route)
0469 {
0470     const struct imx_media_pixfmt *cc;
0471     bool is_rgb = false;
0472 
0473     cc = imx_media_find_mbus_format(tryfmt->code, PIXFMT_SEL_ANY);
0474     if (!cc)
0475         cc = imx_media_find_ipu_format(tryfmt->code,
0476                            PIXFMT_SEL_YUV_RGB);
0477 
0478     if (cc && cc->cs == IPUV3_COLORSPACE_RGB)
0479         is_rgb = true;
0480 
0481     switch (tryfmt->colorspace) {
0482     case V4L2_COLORSPACE_SMPTE170M:
0483     case V4L2_COLORSPACE_REC709:
0484     case V4L2_COLORSPACE_JPEG:
0485     case V4L2_COLORSPACE_SRGB:
0486     case V4L2_COLORSPACE_BT2020:
0487     case V4L2_COLORSPACE_OPRGB:
0488     case V4L2_COLORSPACE_DCI_P3:
0489     case V4L2_COLORSPACE_RAW:
0490         break;
0491     default:
0492         tryfmt->colorspace = V4L2_COLORSPACE_SRGB;
0493         break;
0494     }
0495 
0496     if (tryfmt->xfer_func == V4L2_XFER_FUNC_DEFAULT)
0497         tryfmt->xfer_func =
0498             V4L2_MAP_XFER_FUNC_DEFAULT(tryfmt->colorspace);
0499 
0500     if (ic_route) {
0501         if (tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_601 &&
0502             tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_709)
0503             tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
0504     } else {
0505         if (tryfmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
0506             tryfmt->ycbcr_enc =
0507                 V4L2_MAP_YCBCR_ENC_DEFAULT(tryfmt->colorspace);
0508         }
0509     }
0510 
0511     if (tryfmt->quantization == V4L2_QUANTIZATION_DEFAULT)
0512         tryfmt->quantization =
0513             V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb,
0514                               tryfmt->colorspace,
0515                               tryfmt->ycbcr_enc);
0516 }
0517 EXPORT_SYMBOL_GPL(imx_media_try_colorimetry);
0518 
0519 int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
0520                   const struct v4l2_mbus_framefmt *mbus,
0521                   const struct imx_media_pixfmt *cc)
0522 {
0523     u32 width;
0524     u32 stride;
0525 
0526     if (!cc) {
0527         cc = imx_media_find_ipu_format(mbus->code,
0528                            PIXFMT_SEL_YUV_RGB);
0529         if (!cc)
0530             cc = imx_media_find_mbus_format(mbus->code,
0531                             PIXFMT_SEL_ANY);
0532         if (!cc)
0533             return -EINVAL;
0534     }
0535 
0536     /*
0537      * TODO: the IPU currently does not support the AYUV32 format,
0538      * so until it does convert to a supported YUV format.
0539      */
0540     if (cc->ipufmt && cc->cs == IPUV3_COLORSPACE_YUV) {
0541         u32 code;
0542 
0543         imx_media_enum_mbus_formats(&code, 0, PIXFMT_SEL_YUV);
0544         cc = imx_media_find_mbus_format(code, PIXFMT_SEL_YUV);
0545     }
0546 
0547     /* Round up width for minimum burst size */
0548     width = round_up(mbus->width, 8);
0549 
0550     /* Round up stride for IDMAC line start address alignment */
0551     if (cc->planar)
0552         stride = round_up(width, 16);
0553     else
0554         stride = round_up((width * cc->bpp) >> 3, 8);
0555 
0556     pix->width = width;
0557     pix->height = mbus->height;
0558     pix->pixelformat = cc->fourcc;
0559     pix->colorspace = mbus->colorspace;
0560     pix->xfer_func = mbus->xfer_func;
0561     pix->ycbcr_enc = mbus->ycbcr_enc;
0562     pix->quantization = mbus->quantization;
0563     pix->field = mbus->field;
0564     pix->bytesperline = stride;
0565     pix->sizeimage = cc->planar ? ((stride * pix->height * cc->bpp) >> 3) :
0566              stride * pix->height;
0567 
0568     return 0;
0569 }
0570 EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_pix_fmt);
0571 
0572 void imx_media_free_dma_buf(struct device *dev,
0573                 struct imx_media_dma_buf *buf)
0574 {
0575     if (buf->virt)
0576         dma_free_coherent(dev, buf->len, buf->virt, buf->phys);
0577 
0578     buf->virt = NULL;
0579     buf->phys = 0;
0580 }
0581 EXPORT_SYMBOL_GPL(imx_media_free_dma_buf);
0582 
0583 int imx_media_alloc_dma_buf(struct device *dev,
0584                 struct imx_media_dma_buf *buf,
0585                 int size)
0586 {
0587     imx_media_free_dma_buf(dev, buf);
0588 
0589     buf->len = PAGE_ALIGN(size);
0590     buf->virt = dma_alloc_coherent(dev, buf->len, &buf->phys,
0591                        GFP_DMA | GFP_KERNEL);
0592     if (!buf->virt) {
0593         dev_err(dev, "%s: failed\n", __func__);
0594         return -ENOMEM;
0595     }
0596 
0597     return 0;
0598 }
0599 EXPORT_SYMBOL_GPL(imx_media_alloc_dma_buf);
0600 
0601 /* form a subdev name given a group id and ipu id */
0602 void imx_media_grp_id_to_sd_name(char *sd_name, int sz, u32 grp_id, int ipu_id)
0603 {
0604     int id;
0605 
0606     switch (grp_id) {
0607     case IMX_MEDIA_GRP_ID_IPU_CSI0...IMX_MEDIA_GRP_ID_IPU_CSI1:
0608         id = (grp_id >> IMX_MEDIA_GRP_ID_IPU_CSI_BIT) - 1;
0609         snprintf(sd_name, sz, "ipu%d_csi%d", ipu_id + 1, id);
0610         break;
0611     case IMX_MEDIA_GRP_ID_IPU_VDIC:
0612         snprintf(sd_name, sz, "ipu%d_vdic", ipu_id + 1);
0613         break;
0614     case IMX_MEDIA_GRP_ID_IPU_IC_PRP:
0615         snprintf(sd_name, sz, "ipu%d_ic_prp", ipu_id + 1);
0616         break;
0617     case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC:
0618         snprintf(sd_name, sz, "ipu%d_ic_prpenc", ipu_id + 1);
0619         break;
0620     case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF:
0621         snprintf(sd_name, sz, "ipu%d_ic_prpvf", ipu_id + 1);
0622         break;
0623     default:
0624         break;
0625     }
0626 }
0627 EXPORT_SYMBOL_GPL(imx_media_grp_id_to_sd_name);
0628 
0629 struct v4l2_subdev *
0630 imx_media_find_subdev_by_fwnode(struct imx_media_dev *imxmd,
0631                 struct fwnode_handle *fwnode)
0632 {
0633     struct v4l2_subdev *sd;
0634 
0635     list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
0636         if (sd->fwnode == fwnode)
0637             return sd;
0638     }
0639 
0640     return NULL;
0641 }
0642 EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_fwnode);
0643 
0644 struct v4l2_subdev *
0645 imx_media_find_subdev_by_devname(struct imx_media_dev *imxmd,
0646                  const char *devname)
0647 {
0648     struct v4l2_subdev *sd;
0649 
0650     list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
0651         if (!strcmp(devname, dev_name(sd->dev)))
0652             return sd;
0653     }
0654 
0655     return NULL;
0656 }
0657 EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_devname);
0658 
0659 /*
0660  * Adds a video device to the master video device list. This is called
0661  * when a video device is registered.
0662  */
0663 void imx_media_add_video_device(struct imx_media_dev *imxmd,
0664                 struct imx_media_video_dev *vdev)
0665 {
0666     mutex_lock(&imxmd->mutex);
0667 
0668     list_add_tail(&vdev->list, &imxmd->vdev_list);
0669 
0670     mutex_unlock(&imxmd->mutex);
0671 }
0672 EXPORT_SYMBOL_GPL(imx_media_add_video_device);
0673 
0674 /*
0675  * Search upstream/downstream for a subdevice or video device pad in the
0676  * current pipeline, starting from start_entity. Returns the device's
0677  * source/sink pad that it was reached from. Must be called with
0678  * mdev->graph_mutex held.
0679  *
0680  * If grp_id != 0, finds a subdevice's pad of given grp_id.
0681  * Else If buftype != 0, finds a video device's pad of given buffer type.
0682  * Else, returns the nearest source/sink pad to start_entity.
0683  */
0684 struct media_pad *
0685 imx_media_pipeline_pad(struct media_entity *start_entity, u32 grp_id,
0686                enum v4l2_buf_type buftype, bool upstream)
0687 {
0688     struct media_entity *me = start_entity;
0689     struct media_pad *pad = NULL;
0690     struct video_device *vfd;
0691     struct v4l2_subdev *sd;
0692     int i;
0693 
0694     for (i = 0; i < me->num_pads; i++) {
0695         struct media_pad *spad = &me->pads[i];
0696 
0697         if ((upstream && !(spad->flags & MEDIA_PAD_FL_SINK)) ||
0698             (!upstream && !(spad->flags & MEDIA_PAD_FL_SOURCE)))
0699             continue;
0700 
0701         pad = media_pad_remote_pad_first(spad);
0702         if (!pad)
0703             continue;
0704 
0705         if (grp_id) {
0706             if (is_media_entity_v4l2_subdev(pad->entity)) {
0707                 sd = media_entity_to_v4l2_subdev(pad->entity);
0708                 if (sd->grp_id & grp_id)
0709                     return pad;
0710             }
0711 
0712             return imx_media_pipeline_pad(pad->entity, grp_id,
0713                               buftype, upstream);
0714         } else if (buftype) {
0715             if (is_media_entity_v4l2_video_device(pad->entity)) {
0716                 vfd = media_entity_to_video_device(pad->entity);
0717                 if (buftype == vfd->queue->type)
0718                     return pad;
0719             }
0720 
0721             return imx_media_pipeline_pad(pad->entity, grp_id,
0722                               buftype, upstream);
0723         } else {
0724             return pad;
0725         }
0726     }
0727 
0728     return NULL;
0729 }
0730 EXPORT_SYMBOL_GPL(imx_media_pipeline_pad);
0731 
0732 /*
0733  * Search upstream/downstream for a subdev or video device in the current
0734  * pipeline. Must be called with mdev->graph_mutex held.
0735  */
0736 static struct media_entity *
0737 find_pipeline_entity(struct media_entity *start, u32 grp_id,
0738              enum v4l2_buf_type buftype, bool upstream)
0739 {
0740     struct media_pad *pad = NULL;
0741     struct video_device *vfd;
0742     struct v4l2_subdev *sd;
0743 
0744     if (grp_id && is_media_entity_v4l2_subdev(start)) {
0745         sd = media_entity_to_v4l2_subdev(start);
0746         if (sd->grp_id & grp_id)
0747             return &sd->entity;
0748     } else if (buftype && is_media_entity_v4l2_video_device(start)) {
0749         vfd = media_entity_to_video_device(start);
0750         if (buftype == vfd->queue->type)
0751             return &vfd->entity;
0752     }
0753 
0754     pad = imx_media_pipeline_pad(start, grp_id, buftype, upstream);
0755 
0756     return pad ? pad->entity : NULL;
0757 }
0758 
0759 /*
0760  * Find the upstream mipi-csi2 virtual channel reached from the given
0761  * start entity in the current pipeline.
0762  * Must be called with mdev->graph_mutex held.
0763  */
0764 int imx_media_pipeline_csi2_channel(struct media_entity *start_entity)
0765 {
0766     struct media_pad *pad;
0767     int ret = -EPIPE;
0768 
0769     pad = imx_media_pipeline_pad(start_entity, IMX_MEDIA_GRP_ID_CSI2,
0770                      0, true);
0771     if (pad)
0772         ret = pad->index - 1;
0773 
0774     return ret;
0775 }
0776 EXPORT_SYMBOL_GPL(imx_media_pipeline_csi2_channel);
0777 
0778 /*
0779  * Find a subdev reached upstream from the given start entity in
0780  * the current pipeline.
0781  * Must be called with mdev->graph_mutex held.
0782  */
0783 struct v4l2_subdev *
0784 imx_media_pipeline_subdev(struct media_entity *start_entity, u32 grp_id,
0785               bool upstream)
0786 {
0787     struct media_entity *me;
0788 
0789     me = find_pipeline_entity(start_entity, grp_id, 0, upstream);
0790     if (!me)
0791         return ERR_PTR(-ENODEV);
0792 
0793     return media_entity_to_v4l2_subdev(me);
0794 }
0795 EXPORT_SYMBOL_GPL(imx_media_pipeline_subdev);
0796 
0797 /*
0798  * Find a subdev reached upstream from the given start entity in
0799  * the current pipeline.
0800  * Must be called with mdev->graph_mutex held.
0801  */
0802 struct video_device *
0803 imx_media_pipeline_video_device(struct media_entity *start_entity,
0804                 enum v4l2_buf_type buftype, bool upstream)
0805 {
0806     struct media_entity *me;
0807 
0808     me = find_pipeline_entity(start_entity, 0, buftype, upstream);
0809     if (!me)
0810         return ERR_PTR(-ENODEV);
0811 
0812     return media_entity_to_video_device(me);
0813 }
0814 EXPORT_SYMBOL_GPL(imx_media_pipeline_video_device);
0815 
0816 /*
0817  * Find a fwnode endpoint that maps to the given subdevice's pad.
0818  * If there are multiple endpoints that map to the pad, only the
0819  * first endpoint encountered is returned.
0820  *
0821  * On success the refcount of the returned fwnode endpoint is
0822  * incremented.
0823  */
0824 struct fwnode_handle *imx_media_get_pad_fwnode(struct media_pad *pad)
0825 {
0826     struct fwnode_handle *endpoint;
0827     struct v4l2_subdev *sd;
0828 
0829     if (!is_media_entity_v4l2_subdev(pad->entity))
0830         return ERR_PTR(-ENODEV);
0831 
0832     sd = media_entity_to_v4l2_subdev(pad->entity);
0833 
0834     fwnode_graph_for_each_endpoint(dev_fwnode(sd->dev), endpoint) {
0835         int pad_idx = media_entity_get_fwnode_pad(&sd->entity,
0836                               endpoint,
0837                               pad->flags);
0838         if (pad_idx < 0)
0839             continue;
0840 
0841         if (pad_idx == pad->index)
0842             return endpoint;
0843     }
0844 
0845     return ERR_PTR(-ENODEV);
0846 }
0847 EXPORT_SYMBOL_GPL(imx_media_get_pad_fwnode);
0848 
0849 /*
0850  * Turn current pipeline streaming on/off starting from entity.
0851  */
0852 int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd,
0853                   struct media_entity *entity,
0854                   bool on)
0855 {
0856     struct v4l2_subdev *sd;
0857     int ret = 0;
0858 
0859     if (!is_media_entity_v4l2_subdev(entity))
0860         return -EINVAL;
0861     sd = media_entity_to_v4l2_subdev(entity);
0862 
0863     mutex_lock(&imxmd->md.graph_mutex);
0864 
0865     if (on) {
0866         ret = __media_pipeline_start(entity, &imxmd->pipe);
0867         if (ret)
0868             goto out;
0869         ret = v4l2_subdev_call(sd, video, s_stream, 1);
0870         if (ret)
0871             __media_pipeline_stop(entity);
0872     } else {
0873         v4l2_subdev_call(sd, video, s_stream, 0);
0874         if (entity->pipe)
0875             __media_pipeline_stop(entity);
0876     }
0877 
0878 out:
0879     mutex_unlock(&imxmd->md.graph_mutex);
0880     return ret;
0881 }
0882 EXPORT_SYMBOL_GPL(imx_media_pipeline_set_stream);
0883 
0884 MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver");
0885 MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
0886 MODULE_LICENSE("GPL");