742 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2022 MediaTek Inc.4 * Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>5 */6 7#include <linux/platform_device.h>8#include <media/v4l2-ioctl.h>9#include <media/v4l2-event.h>10#include <media/videobuf2-dma-contig.h>11#include "mtk-mdp3-m2m.h"12 13static inline struct mdp_m2m_ctx *fh_to_ctx(struct v4l2_fh *fh)14{15 return container_of(fh, struct mdp_m2m_ctx, fh);16}17 18static inline struct mdp_m2m_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)19{20 return container_of(ctrl->handler, struct mdp_m2m_ctx, ctrl_handler);21}22 23static inline struct mdp_frame *ctx_get_frame(struct mdp_m2m_ctx *ctx,24 enum v4l2_buf_type type)25{26 if (V4L2_TYPE_IS_OUTPUT(type))27 return &ctx->curr_param.output;28 else29 return &ctx->curr_param.captures[0];30}31 32static inline void mdp_m2m_ctx_set_state(struct mdp_m2m_ctx *ctx, u32 state)33{34 atomic_or(state, &ctx->curr_param.state);35}36 37static inline bool mdp_m2m_ctx_is_state_set(struct mdp_m2m_ctx *ctx, u32 mask)38{39 return ((atomic_read(&ctx->curr_param.state) & mask) == mask);40}41 42static void mdp_m2m_process_done(void *priv, int vb_state)43{44 struct mdp_m2m_ctx *ctx = priv;45 struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;46 47 src_vbuf = (struct vb2_v4l2_buffer *)48 v4l2_m2m_src_buf_remove(ctx->m2m_ctx);49 dst_vbuf = (struct vb2_v4l2_buffer *)50 v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);51 ctx->curr_param.frame_no = ctx->frame_count[MDP_M2M_SRC];52 src_vbuf->sequence = ctx->frame_count[MDP_M2M_SRC]++;53 dst_vbuf->sequence = ctx->frame_count[MDP_M2M_DST]++;54 v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, true);55 56 v4l2_m2m_buf_done(src_vbuf, vb_state);57 v4l2_m2m_buf_done(dst_vbuf, vb_state);58 v4l2_m2m_job_finish(ctx->mdp_dev->m2m_dev, ctx->m2m_ctx);59}60 61static void mdp_m2m_device_run(void *priv)62{63 struct mdp_m2m_ctx *ctx = priv;64 struct mdp_frame *frame;65 struct vb2_v4l2_buffer *src_vb, *dst_vb;66 struct img_ipi_frameparam param = {};67 struct mdp_cmdq_param task = {};68 enum vb2_buffer_state vb_state = VB2_BUF_STATE_ERROR;69 int ret;70 71 if (mdp_m2m_ctx_is_state_set(ctx, MDP_M2M_CTX_ERROR)) {72 dev_err(&ctx->mdp_dev->pdev->dev,73 "mdp_m2m_ctx is in error state\n");74 goto worker_end;75 }76 77 param.frame_no = ctx->curr_param.frame_no;78 param.type = ctx->curr_param.type;79 param.num_inputs = 1;80 param.num_outputs = 1;81 82 frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);83 src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);84 mdp_set_src_config(¶m.inputs[0], frame, &src_vb->vb2_buf);85 86 frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);87 dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);88 mdp_set_dst_config(¶m.outputs[0], frame, &dst_vb->vb2_buf);89 90 if (mdp_check_pp_enable(ctx->mdp_dev, frame))91 param.type = MDP_STREAM_TYPE_DUAL_BITBLT;92 93 ret = mdp_vpu_process(&ctx->mdp_dev->vpu, ¶m);94 if (ret) {95 dev_err(&ctx->mdp_dev->pdev->dev,96 "VPU MDP process failed: %d\n", ret);97 goto worker_end;98 }99 100 task.config = ctx->mdp_dev->vpu.config;101 task.param = ¶m;102 task.composes[0] = &frame->compose;103 task.cmdq_cb = NULL;104 task.cb_data = NULL;105 task.mdp_ctx = ctx;106 107 if (refcount_read(&ctx->mdp_dev->job_count)) {108 ret = wait_event_timeout(ctx->mdp_dev->callback_wq,109 !refcount_read(&ctx->mdp_dev->job_count),110 2 * HZ);111 if (ret == 0) {112 dev_err(&ctx->mdp_dev->pdev->dev,113 "%d jobs not yet done\n",114 refcount_read(&ctx->mdp_dev->job_count));115 goto worker_end;116 }117 }118 119 ret = mdp_cmdq_send(ctx->mdp_dev, &task);120 if (ret) {121 dev_err(&ctx->mdp_dev->pdev->dev,122 "CMDQ sendtask failed: %d\n", ret);123 goto worker_end;124 }125 126 return;127 128worker_end:129 mdp_m2m_process_done(ctx, vb_state);130}131 132static int mdp_m2m_start_streaming(struct vb2_queue *q, unsigned int count)133{134 struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);135 struct mdp_frame *capture;136 struct vb2_queue *vq;137 int ret;138 bool out_streaming, cap_streaming;139 140 if (V4L2_TYPE_IS_OUTPUT(q->type))141 ctx->frame_count[MDP_M2M_SRC] = 0;142 143 if (V4L2_TYPE_IS_CAPTURE(q->type))144 ctx->frame_count[MDP_M2M_DST] = 0;145 146 capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);147 vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx);148 out_streaming = vb2_is_streaming(vq);149 vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);150 cap_streaming = vb2_is_streaming(vq);151 152 /* Check to see if scaling ratio is within supported range */153 if ((V4L2_TYPE_IS_OUTPUT(q->type) && cap_streaming) ||154 (V4L2_TYPE_IS_CAPTURE(q->type) && out_streaming)) {155 ret = mdp_check_scaling_ratio(&capture->crop.c,156 &capture->compose,157 capture->rotation,158 ctx->curr_param.limit);159 if (ret) {160 dev_err(&ctx->mdp_dev->pdev->dev,161 "Out of scaling range\n");162 return ret;163 }164 }165 166 if (!mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) {167 ret = mdp_vpu_get_locked(ctx->mdp_dev);168 if (ret) {169 dev_err(&ctx->mdp_dev->pdev->dev,170 "VPU init failed %d\n", ret);171 return -EINVAL;172 }173 mdp_m2m_ctx_set_state(ctx, MDP_VPU_INIT);174 }175 176 return 0;177}178 179static struct vb2_v4l2_buffer *mdp_m2m_buf_remove(struct mdp_m2m_ctx *ctx,180 unsigned int type)181{182 if (V4L2_TYPE_IS_OUTPUT(type))183 return (struct vb2_v4l2_buffer *)184 v4l2_m2m_src_buf_remove(ctx->m2m_ctx);185 else186 return (struct vb2_v4l2_buffer *)187 v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);188}189 190static void mdp_m2m_stop_streaming(struct vb2_queue *q)191{192 struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);193 struct vb2_v4l2_buffer *vb;194 195 vb = mdp_m2m_buf_remove(ctx, q->type);196 while (vb) {197 v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);198 vb = mdp_m2m_buf_remove(ctx, q->type);199 }200}201 202static int mdp_m2m_queue_setup(struct vb2_queue *q,203 unsigned int *num_buffers,204 unsigned int *num_planes, unsigned int sizes[],205 struct device *alloc_devs[])206{207 struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);208 struct v4l2_pix_format_mplane *pix_mp;209 u32 i;210 211 pix_mp = &ctx_get_frame(ctx, q->type)->format.fmt.pix_mp;212 213 /* from VIDIOC_CREATE_BUFS */214 if (*num_planes) {215 if (*num_planes != pix_mp->num_planes)216 return -EINVAL;217 for (i = 0; i < pix_mp->num_planes; ++i)218 if (sizes[i] < pix_mp->plane_fmt[i].sizeimage)219 return -EINVAL;220 } else {/* from VIDIOC_REQBUFS */221 *num_planes = pix_mp->num_planes;222 for (i = 0; i < pix_mp->num_planes; ++i)223 sizes[i] = pix_mp->plane_fmt[i].sizeimage;224 }225 226 return 0;227}228 229static int mdp_m2m_buf_prepare(struct vb2_buffer *vb)230{231 struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);232 struct v4l2_pix_format_mplane *pix_mp;233 struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);234 u32 i;235 236 v4l2_buf->field = V4L2_FIELD_NONE;237 238 if (V4L2_TYPE_IS_CAPTURE(vb->type)) {239 pix_mp = &ctx_get_frame(ctx, vb->type)->format.fmt.pix_mp;240 for (i = 0; i < pix_mp->num_planes; ++i) {241 vb2_set_plane_payload(vb, i,242 pix_mp->plane_fmt[i].sizeimage);243 }244 }245 return 0;246}247 248static int mdp_m2m_buf_out_validate(struct vb2_buffer *vb)249{250 struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);251 252 v4l2_buf->field = V4L2_FIELD_NONE;253 254 return 0;255}256 257static void mdp_m2m_buf_queue(struct vb2_buffer *vb)258{259 struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);260 struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);261 262 v4l2_buf->field = V4L2_FIELD_NONE;263 264 v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb));265}266 267static const struct vb2_ops mdp_m2m_qops = {268 .queue_setup = mdp_m2m_queue_setup,269 .wait_prepare = vb2_ops_wait_prepare,270 .wait_finish = vb2_ops_wait_finish,271 .buf_prepare = mdp_m2m_buf_prepare,272 .start_streaming = mdp_m2m_start_streaming,273 .stop_streaming = mdp_m2m_stop_streaming,274 .buf_queue = mdp_m2m_buf_queue,275 .buf_out_validate = mdp_m2m_buf_out_validate,276};277 278static int mdp_m2m_querycap(struct file *file, void *fh,279 struct v4l2_capability *cap)280{281 strscpy(cap->driver, MDP_MODULE_NAME, sizeof(cap->driver));282 strscpy(cap->card, MDP_DEVICE_NAME, sizeof(cap->card));283 284 return 0;285}286 287static int mdp_m2m_enum_fmt_mplane(struct file *file, void *fh,288 struct v4l2_fmtdesc *f)289{290 struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);291 292 return mdp_enum_fmt_mplane(ctx->mdp_dev, f);293}294 295static int mdp_m2m_g_fmt_mplane(struct file *file, void *fh,296 struct v4l2_format *f)297{298 struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);299 struct mdp_frame *frame;300 struct v4l2_pix_format_mplane *pix_mp;301 302 frame = ctx_get_frame(ctx, f->type);303 *f = frame->format;304 pix_mp = &f->fmt.pix_mp;305 pix_mp->colorspace = ctx->curr_param.colorspace;306 pix_mp->xfer_func = ctx->curr_param.xfer_func;307 pix_mp->ycbcr_enc = ctx->curr_param.ycbcr_enc;308 pix_mp->quantization = ctx->curr_param.quant;309 310 return 0;311}312 313static int mdp_m2m_s_fmt_mplane(struct file *file, void *fh,314 struct v4l2_format *f)315{316 struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);317 struct mdp_frame *frame = ctx_get_frame(ctx, f->type);318 struct mdp_frame *capture;319 const struct mdp_format *fmt;320 struct vb2_queue *vq;321 322 fmt = mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id);323 if (!fmt)324 return -EINVAL;325 326 vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);327 if (vb2_is_busy(vq))328 return -EBUSY;329 330 frame->format = *f;331 frame->mdp_fmt = fmt;332 frame->ycbcr_prof = mdp_map_ycbcr_prof_mplane(f, fmt->mdp_color);333 frame->usage = V4L2_TYPE_IS_OUTPUT(f->type) ?334 MDP_BUFFER_USAGE_HW_READ : MDP_BUFFER_USAGE_MDP;335 336 capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);337 if (V4L2_TYPE_IS_OUTPUT(f->type)) {338 capture->crop.c.left = 0;339 capture->crop.c.top = 0;340 capture->crop.c.width = f->fmt.pix_mp.width;341 capture->crop.c.height = f->fmt.pix_mp.height;342 ctx->curr_param.colorspace = f->fmt.pix_mp.colorspace;343 ctx->curr_param.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;344 ctx->curr_param.quant = f->fmt.pix_mp.quantization;345 ctx->curr_param.xfer_func = f->fmt.pix_mp.xfer_func;346 } else {347 capture->compose.left = 0;348 capture->compose.top = 0;349 capture->compose.width = f->fmt.pix_mp.width;350 capture->compose.height = f->fmt.pix_mp.height;351 }352 353 return 0;354}355 356static int mdp_m2m_try_fmt_mplane(struct file *file, void *fh,357 struct v4l2_format *f)358{359 struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);360 361 if (!mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id))362 return -EINVAL;363 364 return 0;365}366 367static int mdp_m2m_g_selection(struct file *file, void *fh,368 struct v4l2_selection *s)369{370 struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);371 struct mdp_frame *frame;372 bool valid = false;373 374 if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)375 valid = mdp_target_is_crop(s->target);376 else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)377 valid = mdp_target_is_compose(s->target);378 379 if (!valid)380 return -EINVAL;381 382 switch (s->target) {383 case V4L2_SEL_TGT_CROP:384 if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)385 return -EINVAL;386 frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);387 s->r = frame->crop.c;388 return 0;389 case V4L2_SEL_TGT_COMPOSE:390 if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)391 return -EINVAL;392 frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);393 s->r = frame->compose;394 return 0;395 case V4L2_SEL_TGT_CROP_DEFAULT:396 case V4L2_SEL_TGT_CROP_BOUNDS:397 if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)398 return -EINVAL;399 frame = ctx_get_frame(ctx, s->type);400 s->r.left = 0;401 s->r.top = 0;402 s->r.width = frame->format.fmt.pix_mp.width;403 s->r.height = frame->format.fmt.pix_mp.height;404 return 0;405 case V4L2_SEL_TGT_COMPOSE_DEFAULT:406 case V4L2_SEL_TGT_COMPOSE_BOUNDS:407 if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)408 return -EINVAL;409 frame = ctx_get_frame(ctx, s->type);410 s->r.left = 0;411 s->r.top = 0;412 s->r.width = frame->format.fmt.pix_mp.width;413 s->r.height = frame->format.fmt.pix_mp.height;414 return 0;415 }416 return -EINVAL;417}418 419static int mdp_m2m_s_selection(struct file *file, void *fh,420 struct v4l2_selection *s)421{422 struct mdp_m2m_ctx *ctx = fh_to_ctx(fh);423 struct mdp_frame *frame = ctx_get_frame(ctx, s->type);424 struct mdp_frame *capture;425 struct v4l2_rect r;426 struct device *dev = &ctx->mdp_dev->pdev->dev;427 bool valid = false;428 int ret;429 430 if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)431 valid = (s->target == V4L2_SEL_TGT_CROP);432 else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)433 valid = (s->target == V4L2_SEL_TGT_COMPOSE);434 435 if (!valid) {436 dev_dbg(dev, "[%s:%d] invalid type:%u target:%u", __func__,437 ctx->id, s->type, s->target);438 return -EINVAL;439 }440 441 ret = mdp_try_crop(ctx, &r, s, frame);442 if (ret)443 return ret;444 capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);445 446 if (mdp_target_is_crop(s->target))447 capture->crop.c = r;448 else449 capture->compose = r;450 451 s->r = r;452 453 return 0;454}455 456static const struct v4l2_ioctl_ops mdp_m2m_ioctl_ops = {457 .vidioc_querycap = mdp_m2m_querycap,458 .vidioc_enum_fmt_vid_cap = mdp_m2m_enum_fmt_mplane,459 .vidioc_enum_fmt_vid_out = mdp_m2m_enum_fmt_mplane,460 .vidioc_g_fmt_vid_cap_mplane = mdp_m2m_g_fmt_mplane,461 .vidioc_g_fmt_vid_out_mplane = mdp_m2m_g_fmt_mplane,462 .vidioc_s_fmt_vid_cap_mplane = mdp_m2m_s_fmt_mplane,463 .vidioc_s_fmt_vid_out_mplane = mdp_m2m_s_fmt_mplane,464 .vidioc_try_fmt_vid_cap_mplane = mdp_m2m_try_fmt_mplane,465 .vidioc_try_fmt_vid_out_mplane = mdp_m2m_try_fmt_mplane,466 .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,467 .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,468 .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,469 .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,470 .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,471 .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,472 .vidioc_streamon = v4l2_m2m_ioctl_streamon,473 .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,474 .vidioc_g_selection = mdp_m2m_g_selection,475 .vidioc_s_selection = mdp_m2m_s_selection,476 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,477 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,478};479 480static int mdp_m2m_queue_init(void *priv,481 struct vb2_queue *src_vq,482 struct vb2_queue *dst_vq)483{484 struct mdp_m2m_ctx *ctx = priv;485 int ret;486 487 src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;488 src_vq->io_modes = VB2_MMAP | VB2_DMABUF;489 src_vq->ops = &mdp_m2m_qops;490 src_vq->mem_ops = &vb2_dma_contig_memops;491 src_vq->drv_priv = ctx;492 src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);493 src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;494 src_vq->dev = &ctx->mdp_dev->pdev->dev;495 src_vq->lock = &ctx->ctx_lock;496 497 ret = vb2_queue_init(src_vq);498 if (ret)499 return ret;500 501 dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;502 dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;503 dst_vq->ops = &mdp_m2m_qops;504 dst_vq->mem_ops = &vb2_dma_contig_memops;505 dst_vq->drv_priv = ctx;506 dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);507 dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;508 dst_vq->dev = &ctx->mdp_dev->pdev->dev;509 dst_vq->lock = &ctx->ctx_lock;510 511 return vb2_queue_init(dst_vq);512}513 514static int mdp_m2m_s_ctrl(struct v4l2_ctrl *ctrl)515{516 struct mdp_m2m_ctx *ctx = ctrl_to_ctx(ctrl);517 struct mdp_frame *capture;518 519 capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);520 switch (ctrl->id) {521 case V4L2_CID_HFLIP:522 capture->hflip = ctrl->val;523 break;524 case V4L2_CID_VFLIP:525 capture->vflip = ctrl->val;526 break;527 case V4L2_CID_ROTATE:528 capture->rotation = ctrl->val;529 break;530 }531 532 return 0;533}534 535static const struct v4l2_ctrl_ops mdp_m2m_ctrl_ops = {536 .s_ctrl = mdp_m2m_s_ctrl,537};538 539static int mdp_m2m_ctrls_create(struct mdp_m2m_ctx *ctx)540{541 v4l2_ctrl_handler_init(&ctx->ctrl_handler, MDP_MAX_CTRLS);542 ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,543 &mdp_m2m_ctrl_ops, V4L2_CID_HFLIP,544 0, 1, 1, 0);545 ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,546 &mdp_m2m_ctrl_ops, V4L2_CID_VFLIP,547 0, 1, 1, 0);548 ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,549 &mdp_m2m_ctrl_ops,550 V4L2_CID_ROTATE, 0, 270, 90, 0);551 552 if (ctx->ctrl_handler.error) {553 int err = ctx->ctrl_handler.error;554 555 v4l2_ctrl_handler_free(&ctx->ctrl_handler);556 dev_err(&ctx->mdp_dev->pdev->dev,557 "Failed to register controls\n");558 return err;559 }560 return 0;561}562 563static int mdp_m2m_open(struct file *file)564{565 struct video_device *vdev = video_devdata(file);566 struct mdp_dev *mdp = video_get_drvdata(vdev);567 struct mdp_m2m_ctx *ctx;568 struct device *dev = &mdp->pdev->dev;569 int ret;570 struct v4l2_format default_format = {};571 const struct mdp_limit *limit = mdp->mdp_data->def_limit;572 573 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);574 if (!ctx)575 return -ENOMEM;576 577 if (mutex_lock_interruptible(&mdp->m2m_lock)) {578 ret = -ERESTARTSYS;579 goto err_free_ctx;580 }581 582 ret = ida_alloc(&mdp->mdp_ida, GFP_KERNEL);583 if (ret < 0)584 goto err_unlock_mutex;585 ctx->id = ret;586 587 ctx->mdp_dev = mdp;588 589 v4l2_fh_init(&ctx->fh, vdev);590 file->private_data = &ctx->fh;591 ret = mdp_m2m_ctrls_create(ctx);592 if (ret)593 goto err_exit_fh;594 595 /* Use separate control handler per file handle */596 ctx->fh.ctrl_handler = &ctx->ctrl_handler;597 v4l2_fh_add(&ctx->fh);598 599 mutex_init(&ctx->ctx_lock);600 ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx, mdp_m2m_queue_init);601 if (IS_ERR(ctx->m2m_ctx)) {602 dev_err(dev, "Failed to initialize m2m context\n");603 ret = PTR_ERR(ctx->m2m_ctx);604 goto err_release_handler;605 }606 ctx->fh.m2m_ctx = ctx->m2m_ctx;607 608 ctx->curr_param.ctx = ctx;609 ret = mdp_frameparam_init(mdp, &ctx->curr_param);610 if (ret) {611 dev_err(dev, "Failed to initialize mdp parameter\n");612 goto err_release_m2m_ctx;613 }614 615 mutex_unlock(&mdp->m2m_lock);616 617 /* Default format */618 default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;619 default_format.fmt.pix_mp.width = limit->out_limit.wmin;620 default_format.fmt.pix_mp.height = limit->out_limit.hmin;621 default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;622 mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);623 default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;624 mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);625 626 dev_dbg(dev, "%s:[%d]", __func__, ctx->id);627 628 return 0;629 630err_release_m2m_ctx:631 v4l2_m2m_ctx_release(ctx->m2m_ctx);632err_release_handler:633 v4l2_ctrl_handler_free(&ctx->ctrl_handler);634 v4l2_fh_del(&ctx->fh);635err_exit_fh:636 v4l2_fh_exit(&ctx->fh);637 ida_free(&mdp->mdp_ida, ctx->id);638err_unlock_mutex:639 mutex_unlock(&mdp->m2m_lock);640err_free_ctx:641 kfree(ctx);642 643 return ret;644}645 646static int mdp_m2m_release(struct file *file)647{648 struct mdp_m2m_ctx *ctx = fh_to_ctx(file->private_data);649 struct mdp_dev *mdp = video_drvdata(file);650 struct device *dev = &mdp->pdev->dev;651 652 mutex_lock(&mdp->m2m_lock);653 v4l2_m2m_ctx_release(ctx->m2m_ctx);654 if (mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT))655 mdp_vpu_put_locked(mdp);656 657 v4l2_ctrl_handler_free(&ctx->ctrl_handler);658 v4l2_fh_del(&ctx->fh);659 v4l2_fh_exit(&ctx->fh);660 ida_free(&mdp->mdp_ida, ctx->id);661 mutex_unlock(&mdp->m2m_lock);662 663 dev_dbg(dev, "%s:[%d]", __func__, ctx->id);664 kfree(ctx);665 666 return 0;667}668 669static const struct v4l2_file_operations mdp_m2m_fops = {670 .owner = THIS_MODULE,671 .poll = v4l2_m2m_fop_poll,672 .unlocked_ioctl = video_ioctl2,673 .mmap = v4l2_m2m_fop_mmap,674 .open = mdp_m2m_open,675 .release = mdp_m2m_release,676};677 678static const struct v4l2_m2m_ops mdp_m2m_ops = {679 .device_run = mdp_m2m_device_run,680};681 682int mdp_m2m_device_register(struct mdp_dev *mdp)683{684 struct device *dev = &mdp->pdev->dev;685 int ret = 0;686 687 mdp->m2m_vdev = video_device_alloc();688 if (!mdp->m2m_vdev) {689 dev_err(dev, "Failed to allocate video device\n");690 ret = -ENOMEM;691 goto err_video_alloc;692 }693 mdp->m2m_vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE |694 V4L2_CAP_STREAMING;695 mdp->m2m_vdev->fops = &mdp_m2m_fops;696 mdp->m2m_vdev->ioctl_ops = &mdp_m2m_ioctl_ops;697 mdp->m2m_vdev->release = mdp_video_device_release;698 mdp->m2m_vdev->lock = &mdp->m2m_lock;699 mdp->m2m_vdev->vfl_dir = VFL_DIR_M2M;700 mdp->m2m_vdev->v4l2_dev = &mdp->v4l2_dev;701 snprintf(mdp->m2m_vdev->name, sizeof(mdp->m2m_vdev->name), "%s:m2m",702 MDP_MODULE_NAME);703 video_set_drvdata(mdp->m2m_vdev, mdp);704 705 mdp->m2m_dev = v4l2_m2m_init(&mdp_m2m_ops);706 if (IS_ERR(mdp->m2m_dev)) {707 dev_err(dev, "Failed to initialize v4l2-m2m device\n");708 ret = PTR_ERR(mdp->m2m_dev);709 goto err_m2m_init;710 }711 712 ret = video_register_device(mdp->m2m_vdev, VFL_TYPE_VIDEO, -1);713 if (ret) {714 dev_err(dev, "Failed to register video device\n");715 goto err_video_register;716 }717 718 v4l2_info(&mdp->v4l2_dev, "Driver registered as /dev/video%d",719 mdp->m2m_vdev->num);720 return 0;721 722err_video_register:723 v4l2_m2m_release(mdp->m2m_dev);724err_m2m_init:725 video_device_release(mdp->m2m_vdev);726err_video_alloc:727 728 return ret;729}730 731void mdp_m2m_device_unregister(struct mdp_dev *mdp)732{733 video_unregister_device(mdp->m2m_vdev);734}735 736void mdp_m2m_job_finish(struct mdp_m2m_ctx *ctx)737{738 enum vb2_buffer_state vb_state = VB2_BUF_STATE_DONE;739 740 mdp_m2m_process_done(ctx, vb_state);741}742