0001
0002
0003
0004
0005
0006
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;
0044 };
0045
0046 #define vdev_to_priv(v) container_of(v, struct ipu_csc_scaler_priv, vdev)
0047
0048
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
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
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
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
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
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
0290
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
0360
0361
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
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
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
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
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
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
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");