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