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 
fh_to_ctx(struct v4l2_fh * fh)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 
ctrl_to_ctx(struct v4l2_ctrl * ctrl)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 
ctx_get_frame(struct mdp_m2m_ctx * ctx,enum v4l2_buf_type type)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 
mdp_m2m_ctx_set_state(struct mdp_m2m_ctx * ctx,u32 state)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 
mdp_m2m_ctx_is_state_set(struct mdp_m2m_ctx * ctx,u32 mask)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 
mdp_m2m_process_done(void * priv,int vb_state)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 
mdp_m2m_device_run(void * priv)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 
90*b4e52199SMoudy Ho 	ret = mdp_vpu_process(&ctx->mdp_dev->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 
97*b4e52199SMoudy Ho 	task.config = ctx->mdp_dev->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 
mdp_m2m_start_streaming(struct vb2_queue * q,unsigned int count)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 			dev_err(&ctx->mdp_dev->pdev->dev,
15561890ccaSMoudy Ho 				"VPU init failed %d\n", ret);
15661890ccaSMoudy Ho 			return -EINVAL;
15761890ccaSMoudy Ho 		}
15861890ccaSMoudy Ho 		mdp_m2m_ctx_set_state(ctx, MDP_VPU_INIT);
15961890ccaSMoudy Ho 	}
16061890ccaSMoudy Ho 
16161890ccaSMoudy Ho 	return 0;
16261890ccaSMoudy Ho }
16361890ccaSMoudy Ho 
mdp_m2m_buf_remove(struct mdp_m2m_ctx * ctx,unsigned int type)16461890ccaSMoudy Ho static struct vb2_v4l2_buffer *mdp_m2m_buf_remove(struct mdp_m2m_ctx *ctx,
16561890ccaSMoudy Ho 						  unsigned int type)
16661890ccaSMoudy Ho {
16761890ccaSMoudy Ho 	if (V4L2_TYPE_IS_OUTPUT(type))
16861890ccaSMoudy Ho 		return (struct vb2_v4l2_buffer *)
16961890ccaSMoudy Ho 			v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
17061890ccaSMoudy Ho 	else
17161890ccaSMoudy Ho 		return (struct vb2_v4l2_buffer *)
17261890ccaSMoudy Ho 			v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
17361890ccaSMoudy Ho }
17461890ccaSMoudy Ho 
mdp_m2m_stop_streaming(struct vb2_queue * q)17561890ccaSMoudy Ho static void mdp_m2m_stop_streaming(struct vb2_queue *q)
17661890ccaSMoudy Ho {
17761890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
17861890ccaSMoudy Ho 	struct vb2_v4l2_buffer *vb;
17961890ccaSMoudy Ho 
18061890ccaSMoudy Ho 	vb = mdp_m2m_buf_remove(ctx, q->type);
18161890ccaSMoudy Ho 	while (vb) {
18261890ccaSMoudy Ho 		v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
18361890ccaSMoudy Ho 		vb = mdp_m2m_buf_remove(ctx, q->type);
18461890ccaSMoudy Ho 	}
18561890ccaSMoudy Ho }
18661890ccaSMoudy Ho 
mdp_m2m_queue_setup(struct vb2_queue * q,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])18761890ccaSMoudy Ho static int mdp_m2m_queue_setup(struct vb2_queue *q,
18861890ccaSMoudy Ho 			       unsigned int *num_buffers,
18961890ccaSMoudy Ho 			       unsigned int *num_planes, unsigned int sizes[],
19061890ccaSMoudy Ho 			       struct device *alloc_devs[])
19161890ccaSMoudy Ho {
19261890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
19361890ccaSMoudy Ho 	struct v4l2_pix_format_mplane *pix_mp;
19461890ccaSMoudy Ho 	u32 i;
19561890ccaSMoudy Ho 
19661890ccaSMoudy Ho 	pix_mp = &ctx_get_frame(ctx, q->type)->format.fmt.pix_mp;
19761890ccaSMoudy Ho 
19861890ccaSMoudy Ho 	/* from VIDIOC_CREATE_BUFS */
19961890ccaSMoudy Ho 	if (*num_planes) {
20061890ccaSMoudy Ho 		if (*num_planes != pix_mp->num_planes)
20161890ccaSMoudy Ho 			return -EINVAL;
20261890ccaSMoudy Ho 		for (i = 0; i < pix_mp->num_planes; ++i)
20361890ccaSMoudy Ho 			if (sizes[i] < pix_mp->plane_fmt[i].sizeimage)
20461890ccaSMoudy Ho 				return -EINVAL;
20561890ccaSMoudy Ho 	} else {/* from VIDIOC_REQBUFS */
20661890ccaSMoudy Ho 		*num_planes = pix_mp->num_planes;
20761890ccaSMoudy Ho 		for (i = 0; i < pix_mp->num_planes; ++i)
20861890ccaSMoudy Ho 			sizes[i] = pix_mp->plane_fmt[i].sizeimage;
20961890ccaSMoudy Ho 	}
21061890ccaSMoudy Ho 
21161890ccaSMoudy Ho 	return 0;
21261890ccaSMoudy Ho }
21361890ccaSMoudy Ho 
mdp_m2m_buf_prepare(struct vb2_buffer * vb)21461890ccaSMoudy Ho static int mdp_m2m_buf_prepare(struct vb2_buffer *vb)
21561890ccaSMoudy Ho {
21661890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
21761890ccaSMoudy Ho 	struct v4l2_pix_format_mplane *pix_mp;
21861890ccaSMoudy Ho 	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
21961890ccaSMoudy Ho 	u32 i;
22061890ccaSMoudy Ho 
22161890ccaSMoudy Ho 	v4l2_buf->field = V4L2_FIELD_NONE;
22261890ccaSMoudy Ho 
22361890ccaSMoudy Ho 	if (V4L2_TYPE_IS_CAPTURE(vb->type)) {
22461890ccaSMoudy Ho 		pix_mp = &ctx_get_frame(ctx, vb->type)->format.fmt.pix_mp;
22561890ccaSMoudy Ho 		for (i = 0; i < pix_mp->num_planes; ++i) {
22661890ccaSMoudy Ho 			vb2_set_plane_payload(vb, i,
22761890ccaSMoudy Ho 					      pix_mp->plane_fmt[i].sizeimage);
22861890ccaSMoudy Ho 		}
22961890ccaSMoudy Ho 	}
23061890ccaSMoudy Ho 	return 0;
23161890ccaSMoudy Ho }
23261890ccaSMoudy Ho 
mdp_m2m_buf_out_validate(struct vb2_buffer * vb)23361890ccaSMoudy Ho static int mdp_m2m_buf_out_validate(struct vb2_buffer *vb)
23461890ccaSMoudy Ho {
23561890ccaSMoudy Ho 	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
23661890ccaSMoudy Ho 
23761890ccaSMoudy Ho 	v4l2_buf->field = V4L2_FIELD_NONE;
23861890ccaSMoudy Ho 
23961890ccaSMoudy Ho 	return 0;
24061890ccaSMoudy Ho }
24161890ccaSMoudy Ho 
mdp_m2m_buf_queue(struct vb2_buffer * vb)24261890ccaSMoudy Ho static void mdp_m2m_buf_queue(struct vb2_buffer *vb)
24361890ccaSMoudy Ho {
24461890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
24561890ccaSMoudy Ho 	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
24661890ccaSMoudy Ho 
24761890ccaSMoudy Ho 	v4l2_buf->field = V4L2_FIELD_NONE;
24861890ccaSMoudy Ho 
24961890ccaSMoudy Ho 	v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb));
25061890ccaSMoudy Ho }
25161890ccaSMoudy Ho 
25261890ccaSMoudy Ho static const struct vb2_ops mdp_m2m_qops = {
25361890ccaSMoudy Ho 	.queue_setup	= mdp_m2m_queue_setup,
25461890ccaSMoudy Ho 	.wait_prepare	= vb2_ops_wait_prepare,
25561890ccaSMoudy Ho 	.wait_finish	= vb2_ops_wait_finish,
25661890ccaSMoudy Ho 	.buf_prepare	= mdp_m2m_buf_prepare,
25761890ccaSMoudy Ho 	.start_streaming = mdp_m2m_start_streaming,
25861890ccaSMoudy Ho 	.stop_streaming	= mdp_m2m_stop_streaming,
25961890ccaSMoudy Ho 	.buf_queue	= mdp_m2m_buf_queue,
26061890ccaSMoudy Ho 	.buf_out_validate = mdp_m2m_buf_out_validate,
26161890ccaSMoudy Ho };
26261890ccaSMoudy Ho 
mdp_m2m_querycap(struct file * file,void * fh,struct v4l2_capability * cap)26361890ccaSMoudy Ho static int mdp_m2m_querycap(struct file *file, void *fh,
26461890ccaSMoudy Ho 			    struct v4l2_capability *cap)
26561890ccaSMoudy Ho {
26661890ccaSMoudy Ho 	strscpy(cap->driver, MDP_MODULE_NAME, sizeof(cap->driver));
26761890ccaSMoudy Ho 	strscpy(cap->card, MDP_DEVICE_NAME, sizeof(cap->card));
26861890ccaSMoudy Ho 
26961890ccaSMoudy Ho 	return 0;
27061890ccaSMoudy Ho }
27161890ccaSMoudy Ho 
mdp_m2m_enum_fmt_mplane(struct file * file,void * fh,struct v4l2_fmtdesc * f)27261890ccaSMoudy Ho static int mdp_m2m_enum_fmt_mplane(struct file *file, void *fh,
27361890ccaSMoudy Ho 				   struct v4l2_fmtdesc *f)
27461890ccaSMoudy Ho {
2756b8910e3SMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
2766b8910e3SMoudy Ho 
2776b8910e3SMoudy Ho 	return mdp_enum_fmt_mplane(ctx->mdp_dev, f);
27861890ccaSMoudy Ho }
27961890ccaSMoudy Ho 
mdp_m2m_g_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)28061890ccaSMoudy Ho static int mdp_m2m_g_fmt_mplane(struct file *file, void *fh,
28161890ccaSMoudy Ho 				struct v4l2_format *f)
28261890ccaSMoudy Ho {
28361890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
28461890ccaSMoudy Ho 	struct mdp_frame *frame;
28561890ccaSMoudy Ho 	struct v4l2_pix_format_mplane *pix_mp;
28661890ccaSMoudy Ho 
28761890ccaSMoudy Ho 	frame = ctx_get_frame(ctx, f->type);
28861890ccaSMoudy Ho 	*f = frame->format;
28961890ccaSMoudy Ho 	pix_mp = &f->fmt.pix_mp;
29061890ccaSMoudy Ho 	pix_mp->colorspace = ctx->curr_param.colorspace;
29161890ccaSMoudy Ho 	pix_mp->xfer_func = ctx->curr_param.xfer_func;
29261890ccaSMoudy Ho 	pix_mp->ycbcr_enc = ctx->curr_param.ycbcr_enc;
29361890ccaSMoudy Ho 	pix_mp->quantization = ctx->curr_param.quant;
29461890ccaSMoudy Ho 
29561890ccaSMoudy Ho 	return 0;
29661890ccaSMoudy Ho }
29761890ccaSMoudy Ho 
mdp_m2m_s_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)29861890ccaSMoudy Ho static int mdp_m2m_s_fmt_mplane(struct file *file, void *fh,
29961890ccaSMoudy Ho 				struct v4l2_format *f)
30061890ccaSMoudy Ho {
30161890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
30261890ccaSMoudy Ho 	struct mdp_frame *frame = ctx_get_frame(ctx, f->type);
30361890ccaSMoudy Ho 	struct mdp_frame *capture;
30461890ccaSMoudy Ho 	const struct mdp_format *fmt;
30561890ccaSMoudy Ho 	struct vb2_queue *vq;
30661890ccaSMoudy Ho 
3076b8910e3SMoudy Ho 	fmt = mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id);
30861890ccaSMoudy Ho 	if (!fmt)
30961890ccaSMoudy Ho 		return -EINVAL;
31061890ccaSMoudy Ho 
31161890ccaSMoudy Ho 	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
31261890ccaSMoudy Ho 	if (vb2_is_busy(vq))
31361890ccaSMoudy Ho 		return -EBUSY;
31461890ccaSMoudy Ho 
31561890ccaSMoudy Ho 	frame->format = *f;
31661890ccaSMoudy Ho 	frame->mdp_fmt = fmt;
31761890ccaSMoudy Ho 	frame->ycbcr_prof = mdp_map_ycbcr_prof_mplane(f, fmt->mdp_color);
31861890ccaSMoudy Ho 	frame->usage = V4L2_TYPE_IS_OUTPUT(f->type) ?
31961890ccaSMoudy Ho 		MDP_BUFFER_USAGE_HW_READ : MDP_BUFFER_USAGE_MDP;
32061890ccaSMoudy Ho 
32161890ccaSMoudy Ho 	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
32261890ccaSMoudy Ho 	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
32361890ccaSMoudy Ho 		capture->crop.c.left = 0;
32461890ccaSMoudy Ho 		capture->crop.c.top = 0;
32561890ccaSMoudy Ho 		capture->crop.c.width = f->fmt.pix_mp.width;
32661890ccaSMoudy Ho 		capture->crop.c.height = f->fmt.pix_mp.height;
32761890ccaSMoudy Ho 		ctx->curr_param.colorspace = f->fmt.pix_mp.colorspace;
32861890ccaSMoudy Ho 		ctx->curr_param.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
32961890ccaSMoudy Ho 		ctx->curr_param.quant = f->fmt.pix_mp.quantization;
33061890ccaSMoudy Ho 		ctx->curr_param.xfer_func = f->fmt.pix_mp.xfer_func;
33161890ccaSMoudy Ho 	} else {
33261890ccaSMoudy Ho 		capture->compose.left = 0;
33361890ccaSMoudy Ho 		capture->compose.top = 0;
33461890ccaSMoudy Ho 		capture->compose.width = f->fmt.pix_mp.width;
33561890ccaSMoudy Ho 		capture->compose.height = f->fmt.pix_mp.height;
33661890ccaSMoudy Ho 	}
33761890ccaSMoudy Ho 
33861890ccaSMoudy Ho 	return 0;
33961890ccaSMoudy Ho }
34061890ccaSMoudy Ho 
mdp_m2m_try_fmt_mplane(struct file * file,void * fh,struct v4l2_format * f)34161890ccaSMoudy Ho static int mdp_m2m_try_fmt_mplane(struct file *file, void *fh,
34261890ccaSMoudy Ho 				  struct v4l2_format *f)
34361890ccaSMoudy Ho {
34461890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
34561890ccaSMoudy Ho 
3466b8910e3SMoudy Ho 	if (!mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id))
34761890ccaSMoudy Ho 		return -EINVAL;
34861890ccaSMoudy Ho 
34961890ccaSMoudy Ho 	return 0;
35061890ccaSMoudy Ho }
35161890ccaSMoudy Ho 
mdp_m2m_g_selection(struct file * file,void * fh,struct v4l2_selection * s)35261890ccaSMoudy Ho static int mdp_m2m_g_selection(struct file *file, void *fh,
35361890ccaSMoudy Ho 			       struct v4l2_selection *s)
35461890ccaSMoudy Ho {
35561890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
35661890ccaSMoudy Ho 	struct mdp_frame *frame;
35761890ccaSMoudy Ho 	bool valid = false;
35861890ccaSMoudy Ho 
35961890ccaSMoudy Ho 	if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
36061890ccaSMoudy Ho 		valid = mdp_target_is_crop(s->target);
36161890ccaSMoudy Ho 	else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
36261890ccaSMoudy Ho 		valid = mdp_target_is_compose(s->target);
36361890ccaSMoudy Ho 
36461890ccaSMoudy Ho 	if (!valid)
36561890ccaSMoudy Ho 		return -EINVAL;
36661890ccaSMoudy Ho 
36761890ccaSMoudy Ho 	switch (s->target) {
36861890ccaSMoudy Ho 	case V4L2_SEL_TGT_CROP:
36961890ccaSMoudy Ho 		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
37061890ccaSMoudy Ho 			return -EINVAL;
37161890ccaSMoudy Ho 		frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
37261890ccaSMoudy Ho 		s->r = frame->crop.c;
37361890ccaSMoudy Ho 		return 0;
37461890ccaSMoudy Ho 	case V4L2_SEL_TGT_COMPOSE:
37561890ccaSMoudy Ho 		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
37661890ccaSMoudy Ho 			return -EINVAL;
37761890ccaSMoudy Ho 		frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
37861890ccaSMoudy Ho 		s->r = frame->compose;
37961890ccaSMoudy Ho 		return 0;
38061890ccaSMoudy Ho 	case V4L2_SEL_TGT_CROP_DEFAULT:
38161890ccaSMoudy Ho 	case V4L2_SEL_TGT_CROP_BOUNDS:
38261890ccaSMoudy Ho 		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
38361890ccaSMoudy Ho 			return -EINVAL;
38461890ccaSMoudy Ho 		frame = ctx_get_frame(ctx, s->type);
38561890ccaSMoudy Ho 		s->r.left = 0;
38661890ccaSMoudy Ho 		s->r.top = 0;
38761890ccaSMoudy Ho 		s->r.width = frame->format.fmt.pix_mp.width;
38861890ccaSMoudy Ho 		s->r.height = frame->format.fmt.pix_mp.height;
38961890ccaSMoudy Ho 		return 0;
39061890ccaSMoudy Ho 	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
39161890ccaSMoudy Ho 	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
39261890ccaSMoudy Ho 		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
39361890ccaSMoudy Ho 			return -EINVAL;
39461890ccaSMoudy Ho 		frame = ctx_get_frame(ctx, s->type);
39561890ccaSMoudy Ho 		s->r.left = 0;
39661890ccaSMoudy Ho 		s->r.top = 0;
39761890ccaSMoudy Ho 		s->r.width = frame->format.fmt.pix_mp.width;
39861890ccaSMoudy Ho 		s->r.height = frame->format.fmt.pix_mp.height;
39961890ccaSMoudy Ho 		return 0;
40061890ccaSMoudy Ho 	}
40161890ccaSMoudy Ho 	return -EINVAL;
40261890ccaSMoudy Ho }
40361890ccaSMoudy Ho 
mdp_m2m_s_selection(struct file * file,void * fh,struct v4l2_selection * s)40461890ccaSMoudy Ho static int mdp_m2m_s_selection(struct file *file, void *fh,
40561890ccaSMoudy Ho 			       struct v4l2_selection *s)
40661890ccaSMoudy Ho {
40761890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
40861890ccaSMoudy Ho 	struct mdp_frame *frame = ctx_get_frame(ctx, s->type);
40961890ccaSMoudy Ho 	struct mdp_frame *capture;
41061890ccaSMoudy Ho 	struct v4l2_rect r;
41161890ccaSMoudy Ho 	struct device *dev = &ctx->mdp_dev->pdev->dev;
41261890ccaSMoudy Ho 	bool valid = false;
41361890ccaSMoudy Ho 	int ret;
41461890ccaSMoudy Ho 
41561890ccaSMoudy Ho 	if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
41661890ccaSMoudy Ho 		valid = (s->target == V4L2_SEL_TGT_CROP);
41761890ccaSMoudy Ho 	else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
41861890ccaSMoudy Ho 		valid = (s->target == V4L2_SEL_TGT_COMPOSE);
41961890ccaSMoudy Ho 
42061890ccaSMoudy Ho 	if (!valid) {
42161890ccaSMoudy Ho 		dev_dbg(dev, "[%s:%d] invalid type:%u target:%u", __func__,
42261890ccaSMoudy Ho 			ctx->id, s->type, s->target);
42361890ccaSMoudy Ho 		return -EINVAL;
42461890ccaSMoudy Ho 	}
42561890ccaSMoudy Ho 
42661890ccaSMoudy Ho 	ret = mdp_try_crop(ctx, &r, s, frame);
42761890ccaSMoudy Ho 	if (ret)
42861890ccaSMoudy Ho 		return ret;
42961890ccaSMoudy Ho 	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
43061890ccaSMoudy Ho 
43161890ccaSMoudy Ho 	if (mdp_target_is_crop(s->target))
43261890ccaSMoudy Ho 		capture->crop.c = r;
43361890ccaSMoudy Ho 	else
43461890ccaSMoudy Ho 		capture->compose = r;
43561890ccaSMoudy Ho 
43661890ccaSMoudy Ho 	s->r = r;
43761890ccaSMoudy Ho 
43861890ccaSMoudy Ho 	return 0;
43961890ccaSMoudy Ho }
44061890ccaSMoudy Ho 
44161890ccaSMoudy Ho static const struct v4l2_ioctl_ops mdp_m2m_ioctl_ops = {
44261890ccaSMoudy Ho 	.vidioc_querycap		= mdp_m2m_querycap,
44361890ccaSMoudy Ho 	.vidioc_enum_fmt_vid_cap	= mdp_m2m_enum_fmt_mplane,
44461890ccaSMoudy Ho 	.vidioc_enum_fmt_vid_out	= mdp_m2m_enum_fmt_mplane,
44561890ccaSMoudy Ho 	.vidioc_g_fmt_vid_cap_mplane	= mdp_m2m_g_fmt_mplane,
44661890ccaSMoudy Ho 	.vidioc_g_fmt_vid_out_mplane	= mdp_m2m_g_fmt_mplane,
44761890ccaSMoudy Ho 	.vidioc_s_fmt_vid_cap_mplane	= mdp_m2m_s_fmt_mplane,
44861890ccaSMoudy Ho 	.vidioc_s_fmt_vid_out_mplane	= mdp_m2m_s_fmt_mplane,
44961890ccaSMoudy Ho 	.vidioc_try_fmt_vid_cap_mplane	= mdp_m2m_try_fmt_mplane,
45061890ccaSMoudy Ho 	.vidioc_try_fmt_vid_out_mplane	= mdp_m2m_try_fmt_mplane,
45161890ccaSMoudy Ho 	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
45261890ccaSMoudy Ho 	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
45361890ccaSMoudy Ho 	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
45461890ccaSMoudy Ho 	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
45561890ccaSMoudy Ho 	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
45661890ccaSMoudy Ho 	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
45761890ccaSMoudy Ho 	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
45861890ccaSMoudy Ho 	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
45961890ccaSMoudy Ho 	.vidioc_g_selection		= mdp_m2m_g_selection,
46061890ccaSMoudy Ho 	.vidioc_s_selection		= mdp_m2m_s_selection,
46161890ccaSMoudy Ho 	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
46261890ccaSMoudy Ho 	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
46361890ccaSMoudy Ho };
46461890ccaSMoudy Ho 
mdp_m2m_queue_init(void * priv,struct vb2_queue * src_vq,struct vb2_queue * dst_vq)46561890ccaSMoudy Ho static int mdp_m2m_queue_init(void *priv,
46661890ccaSMoudy Ho 			      struct vb2_queue *src_vq,
46761890ccaSMoudy Ho 			      struct vb2_queue *dst_vq)
46861890ccaSMoudy Ho {
46961890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = priv;
47061890ccaSMoudy Ho 	int ret;
47161890ccaSMoudy Ho 
47261890ccaSMoudy Ho 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
47361890ccaSMoudy Ho 	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
47461890ccaSMoudy Ho 	src_vq->ops = &mdp_m2m_qops;
47561890ccaSMoudy Ho 	src_vq->mem_ops = &vb2_dma_contig_memops;
47661890ccaSMoudy Ho 	src_vq->drv_priv = ctx;
47761890ccaSMoudy Ho 	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
47861890ccaSMoudy Ho 	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
47961890ccaSMoudy Ho 	src_vq->dev = &ctx->mdp_dev->pdev->dev;
48061890ccaSMoudy Ho 	src_vq->lock = &ctx->ctx_lock;
48161890ccaSMoudy Ho 
48261890ccaSMoudy Ho 	ret = vb2_queue_init(src_vq);
48361890ccaSMoudy Ho 	if (ret)
48461890ccaSMoudy Ho 		return ret;
48561890ccaSMoudy Ho 
48661890ccaSMoudy Ho 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
48761890ccaSMoudy Ho 	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
48861890ccaSMoudy Ho 	dst_vq->ops = &mdp_m2m_qops;
48961890ccaSMoudy Ho 	dst_vq->mem_ops = &vb2_dma_contig_memops;
49061890ccaSMoudy Ho 	dst_vq->drv_priv = ctx;
49161890ccaSMoudy Ho 	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
49261890ccaSMoudy Ho 	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
49361890ccaSMoudy Ho 	dst_vq->dev = &ctx->mdp_dev->pdev->dev;
49461890ccaSMoudy Ho 	dst_vq->lock = &ctx->ctx_lock;
49561890ccaSMoudy Ho 
49661890ccaSMoudy Ho 	return vb2_queue_init(dst_vq);
49761890ccaSMoudy Ho }
49861890ccaSMoudy Ho 
mdp_m2m_s_ctrl(struct v4l2_ctrl * ctrl)49961890ccaSMoudy Ho static int mdp_m2m_s_ctrl(struct v4l2_ctrl *ctrl)
50061890ccaSMoudy Ho {
50161890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = ctrl_to_ctx(ctrl);
50261890ccaSMoudy Ho 	struct mdp_frame *capture;
50361890ccaSMoudy Ho 
50461890ccaSMoudy Ho 	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
50561890ccaSMoudy Ho 	switch (ctrl->id) {
50661890ccaSMoudy Ho 	case V4L2_CID_HFLIP:
50761890ccaSMoudy Ho 		capture->hflip = ctrl->val;
50861890ccaSMoudy Ho 		break;
50961890ccaSMoudy Ho 	case V4L2_CID_VFLIP:
51061890ccaSMoudy Ho 		capture->vflip = ctrl->val;
51161890ccaSMoudy Ho 		break;
51261890ccaSMoudy Ho 	case V4L2_CID_ROTATE:
51361890ccaSMoudy Ho 		capture->rotation = ctrl->val;
51461890ccaSMoudy Ho 		break;
51561890ccaSMoudy Ho 	}
51661890ccaSMoudy Ho 
51761890ccaSMoudy Ho 	return 0;
51861890ccaSMoudy Ho }
51961890ccaSMoudy Ho 
52061890ccaSMoudy Ho static const struct v4l2_ctrl_ops mdp_m2m_ctrl_ops = {
52161890ccaSMoudy Ho 	.s_ctrl	= mdp_m2m_s_ctrl,
52261890ccaSMoudy Ho };
52361890ccaSMoudy Ho 
mdp_m2m_ctrls_create(struct mdp_m2m_ctx * ctx)52461890ccaSMoudy Ho static int mdp_m2m_ctrls_create(struct mdp_m2m_ctx *ctx)
52561890ccaSMoudy Ho {
52661890ccaSMoudy Ho 	v4l2_ctrl_handler_init(&ctx->ctrl_handler, MDP_MAX_CTRLS);
52761890ccaSMoudy Ho 	ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
52861890ccaSMoudy Ho 					     &mdp_m2m_ctrl_ops, V4L2_CID_HFLIP,
52961890ccaSMoudy Ho 					     0, 1, 1, 0);
53061890ccaSMoudy Ho 	ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
53161890ccaSMoudy Ho 					     &mdp_m2m_ctrl_ops, V4L2_CID_VFLIP,
53261890ccaSMoudy Ho 					     0, 1, 1, 0);
53361890ccaSMoudy Ho 	ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
53461890ccaSMoudy Ho 					      &mdp_m2m_ctrl_ops,
53561890ccaSMoudy Ho 					      V4L2_CID_ROTATE, 0, 270, 90, 0);
53661890ccaSMoudy Ho 
53761890ccaSMoudy Ho 	if (ctx->ctrl_handler.error) {
53861890ccaSMoudy Ho 		int err = ctx->ctrl_handler.error;
53961890ccaSMoudy Ho 
54061890ccaSMoudy Ho 		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
54161890ccaSMoudy Ho 		dev_err(&ctx->mdp_dev->pdev->dev,
54261890ccaSMoudy Ho 			"Failed to register controls\n");
54361890ccaSMoudy Ho 		return err;
54461890ccaSMoudy Ho 	}
54561890ccaSMoudy Ho 	return 0;
54661890ccaSMoudy Ho }
54761890ccaSMoudy Ho 
mdp_m2m_open(struct file * file)54861890ccaSMoudy Ho static int mdp_m2m_open(struct file *file)
54961890ccaSMoudy Ho {
55061890ccaSMoudy Ho 	struct video_device *vdev = video_devdata(file);
55161890ccaSMoudy Ho 	struct mdp_dev *mdp = video_get_drvdata(vdev);
55261890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx;
55361890ccaSMoudy Ho 	struct device *dev = &mdp->pdev->dev;
55461890ccaSMoudy Ho 	int ret;
55561890ccaSMoudy Ho 	struct v4l2_format default_format = {};
556b35bf333SMoudy Ho 	const struct mdp_limit *limit = mdp->mdp_data->def_limit;
55761890ccaSMoudy Ho 
55861890ccaSMoudy Ho 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
55961890ccaSMoudy Ho 	if (!ctx)
56061890ccaSMoudy Ho 		return -ENOMEM;
56161890ccaSMoudy Ho 
56261890ccaSMoudy Ho 	if (mutex_lock_interruptible(&mdp->m2m_lock)) {
56361890ccaSMoudy Ho 		ret = -ERESTARTSYS;
56461890ccaSMoudy Ho 		goto err_free_ctx;
56561890ccaSMoudy Ho 	}
56661890ccaSMoudy Ho 
567d00f5922SJiasheng Jiang 	ret = ida_alloc(&mdp->mdp_ida, GFP_KERNEL);
568d00f5922SJiasheng Jiang 	if (ret < 0)
569d00f5922SJiasheng Jiang 		goto err_unlock_mutex;
570d00f5922SJiasheng Jiang 	ctx->id = ret;
571d00f5922SJiasheng Jiang 
57261890ccaSMoudy Ho 	ctx->mdp_dev = mdp;
57361890ccaSMoudy Ho 
57461890ccaSMoudy Ho 	v4l2_fh_init(&ctx->fh, vdev);
57561890ccaSMoudy Ho 	file->private_data = &ctx->fh;
57661890ccaSMoudy Ho 	ret = mdp_m2m_ctrls_create(ctx);
57761890ccaSMoudy Ho 	if (ret)
57861890ccaSMoudy Ho 		goto err_exit_fh;
57961890ccaSMoudy Ho 
58061890ccaSMoudy Ho 	/* Use separate control handler per file handle */
58161890ccaSMoudy Ho 	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
58261890ccaSMoudy Ho 	v4l2_fh_add(&ctx->fh);
58361890ccaSMoudy Ho 
58461890ccaSMoudy Ho 	mutex_init(&ctx->ctx_lock);
58561890ccaSMoudy Ho 	ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx, mdp_m2m_queue_init);
58661890ccaSMoudy Ho 	if (IS_ERR(ctx->m2m_ctx)) {
58761890ccaSMoudy Ho 		dev_err(dev, "Failed to initialize m2m context\n");
58861890ccaSMoudy Ho 		ret = PTR_ERR(ctx->m2m_ctx);
58961890ccaSMoudy Ho 		goto err_release_handler;
59061890ccaSMoudy Ho 	}
59161890ccaSMoudy Ho 	ctx->fh.m2m_ctx = ctx->m2m_ctx;
59261890ccaSMoudy Ho 
59361890ccaSMoudy Ho 	ctx->curr_param.ctx = ctx;
5946b8910e3SMoudy Ho 	ret = mdp_frameparam_init(mdp, &ctx->curr_param);
59561890ccaSMoudy Ho 	if (ret) {
59661890ccaSMoudy Ho 		dev_err(dev, "Failed to initialize mdp parameter\n");
59761890ccaSMoudy Ho 		goto err_release_m2m_ctx;
59861890ccaSMoudy Ho 	}
59961890ccaSMoudy Ho 
60061890ccaSMoudy Ho 	mutex_unlock(&mdp->m2m_lock);
60161890ccaSMoudy Ho 
60261890ccaSMoudy Ho 	/* Default format */
60361890ccaSMoudy Ho 	default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
604b35bf333SMoudy Ho 	default_format.fmt.pix_mp.width = limit->out_limit.wmin;
605b35bf333SMoudy Ho 	default_format.fmt.pix_mp.height = limit->out_limit.hmin;
60661890ccaSMoudy Ho 	default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
60761890ccaSMoudy Ho 	mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
60861890ccaSMoudy Ho 	default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
60961890ccaSMoudy Ho 	mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
61061890ccaSMoudy Ho 
61161890ccaSMoudy Ho 	dev_dbg(dev, "%s:[%d]", __func__, ctx->id);
61261890ccaSMoudy Ho 
61361890ccaSMoudy Ho 	return 0;
61461890ccaSMoudy Ho 
61561890ccaSMoudy Ho err_release_m2m_ctx:
61661890ccaSMoudy Ho 	v4l2_m2m_ctx_release(ctx->m2m_ctx);
61761890ccaSMoudy Ho err_release_handler:
61861890ccaSMoudy Ho 	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
61961890ccaSMoudy Ho 	v4l2_fh_del(&ctx->fh);
62061890ccaSMoudy Ho err_exit_fh:
62161890ccaSMoudy Ho 	v4l2_fh_exit(&ctx->fh);
622d00f5922SJiasheng Jiang 	ida_free(&mdp->mdp_ida, ctx->id);
623d00f5922SJiasheng Jiang err_unlock_mutex:
62461890ccaSMoudy Ho 	mutex_unlock(&mdp->m2m_lock);
62561890ccaSMoudy Ho err_free_ctx:
62661890ccaSMoudy Ho 	kfree(ctx);
62761890ccaSMoudy Ho 
62861890ccaSMoudy Ho 	return ret;
62961890ccaSMoudy Ho }
63061890ccaSMoudy Ho 
mdp_m2m_release(struct file * file)63161890ccaSMoudy Ho static int mdp_m2m_release(struct file *file)
63261890ccaSMoudy Ho {
63361890ccaSMoudy Ho 	struct mdp_m2m_ctx *ctx = fh_to_ctx(file->private_data);
63461890ccaSMoudy Ho 	struct mdp_dev *mdp = video_drvdata(file);
63561890ccaSMoudy Ho 	struct device *dev = &mdp->pdev->dev;
63661890ccaSMoudy Ho 
63761890ccaSMoudy Ho 	mutex_lock(&mdp->m2m_lock);
63861890ccaSMoudy Ho 	v4l2_m2m_ctx_release(ctx->m2m_ctx);
639*b4e52199SMoudy Ho 	if (mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT))
64061890ccaSMoudy Ho 		mdp_vpu_put_locked(mdp);
64161890ccaSMoudy Ho 
64261890ccaSMoudy Ho 	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
64361890ccaSMoudy Ho 	v4l2_fh_del(&ctx->fh);
64461890ccaSMoudy Ho 	v4l2_fh_exit(&ctx->fh);
64561890ccaSMoudy Ho 	ida_free(&mdp->mdp_ida, ctx->id);
64661890ccaSMoudy Ho 	mutex_unlock(&mdp->m2m_lock);
64761890ccaSMoudy Ho 
64861890ccaSMoudy Ho 	dev_dbg(dev, "%s:[%d]", __func__, ctx->id);
64961890ccaSMoudy Ho 	kfree(ctx);
65061890ccaSMoudy Ho 
65161890ccaSMoudy Ho 	return 0;
65261890ccaSMoudy Ho }
65361890ccaSMoudy Ho 
65461890ccaSMoudy Ho static const struct v4l2_file_operations mdp_m2m_fops = {
65561890ccaSMoudy Ho 	.owner		= THIS_MODULE,
65661890ccaSMoudy Ho 	.poll		= v4l2_m2m_fop_poll,
65761890ccaSMoudy Ho 	.unlocked_ioctl	= video_ioctl2,
65861890ccaSMoudy Ho 	.mmap		= v4l2_m2m_fop_mmap,
65961890ccaSMoudy Ho 	.open		= mdp_m2m_open,
66061890ccaSMoudy Ho 	.release	= mdp_m2m_release,
66161890ccaSMoudy Ho };
66261890ccaSMoudy Ho 
66361890ccaSMoudy Ho static const struct v4l2_m2m_ops mdp_m2m_ops = {
66461890ccaSMoudy Ho 	.device_run	= mdp_m2m_device_run,
66561890ccaSMoudy Ho };
66661890ccaSMoudy Ho 
mdp_m2m_device_register(struct mdp_dev * mdp)66761890ccaSMoudy Ho int mdp_m2m_device_register(struct mdp_dev *mdp)
66861890ccaSMoudy Ho {
66961890ccaSMoudy Ho 	struct device *dev = &mdp->pdev->dev;
67061890ccaSMoudy Ho 	int ret = 0;
67161890ccaSMoudy Ho 
67261890ccaSMoudy Ho 	mdp->m2m_vdev = video_device_alloc();
67361890ccaSMoudy Ho 	if (!mdp->m2m_vdev) {
67461890ccaSMoudy Ho 		dev_err(dev, "Failed to allocate video device\n");
67561890ccaSMoudy Ho 		ret = -ENOMEM;
67661890ccaSMoudy Ho 		goto err_video_alloc;
67761890ccaSMoudy Ho 	}
67861890ccaSMoudy Ho 	mdp->m2m_vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE |
67961890ccaSMoudy Ho 		V4L2_CAP_STREAMING;
68061890ccaSMoudy Ho 	mdp->m2m_vdev->fops = &mdp_m2m_fops;
68161890ccaSMoudy Ho 	mdp->m2m_vdev->ioctl_ops = &mdp_m2m_ioctl_ops;
68261890ccaSMoudy Ho 	mdp->m2m_vdev->release = mdp_video_device_release;
68361890ccaSMoudy Ho 	mdp->m2m_vdev->lock = &mdp->m2m_lock;
68461890ccaSMoudy Ho 	mdp->m2m_vdev->vfl_dir = VFL_DIR_M2M;
68561890ccaSMoudy Ho 	mdp->m2m_vdev->v4l2_dev = &mdp->v4l2_dev;
68661890ccaSMoudy Ho 	snprintf(mdp->m2m_vdev->name, sizeof(mdp->m2m_vdev->name), "%s:m2m",
68761890ccaSMoudy Ho 		 MDP_MODULE_NAME);
68861890ccaSMoudy Ho 	video_set_drvdata(mdp->m2m_vdev, mdp);
68961890ccaSMoudy Ho 
69061890ccaSMoudy Ho 	mdp->m2m_dev = v4l2_m2m_init(&mdp_m2m_ops);
69161890ccaSMoudy Ho 	if (IS_ERR(mdp->m2m_dev)) {
69261890ccaSMoudy Ho 		dev_err(dev, "Failed to initialize v4l2-m2m device\n");
69361890ccaSMoudy Ho 		ret = PTR_ERR(mdp->m2m_dev);
69461890ccaSMoudy Ho 		goto err_m2m_init;
69561890ccaSMoudy Ho 	}
69661890ccaSMoudy Ho 
69761890ccaSMoudy Ho 	ret = video_register_device(mdp->m2m_vdev, VFL_TYPE_VIDEO, -1);
69861890ccaSMoudy Ho 	if (ret) {
69961890ccaSMoudy Ho 		dev_err(dev, "Failed to register video device\n");
70061890ccaSMoudy Ho 		goto err_video_register;
70161890ccaSMoudy Ho 	}
70261890ccaSMoudy Ho 
70361890ccaSMoudy Ho 	v4l2_info(&mdp->v4l2_dev, "Driver registered as /dev/video%d",
70461890ccaSMoudy Ho 		  mdp->m2m_vdev->num);
70561890ccaSMoudy Ho 	return 0;
70661890ccaSMoudy Ho 
70761890ccaSMoudy Ho err_video_register:
70861890ccaSMoudy Ho 	v4l2_m2m_release(mdp->m2m_dev);
70961890ccaSMoudy Ho err_m2m_init:
71061890ccaSMoudy Ho 	video_device_release(mdp->m2m_vdev);
71161890ccaSMoudy Ho err_video_alloc:
71261890ccaSMoudy Ho 
71361890ccaSMoudy Ho 	return ret;
71461890ccaSMoudy Ho }
71561890ccaSMoudy Ho 
mdp_m2m_device_unregister(struct mdp_dev * mdp)71661890ccaSMoudy Ho void mdp_m2m_device_unregister(struct mdp_dev *mdp)
71761890ccaSMoudy Ho {
71861890ccaSMoudy Ho 	video_unregister_device(mdp->m2m_vdev);
71961890ccaSMoudy Ho }
72061890ccaSMoudy Ho 
mdp_m2m_job_finish(struct mdp_m2m_ctx * ctx)72161890ccaSMoudy Ho void mdp_m2m_job_finish(struct mdp_m2m_ctx *ctx)
72261890ccaSMoudy Ho {
72361890ccaSMoudy Ho 	enum vb2_buffer_state vb_state = VB2_BUF_STATE_DONE;
72461890ccaSMoudy Ho 
72561890ccaSMoudy Ho 	mdp_m2m_process_done(ctx, vb_state);
72661890ccaSMoudy Ho }
727