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(¶m.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(¶m.outputs[0], frame, &dst_vb->vb2_buf); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci ret = mdp_vpu_process(&ctx->mdp_dev->vpu, ¶m); 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 = ¶m; 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