cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

imx-media-csc-scaler.c (24158B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * i.MX IPUv3 IC PP mem2mem CSC/Scaler driver
      4 *
      5 * Copyright (C) 2011 Pengutronix, Sascha Hauer
      6 * Copyright (C) 2018 Pengutronix, Philipp Zabel
      7 */
      8#include <linux/module.h>
      9#include <linux/delay.h>
     10#include <linux/fs.h>
     11#include <linux/sched.h>
     12#include <linux/slab.h>
     13#include <video/imx-ipu-v3.h>
     14#include <video/imx-ipu-image-convert.h>
     15
     16#include <media/media-device.h>
     17#include <media/v4l2-ctrls.h>
     18#include <media/v4l2-event.h>
     19#include <media/v4l2-mem2mem.h>
     20#include <media/v4l2-device.h>
     21#include <media/v4l2-ioctl.h>
     22#include <media/videobuf2-dma-contig.h>
     23
     24#include "imx-media.h"
     25
     26#define fh_to_ctx(__fh)	container_of(__fh, struct ipu_csc_scaler_ctx, fh)
     27
     28#define IMX_CSC_SCALER_NAME "imx-csc-scaler"
     29
     30enum {
     31	V4L2_M2M_SRC = 0,
     32	V4L2_M2M_DST = 1,
     33};
     34
     35struct ipu_csc_scaler_priv {
     36	struct imx_media_video_dev	vdev;
     37
     38	struct v4l2_m2m_dev		*m2m_dev;
     39	struct device			*dev;
     40
     41	struct imx_media_dev		*md;
     42
     43	struct mutex			mutex;	/* mem2mem device mutex */
     44};
     45
     46#define vdev_to_priv(v) container_of(v, struct ipu_csc_scaler_priv, vdev)
     47
     48/* Per-queue, driver-specific private data */
     49struct ipu_csc_scaler_q_data {
     50	struct v4l2_pix_format		cur_fmt;
     51	struct v4l2_rect		rect;
     52};
     53
     54struct ipu_csc_scaler_ctx {
     55	struct ipu_csc_scaler_priv	*priv;
     56
     57	struct v4l2_fh			fh;
     58	struct ipu_csc_scaler_q_data	q_data[2];
     59	struct ipu_image_convert_ctx	*icc;
     60
     61	struct v4l2_ctrl_handler	ctrl_hdlr;
     62	int				rotate;
     63	bool				hflip;
     64	bool				vflip;
     65	enum ipu_rotate_mode		rot_mode;
     66	unsigned int			sequence;
     67};
     68
     69static struct ipu_csc_scaler_q_data *get_q_data(struct ipu_csc_scaler_ctx *ctx,
     70						enum v4l2_buf_type type)
     71{
     72	if (V4L2_TYPE_IS_OUTPUT(type))
     73		return &ctx->q_data[V4L2_M2M_SRC];
     74	else
     75		return &ctx->q_data[V4L2_M2M_DST];
     76}
     77
     78/*
     79 * mem2mem callbacks
     80 */
     81
     82static void job_abort(void *_ctx)
     83{
     84	struct ipu_csc_scaler_ctx *ctx = _ctx;
     85
     86	if (ctx->icc)
     87		ipu_image_convert_abort(ctx->icc);
     88}
     89
     90static void ipu_ic_pp_complete(struct ipu_image_convert_run *run, void *_ctx)
     91{
     92	struct ipu_csc_scaler_ctx *ctx = _ctx;
     93	struct ipu_csc_scaler_priv *priv = ctx->priv;
     94	struct vb2_v4l2_buffer *src_buf, *dst_buf;
     95
     96	src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
     97	dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
     98
     99	v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
    100
    101	src_buf->sequence = ctx->sequence++;
    102	dst_buf->sequence = src_buf->sequence;
    103
    104	v4l2_m2m_buf_done(src_buf, run->status ? VB2_BUF_STATE_ERROR :
    105						 VB2_BUF_STATE_DONE);
    106	v4l2_m2m_buf_done(dst_buf, run->status ? VB2_BUF_STATE_ERROR :
    107						 VB2_BUF_STATE_DONE);
    108
    109	v4l2_m2m_job_finish(priv->m2m_dev, ctx->fh.m2m_ctx);
    110	kfree(run);
    111}
    112
    113static void device_run(void *_ctx)
    114{
    115	struct ipu_csc_scaler_ctx *ctx = _ctx;
    116	struct ipu_csc_scaler_priv *priv = ctx->priv;
    117	struct vb2_v4l2_buffer *src_buf, *dst_buf;
    118	struct ipu_image_convert_run *run;
    119	int ret;
    120
    121	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
    122	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
    123
    124	run = kzalloc(sizeof(*run), GFP_KERNEL);
    125	if (!run)
    126		goto err;
    127
    128	run->ctx = ctx->icc;
    129	run->in_phys = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
    130	run->out_phys = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
    131
    132	ret = ipu_image_convert_queue(run);
    133	if (ret < 0) {
    134		v4l2_err(ctx->priv->vdev.vfd->v4l2_dev,
    135			 "%s: failed to queue: %d\n", __func__, ret);
    136		goto err;
    137	}
    138
    139	return;
    140
    141err:
    142	v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
    143	v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
    144	v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
    145	v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
    146	v4l2_m2m_job_finish(priv->m2m_dev, ctx->fh.m2m_ctx);
    147}
    148
    149/*
    150 * Video ioctls
    151 */
    152static int ipu_csc_scaler_querycap(struct file *file, void *priv,
    153				   struct v4l2_capability *cap)
    154{
    155	strscpy(cap->driver, IMX_CSC_SCALER_NAME, sizeof(cap->driver));
    156	strscpy(cap->card, IMX_CSC_SCALER_NAME, sizeof(cap->card));
    157	snprintf(cap->bus_info, sizeof(cap->bus_info),
    158		 "platform:%s", IMX_CSC_SCALER_NAME);
    159
    160	return 0;
    161}
    162
    163static int ipu_csc_scaler_enum_fmt(struct file *file, void *fh,
    164				   struct v4l2_fmtdesc *f)
    165{
    166	u32 fourcc;
    167	int ret;
    168
    169	ret = imx_media_enum_pixel_formats(&fourcc, f->index,
    170					   PIXFMT_SEL_YUV_RGB, 0);
    171	if (ret)
    172		return ret;
    173
    174	f->pixelformat = fourcc;
    175
    176	return 0;
    177}
    178
    179static int ipu_csc_scaler_g_fmt(struct file *file, void *priv,
    180				struct v4l2_format *f)
    181{
    182	struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(priv);
    183	struct ipu_csc_scaler_q_data *q_data;
    184
    185	q_data = get_q_data(ctx, f->type);
    186
    187	f->fmt.pix = q_data->cur_fmt;
    188
    189	return 0;
    190}
    191
    192static int ipu_csc_scaler_try_fmt(struct file *file, void *priv,
    193				  struct v4l2_format *f)
    194{
    195	struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(priv);
    196	struct ipu_csc_scaler_q_data *q_data = get_q_data(ctx, f->type);
    197	struct ipu_image test_in, test_out;
    198	enum v4l2_field field;
    199
    200	field = f->fmt.pix.field;
    201	if (field == V4L2_FIELD_ANY)
    202		field = V4L2_FIELD_NONE;
    203	else if (field != V4L2_FIELD_NONE)
    204		return -EINVAL;
    205
    206	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
    207		struct ipu_csc_scaler_q_data *q_data_in =
    208			get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
    209
    210		test_out.pix = f->fmt.pix;
    211		test_in.pix = q_data_in->cur_fmt;
    212	} else {
    213		struct ipu_csc_scaler_q_data *q_data_out =
    214			get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
    215
    216		test_in.pix = f->fmt.pix;
    217		test_out.pix = q_data_out->cur_fmt;
    218	}
    219
    220	ipu_image_convert_adjust(&test_in, &test_out, ctx->rot_mode);
    221
    222	f->fmt.pix = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
    223		test_out.pix : test_in.pix;
    224
    225	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
    226		f->fmt.pix.colorspace = q_data->cur_fmt.colorspace;
    227		f->fmt.pix.ycbcr_enc = q_data->cur_fmt.ycbcr_enc;
    228		f->fmt.pix.xfer_func = q_data->cur_fmt.xfer_func;
    229		f->fmt.pix.quantization = q_data->cur_fmt.quantization;
    230	} else if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT) {
    231		f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
    232		f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
    233		f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
    234		f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
    235	}
    236
    237	return 0;
    238}
    239
    240static int ipu_csc_scaler_s_fmt(struct file *file, void *priv,
    241				struct v4l2_format *f)
    242{
    243	struct ipu_csc_scaler_q_data *q_data;
    244	struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(priv);
    245	struct vb2_queue *vq;
    246	int ret;
    247
    248	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
    249	if (vb2_is_busy(vq)) {
    250		v4l2_err(ctx->priv->vdev.vfd->v4l2_dev, "%s: queue busy\n",
    251			 __func__);
    252		return -EBUSY;
    253	}
    254
    255	q_data = get_q_data(ctx, f->type);
    256
    257	ret = ipu_csc_scaler_try_fmt(file, priv, f);
    258	if (ret < 0)
    259		return ret;
    260
    261	q_data->cur_fmt.width = f->fmt.pix.width;
    262	q_data->cur_fmt.height = f->fmt.pix.height;
    263	q_data->cur_fmt.pixelformat = f->fmt.pix.pixelformat;
    264	q_data->cur_fmt.field = f->fmt.pix.field;
    265	q_data->cur_fmt.bytesperline = f->fmt.pix.bytesperline;
    266	q_data->cur_fmt.sizeimage = f->fmt.pix.sizeimage;
    267
    268	/* Reset cropping/composing rectangle */
    269	q_data->rect.left = 0;
    270	q_data->rect.top = 0;
    271	q_data->rect.width = q_data->cur_fmt.width;
    272	q_data->rect.height = q_data->cur_fmt.height;
    273
    274	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
    275		/* Set colorimetry on the output queue */
    276		q_data->cur_fmt.colorspace = f->fmt.pix.colorspace;
    277		q_data->cur_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
    278		q_data->cur_fmt.xfer_func = f->fmt.pix.xfer_func;
    279		q_data->cur_fmt.quantization = f->fmt.pix.quantization;
    280		/* Propagate colorimetry to the capture queue */
    281		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
    282		q_data->cur_fmt.colorspace = f->fmt.pix.colorspace;
    283		q_data->cur_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc;
    284		q_data->cur_fmt.xfer_func = f->fmt.pix.xfer_func;
    285		q_data->cur_fmt.quantization = f->fmt.pix.quantization;
    286	}
    287
    288	/*
    289	 * TODO: Setting colorimetry on the capture queue is currently not
    290	 * supported by the V4L2 API
    291	 */
    292
    293	return 0;
    294}
    295
    296static int ipu_csc_scaler_g_selection(struct file *file, void *priv,
    297				      struct v4l2_selection *s)
    298{
    299	struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(priv);
    300	struct ipu_csc_scaler_q_data *q_data;
    301
    302	switch (s->target) {
    303	case V4L2_SEL_TGT_CROP:
    304	case V4L2_SEL_TGT_CROP_DEFAULT:
    305	case V4L2_SEL_TGT_CROP_BOUNDS:
    306		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
    307			return -EINVAL;
    308		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
    309		break;
    310	case V4L2_SEL_TGT_COMPOSE:
    311	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
    312	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
    313		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
    314			return -EINVAL;
    315		q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
    316		break;
    317	default:
    318		return -EINVAL;
    319	}
    320
    321	if (s->target == V4L2_SEL_TGT_CROP ||
    322	    s->target == V4L2_SEL_TGT_COMPOSE) {
    323		s->r = q_data->rect;
    324	} else {
    325		s->r.left = 0;
    326		s->r.top = 0;
    327		s->r.width = q_data->cur_fmt.width;
    328		s->r.height = q_data->cur_fmt.height;
    329	}
    330
    331	return 0;
    332}
    333
    334static int ipu_csc_scaler_s_selection(struct file *file, void *priv,
    335				      struct v4l2_selection *s)
    336{
    337	struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(priv);
    338	struct ipu_csc_scaler_q_data *q_data;
    339
    340	switch (s->target) {
    341	case V4L2_SEL_TGT_CROP:
    342		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
    343			return -EINVAL;
    344		break;
    345	case V4L2_SEL_TGT_COMPOSE:
    346		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
    347			return -EINVAL;
    348		break;
    349	default:
    350		return -EINVAL;
    351	}
    352
    353	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
    354	    s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
    355		return -EINVAL;
    356
    357	q_data = get_q_data(ctx, s->type);
    358
    359	/* The input's frame width to the IC must be a multiple of 8 pixels
    360	 * When performing resizing the frame width must be multiple of burst
    361	 * size - 8 or 16 pixels as defined by CB#_BURST_16 parameter.
    362	 */
    363	if (s->flags & V4L2_SEL_FLAG_GE)
    364		s->r.width = round_up(s->r.width, 8);
    365	if (s->flags & V4L2_SEL_FLAG_LE)
    366		s->r.width = round_down(s->r.width, 8);
    367	s->r.width = clamp_t(unsigned int, s->r.width, 8,
    368			     round_down(q_data->cur_fmt.width, 8));
    369	s->r.height = clamp_t(unsigned int, s->r.height, 1,
    370			      q_data->cur_fmt.height);
    371	s->r.left = clamp_t(unsigned int, s->r.left, 0,
    372			    q_data->cur_fmt.width - s->r.width);
    373	s->r.top = clamp_t(unsigned int, s->r.top, 0,
    374			   q_data->cur_fmt.height - s->r.height);
    375
    376	/* V4L2_SEL_FLAG_KEEP_CONFIG is only valid for subdevices */
    377	q_data->rect = s->r;
    378
    379	return 0;
    380}
    381
    382static const struct v4l2_ioctl_ops ipu_csc_scaler_ioctl_ops = {
    383	.vidioc_querycap		= ipu_csc_scaler_querycap,
    384
    385	.vidioc_enum_fmt_vid_cap	= ipu_csc_scaler_enum_fmt,
    386	.vidioc_g_fmt_vid_cap		= ipu_csc_scaler_g_fmt,
    387	.vidioc_try_fmt_vid_cap		= ipu_csc_scaler_try_fmt,
    388	.vidioc_s_fmt_vid_cap		= ipu_csc_scaler_s_fmt,
    389
    390	.vidioc_enum_fmt_vid_out	= ipu_csc_scaler_enum_fmt,
    391	.vidioc_g_fmt_vid_out		= ipu_csc_scaler_g_fmt,
    392	.vidioc_try_fmt_vid_out		= ipu_csc_scaler_try_fmt,
    393	.vidioc_s_fmt_vid_out		= ipu_csc_scaler_s_fmt,
    394
    395	.vidioc_g_selection		= ipu_csc_scaler_g_selection,
    396	.vidioc_s_selection		= ipu_csc_scaler_s_selection,
    397
    398	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
    399	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
    400
    401	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
    402	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
    403	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
    404	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
    405	.vidioc_prepare_buf		= v4l2_m2m_ioctl_prepare_buf,
    406
    407	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
    408	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
    409
    410	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
    411	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
    412};
    413
    414/*
    415 * Queue operations
    416 */
    417
    418static int ipu_csc_scaler_queue_setup(struct vb2_queue *vq,
    419				      unsigned int *nbuffers,
    420				      unsigned int *nplanes,
    421				      unsigned int sizes[],
    422				      struct device *alloc_devs[])
    423{
    424	struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(vq);
    425	struct ipu_csc_scaler_q_data *q_data;
    426	unsigned int size, count = *nbuffers;
    427
    428	q_data = get_q_data(ctx, vq->type);
    429
    430	size = q_data->cur_fmt.sizeimage;
    431
    432	*nbuffers = count;
    433
    434	if (*nplanes)
    435		return sizes[0] < size ? -EINVAL : 0;
    436
    437	*nplanes = 1;
    438	sizes[0] = size;
    439
    440	dev_dbg(ctx->priv->dev, "get %d buffer(s) of size %d each.\n",
    441		count, size);
    442
    443	return 0;
    444}
    445
    446static int ipu_csc_scaler_buf_prepare(struct vb2_buffer *vb)
    447{
    448	struct vb2_queue *vq = vb->vb2_queue;
    449	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
    450	struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(vq);
    451	struct ipu_csc_scaler_q_data *q_data;
    452	unsigned long size;
    453
    454	dev_dbg(ctx->priv->dev, "type: %d\n", vq->type);
    455
    456	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
    457		if (vbuf->field == V4L2_FIELD_ANY)
    458			vbuf->field = V4L2_FIELD_NONE;
    459		if (vbuf->field != V4L2_FIELD_NONE) {
    460			dev_dbg(ctx->priv->dev, "%s: field isn't supported\n",
    461				__func__);
    462			return -EINVAL;
    463		}
    464	}
    465
    466	q_data = get_q_data(ctx, vq->type);
    467	size = q_data->cur_fmt.sizeimage;
    468
    469	if (vb2_plane_size(vb, 0) < size) {
    470		dev_dbg(ctx->priv->dev,
    471			"%s: data will not fit into plane (%lu < %lu)\n",
    472			__func__, vb2_plane_size(vb, 0), size);
    473		return -EINVAL;
    474	}
    475
    476	vb2_set_plane_payload(vb, 0, size);
    477
    478	return 0;
    479}
    480
    481static void ipu_csc_scaler_buf_queue(struct vb2_buffer *vb)
    482{
    483	struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
    484
    485	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb));
    486}
    487
    488static void ipu_image_from_q_data(struct ipu_image *im,
    489				  struct ipu_csc_scaler_q_data *q_data)
    490{
    491	struct v4l2_pix_format *fmt = &q_data->cur_fmt;
    492
    493	im->pix = *fmt;
    494	if (fmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
    495		im->pix.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
    496	if (fmt->quantization == V4L2_QUANTIZATION_DEFAULT)
    497		im->pix.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
    498	im->rect = q_data->rect;
    499}
    500
    501static int ipu_csc_scaler_start_streaming(struct vb2_queue *q,
    502					  unsigned int count)
    503{
    504	const enum ipu_ic_task ic_task = IC_TASK_POST_PROCESSOR;
    505	struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(q);
    506	struct ipu_csc_scaler_priv *priv = ctx->priv;
    507	struct ipu_soc *ipu = priv->md->ipu[0];
    508	struct ipu_csc_scaler_q_data *q_data;
    509	struct vb2_queue *other_q;
    510	struct ipu_image in, out;
    511
    512	other_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
    513				  (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
    514				  V4L2_BUF_TYPE_VIDEO_OUTPUT :
    515				  V4L2_BUF_TYPE_VIDEO_CAPTURE);
    516	if (!vb2_is_streaming(other_q))
    517		return 0;
    518
    519	if (ctx->icc) {
    520		v4l2_warn(ctx->priv->vdev.vfd->v4l2_dev, "removing old ICC\n");
    521		ipu_image_convert_unprepare(ctx->icc);
    522	}
    523
    524	q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
    525	ipu_image_from_q_data(&in, q_data);
    526
    527	q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
    528	ipu_image_from_q_data(&out, q_data);
    529
    530	ctx->icc = ipu_image_convert_prepare(ipu, ic_task, &in, &out,
    531					     ctx->rot_mode,
    532					     ipu_ic_pp_complete, ctx);
    533	if (IS_ERR(ctx->icc)) {
    534		struct vb2_v4l2_buffer *buf;
    535		int ret = PTR_ERR(ctx->icc);
    536
    537		ctx->icc = NULL;
    538		v4l2_err(ctx->priv->vdev.vfd->v4l2_dev, "%s: error %d\n",
    539			 __func__, ret);
    540		while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
    541			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
    542		while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
    543			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
    544		return ret;
    545	}
    546
    547	return 0;
    548}
    549
    550static void ipu_csc_scaler_stop_streaming(struct vb2_queue *q)
    551{
    552	struct ipu_csc_scaler_ctx *ctx = vb2_get_drv_priv(q);
    553	struct vb2_v4l2_buffer *buf;
    554
    555	if (ctx->icc) {
    556		ipu_image_convert_unprepare(ctx->icc);
    557		ctx->icc = NULL;
    558	}
    559
    560	ctx->sequence = 0;
    561
    562	if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
    563		while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
    564			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
    565	} else {
    566		while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
    567			v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
    568	}
    569}
    570
    571static const struct vb2_ops ipu_csc_scaler_qops = {
    572	.queue_setup		= ipu_csc_scaler_queue_setup,
    573	.buf_prepare		= ipu_csc_scaler_buf_prepare,
    574	.buf_queue		= ipu_csc_scaler_buf_queue,
    575	.wait_prepare		= vb2_ops_wait_prepare,
    576	.wait_finish		= vb2_ops_wait_finish,
    577	.start_streaming	= ipu_csc_scaler_start_streaming,
    578	.stop_streaming		= ipu_csc_scaler_stop_streaming,
    579};
    580
    581static int ipu_csc_scaler_queue_init(void *priv, struct vb2_queue *src_vq,
    582				     struct vb2_queue *dst_vq)
    583{
    584	struct ipu_csc_scaler_ctx *ctx = priv;
    585	int ret;
    586
    587	memset(src_vq, 0, sizeof(*src_vq));
    588	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
    589	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
    590	src_vq->drv_priv = ctx;
    591	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
    592	src_vq->ops = &ipu_csc_scaler_qops;
    593	src_vq->mem_ops = &vb2_dma_contig_memops;
    594	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
    595	src_vq->lock = &ctx->priv->mutex;
    596	src_vq->dev = ctx->priv->dev;
    597
    598	ret = vb2_queue_init(src_vq);
    599	if (ret)
    600		return ret;
    601
    602	memset(dst_vq, 0, sizeof(*dst_vq));
    603	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    604	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
    605	dst_vq->drv_priv = ctx;
    606	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
    607	dst_vq->ops = &ipu_csc_scaler_qops;
    608	dst_vq->mem_ops = &vb2_dma_contig_memops;
    609	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
    610	dst_vq->lock = &ctx->priv->mutex;
    611	dst_vq->dev = ctx->priv->dev;
    612
    613	return vb2_queue_init(dst_vq);
    614}
    615
    616static int ipu_csc_scaler_s_ctrl(struct v4l2_ctrl *ctrl)
    617{
    618	struct ipu_csc_scaler_ctx *ctx = container_of(ctrl->handler,
    619						      struct ipu_csc_scaler_ctx,
    620						      ctrl_hdlr);
    621	enum ipu_rotate_mode rot_mode;
    622	int rotate;
    623	bool hflip, vflip;
    624	int ret = 0;
    625
    626	rotate = ctx->rotate;
    627	hflip = ctx->hflip;
    628	vflip = ctx->vflip;
    629
    630	switch (ctrl->id) {
    631	case V4L2_CID_HFLIP:
    632		hflip = ctrl->val;
    633		break;
    634	case V4L2_CID_VFLIP:
    635		vflip = ctrl->val;
    636		break;
    637	case V4L2_CID_ROTATE:
    638		rotate = ctrl->val;
    639		break;
    640	default:
    641		return -EINVAL;
    642	}
    643
    644	ret = ipu_degrees_to_rot_mode(&rot_mode, rotate, hflip, vflip);
    645	if (ret)
    646		return ret;
    647
    648	if (rot_mode != ctx->rot_mode) {
    649		struct v4l2_pix_format *in_fmt, *out_fmt;
    650		struct ipu_image test_in, test_out;
    651
    652		in_fmt = &ctx->q_data[V4L2_M2M_SRC].cur_fmt;
    653		out_fmt = &ctx->q_data[V4L2_M2M_DST].cur_fmt;
    654
    655		test_in.pix = *in_fmt;
    656		test_out.pix = *out_fmt;
    657
    658		if (ipu_rot_mode_is_irt(rot_mode) !=
    659		    ipu_rot_mode_is_irt(ctx->rot_mode)) {
    660			/* Switch width & height to keep aspect ratio intact */
    661			test_out.pix.width = out_fmt->height;
    662			test_out.pix.height = out_fmt->width;
    663		}
    664
    665		ipu_image_convert_adjust(&test_in, &test_out, ctx->rot_mode);
    666
    667		/* Check if output format needs to be changed */
    668		if (test_in.pix.width != in_fmt->width ||
    669		    test_in.pix.height != in_fmt->height ||
    670		    test_in.pix.bytesperline != in_fmt->bytesperline ||
    671		    test_in.pix.sizeimage != in_fmt->sizeimage) {
    672			struct vb2_queue *out_q;
    673
    674			out_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
    675						V4L2_BUF_TYPE_VIDEO_OUTPUT);
    676			if (vb2_is_busy(out_q))
    677				return -EBUSY;
    678		}
    679
    680		/* Check if capture format needs to be changed */
    681		if (test_out.pix.width != out_fmt->width ||
    682		    test_out.pix.height != out_fmt->height ||
    683		    test_out.pix.bytesperline != out_fmt->bytesperline ||
    684		    test_out.pix.sizeimage != out_fmt->sizeimage) {
    685			struct vb2_queue *cap_q;
    686
    687			cap_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
    688						V4L2_BUF_TYPE_VIDEO_CAPTURE);
    689			if (vb2_is_busy(cap_q))
    690				return -EBUSY;
    691		}
    692
    693		*in_fmt = test_in.pix;
    694		*out_fmt = test_out.pix;
    695
    696		ctx->rot_mode = rot_mode;
    697		ctx->rotate = rotate;
    698		ctx->hflip = hflip;
    699		ctx->vflip = vflip;
    700	}
    701
    702	return 0;
    703}
    704
    705static const struct v4l2_ctrl_ops ipu_csc_scaler_ctrl_ops = {
    706	.s_ctrl = ipu_csc_scaler_s_ctrl,
    707};
    708
    709static int ipu_csc_scaler_init_controls(struct ipu_csc_scaler_ctx *ctx)
    710{
    711	struct v4l2_ctrl_handler *hdlr = &ctx->ctrl_hdlr;
    712
    713	v4l2_ctrl_handler_init(hdlr, 3);
    714
    715	v4l2_ctrl_new_std(hdlr, &ipu_csc_scaler_ctrl_ops, V4L2_CID_HFLIP,
    716			  0, 1, 1, 0);
    717	v4l2_ctrl_new_std(hdlr, &ipu_csc_scaler_ctrl_ops, V4L2_CID_VFLIP,
    718			  0, 1, 1, 0);
    719	v4l2_ctrl_new_std(hdlr, &ipu_csc_scaler_ctrl_ops, V4L2_CID_ROTATE,
    720			  0, 270, 90, 0);
    721
    722	if (hdlr->error) {
    723		v4l2_ctrl_handler_free(hdlr);
    724		return hdlr->error;
    725	}
    726
    727	v4l2_ctrl_handler_setup(hdlr);
    728	return 0;
    729}
    730
    731#define DEFAULT_WIDTH	720
    732#define DEFAULT_HEIGHT	576
    733static const struct ipu_csc_scaler_q_data ipu_csc_scaler_q_data_default = {
    734	.cur_fmt = {
    735		.width = DEFAULT_WIDTH,
    736		.height = DEFAULT_HEIGHT,
    737		.pixelformat = V4L2_PIX_FMT_YUV420,
    738		.field = V4L2_FIELD_NONE,
    739		.bytesperline = DEFAULT_WIDTH,
    740		.sizeimage = DEFAULT_WIDTH * DEFAULT_HEIGHT * 3 / 2,
    741		.colorspace = V4L2_COLORSPACE_SRGB,
    742	},
    743	.rect = {
    744		.width = DEFAULT_WIDTH,
    745		.height = DEFAULT_HEIGHT,
    746	},
    747};
    748
    749/*
    750 * File operations
    751 */
    752static int ipu_csc_scaler_open(struct file *file)
    753{
    754	struct ipu_csc_scaler_priv *priv = video_drvdata(file);
    755	struct ipu_csc_scaler_ctx *ctx = NULL;
    756	int ret;
    757
    758	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
    759	if (!ctx)
    760		return -ENOMEM;
    761
    762	ctx->rot_mode = IPU_ROTATE_NONE;
    763
    764	v4l2_fh_init(&ctx->fh, video_devdata(file));
    765	file->private_data = &ctx->fh;
    766	v4l2_fh_add(&ctx->fh);
    767	ctx->priv = priv;
    768
    769	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(priv->m2m_dev, ctx,
    770					    &ipu_csc_scaler_queue_init);
    771	if (IS_ERR(ctx->fh.m2m_ctx)) {
    772		ret = PTR_ERR(ctx->fh.m2m_ctx);
    773		goto err_ctx;
    774	}
    775
    776	ret = ipu_csc_scaler_init_controls(ctx);
    777	if (ret)
    778		goto err_ctrls;
    779
    780	ctx->fh.ctrl_handler = &ctx->ctrl_hdlr;
    781
    782	ctx->q_data[V4L2_M2M_SRC] = ipu_csc_scaler_q_data_default;
    783	ctx->q_data[V4L2_M2M_DST] = ipu_csc_scaler_q_data_default;
    784
    785	dev_dbg(priv->dev, "Created instance %p, m2m_ctx: %p\n", ctx,
    786		ctx->fh.m2m_ctx);
    787
    788	return 0;
    789
    790err_ctrls:
    791	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
    792err_ctx:
    793	v4l2_fh_del(&ctx->fh);
    794	v4l2_fh_exit(&ctx->fh);
    795	kfree(ctx);
    796	return ret;
    797}
    798
    799static int ipu_csc_scaler_release(struct file *file)
    800{
    801	struct ipu_csc_scaler_priv *priv = video_drvdata(file);
    802	struct ipu_csc_scaler_ctx *ctx = fh_to_ctx(file->private_data);
    803
    804	dev_dbg(priv->dev, "Releasing instance %p\n", ctx);
    805
    806	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
    807	v4l2_fh_del(&ctx->fh);
    808	v4l2_fh_exit(&ctx->fh);
    809	kfree(ctx);
    810
    811	return 0;
    812}
    813
    814static const struct v4l2_file_operations ipu_csc_scaler_fops = {
    815	.owner		= THIS_MODULE,
    816	.open		= ipu_csc_scaler_open,
    817	.release	= ipu_csc_scaler_release,
    818	.poll		= v4l2_m2m_fop_poll,
    819	.unlocked_ioctl	= video_ioctl2,
    820	.mmap		= v4l2_m2m_fop_mmap,
    821};
    822
    823static const struct v4l2_m2m_ops m2m_ops = {
    824	.device_run	= device_run,
    825	.job_abort	= job_abort,
    826};
    827
    828static void ipu_csc_scaler_video_device_release(struct video_device *vdev)
    829{
    830	struct ipu_csc_scaler_priv *priv = video_get_drvdata(vdev);
    831
    832	v4l2_m2m_release(priv->m2m_dev);
    833	video_device_release(vdev);
    834	kfree(priv);
    835}
    836
    837static const struct video_device ipu_csc_scaler_videodev_template = {
    838	.name		= "ipu_ic_pp csc/scaler",
    839	.fops		= &ipu_csc_scaler_fops,
    840	.ioctl_ops	= &ipu_csc_scaler_ioctl_ops,
    841	.minor		= -1,
    842	.release	= ipu_csc_scaler_video_device_release,
    843	.vfl_dir	= VFL_DIR_M2M,
    844	.device_caps	= V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
    845};
    846
    847int imx_media_csc_scaler_device_register(struct imx_media_video_dev *vdev)
    848{
    849	struct ipu_csc_scaler_priv *priv = vdev_to_priv(vdev);
    850	struct video_device *vfd = vdev->vfd;
    851	int ret;
    852
    853	vfd->v4l2_dev = &priv->md->v4l2_dev;
    854
    855	ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
    856	if (ret) {
    857		v4l2_err(vfd->v4l2_dev, "Failed to register video device\n");
    858		return ret;
    859	}
    860
    861	v4l2_info(vfd->v4l2_dev, "Registered %s as /dev/%s\n", vfd->name,
    862		  video_device_node_name(vfd));
    863
    864	return 0;
    865}
    866
    867void imx_media_csc_scaler_device_unregister(struct imx_media_video_dev *vdev)
    868{
    869	struct ipu_csc_scaler_priv *priv = vdev_to_priv(vdev);
    870	struct video_device *vfd = priv->vdev.vfd;
    871
    872	video_unregister_device(vfd);
    873}
    874
    875struct imx_media_video_dev *
    876imx_media_csc_scaler_device_init(struct imx_media_dev *md)
    877{
    878	struct ipu_csc_scaler_priv *priv;
    879	struct video_device *vfd;
    880	int ret;
    881
    882	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    883	if (!priv)
    884		return ERR_PTR(-ENOMEM);
    885
    886	priv->md = md;
    887	priv->dev = md->md.dev;
    888
    889	mutex_init(&priv->mutex);
    890
    891	vfd = video_device_alloc();
    892	if (!vfd) {
    893		ret = -ENOMEM;
    894		goto err_vfd;
    895	}
    896
    897	*vfd = ipu_csc_scaler_videodev_template;
    898	vfd->lock = &priv->mutex;
    899	priv->vdev.vfd = vfd;
    900
    901	INIT_LIST_HEAD(&priv->vdev.list);
    902
    903	video_set_drvdata(vfd, priv);
    904
    905	priv->m2m_dev = v4l2_m2m_init(&m2m_ops);
    906	if (IS_ERR(priv->m2m_dev)) {
    907		ret = PTR_ERR(priv->m2m_dev);
    908		v4l2_err(&md->v4l2_dev, "Failed to init mem2mem device: %d\n",
    909			 ret);
    910		goto err_m2m;
    911	}
    912
    913	return &priv->vdev;
    914
    915err_m2m:
    916	video_set_drvdata(vfd, NULL);
    917err_vfd:
    918	kfree(priv);
    919	return ERR_PTR(ret);
    920}
    921
    922MODULE_DESCRIPTION("i.MX IPUv3 mem2mem scaler/CSC driver");
    923MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
    924MODULE_LICENSE("GPL");