162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2022 MediaTek Inc.
462306a36Sopenharmony_ci * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/platform_device.h>
862306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
962306a36Sopenharmony_ci#include <media/v4l2-event.h>
1062306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
1162306a36Sopenharmony_ci#include "mtk-mdp3-m2m.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic inline struct mdp_m2m_ctx *fh_to_ctx(struct v4l2_fh *fh)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	return container_of(fh, struct mdp_m2m_ctx, fh);
1662306a36Sopenharmony_ci}
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic inline struct mdp_m2m_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	return container_of(ctrl->handler, struct mdp_m2m_ctx, ctrl_handler);
2162306a36Sopenharmony_ci}
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic inline struct mdp_frame *ctx_get_frame(struct mdp_m2m_ctx *ctx,
2462306a36Sopenharmony_ci					      enum v4l2_buf_type type)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(type))
2762306a36Sopenharmony_ci		return &ctx->curr_param.output;
2862306a36Sopenharmony_ci	else
2962306a36Sopenharmony_ci		return &ctx->curr_param.captures[0];
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic inline void mdp_m2m_ctx_set_state(struct mdp_m2m_ctx *ctx, u32 state)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	atomic_or(state, &ctx->curr_param.state);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic inline bool mdp_m2m_ctx_is_state_set(struct mdp_m2m_ctx *ctx, u32 mask)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	return ((atomic_read(&ctx->curr_param.state) & mask) == mask);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void mdp_m2m_process_done(void *priv, int vb_state)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = priv;
4562306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	src_vbuf = (struct vb2_v4l2_buffer *)
4862306a36Sopenharmony_ci			v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
4962306a36Sopenharmony_ci	dst_vbuf = (struct vb2_v4l2_buffer *)
5062306a36Sopenharmony_ci			v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
5162306a36Sopenharmony_ci	ctx->curr_param.frame_no = ctx->frame_count[MDP_M2M_SRC];
5262306a36Sopenharmony_ci	src_vbuf->sequence = ctx->frame_count[MDP_M2M_SRC]++;
5362306a36Sopenharmony_ci	dst_vbuf->sequence = ctx->frame_count[MDP_M2M_DST]++;
5462306a36Sopenharmony_ci	v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, true);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	v4l2_m2m_buf_done(src_vbuf, vb_state);
5762306a36Sopenharmony_ci	v4l2_m2m_buf_done(dst_vbuf, vb_state);
5862306a36Sopenharmony_ci	v4l2_m2m_job_finish(ctx->mdp_dev->m2m_dev, ctx->m2m_ctx);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void mdp_m2m_device_run(void *priv)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = priv;
6462306a36Sopenharmony_ci	struct mdp_frame *frame;
6562306a36Sopenharmony_ci	struct vb2_v4l2_buffer *src_vb, *dst_vb;
6662306a36Sopenharmony_ci	struct img_ipi_frameparam param = {};
6762306a36Sopenharmony_ci	struct mdp_cmdq_param task = {};
6862306a36Sopenharmony_ci	enum vb2_buffer_state vb_state = VB2_BUF_STATE_ERROR;
6962306a36Sopenharmony_ci	int ret;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (mdp_m2m_ctx_is_state_set(ctx, MDP_M2M_CTX_ERROR)) {
7262306a36Sopenharmony_ci		dev_err(&ctx->mdp_dev->pdev->dev,
7362306a36Sopenharmony_ci			"mdp_m2m_ctx is in error state\n");
7462306a36Sopenharmony_ci		goto worker_end;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	param.frame_no = ctx->curr_param.frame_no;
7862306a36Sopenharmony_ci	param.type = ctx->curr_param.type;
7962306a36Sopenharmony_ci	param.num_inputs = 1;
8062306a36Sopenharmony_ci	param.num_outputs = 1;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
8362306a36Sopenharmony_ci	src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
8462306a36Sopenharmony_ci	mdp_set_src_config(&param.inputs[0], frame, &src_vb->vb2_buf);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
8762306a36Sopenharmony_ci	dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
8862306a36Sopenharmony_ci	mdp_set_dst_config(&param.outputs[0], frame, &dst_vb->vb2_buf);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	ret = mdp_vpu_process(&ctx->mdp_dev->vpu, &param);
9162306a36Sopenharmony_ci	if (ret) {
9262306a36Sopenharmony_ci		dev_err(&ctx->mdp_dev->pdev->dev,
9362306a36Sopenharmony_ci			"VPU MDP process failed: %d\n", ret);
9462306a36Sopenharmony_ci		goto worker_end;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	task.config = ctx->mdp_dev->vpu.config;
9862306a36Sopenharmony_ci	task.param = &param;
9962306a36Sopenharmony_ci	task.composes[0] = &frame->compose;
10062306a36Sopenharmony_ci	task.cmdq_cb = NULL;
10162306a36Sopenharmony_ci	task.cb_data = NULL;
10262306a36Sopenharmony_ci	task.mdp_ctx = ctx;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	ret = mdp_cmdq_send(ctx->mdp_dev, &task);
10562306a36Sopenharmony_ci	if (ret) {
10662306a36Sopenharmony_ci		dev_err(&ctx->mdp_dev->pdev->dev,
10762306a36Sopenharmony_ci			"CMDQ sendtask failed: %d\n", ret);
10862306a36Sopenharmony_ci		goto worker_end;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciworker_end:
11462306a36Sopenharmony_ci	mdp_m2m_process_done(ctx, vb_state);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic int mdp_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
12062306a36Sopenharmony_ci	struct mdp_frame *capture;
12162306a36Sopenharmony_ci	struct vb2_queue *vq;
12262306a36Sopenharmony_ci	int ret;
12362306a36Sopenharmony_ci	bool out_streaming, cap_streaming;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(q->type))
12662306a36Sopenharmony_ci		ctx->frame_count[MDP_M2M_SRC] = 0;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (V4L2_TYPE_IS_CAPTURE(q->type))
12962306a36Sopenharmony_ci		ctx->frame_count[MDP_M2M_DST] = 0;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
13262306a36Sopenharmony_ci	vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx);
13362306a36Sopenharmony_ci	out_streaming = vb2_is_streaming(vq);
13462306a36Sopenharmony_ci	vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
13562306a36Sopenharmony_ci	cap_streaming = vb2_is_streaming(vq);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Check to see if scaling ratio is within supported range */
13862306a36Sopenharmony_ci	if ((V4L2_TYPE_IS_OUTPUT(q->type) && cap_streaming) ||
13962306a36Sopenharmony_ci	    (V4L2_TYPE_IS_CAPTURE(q->type) && out_streaming)) {
14062306a36Sopenharmony_ci		ret = mdp_check_scaling_ratio(&capture->crop.c,
14162306a36Sopenharmony_ci					      &capture->compose,
14262306a36Sopenharmony_ci					      capture->rotation,
14362306a36Sopenharmony_ci					      ctx->curr_param.limit);
14462306a36Sopenharmony_ci		if (ret) {
14562306a36Sopenharmony_ci			dev_err(&ctx->mdp_dev->pdev->dev,
14662306a36Sopenharmony_ci				"Out of scaling range\n");
14762306a36Sopenharmony_ci			return ret;
14862306a36Sopenharmony_ci		}
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (!mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) {
15262306a36Sopenharmony_ci		ret = mdp_vpu_get_locked(ctx->mdp_dev);
15362306a36Sopenharmony_ci		if (ret) {
15462306a36Sopenharmony_ci			dev_err(&ctx->mdp_dev->pdev->dev,
15562306a36Sopenharmony_ci				"VPU init failed %d\n", ret);
15662306a36Sopenharmony_ci			return -EINVAL;
15762306a36Sopenharmony_ci		}
15862306a36Sopenharmony_ci		mdp_m2m_ctx_set_state(ctx, MDP_VPU_INIT);
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic struct vb2_v4l2_buffer *mdp_m2m_buf_remove(struct mdp_m2m_ctx *ctx,
16562306a36Sopenharmony_ci						  unsigned int type)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(type))
16862306a36Sopenharmony_ci		return (struct vb2_v4l2_buffer *)
16962306a36Sopenharmony_ci			v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
17062306a36Sopenharmony_ci	else
17162306a36Sopenharmony_ci		return (struct vb2_v4l2_buffer *)
17262306a36Sopenharmony_ci			v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void mdp_m2m_stop_streaming(struct vb2_queue *q)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
17862306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vb;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	vb = mdp_m2m_buf_remove(ctx, q->type);
18162306a36Sopenharmony_ci	while (vb) {
18262306a36Sopenharmony_ci		v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
18362306a36Sopenharmony_ci		vb = mdp_m2m_buf_remove(ctx, q->type);
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int mdp_m2m_queue_setup(struct vb2_queue *q,
18862306a36Sopenharmony_ci			       unsigned int *num_buffers,
18962306a36Sopenharmony_ci			       unsigned int *num_planes, unsigned int sizes[],
19062306a36Sopenharmony_ci			       struct device *alloc_devs[])
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
19362306a36Sopenharmony_ci	struct v4l2_pix_format_mplane *pix_mp;
19462306a36Sopenharmony_ci	u32 i;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	pix_mp = &ctx_get_frame(ctx, q->type)->format.fmt.pix_mp;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* from VIDIOC_CREATE_BUFS */
19962306a36Sopenharmony_ci	if (*num_planes) {
20062306a36Sopenharmony_ci		if (*num_planes != pix_mp->num_planes)
20162306a36Sopenharmony_ci			return -EINVAL;
20262306a36Sopenharmony_ci		for (i = 0; i < pix_mp->num_planes; ++i)
20362306a36Sopenharmony_ci			if (sizes[i] < pix_mp->plane_fmt[i].sizeimage)
20462306a36Sopenharmony_ci				return -EINVAL;
20562306a36Sopenharmony_ci	} else {/* from VIDIOC_REQBUFS */
20662306a36Sopenharmony_ci		*num_planes = pix_mp->num_planes;
20762306a36Sopenharmony_ci		for (i = 0; i < pix_mp->num_planes; ++i)
20862306a36Sopenharmony_ci			sizes[i] = pix_mp->plane_fmt[i].sizeimage;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return 0;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic int mdp_m2m_buf_prepare(struct vb2_buffer *vb)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
21762306a36Sopenharmony_ci	struct v4l2_pix_format_mplane *pix_mp;
21862306a36Sopenharmony_ci	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
21962306a36Sopenharmony_ci	u32 i;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	v4l2_buf->field = V4L2_FIELD_NONE;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (V4L2_TYPE_IS_CAPTURE(vb->type)) {
22462306a36Sopenharmony_ci		pix_mp = &ctx_get_frame(ctx, vb->type)->format.fmt.pix_mp;
22562306a36Sopenharmony_ci		for (i = 0; i < pix_mp->num_planes; ++i) {
22662306a36Sopenharmony_ci			vb2_set_plane_payload(vb, i,
22762306a36Sopenharmony_ci					      pix_mp->plane_fmt[i].sizeimage);
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	return 0;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int mdp_m2m_buf_out_validate(struct vb2_buffer *vb)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	v4l2_buf->field = V4L2_FIELD_NONE;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return 0;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void mdp_m2m_buf_queue(struct vb2_buffer *vb)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
24562306a36Sopenharmony_ci	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	v4l2_buf->field = V4L2_FIELD_NONE;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb));
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic const struct vb2_ops mdp_m2m_qops = {
25362306a36Sopenharmony_ci	.queue_setup	= mdp_m2m_queue_setup,
25462306a36Sopenharmony_ci	.wait_prepare	= vb2_ops_wait_prepare,
25562306a36Sopenharmony_ci	.wait_finish	= vb2_ops_wait_finish,
25662306a36Sopenharmony_ci	.buf_prepare	= mdp_m2m_buf_prepare,
25762306a36Sopenharmony_ci	.start_streaming = mdp_m2m_start_streaming,
25862306a36Sopenharmony_ci	.stop_streaming	= mdp_m2m_stop_streaming,
25962306a36Sopenharmony_ci	.buf_queue	= mdp_m2m_buf_queue,
26062306a36Sopenharmony_ci	.buf_out_validate = mdp_m2m_buf_out_validate,
26162306a36Sopenharmony_ci};
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int mdp_m2m_querycap(struct file *file, void *fh,
26462306a36Sopenharmony_ci			    struct v4l2_capability *cap)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	strscpy(cap->driver, MDP_MODULE_NAME, sizeof(cap->driver));
26762306a36Sopenharmony_ci	strscpy(cap->card, MDP_DEVICE_NAME, sizeof(cap->card));
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int mdp_m2m_enum_fmt_mplane(struct file *file, void *fh,
27362306a36Sopenharmony_ci				   struct v4l2_fmtdesc *f)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return mdp_enum_fmt_mplane(ctx->mdp_dev, f);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int mdp_m2m_g_fmt_mplane(struct file *file, void *fh,
28162306a36Sopenharmony_ci				struct v4l2_format *f)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
28462306a36Sopenharmony_ci	struct mdp_frame *frame;
28562306a36Sopenharmony_ci	struct v4l2_pix_format_mplane *pix_mp;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	frame = ctx_get_frame(ctx, f->type);
28862306a36Sopenharmony_ci	*f = frame->format;
28962306a36Sopenharmony_ci	pix_mp = &f->fmt.pix_mp;
29062306a36Sopenharmony_ci	pix_mp->colorspace = ctx->curr_param.colorspace;
29162306a36Sopenharmony_ci	pix_mp->xfer_func = ctx->curr_param.xfer_func;
29262306a36Sopenharmony_ci	pix_mp->ycbcr_enc = ctx->curr_param.ycbcr_enc;
29362306a36Sopenharmony_ci	pix_mp->quantization = ctx->curr_param.quant;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return 0;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int mdp_m2m_s_fmt_mplane(struct file *file, void *fh,
29962306a36Sopenharmony_ci				struct v4l2_format *f)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
30262306a36Sopenharmony_ci	struct mdp_frame *frame = ctx_get_frame(ctx, f->type);
30362306a36Sopenharmony_ci	struct mdp_frame *capture;
30462306a36Sopenharmony_ci	const struct mdp_format *fmt;
30562306a36Sopenharmony_ci	struct vb2_queue *vq;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	fmt = mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id);
30862306a36Sopenharmony_ci	if (!fmt)
30962306a36Sopenharmony_ci		return -EINVAL;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
31262306a36Sopenharmony_ci	if (vb2_is_busy(vq))
31362306a36Sopenharmony_ci		return -EBUSY;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	frame->format = *f;
31662306a36Sopenharmony_ci	frame->mdp_fmt = fmt;
31762306a36Sopenharmony_ci	frame->ycbcr_prof = mdp_map_ycbcr_prof_mplane(f, fmt->mdp_color);
31862306a36Sopenharmony_ci	frame->usage = V4L2_TYPE_IS_OUTPUT(f->type) ?
31962306a36Sopenharmony_ci		MDP_BUFFER_USAGE_HW_READ : MDP_BUFFER_USAGE_MDP;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
32262306a36Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
32362306a36Sopenharmony_ci		capture->crop.c.left = 0;
32462306a36Sopenharmony_ci		capture->crop.c.top = 0;
32562306a36Sopenharmony_ci		capture->crop.c.width = f->fmt.pix_mp.width;
32662306a36Sopenharmony_ci		capture->crop.c.height = f->fmt.pix_mp.height;
32762306a36Sopenharmony_ci		ctx->curr_param.colorspace = f->fmt.pix_mp.colorspace;
32862306a36Sopenharmony_ci		ctx->curr_param.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
32962306a36Sopenharmony_ci		ctx->curr_param.quant = f->fmt.pix_mp.quantization;
33062306a36Sopenharmony_ci		ctx->curr_param.xfer_func = f->fmt.pix_mp.xfer_func;
33162306a36Sopenharmony_ci	} else {
33262306a36Sopenharmony_ci		capture->compose.left = 0;
33362306a36Sopenharmony_ci		capture->compose.top = 0;
33462306a36Sopenharmony_ci		capture->compose.width = f->fmt.pix_mp.width;
33562306a36Sopenharmony_ci		capture->compose.height = f->fmt.pix_mp.height;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return 0;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int mdp_m2m_try_fmt_mplane(struct file *file, void *fh,
34262306a36Sopenharmony_ci				  struct v4l2_format *f)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (!mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id))
34762306a36Sopenharmony_ci		return -EINVAL;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return 0;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic int mdp_m2m_g_selection(struct file *file, void *fh,
35362306a36Sopenharmony_ci			       struct v4l2_selection *s)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
35662306a36Sopenharmony_ci	struct mdp_frame *frame;
35762306a36Sopenharmony_ci	bool valid = false;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
36062306a36Sopenharmony_ci		valid = mdp_target_is_crop(s->target);
36162306a36Sopenharmony_ci	else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
36262306a36Sopenharmony_ci		valid = mdp_target_is_compose(s->target);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (!valid)
36562306a36Sopenharmony_ci		return -EINVAL;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	switch (s->target) {
36862306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
36962306a36Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
37062306a36Sopenharmony_ci			return -EINVAL;
37162306a36Sopenharmony_ci		frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
37262306a36Sopenharmony_ci		s->r = frame->crop.c;
37362306a36Sopenharmony_ci		return 0;
37462306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
37562306a36Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
37662306a36Sopenharmony_ci			return -EINVAL;
37762306a36Sopenharmony_ci		frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
37862306a36Sopenharmony_ci		s->r = frame->compose;
37962306a36Sopenharmony_ci		return 0;
38062306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
38162306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
38262306a36Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
38362306a36Sopenharmony_ci			return -EINVAL;
38462306a36Sopenharmony_ci		frame = ctx_get_frame(ctx, s->type);
38562306a36Sopenharmony_ci		s->r.left = 0;
38662306a36Sopenharmony_ci		s->r.top = 0;
38762306a36Sopenharmony_ci		s->r.width = frame->format.fmt.pix_mp.width;
38862306a36Sopenharmony_ci		s->r.height = frame->format.fmt.pix_mp.height;
38962306a36Sopenharmony_ci		return 0;
39062306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
39162306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
39262306a36Sopenharmony_ci		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
39362306a36Sopenharmony_ci			return -EINVAL;
39462306a36Sopenharmony_ci		frame = ctx_get_frame(ctx, s->type);
39562306a36Sopenharmony_ci		s->r.left = 0;
39662306a36Sopenharmony_ci		s->r.top = 0;
39762306a36Sopenharmony_ci		s->r.width = frame->format.fmt.pix_mp.width;
39862306a36Sopenharmony_ci		s->r.height = frame->format.fmt.pix_mp.height;
39962306a36Sopenharmony_ci		return 0;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci	return -EINVAL;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int mdp_m2m_s_selection(struct file *file, void *fh,
40562306a36Sopenharmony_ci			       struct v4l2_selection *s)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);
40862306a36Sopenharmony_ci	struct mdp_frame *frame = ctx_get_frame(ctx, s->type);
40962306a36Sopenharmony_ci	struct mdp_frame *capture;
41062306a36Sopenharmony_ci	struct v4l2_rect r;
41162306a36Sopenharmony_ci	struct device *dev = &ctx->mdp_dev->pdev->dev;
41262306a36Sopenharmony_ci	bool valid = false;
41362306a36Sopenharmony_ci	int ret;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
41662306a36Sopenharmony_ci		valid = (s->target == V4L2_SEL_TGT_CROP);
41762306a36Sopenharmony_ci	else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
41862306a36Sopenharmony_ci		valid = (s->target == V4L2_SEL_TGT_COMPOSE);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (!valid) {
42162306a36Sopenharmony_ci		dev_dbg(dev, "[%s:%d] invalid type:%u target:%u", __func__,
42262306a36Sopenharmony_ci			ctx->id, s->type, s->target);
42362306a36Sopenharmony_ci		return -EINVAL;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	ret = mdp_try_crop(ctx, &r, s, frame);
42762306a36Sopenharmony_ci	if (ret)
42862306a36Sopenharmony_ci		return ret;
42962306a36Sopenharmony_ci	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (mdp_target_is_crop(s->target))
43262306a36Sopenharmony_ci		capture->crop.c = r;
43362306a36Sopenharmony_ci	else
43462306a36Sopenharmony_ci		capture->compose = r;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	s->r = r;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	return 0;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops mdp_m2m_ioctl_ops = {
44262306a36Sopenharmony_ci	.vidioc_querycap		= mdp_m2m_querycap,
44362306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= mdp_m2m_enum_fmt_mplane,
44462306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_out	= mdp_m2m_enum_fmt_mplane,
44562306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap_mplane	= mdp_m2m_g_fmt_mplane,
44662306a36Sopenharmony_ci	.vidioc_g_fmt_vid_out_mplane	= mdp_m2m_g_fmt_mplane,
44762306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap_mplane	= mdp_m2m_s_fmt_mplane,
44862306a36Sopenharmony_ci	.vidioc_s_fmt_vid_out_mplane	= mdp_m2m_s_fmt_mplane,
44962306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap_mplane	= mdp_m2m_try_fmt_mplane,
45062306a36Sopenharmony_ci	.vidioc_try_fmt_vid_out_mplane	= mdp_m2m_try_fmt_mplane,
45162306a36Sopenharmony_ci	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
45262306a36Sopenharmony_ci	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
45362306a36Sopenharmony_ci	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
45462306a36Sopenharmony_ci	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
45562306a36Sopenharmony_ci	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
45662306a36Sopenharmony_ci	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
45762306a36Sopenharmony_ci	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
45862306a36Sopenharmony_ci	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
45962306a36Sopenharmony_ci	.vidioc_g_selection		= mdp_m2m_g_selection,
46062306a36Sopenharmony_ci	.vidioc_s_selection		= mdp_m2m_s_selection,
46162306a36Sopenharmony_ci	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
46262306a36Sopenharmony_ci	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
46362306a36Sopenharmony_ci};
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic int mdp_m2m_queue_init(void *priv,
46662306a36Sopenharmony_ci			      struct vb2_queue *src_vq,
46762306a36Sopenharmony_ci			      struct vb2_queue *dst_vq)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = priv;
47062306a36Sopenharmony_ci	int ret;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
47362306a36Sopenharmony_ci	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
47462306a36Sopenharmony_ci	src_vq->ops = &mdp_m2m_qops;
47562306a36Sopenharmony_ci	src_vq->mem_ops = &vb2_dma_contig_memops;
47662306a36Sopenharmony_ci	src_vq->drv_priv = ctx;
47762306a36Sopenharmony_ci	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
47862306a36Sopenharmony_ci	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
47962306a36Sopenharmony_ci	src_vq->dev = &ctx->mdp_dev->pdev->dev;
48062306a36Sopenharmony_ci	src_vq->lock = &ctx->ctx_lock;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	ret = vb2_queue_init(src_vq);
48362306a36Sopenharmony_ci	if (ret)
48462306a36Sopenharmony_ci		return ret;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
48762306a36Sopenharmony_ci	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
48862306a36Sopenharmony_ci	dst_vq->ops = &mdp_m2m_qops;
48962306a36Sopenharmony_ci	dst_vq->mem_ops = &vb2_dma_contig_memops;
49062306a36Sopenharmony_ci	dst_vq->drv_priv = ctx;
49162306a36Sopenharmony_ci	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
49262306a36Sopenharmony_ci	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
49362306a36Sopenharmony_ci	dst_vq->dev = &ctx->mdp_dev->pdev->dev;
49462306a36Sopenharmony_ci	dst_vq->lock = &ctx->ctx_lock;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return vb2_queue_init(dst_vq);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic int mdp_m2m_s_ctrl(struct v4l2_ctrl *ctrl)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = ctrl_to_ctx(ctrl);
50262306a36Sopenharmony_ci	struct mdp_frame *capture;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
50562306a36Sopenharmony_ci	switch (ctrl->id) {
50662306a36Sopenharmony_ci	case V4L2_CID_HFLIP:
50762306a36Sopenharmony_ci		capture->hflip = ctrl->val;
50862306a36Sopenharmony_ci		break;
50962306a36Sopenharmony_ci	case V4L2_CID_VFLIP:
51062306a36Sopenharmony_ci		capture->vflip = ctrl->val;
51162306a36Sopenharmony_ci		break;
51262306a36Sopenharmony_ci	case V4L2_CID_ROTATE:
51362306a36Sopenharmony_ci		capture->rotation = ctrl->val;
51462306a36Sopenharmony_ci		break;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops mdp_m2m_ctrl_ops = {
52162306a36Sopenharmony_ci	.s_ctrl	= mdp_m2m_s_ctrl,
52262306a36Sopenharmony_ci};
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int mdp_m2m_ctrls_create(struct mdp_m2m_ctx *ctx)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	v4l2_ctrl_handler_init(&ctx->ctrl_handler, MDP_MAX_CTRLS);
52762306a36Sopenharmony_ci	ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
52862306a36Sopenharmony_ci					     &mdp_m2m_ctrl_ops, V4L2_CID_HFLIP,
52962306a36Sopenharmony_ci					     0, 1, 1, 0);
53062306a36Sopenharmony_ci	ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
53162306a36Sopenharmony_ci					     &mdp_m2m_ctrl_ops, V4L2_CID_VFLIP,
53262306a36Sopenharmony_ci					     0, 1, 1, 0);
53362306a36Sopenharmony_ci	ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
53462306a36Sopenharmony_ci					      &mdp_m2m_ctrl_ops,
53562306a36Sopenharmony_ci					      V4L2_CID_ROTATE, 0, 270, 90, 0);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (ctx->ctrl_handler.error) {
53862306a36Sopenharmony_ci		int err = ctx->ctrl_handler.error;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
54162306a36Sopenharmony_ci		dev_err(&ctx->mdp_dev->pdev->dev,
54262306a36Sopenharmony_ci			"Failed to register controls\n");
54362306a36Sopenharmony_ci		return err;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci	return 0;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic int mdp_m2m_open(struct file *file)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
55162306a36Sopenharmony_ci	struct mdp_dev *mdp = video_get_drvdata(vdev);
55262306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx;
55362306a36Sopenharmony_ci	struct device *dev = &mdp->pdev->dev;
55462306a36Sopenharmony_ci	int ret;
55562306a36Sopenharmony_ci	struct v4l2_format default_format = {};
55662306a36Sopenharmony_ci	const struct mdp_limit *limit = mdp->mdp_data->def_limit;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
55962306a36Sopenharmony_ci	if (!ctx)
56062306a36Sopenharmony_ci		return -ENOMEM;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (mutex_lock_interruptible(&mdp->m2m_lock)) {
56362306a36Sopenharmony_ci		ret = -ERESTARTSYS;
56462306a36Sopenharmony_ci		goto err_free_ctx;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	ret = ida_alloc(&mdp->mdp_ida, GFP_KERNEL);
56862306a36Sopenharmony_ci	if (ret < 0)
56962306a36Sopenharmony_ci		goto err_unlock_mutex;
57062306a36Sopenharmony_ci	ctx->id = ret;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	ctx->mdp_dev = mdp;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	v4l2_fh_init(&ctx->fh, vdev);
57562306a36Sopenharmony_ci	file->private_data = &ctx->fh;
57662306a36Sopenharmony_ci	ret = mdp_m2m_ctrls_create(ctx);
57762306a36Sopenharmony_ci	if (ret)
57862306a36Sopenharmony_ci		goto err_exit_fh;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/* Use separate control handler per file handle */
58162306a36Sopenharmony_ci	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
58262306a36Sopenharmony_ci	v4l2_fh_add(&ctx->fh);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	mutex_init(&ctx->ctx_lock);
58562306a36Sopenharmony_ci	ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx, mdp_m2m_queue_init);
58662306a36Sopenharmony_ci	if (IS_ERR(ctx->m2m_ctx)) {
58762306a36Sopenharmony_ci		dev_err(dev, "Failed to initialize m2m context\n");
58862306a36Sopenharmony_ci		ret = PTR_ERR(ctx->m2m_ctx);
58962306a36Sopenharmony_ci		goto err_release_handler;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci	ctx->fh.m2m_ctx = ctx->m2m_ctx;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	ctx->curr_param.ctx = ctx;
59462306a36Sopenharmony_ci	ret = mdp_frameparam_init(mdp, &ctx->curr_param);
59562306a36Sopenharmony_ci	if (ret) {
59662306a36Sopenharmony_ci		dev_err(dev, "Failed to initialize mdp parameter\n");
59762306a36Sopenharmony_ci		goto err_release_m2m_ctx;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	mutex_unlock(&mdp->m2m_lock);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/* Default format */
60362306a36Sopenharmony_ci	default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
60462306a36Sopenharmony_ci	default_format.fmt.pix_mp.width = limit->out_limit.wmin;
60562306a36Sopenharmony_ci	default_format.fmt.pix_mp.height = limit->out_limit.hmin;
60662306a36Sopenharmony_ci	default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
60762306a36Sopenharmony_ci	mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
60862306a36Sopenharmony_ci	default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
60962306a36Sopenharmony_ci	mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	dev_dbg(dev, "%s:[%d]", __func__, ctx->id);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	return 0;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cierr_release_m2m_ctx:
61662306a36Sopenharmony_ci	v4l2_m2m_ctx_release(ctx->m2m_ctx);
61762306a36Sopenharmony_cierr_release_handler:
61862306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
61962306a36Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
62062306a36Sopenharmony_cierr_exit_fh:
62162306a36Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
62262306a36Sopenharmony_ci	ida_free(&mdp->mdp_ida, ctx->id);
62362306a36Sopenharmony_cierr_unlock_mutex:
62462306a36Sopenharmony_ci	mutex_unlock(&mdp->m2m_lock);
62562306a36Sopenharmony_cierr_free_ctx:
62662306a36Sopenharmony_ci	kfree(ctx);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	return ret;
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic int mdp_m2m_release(struct file *file)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	struct mdp_m2m_ctx *ctx = fh_to_ctx(file->private_data);
63462306a36Sopenharmony_ci	struct mdp_dev *mdp = video_drvdata(file);
63562306a36Sopenharmony_ci	struct device *dev = &mdp->pdev->dev;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	mutex_lock(&mdp->m2m_lock);
63862306a36Sopenharmony_ci	v4l2_m2m_ctx_release(ctx->m2m_ctx);
63962306a36Sopenharmony_ci	if (mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT))
64062306a36Sopenharmony_ci		mdp_vpu_put_locked(mdp);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
64362306a36Sopenharmony_ci	v4l2_fh_del(&ctx->fh);
64462306a36Sopenharmony_ci	v4l2_fh_exit(&ctx->fh);
64562306a36Sopenharmony_ci	ida_free(&mdp->mdp_ida, ctx->id);
64662306a36Sopenharmony_ci	mutex_unlock(&mdp->m2m_lock);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	dev_dbg(dev, "%s:[%d]", __func__, ctx->id);
64962306a36Sopenharmony_ci	kfree(ctx);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return 0;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic const struct v4l2_file_operations mdp_m2m_fops = {
65562306a36Sopenharmony_ci	.owner		= THIS_MODULE,
65662306a36Sopenharmony_ci	.poll		= v4l2_m2m_fop_poll,
65762306a36Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
65862306a36Sopenharmony_ci	.mmap		= v4l2_m2m_fop_mmap,
65962306a36Sopenharmony_ci	.open		= mdp_m2m_open,
66062306a36Sopenharmony_ci	.release	= mdp_m2m_release,
66162306a36Sopenharmony_ci};
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic const struct v4l2_m2m_ops mdp_m2m_ops = {
66462306a36Sopenharmony_ci	.device_run	= mdp_m2m_device_run,
66562306a36Sopenharmony_ci};
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ciint mdp_m2m_device_register(struct mdp_dev *mdp)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct device *dev = &mdp->pdev->dev;
67062306a36Sopenharmony_ci	int ret = 0;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	mdp->m2m_vdev = video_device_alloc();
67362306a36Sopenharmony_ci	if (!mdp->m2m_vdev) {
67462306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate video device\n");
67562306a36Sopenharmony_ci		ret = -ENOMEM;
67662306a36Sopenharmony_ci		goto err_video_alloc;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci	mdp->m2m_vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE |
67962306a36Sopenharmony_ci		V4L2_CAP_STREAMING;
68062306a36Sopenharmony_ci	mdp->m2m_vdev->fops = &mdp_m2m_fops;
68162306a36Sopenharmony_ci	mdp->m2m_vdev->ioctl_ops = &mdp_m2m_ioctl_ops;
68262306a36Sopenharmony_ci	mdp->m2m_vdev->release = mdp_video_device_release;
68362306a36Sopenharmony_ci	mdp->m2m_vdev->lock = &mdp->m2m_lock;
68462306a36Sopenharmony_ci	mdp->m2m_vdev->vfl_dir = VFL_DIR_M2M;
68562306a36Sopenharmony_ci	mdp->m2m_vdev->v4l2_dev = &mdp->v4l2_dev;
68662306a36Sopenharmony_ci	snprintf(mdp->m2m_vdev->name, sizeof(mdp->m2m_vdev->name), "%s:m2m",
68762306a36Sopenharmony_ci		 MDP_MODULE_NAME);
68862306a36Sopenharmony_ci	video_set_drvdata(mdp->m2m_vdev, mdp);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	mdp->m2m_dev = v4l2_m2m_init(&mdp_m2m_ops);
69162306a36Sopenharmony_ci	if (IS_ERR(mdp->m2m_dev)) {
69262306a36Sopenharmony_ci		dev_err(dev, "Failed to initialize v4l2-m2m device\n");
69362306a36Sopenharmony_ci		ret = PTR_ERR(mdp->m2m_dev);
69462306a36Sopenharmony_ci		goto err_m2m_init;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	ret = video_register_device(mdp->m2m_vdev, VFL_TYPE_VIDEO, -1);
69862306a36Sopenharmony_ci	if (ret) {
69962306a36Sopenharmony_ci		dev_err(dev, "Failed to register video device\n");
70062306a36Sopenharmony_ci		goto err_video_register;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	v4l2_info(&mdp->v4l2_dev, "Driver registered as /dev/video%d",
70462306a36Sopenharmony_ci		  mdp->m2m_vdev->num);
70562306a36Sopenharmony_ci	return 0;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cierr_video_register:
70862306a36Sopenharmony_ci	v4l2_m2m_release(mdp->m2m_dev);
70962306a36Sopenharmony_cierr_m2m_init:
71062306a36Sopenharmony_ci	video_device_release(mdp->m2m_vdev);
71162306a36Sopenharmony_cierr_video_alloc:
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	return ret;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_civoid mdp_m2m_device_unregister(struct mdp_dev *mdp)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	video_unregister_device(mdp->m2m_vdev);
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_civoid mdp_m2m_job_finish(struct mdp_m2m_ctx *ctx)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	enum vb2_buffer_state vb_state = VB2_BUF_STATE_DONE;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	mdp_m2m_process_done(ctx, vb_state);
72662306a36Sopenharmony_ci}
727