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(¶m.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(¶m.outputs[0], frame, &dst_vb->vb2_buf);
8961890ccaSMoudy Ho
90*b4e52199SMoudy Ho ret = mdp_vpu_process(&ctx->mdp_dev->vpu, ¶m);
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 = ¶m;
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