Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * i.MX IPUv3 IC PP mem2mem CSC/Scaler driver
0004  *
0005  * Copyright (C) 2011 Pengutronix, Sascha Hauer
0006  * Copyright (C) 2018 Pengutronix, Philipp Zabel
0007  */
0008 #include <linux/module.h>
0009 #include <linux/delay.h>
0010 #include <linux/fs.h>
0011 #include <linux/sched.h>
0012 #include <linux/slab.h>
0013 #include <video/imx-ipu-v3.h>
0014 #include <video/imx-ipu-image-convert.h>
0015 
0016 #include <media/media-device.h>
0017 #include <media/v4l2-ctrls.h>
0018 #include <media/v4l2-event.h>
0019 #include <media/v4l2-mem2mem.h>
0020 #include <media/v4l2-device.h>
0021 #include <media/v4l2-ioctl.h>
0022 #include <media/videobuf2-dma-contig.h>
0023 
0024 #include "imx-media.h"
0025 
0026 #define fh_to_ctx(__fh) container_of(__fh, struct ipu_csc_scaler_ctx, fh)
0027 
0028 #define IMX_CSC_SCALER_NAME "imx-csc-scaler"
0029 
0030 enum {
0031     V4L2_M2M_SRC = 0,
0032     V4L2_M2M_DST = 1,
0033 };
0034 
0035 struct ipu_csc_scaler_priv {
0036     struct imx_media_video_dev  vdev;
0037 
0038     struct v4l2_m2m_dev     *m2m_dev;
0039     struct device           *dev;
0040 
0041     struct imx_media_dev        *md;
0042 
0043     struct mutex            mutex;  /* mem2mem device mutex */
0044 };
0045 
0046 #define vdev_to_priv(v) container_of(v, struct ipu_csc_scaler_priv, vdev)
0047 
0048 /* Per-queue, driver-specific private data */
0049 struct ipu_csc_scaler_q_data {
0050     struct v4l2_pix_format      cur_fmt;
0051     struct v4l2_rect        rect;
0052 };
0053 
0054 struct ipu_csc_scaler_ctx {
0055     struct ipu_csc_scaler_priv  *priv;
0056 
0057     struct v4l2_fh          fh;
0058     struct ipu_csc_scaler_q_data    q_data[2];
0059     struct ipu_image_convert_ctx    *icc;
0060 
0061     struct v4l2_ctrl_handler    ctrl_hdlr;
0062     int             rotate;
0063     bool                hflip;
0064     bool                vflip;
0065     enum ipu_rotate_mode        rot_mode;
0066     unsigned int            sequence;
0067 };
0068 
0069 static struct ipu_csc_scaler_q_data *get_q_data(struct ipu_csc_scaler_ctx *ctx,
0070                         enum v4l2_buf_type type)
0071 {
0072     if (V4L2_TYPE_IS_OUTPUT(type))
0073         return &ctx->q_data[V4L2_M2M_SRC];
0074     else
0075         return &ctx->q_data[V4L2_M2M_DST];
0076 }
0077 
0078 /*
0079  * mem2mem callbacks
0080  */
0081 
0082 static void job_abort(void *_ctx)
0083 {
0084     struct ipu_csc_scaler_ctx *ctx = _ctx;
0085 
0086     if (ctx->icc)
0087         ipu_image_convert_abort(ctx->icc);
0088 }
0089 
0090 static void ipu_ic_pp_complete(struct ipu_image_convert_run *run, void *_ctx)
0091 {
0092     struct ipu_csc_scaler_ctx *ctx = _ctx;
0093     struct ipu_csc_scaler_priv *priv = ctx->priv;
0094     struct vb2_v4l2_buffer *src_buf, *dst_buf;
0095 
0096     src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
0097     dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
0098 
0099     v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
0100 
0101     src_buf->sequence = ctx->sequence++;
0102     dst_buf->sequence = src_buf->sequence;
0103 
0104     v4l2_m2m_buf_done(src_buf, run->status ? VB2_BUF_STATE_ERROR :
0105                          VB2_BUF_STATE_DONE);
0106     v4l2_m2m_buf_done(dst_buf, run->status ? VB2_BUF_STATE_ERROR :
0107                          VB2_BUF_STATE_DONE);
0108 
0109     v4l2_m2m_job_finish(priv->m2m_dev, ctx->fh.m2m_ctx);
0110     kfree(run);
0111 }
0112 
0113 static void device_run(void *_ctx)
0114 {
0115     struct ipu_csc_scaler_ctx *ctx = _ctx;
0116     struct ipu_csc_scaler_priv *priv = ctx->priv;
0117     struct vb2_v4l2_buffer *src_buf, *dst_buf;
0118     struct ipu_image_convert_run *run;
0119     int ret;
0120 
0121     src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
0122     dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
0123 
0124     run = kzalloc(sizeof(*run), GFP_KERNEL);
0125     if (!run)
0126         goto err;
0127 
0128     run->ctx = ctx->icc;
0129     run->in_phys = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
0130     run->out_phys = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
0131 
0132     ret = ipu_image_convert_queue(run);
0133     if (ret < 0) {
0134         v4l2_err(ctx->priv->vdev.vfd->v4l2_dev,
0135              "%s: failed to queue: %d\n", __func__, ret);
0136         goto err;
0137     }
0138 
0139     return;
0140 
0141 err:
0142     v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
0143     v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
0144     v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
0145     v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
0146     v4l2_m2m_job_finish(priv->m2m_dev, ctx->fh.m2m_ctx);
0147 }
0148 
0149 /*
0150  * Video ioctls
0151  */
0152 static int ipu_csc_scaler_querycap(struct file *file, void *priv,
0153                    struct v4l2_capability *cap)
0154 {
0155     strscpy(cap->driver, IMX_CSC_SCALER_NAME, sizeof(cap->driver));
0156     strscpy(cap->card, IMX_CSC_SCALER_NAME, sizeof(cap->card));
0157     snprintf(cap->bus_info, sizeof(cap->bus_info),
0158          "platform:%s", IMX_CSC_SCALER_NAME);
0159 
0160     return 0;
0161 }
0162 
0163 static int ipu_csc_scaler_enum_fmt(struct file *file, void *fh,
0164                    struct v4l2_fmtdesc *f)
0165 {
0166     u32 fourcc;
0167     int ret;
0168 
0169     ret = imx_media_enum_pixel_formats(&fourcc, f->index,
0170                        PIXFMT_SEL_YUV_RGB, 0);
0171     if (ret)
0172         return ret;
0173 
0174     f->pixelformat = fourcc;
0175 
0176     return 0;
0177 }
0178 
0179 static int ipu_csc_scaler_g_fmt(struct file *file, void *priv,
0180                 struct v4l2_format *f)
0181 {
0182     struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(priv);
0183     struct ipu_csc_scaler_q_data *q_data;
0184 
0185     q_data = get_q_data(ctx, f->type);
0186 
0187     f->fmt.pix = q_data->cur_fmt;
0188 
0189     return 0;
0190 }
0191 
0192 static int ipu_csc_scaler_try_fmt(struct file *file, void *priv,
0193                   struct v4l2_format *f)
0194 {
0195     struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(priv);
0196     struct ipu_csc_scaler_q_data *q_data = get_q_data(ctx, f->type);
0197     struct ipu_image test_in, test_out;
0198     enum v4l2_field field;
0199 
0200     field = f->fmt.pix.field;
0201     if (field == V4L2_FIELD_ANY)
0202         field = V4L2_FIELD_NONE;
0203     else if (field != V4L2_FIELD_NONE)
0204         return -EINVAL;
0205 
0206     if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
0207         struct ipu_csc_scaler_q_data *q_data_in =
0208             get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
0209 
0210         test_out.pix = f->fmt.pix;
0211         test_in.pix = q_data_in->cur_fmt;
0212     } else {
0213         struct ipu_csc_scaler_q_data *q_data_out =
0214             get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
0215 
0216         test_in.pix = f->fmt.pix;
0217         test_out.pix = q_data_out->cur_fmt;
0218     }
0219 
0220     ipu_image_convert_adjust(&test_in, &test_out, ctx->rot_mode);
0221 
0222     f->fmt.pix = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
0223         test_out.pix : test_in.pix;
0224 
0225     if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
0226         f->fmt.pix.colorspace = q_data->cur_fmt.colorspace;
0227         f->fmt.pix.ycbcr_enc = q_data->cur_fmt.ycbcr_enc;
0228         f->fmt.pix.xfer_func = q_data->cur_fmt.xfer_func;
0229         f->fmt.pix.quantization = q_data->cur_fmt.quantization;
0230     } else if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) {
0231         f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
0232         f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
0233         f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
0234         f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
0235     }
0236 
0237     return 0;
0238 }
0239 
0240 static int ipu_csc_scaler_s_fmt(struct file *file, void *priv,
0241                 struct v4l2_format *f)
0242 {
0243     struct ipu_csc_scaler_q_data *q_data;
0244     struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(priv);
0245     struct vb2_queue *vq;
0246     int ret;
0247 
0248     vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
0249     if (vb2_is_busy(vq)) {
0250         v4l2_err(ctx->priv->vdev.vfd->v4l2_dev, "%s: queue busy\n",
0251              __func__);
0252         return -EBUSY;
0253     }
0254 
0255     q_data = get_q_data(ctx, f->type);
0256 
0257     ret = ipu_csc_scaler_try_fmt(file, priv, f);
0258     if (ret < 0)
0259         return ret;
0260 
0261     q_data->cur_fmt.width = f->fmt.pix.width;
0262     q_data->cur_fmt.height = f->fmt.pix.height;
0263     q_data->cur_fmt.pixelformat = f->fmt.pix.pixelformat;
0264     q_data->cur_fmt.field = f->fmt.pix.field;
0265     q_data->cur_fmt.bytesperline = f->fmt.pix.bytesperline;
0266     q_data->cur_fmt.sizeimage = f->fmt.pix.sizeimage;
0267 
0268     /* Reset cropping/composing rectangle */
0269     q_data->rect.left = 0;
0270     q_data->rect.top = 0;
0271     q_data->rect.width = q_data->cur_fmt.width;
0272     q_data->rect.height = q_data->cur_fmt.height;
0273 
0274     if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
0275         /* Set colorimetry on the output queue */
0276         q_data->cur_fmt.colorspace = f->fmt.pix.colorspace;
0277         q_data->cur_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
0278         q_data->cur_fmt.xfer_func = f->fmt.pix.xfer_func;
0279         q_data->cur_fmt.quantization = f->fmt.pix.quantization;
0280         /* Propagate colorimetry to the capture queue */
0281         q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
0282         q_data->cur_fmt.colorspace = f->fmt.pix.colorspace;
0283         q_data->cur_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
0284         q_data->cur_fmt.xfer_func = f->fmt.pix.xfer_func;
0285         q_data->cur_fmt.quantization = f->fmt.pix.quantization;
0286     }
0287 
0288     /*
0289      * TODO: Setting colorimetry on the capture queue is currently not
0290      * supported by the V4L2 API
0291      */
0292 
0293     return 0;
0294 }
0295 
0296 static int ipu_csc_scaler_g_selection(struct file *file, void *priv,
0297                       struct v4l2_selection *s)
0298 {
0299     struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(priv);
0300     struct ipu_csc_scaler_q_data *q_data;
0301 
0302     switch (s->target) {
0303     case V4L2_SEL_TGT_CROP:
0304     case V4L2_SEL_TGT_CROP_DEFAULT:
0305     case V4L2_SEL_TGT_CROP_BOUNDS:
0306         if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
0307             return -EINVAL;
0308         q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
0309         break;
0310     case V4L2_SEL_TGT_COMPOSE:
0311     case V4L2_SEL_TGT_COMPOSE_DEFAULT:
0312     case V4L2_SEL_TGT_COMPOSE_BOUNDS:
0313         if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
0314             return -EINVAL;
0315         q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
0316         break;
0317     default:
0318         return -EINVAL;
0319     }
0320 
0321     if (s->target == V4L2_SEL_TGT_CROP ||
0322         s->target == V4L2_SEL_TGT_COMPOSE) {
0323         s->r = q_data->rect;
0324     } else {
0325         s->r.left = 0;
0326         s->r.top = 0;
0327         s->r.width = q_data->cur_fmt.width;
0328         s->r.height = q_data->cur_fmt.height;
0329     }
0330 
0331     return 0;
0332 }
0333 
0334 static int ipu_csc_scaler_s_selection(struct file *file, void *priv,
0335                       struct v4l2_selection *s)
0336 {
0337     struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(priv);
0338     struct ipu_csc_scaler_q_data *q_data;
0339 
0340     switch (s->target) {
0341     case V4L2_SEL_TGT_CROP:
0342         if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
0343             return -EINVAL;
0344         break;
0345     case V4L2_SEL_TGT_COMPOSE:
0346         if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
0347             return -EINVAL;
0348         break;
0349     default:
0350         return -EINVAL;
0351     }
0352 
0353     if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
0354         s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
0355         return -EINVAL;
0356 
0357     q_data = get_q_data(ctx, s->type);
0358 
0359     /* The input's frame width to the IC must be a multiple of 8 pixels
0360      * When performing resizing the frame width must be multiple of burst
0361      * size - 8 or 16 pixels as defined by CB#_BURST_16 parameter.
0362      */
0363     if (s->flags & V4L2_SEL_FLAG_GE)
0364         s->r.width = round_up(s->r.width, 8);
0365     if (s->flags & V4L2_SEL_FLAG_LE)
0366         s->r.width = round_down(s->r.width, 8);
0367     s->r.width = clamp_t(unsigned int, s->r.width, 8,
0368                  round_down(q_data->cur_fmt.width, 8));
0369     s->r.height = clamp_t(unsigned int, s->r.height, 1,
0370                   q_data->cur_fmt.height);
0371     s->r.left = clamp_t(unsigned int, s->r.left, 0,
0372                 q_data->cur_fmt.width - s->r.width);
0373     s->r.top = clamp_t(unsigned int, s->r.top, 0,
0374                q_data->cur_fmt.height - s->r.height);
0375 
0376     /* V4L2_SEL_FLAG_KEEP_CONFIG is only valid for subdevices */
0377     q_data->rect = s->r;
0378 
0379     return 0;
0380 }
0381 
0382 static const struct v4l2_ioctl_ops ipu_csc_scaler_ioctl_ops = {
0383     .vidioc_querycap        = ipu_csc_scaler_querycap,
0384 
0385     .vidioc_enum_fmt_vid_cap    = ipu_csc_scaler_enum_fmt,
0386     .vidioc_g_fmt_vid_cap       = ipu_csc_scaler_g_fmt,
0387     .vidioc_try_fmt_vid_cap     = ipu_csc_scaler_try_fmt,
0388     .vidioc_s_fmt_vid_cap       = ipu_csc_scaler_s_fmt,
0389 
0390     .vidioc_enum_fmt_vid_out    = ipu_csc_scaler_enum_fmt,
0391     .vidioc_g_fmt_vid_out       = ipu_csc_scaler_g_fmt,
0392     .vidioc_try_fmt_vid_out     = ipu_csc_scaler_try_fmt,
0393     .vidioc_s_fmt_vid_out       = ipu_csc_scaler_s_fmt,
0394 
0395     .vidioc_g_selection     = ipu_csc_scaler_g_selection,
0396     .vidioc_s_selection     = ipu_csc_scaler_s_selection,
0397 
0398     .vidioc_reqbufs         = v4l2_m2m_ioctl_reqbufs,
0399     .vidioc_querybuf        = v4l2_m2m_ioctl_querybuf,
0400 
0401     .vidioc_qbuf            = v4l2_m2m_ioctl_qbuf,
0402     .vidioc_expbuf          = v4l2_m2m_ioctl_expbuf,
0403     .vidioc_dqbuf           = v4l2_m2m_ioctl_dqbuf,
0404     .vidioc_create_bufs     = v4l2_m2m_ioctl_create_bufs,
0405     .vidioc_prepare_buf     = v4l2_m2m_ioctl_prepare_buf,
0406 
0407     .vidioc_streamon        = v4l2_m2m_ioctl_streamon,
0408     .vidioc_streamoff       = v4l2_m2m_ioctl_streamoff,
0409 
0410     .vidioc_subscribe_event     = v4l2_ctrl_subscribe_event,
0411     .vidioc_unsubscribe_event   = v4l2_event_unsubscribe,
0412 };
0413 
0414 /*
0415  * Queue operations
0416  */
0417 
0418 static int ipu_csc_scaler_queue_setup(struct vb2_queue *vq,
0419                       unsigned int *nbuffers,
0420                       unsigned int *nplanes,
0421                       unsigned int sizes[],
0422                       struct device *alloc_devs[])
0423 {
0424     struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(vq);
0425     struct ipu_csc_scaler_q_data *q_data;
0426     unsigned int size, count = *nbuffers;
0427 
0428     q_data = get_q_data(ctx, vq->type);
0429 
0430     size = q_data->cur_fmt.sizeimage;
0431 
0432     *nbuffers = count;
0433 
0434     if (*nplanes)
0435         return sizes[0] < size ? -EINVAL : 0;
0436 
0437     *nplanes = 1;
0438     sizes[0] = size;
0439 
0440     dev_dbg(ctx->priv->dev, "get %d buffer(s) of size %d each.\n",
0441         count, size);
0442 
0443     return 0;
0444 }
0445 
0446 static int ipu_csc_scaler_buf_prepare(struct vb2_buffer *vb)
0447 {
0448     struct vb2_queue *vq = vb->vb2_queue;
0449     struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
0450     struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(vq);
0451     struct ipu_csc_scaler_q_data *q_data;
0452     unsigned long size;
0453 
0454     dev_dbg(ctx->priv->dev, "type: %d\n", vq->type);
0455 
0456     if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
0457         if (vbuf->field == V4L2_FIELD_ANY)
0458             vbuf->field = V4L2_FIELD_NONE;
0459         if (vbuf->field != V4L2_FIELD_NONE) {
0460             dev_dbg(ctx->priv->dev, "%s: field isn't supported\n",
0461                 __func__);
0462             return -EINVAL;
0463         }
0464     }
0465 
0466     q_data = get_q_data(ctx, vq->type);
0467     size = q_data->cur_fmt.sizeimage;
0468 
0469     if (vb2_plane_size(vb, 0) < size) {
0470         dev_dbg(ctx->priv->dev,
0471             "%s: data will not fit into plane (%lu < %lu)\n",
0472             __func__, vb2_plane_size(vb, 0), size);
0473         return -EINVAL;
0474     }
0475 
0476     vb2_set_plane_payload(vb, 0, size);
0477 
0478     return 0;
0479 }
0480 
0481 static void ipu_csc_scaler_buf_queue(struct vb2_buffer *vb)
0482 {
0483     struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
0484 
0485     v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb));
0486 }
0487 
0488 static void ipu_image_from_q_data(struct ipu_image *im,
0489                   struct ipu_csc_scaler_q_data *q_data)
0490 {
0491     struct v4l2_pix_format *fmt = &q_data->cur_fmt;
0492 
0493     im->pix = *fmt;
0494     if (fmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
0495         im->pix.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
0496     if (fmt->quantization == V4L2_QUANTIZATION_DEFAULT)
0497         im->pix.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
0498     im->rect = q_data->rect;
0499 }
0500 
0501 static int ipu_csc_scaler_start_streaming(struct vb2_queue *q,
0502                       unsigned int count)
0503 {
0504     const enum ipu_ic_task ic_task = IC_TASK_POST_PROCESSOR;
0505     struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(q);
0506     struct ipu_csc_scaler_priv *priv = ctx->priv;
0507     struct ipu_soc *ipu = priv->md->ipu[0];
0508     struct ipu_csc_scaler_q_data *q_data;
0509     struct vb2_queue *other_q;
0510     struct ipu_image in, out;
0511 
0512     other_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
0513                   (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
0514                   V4L2_BUF_TYPE_VIDEO_OUTPUT :
0515                   V4L2_BUF_TYPE_VIDEO_CAPTURE);
0516     if (!vb2_is_streaming(other_q))
0517         return 0;
0518 
0519     if (ctx->icc) {
0520         v4l2_warn(ctx->priv->vdev.vfd->v4l2_dev, "removing old ICC\n");
0521         ipu_image_convert_unprepare(ctx->icc);
0522     }
0523 
0524     q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
0525     ipu_image_from_q_data(&in, q_data);
0526 
0527     q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
0528     ipu_image_from_q_data(&out, q_data);
0529 
0530     ctx->icc = ipu_image_convert_prepare(ipu, ic_task, &in, &out,
0531                          ctx->rot_mode,
0532                          ipu_ic_pp_complete, ctx);
0533     if (IS_ERR(ctx->icc)) {
0534         struct vb2_v4l2_buffer *buf;
0535         int ret = PTR_ERR(ctx->icc);
0536 
0537         ctx->icc = NULL;
0538         v4l2_err(ctx->priv->vdev.vfd->v4l2_dev, "%s: error %d\n",
0539              __func__, ret);
0540         while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
0541             v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
0542         while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
0543             v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
0544         return ret;
0545     }
0546 
0547     return 0;
0548 }
0549 
0550 static void ipu_csc_scaler_stop_streaming(struct vb2_queue *q)
0551 {
0552     struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(q);
0553     struct vb2_v4l2_buffer *buf;
0554 
0555     if (ctx->icc) {
0556         ipu_image_convert_unprepare(ctx->icc);
0557         ctx->icc = NULL;
0558     }
0559 
0560     ctx->sequence = 0;
0561 
0562     if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
0563         while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
0564             v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
0565     } else {
0566         while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
0567             v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
0568     }
0569 }
0570 
0571 static const struct vb2_ops ipu_csc_scaler_qops = {
0572     .queue_setup        = ipu_csc_scaler_queue_setup,
0573     .buf_prepare        = ipu_csc_scaler_buf_prepare,
0574     .buf_queue      = ipu_csc_scaler_buf_queue,
0575     .wait_prepare       = vb2_ops_wait_prepare,
0576     .wait_finish        = vb2_ops_wait_finish,
0577     .start_streaming    = ipu_csc_scaler_start_streaming,
0578     .stop_streaming     = ipu_csc_scaler_stop_streaming,
0579 };
0580 
0581 static int ipu_csc_scaler_queue_init(void *priv, struct vb2_queue *src_vq,
0582                      struct vb2_queue *dst_vq)
0583 {
0584     struct ipu_csc_scaler_ctx *ctx = priv;
0585     int ret;
0586 
0587     memset(src_vq, 0, sizeof(*src_vq));
0588     src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
0589     src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
0590     src_vq->drv_priv = ctx;
0591     src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
0592     src_vq->ops = &ipu_csc_scaler_qops;
0593     src_vq->mem_ops = &vb2_dma_contig_memops;
0594     src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
0595     src_vq->lock = &ctx->priv->mutex;
0596     src_vq->dev = ctx->priv->dev;
0597 
0598     ret = vb2_queue_init(src_vq);
0599     if (ret)
0600         return ret;
0601 
0602     memset(dst_vq, 0, sizeof(*dst_vq));
0603     dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
0604     dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
0605     dst_vq->drv_priv = ctx;
0606     dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
0607     dst_vq->ops = &ipu_csc_scaler_qops;
0608     dst_vq->mem_ops = &vb2_dma_contig_memops;
0609     dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
0610     dst_vq->lock = &ctx->priv->mutex;
0611     dst_vq->dev = ctx->priv->dev;
0612 
0613     return vb2_queue_init(dst_vq);
0614 }
0615 
0616 static int ipu_csc_scaler_s_ctrl(struct v4l2_ctrl *ctrl)
0617 {
0618     struct ipu_csc_scaler_ctx *ctx = container_of(ctrl->handler,
0619                               struct ipu_csc_scaler_ctx,
0620                               ctrl_hdlr);
0621     enum ipu_rotate_mode rot_mode;
0622     int rotate;
0623     bool hflip, vflip;
0624     int ret = 0;
0625 
0626     rotate = ctx->rotate;
0627     hflip = ctx->hflip;
0628     vflip = ctx->vflip;
0629 
0630     switch (ctrl->id) {
0631     case V4L2_CID_HFLIP:
0632         hflip = ctrl->val;
0633         break;
0634     case V4L2_CID_VFLIP:
0635         vflip = ctrl->val;
0636         break;
0637     case V4L2_CID_ROTATE:
0638         rotate = ctrl->val;
0639         break;
0640     default:
0641         return -EINVAL;
0642     }
0643 
0644     ret = ipu_degrees_to_rot_mode(&rot_mode, rotate, hflip, vflip);
0645     if (ret)
0646         return ret;
0647 
0648     if (rot_mode != ctx->rot_mode) {
0649         struct v4l2_pix_format *in_fmt, *out_fmt;
0650         struct ipu_image test_in, test_out;
0651 
0652         in_fmt = &ctx->q_data[V4L2_M2M_SRC].cur_fmt;
0653         out_fmt = &ctx->q_data[V4L2_M2M_DST].cur_fmt;
0654 
0655         test_in.pix = *in_fmt;
0656         test_out.pix = *out_fmt;
0657 
0658         if (ipu_rot_mode_is_irt(rot_mode) !=
0659             ipu_rot_mode_is_irt(ctx->rot_mode)) {
0660             /* Switch width & height to keep aspect ratio intact */
0661             test_out.pix.width = out_fmt->height;
0662             test_out.pix.height = out_fmt->width;
0663         }
0664 
0665         ipu_image_convert_adjust(&test_in, &test_out, ctx->rot_mode);
0666 
0667         /* Check if output format needs to be changed */
0668         if (test_in.pix.width != in_fmt->width ||
0669             test_in.pix.height != in_fmt->height ||
0670             test_in.pix.bytesperline != in_fmt->bytesperline ||
0671             test_in.pix.sizeimage != in_fmt->sizeimage) {
0672             struct vb2_queue *out_q;
0673 
0674             out_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
0675                         V4L2_BUF_TYPE_VIDEO_OUTPUT);
0676             if (vb2_is_busy(out_q))
0677                 return -EBUSY;
0678         }
0679 
0680         /* Check if capture format needs to be changed */
0681         if (test_out.pix.width != out_fmt->width ||
0682             test_out.pix.height != out_fmt->height ||
0683             test_out.pix.bytesperline != out_fmt->bytesperline ||
0684             test_out.pix.sizeimage != out_fmt->sizeimage) {
0685             struct vb2_queue *cap_q;
0686 
0687             cap_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
0688                         V4L2_BUF_TYPE_VIDEO_CAPTURE);
0689             if (vb2_is_busy(cap_q))
0690                 return -EBUSY;
0691         }
0692 
0693         *in_fmt = test_in.pix;
0694         *out_fmt = test_out.pix;
0695 
0696         ctx->rot_mode = rot_mode;
0697         ctx->rotate = rotate;
0698         ctx->hflip = hflip;
0699         ctx->vflip = vflip;
0700     }
0701 
0702     return 0;
0703 }
0704 
0705 static const struct v4l2_ctrl_ops ipu_csc_scaler_ctrl_ops = {
0706     .s_ctrl = ipu_csc_scaler_s_ctrl,
0707 };
0708 
0709 static int ipu_csc_scaler_init_controls(struct ipu_csc_scaler_ctx *ctx)
0710 {
0711     struct v4l2_ctrl_handler *hdlr = &ctx->ctrl_hdlr;
0712 
0713     v4l2_ctrl_handler_init(hdlr, 3);
0714 
0715     v4l2_ctrl_new_std(hdlr, &ipu_csc_scaler_ctrl_ops, V4L2_CID_HFLIP,
0716               0, 1, 1, 0);
0717     v4l2_ctrl_new_std(hdlr, &ipu_csc_scaler_ctrl_ops, V4L2_CID_VFLIP,
0718               0, 1, 1, 0);
0719     v4l2_ctrl_new_std(hdlr, &ipu_csc_scaler_ctrl_ops, V4L2_CID_ROTATE,
0720               0, 270, 90, 0);
0721 
0722     if (hdlr->error) {
0723         v4l2_ctrl_handler_free(hdlr);
0724         return hdlr->error;
0725     }
0726 
0727     v4l2_ctrl_handler_setup(hdlr);
0728     return 0;
0729 }
0730 
0731 #define DEFAULT_WIDTH   720
0732 #define DEFAULT_HEIGHT  576
0733 static const struct ipu_csc_scaler_q_data ipu_csc_scaler_q_data_default = {
0734     .cur_fmt = {
0735         .width = DEFAULT_WIDTH,
0736         .height = DEFAULT_HEIGHT,
0737         .pixelformat = V4L2_PIX_FMT_YUV420,
0738         .field = V4L2_FIELD_NONE,
0739         .bytesperline = DEFAULT_WIDTH,
0740         .sizeimage = DEFAULT_WIDTH * DEFAULT_HEIGHT * 3 / 2,
0741         .colorspace = V4L2_COLORSPACE_SRGB,
0742     },
0743     .rect = {
0744         .width = DEFAULT_WIDTH,
0745         .height = DEFAULT_HEIGHT,
0746     },
0747 };
0748 
0749 /*
0750  * File operations
0751  */
0752 static int ipu_csc_scaler_open(struct file *file)
0753 {
0754     struct ipu_csc_scaler_priv *priv = video_drvdata(file);
0755     struct ipu_csc_scaler_ctx *ctx = NULL;
0756     int ret;
0757 
0758     ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
0759     if (!ctx)
0760         return -ENOMEM;
0761 
0762     ctx->rot_mode = IPU_ROTATE_NONE;
0763 
0764     v4l2_fh_init(&ctx->fh, video_devdata(file));
0765     file->private_data = &ctx->fh;
0766     v4l2_fh_add(&ctx->fh);
0767     ctx->priv = priv;
0768 
0769     ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(priv->m2m_dev, ctx,
0770                         &ipu_csc_scaler_queue_init);
0771     if (IS_ERR(ctx->fh.m2m_ctx)) {
0772         ret = PTR_ERR(ctx->fh.m2m_ctx);
0773         goto err_ctx;
0774     }
0775 
0776     ret = ipu_csc_scaler_init_controls(ctx);
0777     if (ret)
0778         goto err_ctrls;
0779 
0780     ctx->fh.ctrl_handler = &ctx->ctrl_hdlr;
0781 
0782     ctx->q_data[V4L2_M2M_SRC] = ipu_csc_scaler_q_data_default;
0783     ctx->q_data[V4L2_M2M_DST] = ipu_csc_scaler_q_data_default;
0784 
0785     dev_dbg(priv->dev, "Created instance %p, m2m_ctx: %p\n", ctx,
0786         ctx->fh.m2m_ctx);
0787 
0788     return 0;
0789 
0790 err_ctrls:
0791     v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
0792 err_ctx:
0793     v4l2_fh_del(&ctx->fh);
0794     v4l2_fh_exit(&ctx->fh);
0795     kfree(ctx);
0796     return ret;
0797 }
0798 
0799 static int ipu_csc_scaler_release(struct file *file)
0800 {
0801     struct ipu_csc_scaler_priv *priv = video_drvdata(file);
0802     struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(file->private_data);
0803 
0804     dev_dbg(priv->dev, "Releasing instance %p\n", ctx);
0805 
0806     v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
0807     v4l2_fh_del(&ctx->fh);
0808     v4l2_fh_exit(&ctx->fh);
0809     kfree(ctx);
0810 
0811     return 0;
0812 }
0813 
0814 static const struct v4l2_file_operations ipu_csc_scaler_fops = {
0815     .owner      = THIS_MODULE,
0816     .open       = ipu_csc_scaler_open,
0817     .release    = ipu_csc_scaler_release,
0818     .poll       = v4l2_m2m_fop_poll,
0819     .unlocked_ioctl = video_ioctl2,
0820     .mmap       = v4l2_m2m_fop_mmap,
0821 };
0822 
0823 static const struct v4l2_m2m_ops m2m_ops = {
0824     .device_run = device_run,
0825     .job_abort  = job_abort,
0826 };
0827 
0828 static void ipu_csc_scaler_video_device_release(struct video_device *vdev)
0829 {
0830     struct ipu_csc_scaler_priv *priv = video_get_drvdata(vdev);
0831 
0832     v4l2_m2m_release(priv->m2m_dev);
0833     video_device_release(vdev);
0834     kfree(priv);
0835 }
0836 
0837 static const struct video_device ipu_csc_scaler_videodev_template = {
0838     .name       = "ipu_ic_pp csc/scaler",
0839     .fops       = &ipu_csc_scaler_fops,
0840     .ioctl_ops  = &ipu_csc_scaler_ioctl_ops,
0841     .minor      = -1,
0842     .release    = ipu_csc_scaler_video_device_release,
0843     .vfl_dir    = VFL_DIR_M2M,
0844     .device_caps    = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
0845 };
0846 
0847 int imx_media_csc_scaler_device_register(struct imx_media_video_dev *vdev)
0848 {
0849     struct ipu_csc_scaler_priv *priv = vdev_to_priv(vdev);
0850     struct video_device *vfd = vdev->vfd;
0851     int ret;
0852 
0853     vfd->v4l2_dev = &priv->md->v4l2_dev;
0854 
0855     ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
0856     if (ret) {
0857         v4l2_err(vfd->v4l2_dev, "Failed to register video device\n");
0858         return ret;
0859     }
0860 
0861     v4l2_info(vfd->v4l2_dev, "Registered %s as /dev/%s\n", vfd->name,
0862           video_device_node_name(vfd));
0863 
0864     return 0;
0865 }
0866 
0867 void imx_media_csc_scaler_device_unregister(struct imx_media_video_dev *vdev)
0868 {
0869     struct ipu_csc_scaler_priv *priv = vdev_to_priv(vdev);
0870     struct video_device *vfd = priv->vdev.vfd;
0871 
0872     video_unregister_device(vfd);
0873 }
0874 
0875 struct imx_media_video_dev *
0876 imx_media_csc_scaler_device_init(struct imx_media_dev *md)
0877 {
0878     struct ipu_csc_scaler_priv *priv;
0879     struct video_device *vfd;
0880     int ret;
0881 
0882     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
0883     if (!priv)
0884         return ERR_PTR(-ENOMEM);
0885 
0886     priv->md = md;
0887     priv->dev = md->md.dev;
0888 
0889     mutex_init(&priv->mutex);
0890 
0891     vfd = video_device_alloc();
0892     if (!vfd) {
0893         ret = -ENOMEM;
0894         goto err_vfd;
0895     }
0896 
0897     *vfd = ipu_csc_scaler_videodev_template;
0898     vfd->lock = &priv->mutex;
0899     priv->vdev.vfd = vfd;
0900 
0901     INIT_LIST_HEAD(&priv->vdev.list);
0902 
0903     video_set_drvdata(vfd, priv);
0904 
0905     priv->m2m_dev = v4l2_m2m_init(&m2m_ops);
0906     if (IS_ERR(priv->m2m_dev)) {
0907         ret = PTR_ERR(priv->m2m_dev);
0908         v4l2_err(&md->v4l2_dev, "Failed to init mem2mem device: %d\n",
0909              ret);
0910         goto err_m2m;
0911     }
0912 
0913     return &priv->vdev;
0914 
0915 err_m2m:
0916     video_set_drvdata(vfd, NULL);
0917 err_vfd:
0918     kfree(priv);
0919     return ERR_PTR(ret);
0920 }
0921 
0922 MODULE_DESCRIPTION("i.MX IPUv3 mem2mem scaler/CSC driver");
0923 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
0924 MODULE_LICENSE("GPL");