161890ccaSMoudy Ho // SPDX-License-Identifier: GPL-2.0-only
261890ccaSMoudy Ho /*
361890ccaSMoudy Ho  * Copyright (c) 2022 MediaTek Inc.
461890ccaSMoudy Ho  * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
561890ccaSMoudy Ho  */
661890ccaSMoudy Ho 
761890ccaSMoudy Ho #include <linux/platform_device.h>
861890ccaSMoudy Ho #include <media/v4l2-ioctl.h>
961890ccaSMoudy Ho #include <media/v4l2-event.h>
1061890ccaSMoudy Ho #include <media/videobuf2-dma-contig.h>
1161890ccaSMoudy Ho #include "mtk-mdp3-m2m.h"
1261890ccaSMoudy Ho 
1361890ccaSMoudy Ho static inline struct mdp_m2m_ctx *fh_to_ctx(struct v4l2_fh *fh)
1461890ccaSMoudy Ho {
1561890ccaSMoudy Ho 	return container_of(fh, struct mdp_m2m_ctx, fh);
1661890ccaSMoudy Ho }
1761890ccaSMoudy Ho 
1861890ccaSMoudy Ho static inline struct mdp_m2m_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
1961890ccaSMoudy Ho {
2061890ccaSMoudy Ho 	return container_of(ctrl->handler, struct mdp_m2m_ctx, ctrl_handler);
2161890ccaSMoudy Ho }
2261890ccaSMoudy Ho 
2361890ccaSMoudy Ho static inline struct mdp_frame *ctx_get_frame(struct mdp_m2m_ctx *ctx,
2461890ccaSMoudy Ho 					      enum v4l2_buf_type type)
2561890ccaSMoudy Ho {
2661890ccaSMoudy Ho 	if (V4L2_TYPE_IS_OUTPUT(type))
2761890ccaSMoudy Ho 		return &ctx->curr_param.output;
2861890ccaSMoudy Ho 	else
2961890ccaSMoudy Ho 		return &ctx->curr_param.captures[0];
3061890ccaSMoudy Ho }
3161890ccaSMoudy Ho 
3261890ccaSMoudy Ho static inline void mdp_m2m_ctx_set_state(struct mdp_m2m_ctx *ctx, u32 state)
3361890ccaSMoudy Ho {
3461890ccaSMoudy Ho 	atomic_or(state, &ctx->curr_param.state);
3561890ccaSMoudy Ho }
3661890ccaSMoudy Ho 
3761890ccaSMoudy Ho static inline bool mdp_m2m_ctx_is_state_set(struct mdp_m2m_ctx *ctx, u32 mask)
3861890ccaSMoudy Ho {
3961890ccaSMoudy Ho 	return ((atomic_read(&ctx->curr_param.state) & mask) == mask);
4061890ccaSMoudy Ho }
4161890ccaSMoudy Ho 
4261890ccaSMoudy Ho static void mdp_m2m_process_done(void *priv, int vb_state)
4361890ccaSMoudy Ho {
4461890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = priv;
4561890ccaSMoudy Ho 	struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
4661890ccaSMoudy Ho 
4761890ccaSMoudy Ho 	src_vbuf = (struct vb2_v4l2_buffer *)
4861890ccaSMoudy Ho 			v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
4961890ccaSMoudy Ho 	dst_vbuf = (struct vb2_v4l2_buffer *)
5061890ccaSMoudy Ho 			v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
5161890ccaSMoudy Ho 	ctx->curr_param.frame_no = ctx->frame_count[MDP_M2M_SRC];
5261890ccaSMoudy Ho 	src_vbuf->sequence = ctx->frame_count[MDP_M2M_SRC]++;
5361890ccaSMoudy Ho 	dst_vbuf->sequence = ctx->frame_count[MDP_M2M_DST]++;
5461890ccaSMoudy Ho 	v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, true);
5561890ccaSMoudy Ho 
5661890ccaSMoudy Ho 	v4l2_m2m_buf_done(src_vbuf, vb_state);
5761890ccaSMoudy Ho 	v4l2_m2m_buf_done(dst_vbuf, vb_state);
5861890ccaSMoudy Ho 	v4l2_m2m_job_finish(ctx->mdp_dev->m2m_dev, ctx->m2m_ctx);
5961890ccaSMoudy Ho }
6061890ccaSMoudy Ho 
6161890ccaSMoudy Ho static void mdp_m2m_device_run(void *priv)
6261890ccaSMoudy Ho {
6361890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = priv;
6461890ccaSMoudy Ho 	struct mdp_frame *frame;
6561890ccaSMoudy Ho 	struct vb2_v4l2_buffer *src_vb, *dst_vb;
6661890ccaSMoudy Ho 	struct img_ipi_frameparam param = {};
6761890ccaSMoudy Ho 	struct mdp_cmdq_param task = {};
6861890ccaSMoudy Ho 	enum vb2_buffer_state vb_state = VB2_BUF_STATE_ERROR;
6961890ccaSMoudy Ho 	int ret;
7061890ccaSMoudy Ho 
7161890ccaSMoudy Ho 	if (mdp_m2m_ctx_is_state_set(ctx, MDP_M2M_CTX_ERROR)) {
7261890ccaSMoudy Ho 		dev_err(&ctx->mdp_dev->pdev->dev,
7361890ccaSMoudy Ho 			"mdp_m2m_ctx is in error state\n");
7461890ccaSMoudy Ho 		goto worker_end;
7561890ccaSMoudy Ho 	}
7661890ccaSMoudy Ho 
7761890ccaSMoudy Ho 	param.frame_no = ctx->curr_param.frame_no;
7861890ccaSMoudy Ho 	param.type = ctx->curr_param.type;
7961890ccaSMoudy Ho 	param.num_inputs = 1;
8061890ccaSMoudy Ho 	param.num_outputs = 1;
8161890ccaSMoudy Ho 
8261890ccaSMoudy Ho 	frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
8361890ccaSMoudy Ho 	src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
8461890ccaSMoudy Ho 	mdp_set_src_config(&param.inputs[0], frame, &src_vb->vb2_buf);
8561890ccaSMoudy Ho 
8661890ccaSMoudy Ho 	frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
8761890ccaSMoudy Ho 	dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
8861890ccaSMoudy Ho 	mdp_set_dst_config(&param.outputs[0], frame, &dst_vb->vb2_buf);
8961890ccaSMoudy Ho 
9061890ccaSMoudy Ho 	ret = mdp_vpu_process(&ctx->vpu, &param);
9161890ccaSMoudy Ho 	if (ret) {
9261890ccaSMoudy Ho 		dev_err(&ctx->mdp_dev->pdev->dev,
9361890ccaSMoudy Ho 			"VPU MDP process failed: %d\n", ret);
9461890ccaSMoudy Ho 		goto worker_end;
9561890ccaSMoudy Ho 	}
9661890ccaSMoudy Ho 
9761890ccaSMoudy Ho 	task.config = ctx->vpu.config;
9861890ccaSMoudy Ho 	task.param = &param;
9961890ccaSMoudy Ho 	task.composes[0] = &frame->compose;
10061890ccaSMoudy Ho 	task.cmdq_cb = NULL;
10161890ccaSMoudy Ho 	task.cb_data = NULL;
10261890ccaSMoudy Ho 	task.mdp_ctx = ctx;
10361890ccaSMoudy Ho 
10461890ccaSMoudy Ho 	ret = mdp_cmdq_send(ctx->mdp_dev, &task);
10561890ccaSMoudy Ho 	if (ret) {
10661890ccaSMoudy Ho 		dev_err(&ctx->mdp_dev->pdev->dev,
10761890ccaSMoudy Ho 			"CMDQ sendtask failed: %d\n", ret);
10861890ccaSMoudy Ho 		goto worker_end;
10961890ccaSMoudy Ho 	}
11061890ccaSMoudy Ho 
11161890ccaSMoudy Ho 	return;
11261890ccaSMoudy Ho 
11361890ccaSMoudy Ho worker_end:
11461890ccaSMoudy Ho 	mdp_m2m_process_done(ctx, vb_state);
11561890ccaSMoudy Ho }
11661890ccaSMoudy Ho 
11761890ccaSMoudy Ho static int mdp_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
11861890ccaSMoudy Ho {
11961890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
12061890ccaSMoudy Ho 	struct mdp_frame *capture;
12161890ccaSMoudy Ho 	struct vb2_queue *vq;
12261890ccaSMoudy Ho 	int ret;
12361890ccaSMoudy Ho 	bool out_streaming, cap_streaming;
12461890ccaSMoudy Ho 
12561890ccaSMoudy Ho 	if (V4L2_TYPE_IS_OUTPUT(q->type))
12661890ccaSMoudy Ho 		ctx->frame_count[MDP_M2M_SRC] = 0;
12761890ccaSMoudy Ho 
12861890ccaSMoudy Ho 	if (V4L2_TYPE_IS_CAPTURE(q->type))
12961890ccaSMoudy Ho 		ctx->frame_count[MDP_M2M_DST] = 0;
13061890ccaSMoudy Ho 
13161890ccaSMoudy Ho 	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
13261890ccaSMoudy Ho 	vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx);
13361890ccaSMoudy Ho 	out_streaming = vb2_is_streaming(vq);
13461890ccaSMoudy Ho 	vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
13561890ccaSMoudy Ho 	cap_streaming = vb2_is_streaming(vq);
13661890ccaSMoudy Ho 
13761890ccaSMoudy Ho 	/* Check to see if scaling ratio is within supported range */
13861890ccaSMoudy Ho 	if ((V4L2_TYPE_IS_OUTPUT(q->type) && cap_streaming) ||
13961890ccaSMoudy Ho 	    (V4L2_TYPE_IS_CAPTURE(q->type) && out_streaming)) {
14061890ccaSMoudy Ho 		ret = mdp_check_scaling_ratio(&capture->crop.c,
14161890ccaSMoudy Ho 					      &capture->compose,
14261890ccaSMoudy Ho 					      capture->rotation,
14361890ccaSMoudy Ho 					      ctx->curr_param.limit);
14461890ccaSMoudy Ho 		if (ret) {
14561890ccaSMoudy Ho 			dev_err(&ctx->mdp_dev->pdev->dev,
14661890ccaSMoudy Ho 				"Out of scaling range\n");
14761890ccaSMoudy Ho 			return ret;
14861890ccaSMoudy Ho 		}
14961890ccaSMoudy Ho 	}
15061890ccaSMoudy Ho 
15161890ccaSMoudy Ho 	if (!mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) {
15261890ccaSMoudy Ho 		ret = mdp_vpu_get_locked(ctx->mdp_dev);
15361890ccaSMoudy Ho 		if (ret)
15461890ccaSMoudy Ho 			return ret;
15561890ccaSMoudy Ho 
15661890ccaSMoudy Ho 		ret = mdp_vpu_ctx_init(&ctx->vpu, &ctx->mdp_dev->vpu,
15761890ccaSMoudy Ho 				       MDP_DEV_M2M);
15861890ccaSMoudy Ho 		if (ret) {
15961890ccaSMoudy Ho 			dev_err(&ctx->mdp_dev->pdev->dev,
16061890ccaSMoudy Ho 				"VPU init failed %d\n", ret);
16161890ccaSMoudy Ho 			return -EINVAL;
16261890ccaSMoudy Ho 		}
16361890ccaSMoudy Ho 		mdp_m2m_ctx_set_state(ctx, MDP_VPU_INIT);
16461890ccaSMoudy Ho 	}
16561890ccaSMoudy Ho 
16661890ccaSMoudy Ho 	return 0;
16761890ccaSMoudy Ho }
16861890ccaSMoudy Ho 
16961890ccaSMoudy Ho static struct vb2_v4l2_buffer *mdp_m2m_buf_remove(struct mdp_m2m_ctx *ctx,
17061890ccaSMoudy Ho 						  unsigned int type)
17161890ccaSMoudy Ho {
17261890ccaSMoudy Ho 	if (V4L2_TYPE_IS_OUTPUT(type))
17361890ccaSMoudy Ho 		return (struct vb2_v4l2_buffer *)
17461890ccaSMoudy Ho 			v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
17561890ccaSMoudy Ho 	else
17661890ccaSMoudy Ho 		return (struct vb2_v4l2_buffer *)
17761890ccaSMoudy Ho 			v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
17861890ccaSMoudy Ho }
17961890ccaSMoudy Ho 
18061890ccaSMoudy Ho static void mdp_m2m_stop_streaming(struct vb2_queue *q)
18161890ccaSMoudy Ho {
18261890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
18361890ccaSMoudy Ho 	struct vb2_v4l2_buffer *vb;
18461890ccaSMoudy Ho 
18561890ccaSMoudy Ho 	vb = mdp_m2m_buf_remove(ctx, q->type);
18661890ccaSMoudy Ho 	while (vb) {
18761890ccaSMoudy Ho 		v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
18861890ccaSMoudy Ho 		vb = mdp_m2m_buf_remove(ctx, q->type);
18961890ccaSMoudy Ho 	}
19061890ccaSMoudy Ho }
19161890ccaSMoudy Ho 
19261890ccaSMoudy Ho static int mdp_m2m_queue_setup(struct vb2_queue *q,
19361890ccaSMoudy Ho 			       unsigned int *num_buffers,
19461890ccaSMoudy Ho 			       unsigned int *num_planes, unsigned int sizes[],
19561890ccaSMoudy Ho 			       struct device *alloc_devs[])
19661890ccaSMoudy Ho {
19761890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
19861890ccaSMoudy Ho 	struct v4l2_pix_format_mplane *pix_mp;
19961890ccaSMoudy Ho 	u32 i;
20061890ccaSMoudy Ho 
20161890ccaSMoudy Ho 	pix_mp = &ctx_get_frame(ctx, q->type)->format.fmt.pix_mp;
20261890ccaSMoudy Ho 
20361890ccaSMoudy Ho 	/* from VIDIOC_CREATE_BUFS */
20461890ccaSMoudy Ho 	if (*num_planes) {
20561890ccaSMoudy Ho 		if (*num_planes != pix_mp->num_planes)
20661890ccaSMoudy Ho 			return -EINVAL;
20761890ccaSMoudy Ho 		for (i = 0; i < pix_mp->num_planes; ++i)
20861890ccaSMoudy Ho 			if (sizes[i] < pix_mp->plane_fmt[i].sizeimage)
20961890ccaSMoudy Ho 				return -EINVAL;
21061890ccaSMoudy Ho 	} else {/* from VIDIOC_REQBUFS */
21161890ccaSMoudy Ho 		*num_planes = pix_mp->num_planes;
21261890ccaSMoudy Ho 		for (i = 0; i < pix_mp->num_planes; ++i)
21361890ccaSMoudy Ho 			sizes[i] = pix_mp->plane_fmt[i].sizeimage;
21461890ccaSMoudy Ho 	}
21561890ccaSMoudy Ho 
21661890ccaSMoudy Ho 	return 0;
21761890ccaSMoudy Ho }
21861890ccaSMoudy Ho 
21961890ccaSMoudy Ho static int mdp_m2m_buf_prepare(struct vb2_buffer *vb)
22061890ccaSMoudy Ho {
22161890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
22261890ccaSMoudy Ho 	struct v4l2_pix_format_mplane *pix_mp;
22361890ccaSMoudy Ho 	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
22461890ccaSMoudy Ho 	u32 i;
22561890ccaSMoudy Ho 
22661890ccaSMoudy Ho 	v4l2_buf->field = V4L2_FIELD_NONE;
22761890ccaSMoudy Ho 
22861890ccaSMoudy Ho 	if (V4L2_TYPE_IS_CAPTURE(vb->type)) {
22961890ccaSMoudy Ho 		pix_mp = &ctx_get_frame(ctx, vb->type)->format.fmt.pix_mp;
23061890ccaSMoudy Ho 		for (i = 0; i < pix_mp->num_planes; ++i) {
23161890ccaSMoudy Ho 			vb2_set_plane_payload(vb, i,
23261890ccaSMoudy Ho 					      pix_mp->plane_fmt[i].sizeimage);
23361890ccaSMoudy Ho 		}
23461890ccaSMoudy Ho 	}
23561890ccaSMoudy Ho 	return 0;
23661890ccaSMoudy Ho }
23761890ccaSMoudy Ho 
23861890ccaSMoudy Ho static int mdp_m2m_buf_out_validate(struct vb2_buffer *vb)
23961890ccaSMoudy Ho {
24061890ccaSMoudy Ho 	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
24161890ccaSMoudy Ho 
24261890ccaSMoudy Ho 	v4l2_buf->field = V4L2_FIELD_NONE;
24361890ccaSMoudy Ho 
24461890ccaSMoudy Ho 	return 0;
24561890ccaSMoudy Ho }
24661890ccaSMoudy Ho 
24761890ccaSMoudy Ho static void mdp_m2m_buf_queue(struct vb2_buffer *vb)
24861890ccaSMoudy Ho {
24961890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
25061890ccaSMoudy Ho 	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
25161890ccaSMoudy Ho 
25261890ccaSMoudy Ho 	v4l2_buf->field = V4L2_FIELD_NONE;
25361890ccaSMoudy Ho 
25461890ccaSMoudy Ho 	v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb));
25561890ccaSMoudy Ho }
25661890ccaSMoudy Ho 
25761890ccaSMoudy Ho static const struct vb2_ops mdp_m2m_qops = {
25861890ccaSMoudy Ho 	.queue_setup	= mdp_m2m_queue_setup,
25961890ccaSMoudy Ho 	.wait_prepare	= vb2_ops_wait_prepare,
26061890ccaSMoudy Ho 	.wait_finish	= vb2_ops_wait_finish,
26161890ccaSMoudy Ho 	.buf_prepare	= mdp_m2m_buf_prepare,
26261890ccaSMoudy Ho 	.start_streaming = mdp_m2m_start_streaming,
26361890ccaSMoudy Ho 	.stop_streaming	= mdp_m2m_stop_streaming,
26461890ccaSMoudy Ho 	.buf_queue	= mdp_m2m_buf_queue,
26561890ccaSMoudy Ho 	.buf_out_validate = mdp_m2m_buf_out_validate,
26661890ccaSMoudy Ho };
26761890ccaSMoudy Ho 
26861890ccaSMoudy Ho static int mdp_m2m_querycap(struct file *file, void *fh,
26961890ccaSMoudy Ho 			    struct v4l2_capability *cap)
27061890ccaSMoudy Ho {
27161890ccaSMoudy Ho 	strscpy(cap->driver, MDP_MODULE_NAME, sizeof(cap->driver));
27261890ccaSMoudy Ho 	strscpy(cap->card, MDP_DEVICE_NAME, sizeof(cap->card));
27361890ccaSMoudy Ho 
27461890ccaSMoudy Ho 	return 0;
27561890ccaSMoudy Ho }
27661890ccaSMoudy Ho 
27761890ccaSMoudy Ho static int mdp_m2m_enum_fmt_mplane(struct file *file, void *fh,
27861890ccaSMoudy Ho 				   struct v4l2_fmtdesc *f)
27961890ccaSMoudy Ho {
280*6b8910e3SMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
281*6b8910e3SMoudy Ho 
282*6b8910e3SMoudy Ho 	return mdp_enum_fmt_mplane(ctx->mdp_dev, f);
28361890ccaSMoudy Ho }
28461890ccaSMoudy Ho 
28561890ccaSMoudy Ho static int mdp_m2m_g_fmt_mplane(struct file *file, void *fh,
28661890ccaSMoudy Ho 				struct v4l2_format *f)
28761890ccaSMoudy Ho {
28861890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
28961890ccaSMoudy Ho 	struct mdp_frame *frame;
29061890ccaSMoudy Ho 	struct v4l2_pix_format_mplane *pix_mp;
29161890ccaSMoudy Ho 
29261890ccaSMoudy Ho 	frame = ctx_get_frame(ctx, f->type);
29361890ccaSMoudy Ho 	*f = frame->format;
29461890ccaSMoudy Ho 	pix_mp = &f->fmt.pix_mp;
29561890ccaSMoudy Ho 	pix_mp->colorspace = ctx->curr_param.colorspace;
29661890ccaSMoudy Ho 	pix_mp->xfer_func = ctx->curr_param.xfer_func;
29761890ccaSMoudy Ho 	pix_mp->ycbcr_enc = ctx->curr_param.ycbcr_enc;
29861890ccaSMoudy Ho 	pix_mp->quantization = ctx->curr_param.quant;
29961890ccaSMoudy Ho 
30061890ccaSMoudy Ho 	return 0;
30161890ccaSMoudy Ho }
30261890ccaSMoudy Ho 
30361890ccaSMoudy Ho static int mdp_m2m_s_fmt_mplane(struct file *file, void *fh,
30461890ccaSMoudy Ho 				struct v4l2_format *f)
30561890ccaSMoudy Ho {
30661890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
30761890ccaSMoudy Ho 	struct mdp_frame *frame = ctx_get_frame(ctx, f->type);
30861890ccaSMoudy Ho 	struct mdp_frame *capture;
30961890ccaSMoudy Ho 	const struct mdp_format *fmt;
31061890ccaSMoudy Ho 	struct vb2_queue *vq;
31161890ccaSMoudy Ho 
312*6b8910e3SMoudy Ho 	fmt = mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id);
31361890ccaSMoudy Ho 	if (!fmt)
31461890ccaSMoudy Ho 		return -EINVAL;
31561890ccaSMoudy Ho 
31661890ccaSMoudy Ho 	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
31761890ccaSMoudy Ho 	if (vb2_is_busy(vq))
31861890ccaSMoudy Ho 		return -EBUSY;
31961890ccaSMoudy Ho 
32061890ccaSMoudy Ho 	frame->format = *f;
32161890ccaSMoudy Ho 	frame->mdp_fmt = fmt;
32261890ccaSMoudy Ho 	frame->ycbcr_prof = mdp_map_ycbcr_prof_mplane(f, fmt->mdp_color);
32361890ccaSMoudy Ho 	frame->usage = V4L2_TYPE_IS_OUTPUT(f->type) ?
32461890ccaSMoudy Ho 		MDP_BUFFER_USAGE_HW_READ : MDP_BUFFER_USAGE_MDP;
32561890ccaSMoudy Ho 
32661890ccaSMoudy Ho 	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
32761890ccaSMoudy Ho 	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
32861890ccaSMoudy Ho 		capture->crop.c.left = 0;
32961890ccaSMoudy Ho 		capture->crop.c.top = 0;
33061890ccaSMoudy Ho 		capture->crop.c.width = f->fmt.pix_mp.width;
33161890ccaSMoudy Ho 		capture->crop.c.height = f->fmt.pix_mp.height;
33261890ccaSMoudy Ho 		ctx->curr_param.colorspace = f->fmt.pix_mp.colorspace;
33361890ccaSMoudy Ho 		ctx->curr_param.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
33461890ccaSMoudy Ho 		ctx->curr_param.quant = f->fmt.pix_mp.quantization;
33561890ccaSMoudy Ho 		ctx->curr_param.xfer_func = f->fmt.pix_mp.xfer_func;
33661890ccaSMoudy Ho 	} else {
33761890ccaSMoudy Ho 		capture->compose.left = 0;
33861890ccaSMoudy Ho 		capture->compose.top = 0;
33961890ccaSMoudy Ho 		capture->compose.width = f->fmt.pix_mp.width;
34061890ccaSMoudy Ho 		capture->compose.height = f->fmt.pix_mp.height;
34161890ccaSMoudy Ho 	}
34261890ccaSMoudy Ho 
34361890ccaSMoudy Ho 	return 0;
34461890ccaSMoudy Ho }
34561890ccaSMoudy Ho 
34661890ccaSMoudy Ho static int mdp_m2m_try_fmt_mplane(struct file *file, void *fh,
34761890ccaSMoudy Ho 				  struct v4l2_format *f)
34861890ccaSMoudy Ho {
34961890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
35061890ccaSMoudy Ho 
351*6b8910e3SMoudy Ho 	if (!mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id))
35261890ccaSMoudy Ho 		return -EINVAL;
35361890ccaSMoudy Ho 
35461890ccaSMoudy Ho 	return 0;
35561890ccaSMoudy Ho }
35661890ccaSMoudy Ho 
35761890ccaSMoudy Ho static int mdp_m2m_g_selection(struct file *file, void *fh,
35861890ccaSMoudy Ho 			       struct v4l2_selection *s)
35961890ccaSMoudy Ho {
36061890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
36161890ccaSMoudy Ho 	struct mdp_frame *frame;
36261890ccaSMoudy Ho 	bool valid = false;
36361890ccaSMoudy Ho 
36461890ccaSMoudy Ho 	if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
36561890ccaSMoudy Ho 		valid = mdp_target_is_crop(s->target);
36661890ccaSMoudy Ho 	else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
36761890ccaSMoudy Ho 		valid = mdp_target_is_compose(s->target);
36861890ccaSMoudy Ho 
36961890ccaSMoudy Ho 	if (!valid)
37061890ccaSMoudy Ho 		return -EINVAL;
37161890ccaSMoudy Ho 
37261890ccaSMoudy Ho 	switch (s->target) {
37361890ccaSMoudy Ho 	case V4L2_SEL_TGT_CROP:
37461890ccaSMoudy Ho 		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
37561890ccaSMoudy Ho 			return -EINVAL;
37661890ccaSMoudy Ho 		frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
37761890ccaSMoudy Ho 		s->r = frame->crop.c;
37861890ccaSMoudy Ho 		return 0;
37961890ccaSMoudy Ho 	case V4L2_SEL_TGT_COMPOSE:
38061890ccaSMoudy Ho 		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
38161890ccaSMoudy Ho 			return -EINVAL;
38261890ccaSMoudy Ho 		frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
38361890ccaSMoudy Ho 		s->r = frame->compose;
38461890ccaSMoudy Ho 		return 0;
38561890ccaSMoudy Ho 	case V4L2_SEL_TGT_CROP_DEFAULT:
38661890ccaSMoudy Ho 	case V4L2_SEL_TGT_CROP_BOUNDS:
38761890ccaSMoudy Ho 		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
38861890ccaSMoudy Ho 			return -EINVAL;
38961890ccaSMoudy Ho 		frame = ctx_get_frame(ctx, s->type);
39061890ccaSMoudy Ho 		s->r.left = 0;
39161890ccaSMoudy Ho 		s->r.top = 0;
39261890ccaSMoudy Ho 		s->r.width = frame->format.fmt.pix_mp.width;
39361890ccaSMoudy Ho 		s->r.height = frame->format.fmt.pix_mp.height;
39461890ccaSMoudy Ho 		return 0;
39561890ccaSMoudy Ho 	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
39661890ccaSMoudy Ho 	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
39761890ccaSMoudy Ho 		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
39861890ccaSMoudy Ho 			return -EINVAL;
39961890ccaSMoudy Ho 		frame = ctx_get_frame(ctx, s->type);
40061890ccaSMoudy Ho 		s->r.left = 0;
40161890ccaSMoudy Ho 		s->r.top = 0;
40261890ccaSMoudy Ho 		s->r.width = frame->format.fmt.pix_mp.width;
40361890ccaSMoudy Ho 		s->r.height = frame->format.fmt.pix_mp.height;
40461890ccaSMoudy Ho 		return 0;
40561890ccaSMoudy Ho 	}
40661890ccaSMoudy Ho 	return -EINVAL;
40761890ccaSMoudy Ho }
40861890ccaSMoudy Ho 
40961890ccaSMoudy Ho static int mdp_m2m_s_selection(struct file *file, void *fh,
41061890ccaSMoudy Ho 			       struct v4l2_selection *s)
41161890ccaSMoudy Ho {
41261890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
41361890ccaSMoudy Ho 	struct mdp_frame *frame = ctx_get_frame(ctx, s->type);
41461890ccaSMoudy Ho 	struct mdp_frame *capture;
41561890ccaSMoudy Ho 	struct v4l2_rect r;
41661890ccaSMoudy Ho 	struct device *dev = &ctx->mdp_dev->pdev->dev;
41761890ccaSMoudy Ho 	bool valid = false;
41861890ccaSMoudy Ho 	int ret;
41961890ccaSMoudy Ho 
42061890ccaSMoudy Ho 	if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
42161890ccaSMoudy Ho 		valid = (s->target == V4L2_SEL_TGT_CROP);
42261890ccaSMoudy Ho 	else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
42361890ccaSMoudy Ho 		valid = (s->target == V4L2_SEL_TGT_COMPOSE);
42461890ccaSMoudy Ho 
42561890ccaSMoudy Ho 	if (!valid) {
42661890ccaSMoudy Ho 		dev_dbg(dev, "[%s:%d] invalid type:%u target:%u", __func__,
42761890ccaSMoudy Ho 			ctx->id, s->type, s->target);
42861890ccaSMoudy Ho 		return -EINVAL;
42961890ccaSMoudy Ho 	}
43061890ccaSMoudy Ho 
43161890ccaSMoudy Ho 	ret = mdp_try_crop(ctx, &r, s, frame);
43261890ccaSMoudy Ho 	if (ret)
43361890ccaSMoudy Ho 		return ret;
43461890ccaSMoudy Ho 	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
43561890ccaSMoudy Ho 
43661890ccaSMoudy Ho 	if (mdp_target_is_crop(s->target))
43761890ccaSMoudy Ho 		capture->crop.c = r;
43861890ccaSMoudy Ho 	else
43961890ccaSMoudy Ho 		capture->compose = r;
44061890ccaSMoudy Ho 
44161890ccaSMoudy Ho 	s->r = r;
44261890ccaSMoudy Ho 
44361890ccaSMoudy Ho 	return 0;
44461890ccaSMoudy Ho }
44561890ccaSMoudy Ho 
44661890ccaSMoudy Ho static const struct v4l2_ioctl_ops mdp_m2m_ioctl_ops = {
44761890ccaSMoudy Ho 	.vidioc_querycap		= mdp_m2m_querycap,
44861890ccaSMoudy Ho 	.vidioc_enum_fmt_vid_cap	= mdp_m2m_enum_fmt_mplane,
44961890ccaSMoudy Ho 	.vidioc_enum_fmt_vid_out	= mdp_m2m_enum_fmt_mplane,
45061890ccaSMoudy Ho 	.vidioc_g_fmt_vid_cap_mplane	= mdp_m2m_g_fmt_mplane,
45161890ccaSMoudy Ho 	.vidioc_g_fmt_vid_out_mplane	= mdp_m2m_g_fmt_mplane,
45261890ccaSMoudy Ho 	.vidioc_s_fmt_vid_cap_mplane	= mdp_m2m_s_fmt_mplane,
45361890ccaSMoudy Ho 	.vidioc_s_fmt_vid_out_mplane	= mdp_m2m_s_fmt_mplane,
45461890ccaSMoudy Ho 	.vidioc_try_fmt_vid_cap_mplane	= mdp_m2m_try_fmt_mplane,
45561890ccaSMoudy Ho 	.vidioc_try_fmt_vid_out_mplane	= mdp_m2m_try_fmt_mplane,
45661890ccaSMoudy Ho 	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
45761890ccaSMoudy Ho 	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
45861890ccaSMoudy Ho 	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
45961890ccaSMoudy Ho 	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
46061890ccaSMoudy Ho 	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
46161890ccaSMoudy Ho 	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
46261890ccaSMoudy Ho 	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
46361890ccaSMoudy Ho 	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
46461890ccaSMoudy Ho 	.vidioc_g_selection		= mdp_m2m_g_selection,
46561890ccaSMoudy Ho 	.vidioc_s_selection		= mdp_m2m_s_selection,
46661890ccaSMoudy Ho 	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
46761890ccaSMoudy Ho 	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
46861890ccaSMoudy Ho };
46961890ccaSMoudy Ho 
47061890ccaSMoudy Ho static int mdp_m2m_queue_init(void *priv,
47161890ccaSMoudy Ho 			      struct vb2_queue *src_vq,
47261890ccaSMoudy Ho 			      struct vb2_queue *dst_vq)
47361890ccaSMoudy Ho {
47461890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = priv;
47561890ccaSMoudy Ho 	int ret;
47661890ccaSMoudy Ho 
47761890ccaSMoudy Ho 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
47861890ccaSMoudy Ho 	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
47961890ccaSMoudy Ho 	src_vq->ops = &mdp_m2m_qops;
48061890ccaSMoudy Ho 	src_vq->mem_ops = &vb2_dma_contig_memops;
48161890ccaSMoudy Ho 	src_vq->drv_priv = ctx;
48261890ccaSMoudy Ho 	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
48361890ccaSMoudy Ho 	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
48461890ccaSMoudy Ho 	src_vq->dev = &ctx->mdp_dev->pdev->dev;
48561890ccaSMoudy Ho 	src_vq->lock = &ctx->ctx_lock;
48661890ccaSMoudy Ho 
48761890ccaSMoudy Ho 	ret = vb2_queue_init(src_vq);
48861890ccaSMoudy Ho 	if (ret)
48961890ccaSMoudy Ho 		return ret;
49061890ccaSMoudy Ho 
49161890ccaSMoudy Ho 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
49261890ccaSMoudy Ho 	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
49361890ccaSMoudy Ho 	dst_vq->ops = &mdp_m2m_qops;
49461890ccaSMoudy Ho 	dst_vq->mem_ops = &vb2_dma_contig_memops;
49561890ccaSMoudy Ho 	dst_vq->drv_priv = ctx;
49661890ccaSMoudy Ho 	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
49761890ccaSMoudy Ho 	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
49861890ccaSMoudy Ho 	dst_vq->dev = &ctx->mdp_dev->pdev->dev;
49961890ccaSMoudy Ho 	dst_vq->lock = &ctx->ctx_lock;
50061890ccaSMoudy Ho 
50161890ccaSMoudy Ho 	return vb2_queue_init(dst_vq);
50261890ccaSMoudy Ho }
50361890ccaSMoudy Ho 
50461890ccaSMoudy Ho static int mdp_m2m_s_ctrl(struct v4l2_ctrl *ctrl)
50561890ccaSMoudy Ho {
50661890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = ctrl_to_ctx(ctrl);
50761890ccaSMoudy Ho 	struct mdp_frame *capture;
50861890ccaSMoudy Ho 
50961890ccaSMoudy Ho 	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
51061890ccaSMoudy Ho 	switch (ctrl->id) {
51161890ccaSMoudy Ho 	case V4L2_CID_HFLIP:
51261890ccaSMoudy Ho 		capture->hflip = ctrl->val;
51361890ccaSMoudy Ho 		break;
51461890ccaSMoudy Ho 	case V4L2_CID_VFLIP:
51561890ccaSMoudy Ho 		capture->vflip = ctrl->val;
51661890ccaSMoudy Ho 		break;
51761890ccaSMoudy Ho 	case V4L2_CID_ROTATE:
51861890ccaSMoudy Ho 		capture->rotation = ctrl->val;
51961890ccaSMoudy Ho 		break;
52061890ccaSMoudy Ho 	}
52161890ccaSMoudy Ho 
52261890ccaSMoudy Ho 	return 0;
52361890ccaSMoudy Ho }
52461890ccaSMoudy Ho 
52561890ccaSMoudy Ho static const struct v4l2_ctrl_ops mdp_m2m_ctrl_ops = {
52661890ccaSMoudy Ho 	.s_ctrl	= mdp_m2m_s_ctrl,
52761890ccaSMoudy Ho };
52861890ccaSMoudy Ho 
52961890ccaSMoudy Ho static int mdp_m2m_ctrls_create(struct mdp_m2m_ctx *ctx)
53061890ccaSMoudy Ho {
53161890ccaSMoudy Ho 	v4l2_ctrl_handler_init(&ctx->ctrl_handler, MDP_MAX_CTRLS);
53261890ccaSMoudy Ho 	ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
53361890ccaSMoudy Ho 					     &mdp_m2m_ctrl_ops, V4L2_CID_HFLIP,
53461890ccaSMoudy Ho 					     0, 1, 1, 0);
53561890ccaSMoudy Ho 	ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
53661890ccaSMoudy Ho 					     &mdp_m2m_ctrl_ops, V4L2_CID_VFLIP,
53761890ccaSMoudy Ho 					     0, 1, 1, 0);
53861890ccaSMoudy Ho 	ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
53961890ccaSMoudy Ho 					      &mdp_m2m_ctrl_ops,
54061890ccaSMoudy Ho 					      V4L2_CID_ROTATE, 0, 270, 90, 0);
54161890ccaSMoudy Ho 
54261890ccaSMoudy Ho 	if (ctx->ctrl_handler.error) {
54361890ccaSMoudy Ho 		int err = ctx->ctrl_handler.error;
54461890ccaSMoudy Ho 
54561890ccaSMoudy Ho 		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
54661890ccaSMoudy Ho 		dev_err(&ctx->mdp_dev->pdev->dev,
54761890ccaSMoudy Ho 			"Failed to register controls\n");
54861890ccaSMoudy Ho 		return err;
54961890ccaSMoudy Ho 	}
55061890ccaSMoudy Ho 	return 0;
55161890ccaSMoudy Ho }
55261890ccaSMoudy Ho 
55361890ccaSMoudy Ho static int mdp_m2m_open(struct file *file)
55461890ccaSMoudy Ho {
55561890ccaSMoudy Ho 	struct video_device *vdev = video_devdata(file);
55661890ccaSMoudy Ho 	struct mdp_dev *mdp = video_get_drvdata(vdev);
55761890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx;
55861890ccaSMoudy Ho 	struct device *dev = &mdp->pdev->dev;
55961890ccaSMoudy Ho 	int ret;
56061890ccaSMoudy Ho 	struct v4l2_format default_format = {};
56161890ccaSMoudy Ho 
56261890ccaSMoudy Ho 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
56361890ccaSMoudy Ho 	if (!ctx)
56461890ccaSMoudy Ho 		return -ENOMEM;
56561890ccaSMoudy Ho 
56661890ccaSMoudy Ho 	if (mutex_lock_interruptible(&mdp->m2m_lock)) {
56761890ccaSMoudy Ho 		ret = -ERESTARTSYS;
56861890ccaSMoudy Ho 		goto err_free_ctx;
56961890ccaSMoudy Ho 	}
57061890ccaSMoudy Ho 
571d00f5922SJiasheng Jiang 	ret = ida_alloc(&mdp->mdp_ida, GFP_KERNEL);
572d00f5922SJiasheng Jiang 	if (ret < 0)
573d00f5922SJiasheng Jiang 		goto err_unlock_mutex;
574d00f5922SJiasheng Jiang 	ctx->id = ret;
575d00f5922SJiasheng Jiang 
57661890ccaSMoudy Ho 	ctx->mdp_dev = mdp;
57761890ccaSMoudy Ho 
57861890ccaSMoudy Ho 	v4l2_fh_init(&ctx->fh, vdev);
57961890ccaSMoudy Ho 	file->private_data = &ctx->fh;
58061890ccaSMoudy Ho 	ret = mdp_m2m_ctrls_create(ctx);
58161890ccaSMoudy Ho 	if (ret)
58261890ccaSMoudy Ho 		goto err_exit_fh;
58361890ccaSMoudy Ho 
58461890ccaSMoudy Ho 	/* Use separate control handler per file handle */
58561890ccaSMoudy Ho 	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
58661890ccaSMoudy Ho 	v4l2_fh_add(&ctx->fh);
58761890ccaSMoudy Ho 
58861890ccaSMoudy Ho 	mutex_init(&ctx->ctx_lock);
58961890ccaSMoudy Ho 	ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx, mdp_m2m_queue_init);
59061890ccaSMoudy Ho 	if (IS_ERR(ctx->m2m_ctx)) {
59161890ccaSMoudy Ho 		dev_err(dev, "Failed to initialize m2m context\n");
59261890ccaSMoudy Ho 		ret = PTR_ERR(ctx->m2m_ctx);
59361890ccaSMoudy Ho 		goto err_release_handler;
59461890ccaSMoudy Ho 	}
59561890ccaSMoudy Ho 	ctx->fh.m2m_ctx = ctx->m2m_ctx;
59661890ccaSMoudy Ho 
59761890ccaSMoudy Ho 	ctx->curr_param.ctx = ctx;
598*6b8910e3SMoudy Ho 	ret = mdp_frameparam_init(mdp, &ctx->curr_param);
59961890ccaSMoudy Ho 	if (ret) {
60061890ccaSMoudy Ho 		dev_err(dev, "Failed to initialize mdp parameter\n");
60161890ccaSMoudy Ho 		goto err_release_m2m_ctx;
60261890ccaSMoudy Ho 	}
60361890ccaSMoudy Ho 
60461890ccaSMoudy Ho 	mutex_unlock(&mdp->m2m_lock);
60561890ccaSMoudy Ho 
60661890ccaSMoudy Ho 	/* Default format */
60761890ccaSMoudy Ho 	default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
60861890ccaSMoudy Ho 	default_format.fmt.pix_mp.width = 32;
60961890ccaSMoudy Ho 	default_format.fmt.pix_mp.height = 32;
61061890ccaSMoudy Ho 	default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
61161890ccaSMoudy Ho 	mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
61261890ccaSMoudy Ho 	default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
61361890ccaSMoudy Ho 	mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
61461890ccaSMoudy Ho 
61561890ccaSMoudy Ho 	dev_dbg(dev, "%s:[%d]", __func__, ctx->id);
61661890ccaSMoudy Ho 
61761890ccaSMoudy Ho 	return 0;
61861890ccaSMoudy Ho 
61961890ccaSMoudy Ho err_release_m2m_ctx:
62061890ccaSMoudy Ho 	v4l2_m2m_ctx_release(ctx->m2m_ctx);
62161890ccaSMoudy Ho err_release_handler:
62261890ccaSMoudy Ho 	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
62361890ccaSMoudy Ho 	v4l2_fh_del(&ctx->fh);
62461890ccaSMoudy Ho err_exit_fh:
62561890ccaSMoudy Ho 	v4l2_fh_exit(&ctx->fh);
626d00f5922SJiasheng Jiang 	ida_free(&mdp->mdp_ida, ctx->id);
627d00f5922SJiasheng Jiang err_unlock_mutex:
62861890ccaSMoudy Ho 	mutex_unlock(&mdp->m2m_lock);
62961890ccaSMoudy Ho err_free_ctx:
63061890ccaSMoudy Ho 	kfree(ctx);
63161890ccaSMoudy Ho 
63261890ccaSMoudy Ho 	return ret;
63361890ccaSMoudy Ho }
63461890ccaSMoudy Ho 
63561890ccaSMoudy Ho static int mdp_m2m_release(struct file *file)
63661890ccaSMoudy Ho {
63761890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(file->private_data);
63861890ccaSMoudy Ho 	struct mdp_dev *mdp = video_drvdata(file);
63961890ccaSMoudy Ho 	struct device *dev = &mdp->pdev->dev;
64061890ccaSMoudy Ho 
64161890ccaSMoudy Ho 	mutex_lock(&mdp->m2m_lock);
64261890ccaSMoudy Ho 	v4l2_m2m_ctx_release(ctx->m2m_ctx);
64361890ccaSMoudy Ho 	if (mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) {
64461890ccaSMoudy Ho 		mdp_vpu_ctx_deinit(&ctx->vpu);
64561890ccaSMoudy Ho 		mdp_vpu_put_locked(mdp);
64661890ccaSMoudy Ho 	}
64761890ccaSMoudy Ho 
64861890ccaSMoudy Ho 	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
64961890ccaSMoudy Ho 	v4l2_fh_del(&ctx->fh);
65061890ccaSMoudy Ho 	v4l2_fh_exit(&ctx->fh);
65161890ccaSMoudy Ho 	ida_free(&mdp->mdp_ida, ctx->id);
65261890ccaSMoudy Ho 	mutex_unlock(&mdp->m2m_lock);
65361890ccaSMoudy Ho 
65461890ccaSMoudy Ho 	dev_dbg(dev, "%s:[%d]", __func__, ctx->id);
65561890ccaSMoudy Ho 	kfree(ctx);
65661890ccaSMoudy Ho 
65761890ccaSMoudy Ho 	return 0;
65861890ccaSMoudy Ho }
65961890ccaSMoudy Ho 
66061890ccaSMoudy Ho static const struct v4l2_file_operations mdp_m2m_fops = {
66161890ccaSMoudy Ho 	.owner		= THIS_MODULE,
66261890ccaSMoudy Ho 	.poll		= v4l2_m2m_fop_poll,
66361890ccaSMoudy Ho 	.unlocked_ioctl	= video_ioctl2,
66461890ccaSMoudy Ho 	.mmap		= v4l2_m2m_fop_mmap,
66561890ccaSMoudy Ho 	.open		= mdp_m2m_open,
66661890ccaSMoudy Ho 	.release	= mdp_m2m_release,
66761890ccaSMoudy Ho };
66861890ccaSMoudy Ho 
66961890ccaSMoudy Ho static const struct v4l2_m2m_ops mdp_m2m_ops = {
67061890ccaSMoudy Ho 	.device_run	= mdp_m2m_device_run,
67161890ccaSMoudy Ho };
67261890ccaSMoudy Ho 
67361890ccaSMoudy Ho int mdp_m2m_device_register(struct mdp_dev *mdp)
67461890ccaSMoudy Ho {
67561890ccaSMoudy Ho 	struct device *dev = &mdp->pdev->dev;
67661890ccaSMoudy Ho 	int ret = 0;
67761890ccaSMoudy Ho 
67861890ccaSMoudy Ho 	mdp->m2m_vdev = video_device_alloc();
67961890ccaSMoudy Ho 	if (!mdp->m2m_vdev) {
68061890ccaSMoudy Ho 		dev_err(dev, "Failed to allocate video device\n");
68161890ccaSMoudy Ho 		ret = -ENOMEM;
68261890ccaSMoudy Ho 		goto err_video_alloc;
68361890ccaSMoudy Ho 	}
68461890ccaSMoudy Ho 	mdp->m2m_vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE |
68561890ccaSMoudy Ho 		V4L2_CAP_STREAMING;
68661890ccaSMoudy Ho 	mdp->m2m_vdev->fops = &mdp_m2m_fops;
68761890ccaSMoudy Ho 	mdp->m2m_vdev->ioctl_ops = &mdp_m2m_ioctl_ops;
68861890ccaSMoudy Ho 	mdp->m2m_vdev->release = mdp_video_device_release;
68961890ccaSMoudy Ho 	mdp->m2m_vdev->lock = &mdp->m2m_lock;
69061890ccaSMoudy Ho 	mdp->m2m_vdev->vfl_dir = VFL_DIR_M2M;
69161890ccaSMoudy Ho 	mdp->m2m_vdev->v4l2_dev = &mdp->v4l2_dev;
69261890ccaSMoudy Ho 	snprintf(mdp->m2m_vdev->name, sizeof(mdp->m2m_vdev->name), "%s:m2m",
69361890ccaSMoudy Ho 		 MDP_MODULE_NAME);
69461890ccaSMoudy Ho 	video_set_drvdata(mdp->m2m_vdev, mdp);
69561890ccaSMoudy Ho 
69661890ccaSMoudy Ho 	mdp->m2m_dev = v4l2_m2m_init(&mdp_m2m_ops);
69761890ccaSMoudy Ho 	if (IS_ERR(mdp->m2m_dev)) {
69861890ccaSMoudy Ho 		dev_err(dev, "Failed to initialize v4l2-m2m device\n");
69961890ccaSMoudy Ho 		ret = PTR_ERR(mdp->m2m_dev);
70061890ccaSMoudy Ho 		goto err_m2m_init;
70161890ccaSMoudy Ho 	}
70261890ccaSMoudy Ho 
70361890ccaSMoudy Ho 	ret = video_register_device(mdp->m2m_vdev, VFL_TYPE_VIDEO, -1);
70461890ccaSMoudy Ho 	if (ret) {
70561890ccaSMoudy Ho 		dev_err(dev, "Failed to register video device\n");
70661890ccaSMoudy Ho 		goto err_video_register;
70761890ccaSMoudy Ho 	}
70861890ccaSMoudy Ho 
70961890ccaSMoudy Ho 	v4l2_info(&mdp->v4l2_dev, "Driver registered as /dev/video%d",
71061890ccaSMoudy Ho 		  mdp->m2m_vdev->num);
71161890ccaSMoudy Ho 	return 0;
71261890ccaSMoudy Ho 
71361890ccaSMoudy Ho err_video_register:
71461890ccaSMoudy Ho 	v4l2_m2m_release(mdp->m2m_dev);
71561890ccaSMoudy Ho err_m2m_init:
71661890ccaSMoudy Ho 	video_device_release(mdp->m2m_vdev);
71761890ccaSMoudy Ho err_video_alloc:
71861890ccaSMoudy Ho 
71961890ccaSMoudy Ho 	return ret;
72061890ccaSMoudy Ho }
72161890ccaSMoudy Ho 
72261890ccaSMoudy Ho void mdp_m2m_device_unregister(struct mdp_dev *mdp)
72361890ccaSMoudy Ho {
72461890ccaSMoudy Ho 	video_unregister_device(mdp->m2m_vdev);
72561890ccaSMoudy Ho }
72661890ccaSMoudy Ho 
72761890ccaSMoudy Ho void mdp_m2m_job_finish(struct mdp_m2m_ctx *ctx)
72861890ccaSMoudy Ho {
72961890ccaSMoudy Ho 	enum vb2_buffer_state vb_state = VB2_BUF_STATE_DONE;
73061890ccaSMoudy Ho 
73161890ccaSMoudy Ho 	mdp_m2m_process_done(ctx, vb_state);
73261890ccaSMoudy Ho }
733