1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2015-2016 MediaTek Inc.
4 * Author: Houlong Wei <houlong.wei@mediatek.com>
5 * Ming Hsiu Tsai <minghsiu.tsai@mediatek.com>
8 #include <linux/device.h>
9 #include <linux/errno.h>
10 #include <linux/kernel.h>
11 #include <linux/pm_runtime.h>
12 #include <linux/slab.h>
13 #include <linux/workqueue.h>
14 #include <media/v4l2-event.h>
15 #include <media/v4l2-ioctl.h>
17 #include "mtk_mdp_core.h"
18 #include "mtk_mdp_m2m.h"
19 #include "mtk_mdp_regs.h"
24 * struct mtk_mdp_pix_limit - image pixel size limits
25 * @org_w: source pixel width
26 * @org_h: source pixel height
27 * @target_rot_dis_w: pixel dst scaled width with the rotator is off
28 * @target_rot_dis_h: pixel dst scaled height with the rotator is off
29 * @target_rot_en_w: pixel dst scaled width with the rotator is on
30 * @target_rot_en_h: pixel dst scaled height with the rotator is on
32 struct mtk_mdp_pix_limit
{
41 static struct mtk_mdp_pix_align mtk_mdp_size_align
= {
48 static const struct mtk_mdp_fmt mtk_mdp_formats
[] = {
50 .pixelformat
= V4L2_PIX_FMT_MT21C
,
52 .row_depth
= { 8, 8 },
55 .align
= &mtk_mdp_size_align
,
56 .flags
= MTK_MDP_FMT_FLAG_OUTPUT
,
58 .pixelformat
= V4L2_PIX_FMT_NV12M
,
60 .row_depth
= { 8, 8 },
63 .flags
= MTK_MDP_FMT_FLAG_OUTPUT
|
64 MTK_MDP_FMT_FLAG_CAPTURE
,
66 .pixelformat
= V4L2_PIX_FMT_YUV420M
,
68 .row_depth
= { 8, 4, 4 },
71 .flags
= MTK_MDP_FMT_FLAG_OUTPUT
|
72 MTK_MDP_FMT_FLAG_CAPTURE
,
74 .pixelformat
= V4L2_PIX_FMT_YVU420
,
79 .flags
= MTK_MDP_FMT_FLAG_OUTPUT
|
80 MTK_MDP_FMT_FLAG_CAPTURE
,
84 static struct mtk_mdp_pix_limit mtk_mdp_size_max
= {
85 .target_rot_dis_w
= 4096,
86 .target_rot_dis_h
= 4096,
87 .target_rot_en_w
= 4096,
88 .target_rot_en_h
= 4096,
91 static struct mtk_mdp_pix_limit mtk_mdp_size_min
= {
94 .target_rot_dis_w
= 16,
95 .target_rot_dis_h
= 16,
96 .target_rot_en_w
= 16,
97 .target_rot_en_h
= 16,
100 /* align size for normal raster scan pixel format */
101 static struct mtk_mdp_pix_align mtk_mdp_rs_align
= {
108 static struct mtk_mdp_variant mtk_mdp_default_variant
= {
109 .pix_max
= &mtk_mdp_size_max
,
110 .pix_min
= &mtk_mdp_size_min
,
111 .pix_align
= &mtk_mdp_rs_align
,
112 .h_scale_up_max
= 32,
113 .v_scale_up_max
= 32,
114 .h_scale_down_max
= 32,
115 .v_scale_down_max
= 128,
118 static const struct mtk_mdp_fmt
*mtk_mdp_find_fmt(u32 pixelformat
, u32 type
)
122 flag
= V4L2_TYPE_IS_OUTPUT(type
) ? MTK_MDP_FMT_FLAG_OUTPUT
:
123 MTK_MDP_FMT_FLAG_CAPTURE
;
125 for (i
= 0; i
< ARRAY_SIZE(mtk_mdp_formats
); ++i
) {
126 if (!(mtk_mdp_formats
[i
].flags
& flag
))
128 if (mtk_mdp_formats
[i
].pixelformat
== pixelformat
)
129 return &mtk_mdp_formats
[i
];
134 static const struct mtk_mdp_fmt
*mtk_mdp_find_fmt_by_index(u32 index
, u32 type
)
136 u32 i
, flag
, num
= 0;
138 flag
= V4L2_TYPE_IS_OUTPUT(type
) ? MTK_MDP_FMT_FLAG_OUTPUT
:
139 MTK_MDP_FMT_FLAG_CAPTURE
;
141 for (i
= 0; i
< ARRAY_SIZE(mtk_mdp_formats
); ++i
) {
142 if (!(mtk_mdp_formats
[i
].flags
& flag
))
145 return &mtk_mdp_formats
[i
];
151 static void mtk_mdp_bound_align_image(u32
*w
, unsigned int wmin
,
152 unsigned int wmax
, unsigned int align_w
,
153 u32
*h
, unsigned int hmin
,
154 unsigned int hmax
, unsigned int align_h
)
156 int org_w
, org_h
, step_w
, step_h
;
161 walign
= ffs(align_w
) - 1;
162 halign
= ffs(align_h
) - 1;
163 v4l_bound_align_image(w
, wmin
, wmax
, walign
, h
, hmin
, hmax
, halign
, 0);
165 step_w
= 1 << walign
;
166 step_h
= 1 << halign
;
167 if (*w
< org_w
&& (*w
+ step_w
) <= wmax
)
169 if (*h
< org_h
&& (*h
+ step_h
) <= hmax
)
173 static const struct mtk_mdp_fmt
*mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx
*ctx
,
174 struct v4l2_format
*f
)
176 struct mtk_mdp_dev
*mdp
= ctx
->mdp_dev
;
177 struct mtk_mdp_variant
*variant
= mdp
->variant
;
178 struct v4l2_pix_format_mplane
*pix_mp
= &f
->fmt
.pix_mp
;
179 const struct mtk_mdp_fmt
*fmt
;
180 u32 max_w
, max_h
, align_w
, align_h
;
181 u32 min_w
, min_h
, org_w
, org_h
;
184 fmt
= mtk_mdp_find_fmt(pix_mp
->pixelformat
, f
->type
);
186 fmt
= mtk_mdp_find_fmt_by_index(0, f
->type
);
188 dev_dbg(&ctx
->mdp_dev
->pdev
->dev
,
189 "pixelformat format 0x%X invalid\n",
190 pix_mp
->pixelformat
);
194 pix_mp
->field
= V4L2_FIELD_NONE
;
195 pix_mp
->pixelformat
= fmt
->pixelformat
;
196 if (V4L2_TYPE_IS_CAPTURE(f
->type
)) {
197 pix_mp
->colorspace
= ctx
->colorspace
;
198 pix_mp
->xfer_func
= ctx
->xfer_func
;
199 pix_mp
->ycbcr_enc
= ctx
->ycbcr_enc
;
200 pix_mp
->quantization
= ctx
->quant
;
202 memset(pix_mp
->reserved
, 0, sizeof(pix_mp
->reserved
));
204 max_w
= variant
->pix_max
->target_rot_dis_w
;
205 max_h
= variant
->pix_max
->target_rot_dis_h
;
207 if (fmt
->align
== NULL
) {
208 /* use default alignment */
209 align_w
= variant
->pix_align
->org_w
;
210 align_h
= variant
->pix_align
->org_h
;
212 align_w
= fmt
->align
->org_w
;
213 align_h
= fmt
->align
->org_h
;
216 if (V4L2_TYPE_IS_OUTPUT(f
->type
)) {
217 min_w
= variant
->pix_min
->org_w
;
218 min_h
= variant
->pix_min
->org_h
;
220 min_w
= variant
->pix_min
->target_rot_dis_w
;
221 min_h
= variant
->pix_min
->target_rot_dis_h
;
224 mtk_mdp_dbg(2, "[%d] type:%d, wxh:%ux%u, align:%ux%u, max:%ux%u",
225 ctx
->id
, f
->type
, pix_mp
->width
, pix_mp
->height
,
226 align_w
, align_h
, max_w
, max_h
);
228 * To check if image size is modified to adjust parameter against
231 org_w
= pix_mp
->width
;
232 org_h
= pix_mp
->height
;
234 mtk_mdp_bound_align_image(&pix_mp
->width
, min_w
, max_w
, align_w
,
235 &pix_mp
->height
, min_h
, max_h
, align_h
);
237 if (org_w
!= pix_mp
->width
|| org_h
!= pix_mp
->height
)
238 mtk_mdp_dbg(1, "[%d] size change:%ux%u to %ux%u", ctx
->id
,
239 org_w
, org_h
, pix_mp
->width
, pix_mp
->height
);
240 pix_mp
->num_planes
= fmt
->num_planes
;
242 for (i
= 0; i
< pix_mp
->num_planes
; ++i
) {
243 int bpl
= (pix_mp
->width
* fmt
->row_depth
[i
]) / 8;
244 int sizeimage
= (pix_mp
->width
* pix_mp
->height
*
247 pix_mp
->plane_fmt
[i
].bytesperline
= bpl
;
248 if (pix_mp
->plane_fmt
[i
].sizeimage
< sizeimage
)
249 pix_mp
->plane_fmt
[i
].sizeimage
= sizeimage
;
250 memset(pix_mp
->plane_fmt
[i
].reserved
, 0,
251 sizeof(pix_mp
->plane_fmt
[i
].reserved
));
252 mtk_mdp_dbg(2, "[%d] p%d, bpl:%d, sizeimage:%u (%u)", ctx
->id
,
253 i
, bpl
, pix_mp
->plane_fmt
[i
].sizeimage
, sizeimage
);
259 static struct mtk_mdp_frame
*mtk_mdp_ctx_get_frame(struct mtk_mdp_ctx
*ctx
,
260 enum v4l2_buf_type type
)
262 if (V4L2_TYPE_IS_OUTPUT(type
))
263 return &ctx
->s_frame
;
264 return &ctx
->d_frame
;
267 static void mtk_mdp_check_crop_change(u32 new_w
, u32 new_h
, u32
*w
, u32
*h
)
269 if (new_w
!= *w
|| new_h
!= *h
) {
270 mtk_mdp_dbg(1, "size change:%dx%d to %dx%d",
271 *w
, *h
, new_w
, new_h
);
278 static int mtk_mdp_try_crop(struct mtk_mdp_ctx
*ctx
, u32 type
,
281 struct mtk_mdp_frame
*frame
;
282 struct mtk_mdp_dev
*mdp
= ctx
->mdp_dev
;
283 struct mtk_mdp_variant
*variant
= mdp
->variant
;
284 u32 align_w
, align_h
, new_w
, new_h
;
285 u32 min_w
, min_h
, max_w
, max_h
;
287 if (r
->top
< 0 || r
->left
< 0) {
288 dev_err(&ctx
->mdp_dev
->pdev
->dev
,
289 "doesn't support negative values for top & left\n");
293 mtk_mdp_dbg(2, "[%d] type:%d, set wxh:%dx%d", ctx
->id
, type
,
294 r
->width
, r
->height
);
296 frame
= mtk_mdp_ctx_get_frame(ctx
, type
);
297 max_w
= frame
->width
;
298 max_h
= frame
->height
;
302 if (V4L2_TYPE_IS_OUTPUT(type
)) {
308 align_w
= variant
->pix_align
->target_w
;
309 align_h
= variant
->pix_align
->target_h
;
310 if (ctx
->ctrls
.rotate
->val
== 90 ||
311 ctx
->ctrls
.rotate
->val
== 270) {
312 max_w
= frame
->height
;
313 max_h
= frame
->width
;
314 min_w
= variant
->pix_min
->target_rot_en_w
;
315 min_h
= variant
->pix_min
->target_rot_en_h
;
319 min_w
= variant
->pix_min
->target_rot_dis_w
;
320 min_h
= variant
->pix_min
->target_rot_dis_h
;
324 mtk_mdp_dbg(2, "[%d] align:%dx%d, min:%dx%d, new:%dx%d", ctx
->id
,
325 align_w
, align_h
, min_w
, min_h
, new_w
, new_h
);
327 mtk_mdp_bound_align_image(&new_w
, min_w
, max_w
, align_w
,
328 &new_h
, min_h
, max_h
, align_h
);
330 if (V4L2_TYPE_IS_CAPTURE(type
) &&
331 (ctx
->ctrls
.rotate
->val
== 90 || ctx
->ctrls
.rotate
->val
== 270))
332 mtk_mdp_check_crop_change(new_h
, new_w
,
333 &r
->width
, &r
->height
);
335 mtk_mdp_check_crop_change(new_w
, new_h
,
336 &r
->width
, &r
->height
);
338 /* adjust left/top if cropping rectangle is out of bounds */
339 /* Need to add code to algin left value with 2's multiple */
340 if (r
->left
+ new_w
> max_w
)
341 r
->left
= max_w
- new_w
;
342 if (r
->top
+ new_h
> max_h
)
343 r
->top
= max_h
- new_h
;
348 mtk_mdp_dbg(2, "[%d] crop l,t,w,h:%d,%d,%d,%d, max:%dx%d", ctx
->id
,
349 r
->left
, r
->top
, r
->width
,
350 r
->height
, max_w
, max_h
);
354 static inline struct mtk_mdp_ctx
*fh_to_ctx(struct v4l2_fh
*fh
)
356 return container_of(fh
, struct mtk_mdp_ctx
, fh
);
359 static inline struct mtk_mdp_ctx
*ctrl_to_ctx(struct v4l2_ctrl
*ctrl
)
361 return container_of(ctrl
->handler
, struct mtk_mdp_ctx
, ctrl_handler
);
364 void mtk_mdp_ctx_state_lock_set(struct mtk_mdp_ctx
*ctx
, u32 state
)
366 mutex_lock(&ctx
->slock
);
368 mutex_unlock(&ctx
->slock
);
371 static bool mtk_mdp_ctx_state_is_set(struct mtk_mdp_ctx
*ctx
, u32 mask
)
375 mutex_lock(&ctx
->slock
);
376 ret
= (ctx
->state
& mask
) == mask
;
377 mutex_unlock(&ctx
->slock
);
381 static void mtk_mdp_set_frame_size(struct mtk_mdp_frame
*frame
, int width
,
384 frame
->width
= width
;
385 frame
->height
= height
;
386 frame
->crop
.width
= width
;
387 frame
->crop
.height
= height
;
388 frame
->crop
.left
= 0;
392 static int mtk_mdp_m2m_start_streaming(struct vb2_queue
*q
, unsigned int count
)
394 struct mtk_mdp_ctx
*ctx
= q
->drv_priv
;
397 ret
= pm_runtime_get_sync(&ctx
->mdp_dev
->pdev
->dev
);
399 mtk_mdp_dbg(1, "[%d] pm_runtime_get_sync failed:%d",
405 static void *mtk_mdp_m2m_buf_remove(struct mtk_mdp_ctx
*ctx
,
406 enum v4l2_buf_type type
)
408 if (V4L2_TYPE_IS_OUTPUT(type
))
409 return v4l2_m2m_src_buf_remove(ctx
->m2m_ctx
);
411 return v4l2_m2m_dst_buf_remove(ctx
->m2m_ctx
);
414 static void mtk_mdp_m2m_stop_streaming(struct vb2_queue
*q
)
416 struct mtk_mdp_ctx
*ctx
= q
->drv_priv
;
417 struct vb2_buffer
*vb
;
419 vb
= mtk_mdp_m2m_buf_remove(ctx
, q
->type
);
421 v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb
), VB2_BUF_STATE_ERROR
);
422 vb
= mtk_mdp_m2m_buf_remove(ctx
, q
->type
);
425 pm_runtime_put(&ctx
->mdp_dev
->pdev
->dev
);
428 /* The color format (num_planes) must be already configured. */
429 static void mtk_mdp_prepare_addr(struct mtk_mdp_ctx
*ctx
,
430 struct vb2_buffer
*vb
,
431 struct mtk_mdp_frame
*frame
,
432 struct mtk_mdp_addr
*addr
)
434 u32 pix_size
, planes
, i
;
436 pix_size
= frame
->width
* frame
->height
;
437 planes
= min_t(u32
, frame
->fmt
->num_planes
, ARRAY_SIZE(addr
->addr
));
438 for (i
= 0; i
< planes
; i
++)
439 addr
->addr
[i
] = vb2_dma_contig_plane_dma_addr(vb
, i
);
442 if (frame
->fmt
->pixelformat
== V4L2_PIX_FMT_YVU420
) {
443 addr
->addr
[1] = (dma_addr_t
)(addr
->addr
[0] + pix_size
);
444 addr
->addr
[2] = (dma_addr_t
)(addr
->addr
[1] +
447 dev_err(&ctx
->mdp_dev
->pdev
->dev
,
448 "Invalid pixelformat:0x%x\n",
449 frame
->fmt
->pixelformat
);
452 mtk_mdp_dbg(3, "[%d] planes:%d, size:%d, addr:%p,%p,%p",
453 ctx
->id
, planes
, pix_size
, (void *)addr
->addr
[0],
454 (void *)addr
->addr
[1], (void *)addr
->addr
[2]);
457 static void mtk_mdp_m2m_get_bufs(struct mtk_mdp_ctx
*ctx
)
459 struct mtk_mdp_frame
*s_frame
, *d_frame
;
460 struct vb2_v4l2_buffer
*src_vbuf
, *dst_vbuf
;
462 s_frame
= &ctx
->s_frame
;
463 d_frame
= &ctx
->d_frame
;
465 src_vbuf
= v4l2_m2m_next_src_buf(ctx
->m2m_ctx
);
466 mtk_mdp_prepare_addr(ctx
, &src_vbuf
->vb2_buf
, s_frame
, &s_frame
->addr
);
468 dst_vbuf
= v4l2_m2m_next_dst_buf(ctx
->m2m_ctx
);
469 mtk_mdp_prepare_addr(ctx
, &dst_vbuf
->vb2_buf
, d_frame
, &d_frame
->addr
);
471 dst_vbuf
->vb2_buf
.timestamp
= src_vbuf
->vb2_buf
.timestamp
;
474 static void mtk_mdp_process_done(void *priv
, int vb_state
)
476 struct mtk_mdp_dev
*mdp
= priv
;
477 struct mtk_mdp_ctx
*ctx
;
478 struct vb2_v4l2_buffer
*src_vbuf
, *dst_vbuf
;
480 ctx
= v4l2_m2m_get_curr_priv(mdp
->m2m_dev
);
484 src_vbuf
= v4l2_m2m_src_buf_remove(ctx
->m2m_ctx
);
485 dst_vbuf
= v4l2_m2m_dst_buf_remove(ctx
->m2m_ctx
);
487 dst_vbuf
->vb2_buf
.timestamp
= src_vbuf
->vb2_buf
.timestamp
;
488 dst_vbuf
->timecode
= src_vbuf
->timecode
;
489 dst_vbuf
->flags
&= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK
;
490 dst_vbuf
->flags
|= src_vbuf
->flags
& V4L2_BUF_FLAG_TSTAMP_SRC_MASK
;
492 v4l2_m2m_buf_done(src_vbuf
, vb_state
);
493 v4l2_m2m_buf_done(dst_vbuf
, vb_state
);
494 v4l2_m2m_job_finish(ctx
->mdp_dev
->m2m_dev
, ctx
->m2m_ctx
);
497 static void mtk_mdp_m2m_worker(struct work_struct
*work
)
499 struct mtk_mdp_ctx
*ctx
=
500 container_of(work
, struct mtk_mdp_ctx
, work
);
501 struct mtk_mdp_dev
*mdp
= ctx
->mdp_dev
;
502 enum vb2_buffer_state buf_state
= VB2_BUF_STATE_ERROR
;
505 if (mtk_mdp_ctx_state_is_set(ctx
, MTK_MDP_CTX_ERROR
)) {
506 dev_err(&mdp
->pdev
->dev
, "ctx is in error state");
510 mtk_mdp_m2m_get_bufs(ctx
);
512 mtk_mdp_hw_set_input_addr(ctx
, &ctx
->s_frame
.addr
);
513 mtk_mdp_hw_set_output_addr(ctx
, &ctx
->d_frame
.addr
);
515 mtk_mdp_hw_set_in_size(ctx
);
516 mtk_mdp_hw_set_in_image_format(ctx
);
518 mtk_mdp_hw_set_out_size(ctx
);
519 mtk_mdp_hw_set_out_image_format(ctx
);
521 mtk_mdp_hw_set_rotation(ctx
);
522 mtk_mdp_hw_set_global_alpha(ctx
);
524 ret
= mtk_mdp_vpu_process(&ctx
->vpu
);
526 dev_err(&mdp
->pdev
->dev
, "processing failed: %d", ret
);
530 buf_state
= VB2_BUF_STATE_DONE
;
533 mtk_mdp_process_done(mdp
, buf_state
);
536 static void mtk_mdp_m2m_device_run(void *priv
)
538 struct mtk_mdp_ctx
*ctx
= priv
;
540 queue_work(ctx
->mdp_dev
->job_wq
, &ctx
->work
);
543 static int mtk_mdp_m2m_queue_setup(struct vb2_queue
*vq
,
544 unsigned int *num_buffers
, unsigned int *num_planes
,
545 unsigned int sizes
[], struct device
*alloc_devs
[])
547 struct mtk_mdp_ctx
*ctx
= vb2_get_drv_priv(vq
);
548 struct mtk_mdp_frame
*frame
;
551 frame
= mtk_mdp_ctx_get_frame(ctx
, vq
->type
);
552 *num_planes
= frame
->fmt
->num_planes
;
553 for (i
= 0; i
< frame
->fmt
->num_planes
; i
++)
554 sizes
[i
] = frame
->payload
[i
];
555 mtk_mdp_dbg(2, "[%d] type:%d, planes:%d, buffers:%d, size:%u,%u",
556 ctx
->id
, vq
->type
, *num_planes
, *num_buffers
,
561 static int mtk_mdp_m2m_buf_prepare(struct vb2_buffer
*vb
)
563 struct mtk_mdp_ctx
*ctx
= vb2_get_drv_priv(vb
->vb2_queue
);
564 struct mtk_mdp_frame
*frame
;
567 frame
= mtk_mdp_ctx_get_frame(ctx
, vb
->vb2_queue
->type
);
569 if (!V4L2_TYPE_IS_OUTPUT(vb
->vb2_queue
->type
)) {
570 for (i
= 0; i
< frame
->fmt
->num_planes
; i
++)
571 vb2_set_plane_payload(vb
, i
, frame
->payload
[i
]);
577 static void mtk_mdp_m2m_buf_queue(struct vb2_buffer
*vb
)
579 struct mtk_mdp_ctx
*ctx
= vb2_get_drv_priv(vb
->vb2_queue
);
581 v4l2_m2m_buf_queue(ctx
->m2m_ctx
, to_vb2_v4l2_buffer(vb
));
584 static const struct vb2_ops mtk_mdp_m2m_qops
= {
585 .queue_setup
= mtk_mdp_m2m_queue_setup
,
586 .buf_prepare
= mtk_mdp_m2m_buf_prepare
,
587 .buf_queue
= mtk_mdp_m2m_buf_queue
,
588 .stop_streaming
= mtk_mdp_m2m_stop_streaming
,
589 .start_streaming
= mtk_mdp_m2m_start_streaming
,
590 .wait_prepare
= vb2_ops_wait_prepare
,
591 .wait_finish
= vb2_ops_wait_finish
,
594 static int mtk_mdp_m2m_querycap(struct file
*file
, void *fh
,
595 struct v4l2_capability
*cap
)
597 struct mtk_mdp_ctx
*ctx
= fh_to_ctx(fh
);
598 struct mtk_mdp_dev
*mdp
= ctx
->mdp_dev
;
600 strscpy(cap
->driver
, MTK_MDP_MODULE_NAME
, sizeof(cap
->driver
));
601 strscpy(cap
->card
, mdp
->pdev
->name
, sizeof(cap
->card
));
602 strscpy(cap
->bus_info
, "platform:mt8173", sizeof(cap
->bus_info
));
607 static int mtk_mdp_enum_fmt(struct v4l2_fmtdesc
*f
, u32 type
)
609 const struct mtk_mdp_fmt
*fmt
;
611 fmt
= mtk_mdp_find_fmt_by_index(f
->index
, type
);
615 f
->pixelformat
= fmt
->pixelformat
;
620 static int mtk_mdp_m2m_enum_fmt_vid_cap(struct file
*file
, void *priv
,
621 struct v4l2_fmtdesc
*f
)
623 return mtk_mdp_enum_fmt(f
, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
);
626 static int mtk_mdp_m2m_enum_fmt_vid_out(struct file
*file
, void *priv
,
627 struct v4l2_fmtdesc
*f
)
629 return mtk_mdp_enum_fmt(f
, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
);
632 static int mtk_mdp_m2m_g_fmt_mplane(struct file
*file
, void *fh
,
633 struct v4l2_format
*f
)
635 struct mtk_mdp_ctx
*ctx
= fh_to_ctx(fh
);
636 struct mtk_mdp_frame
*frame
;
637 struct v4l2_pix_format_mplane
*pix_mp
;
640 mtk_mdp_dbg(2, "[%d] type:%d", ctx
->id
, f
->type
);
642 frame
= mtk_mdp_ctx_get_frame(ctx
, f
->type
);
643 pix_mp
= &f
->fmt
.pix_mp
;
645 pix_mp
->width
= frame
->width
;
646 pix_mp
->height
= frame
->height
;
647 pix_mp
->field
= V4L2_FIELD_NONE
;
648 pix_mp
->pixelformat
= frame
->fmt
->pixelformat
;
649 pix_mp
->num_planes
= frame
->fmt
->num_planes
;
650 pix_mp
->colorspace
= ctx
->colorspace
;
651 pix_mp
->xfer_func
= ctx
->xfer_func
;
652 pix_mp
->ycbcr_enc
= ctx
->ycbcr_enc
;
653 pix_mp
->quantization
= ctx
->quant
;
654 mtk_mdp_dbg(2, "[%d] wxh:%dx%d", ctx
->id
,
655 pix_mp
->width
, pix_mp
->height
);
657 for (i
= 0; i
< pix_mp
->num_planes
; ++i
) {
658 pix_mp
->plane_fmt
[i
].bytesperline
= (frame
->width
*
659 frame
->fmt
->row_depth
[i
]) / 8;
660 pix_mp
->plane_fmt
[i
].sizeimage
= (frame
->width
*
661 frame
->height
* frame
->fmt
->depth
[i
]) / 8;
663 mtk_mdp_dbg(2, "[%d] p%d, bpl:%d, sizeimage:%d", ctx
->id
, i
,
664 pix_mp
->plane_fmt
[i
].bytesperline
,
665 pix_mp
->plane_fmt
[i
].sizeimage
);
671 static int mtk_mdp_m2m_try_fmt_mplane(struct file
*file
, void *fh
,
672 struct v4l2_format
*f
)
674 struct mtk_mdp_ctx
*ctx
= fh_to_ctx(fh
);
676 if (!mtk_mdp_try_fmt_mplane(ctx
, f
))
681 static int mtk_mdp_m2m_s_fmt_mplane(struct file
*file
, void *fh
,
682 struct v4l2_format
*f
)
684 struct mtk_mdp_ctx
*ctx
= fh_to_ctx(fh
);
685 struct vb2_queue
*vq
;
686 struct mtk_mdp_frame
*frame
;
687 struct v4l2_pix_format_mplane
*pix_mp
;
688 const struct mtk_mdp_fmt
*fmt
;
691 mtk_mdp_dbg(2, "[%d] type:%d", ctx
->id
, f
->type
);
693 frame
= mtk_mdp_ctx_get_frame(ctx
, f
->type
);
694 fmt
= mtk_mdp_try_fmt_mplane(ctx
, f
);
696 mtk_mdp_err("[%d] try_fmt failed, type:%d", ctx
->id
, f
->type
);
701 vq
= v4l2_m2m_get_vq(ctx
->m2m_ctx
, f
->type
);
702 if (vb2_is_streaming(vq
)) {
703 dev_info(&ctx
->mdp_dev
->pdev
->dev
, "queue %d busy", f
->type
);
707 pix_mp
= &f
->fmt
.pix_mp
;
708 for (i
= 0; i
< frame
->fmt
->num_planes
; i
++) {
709 frame
->payload
[i
] = pix_mp
->plane_fmt
[i
].sizeimage
;
710 frame
->pitch
[i
] = pix_mp
->plane_fmt
[i
].bytesperline
;
713 mtk_mdp_set_frame_size(frame
, pix_mp
->width
, pix_mp
->height
);
714 if (V4L2_TYPE_IS_OUTPUT(f
->type
)) {
715 ctx
->colorspace
= pix_mp
->colorspace
;
716 ctx
->xfer_func
= pix_mp
->xfer_func
;
717 ctx
->ycbcr_enc
= pix_mp
->ycbcr_enc
;
718 ctx
->quant
= pix_mp
->quantization
;
721 mtk_mdp_dbg(2, "[%d] type:%d, frame:%dx%d", ctx
->id
, f
->type
,
722 frame
->width
, frame
->height
);
727 static int mtk_mdp_m2m_reqbufs(struct file
*file
, void *fh
,
728 struct v4l2_requestbuffers
*reqbufs
)
730 struct mtk_mdp_ctx
*ctx
= fh_to_ctx(fh
);
732 return v4l2_m2m_reqbufs(file
, ctx
->m2m_ctx
, reqbufs
);
735 static int mtk_mdp_m2m_streamon(struct file
*file
, void *fh
,
736 enum v4l2_buf_type type
)
738 struct mtk_mdp_ctx
*ctx
= fh_to_ctx(fh
);
741 if (!mtk_mdp_ctx_state_is_set(ctx
, MTK_MDP_VPU_INIT
)) {
742 ret
= mtk_mdp_vpu_init(&ctx
->vpu
);
744 dev_err(&ctx
->mdp_dev
->pdev
->dev
,
745 "vpu init failed %d\n",
749 mtk_mdp_ctx_state_lock_set(ctx
, MTK_MDP_VPU_INIT
);
752 return v4l2_m2m_streamon(file
, ctx
->m2m_ctx
, type
);
755 static inline bool mtk_mdp_is_target_compose(u32 target
)
757 if (target
== V4L2_SEL_TGT_COMPOSE_DEFAULT
758 || target
== V4L2_SEL_TGT_COMPOSE_BOUNDS
759 || target
== V4L2_SEL_TGT_COMPOSE
)
764 static inline bool mtk_mdp_is_target_crop(u32 target
)
766 if (target
== V4L2_SEL_TGT_CROP_DEFAULT
767 || target
== V4L2_SEL_TGT_CROP_BOUNDS
768 || target
== V4L2_SEL_TGT_CROP
)
773 static int mtk_mdp_m2m_g_selection(struct file
*file
, void *fh
,
774 struct v4l2_selection
*s
)
776 struct mtk_mdp_frame
*frame
;
777 struct mtk_mdp_ctx
*ctx
= fh_to_ctx(fh
);
780 if (s
->type
== V4L2_BUF_TYPE_VIDEO_CAPTURE
) {
781 if (mtk_mdp_is_target_compose(s
->target
))
783 } else if (s
->type
== V4L2_BUF_TYPE_VIDEO_OUTPUT
) {
784 if (mtk_mdp_is_target_crop(s
->target
))
788 mtk_mdp_dbg(1, "[%d] invalid type:%d,%u", ctx
->id
, s
->type
,
793 frame
= mtk_mdp_ctx_get_frame(ctx
, s
->type
);
796 case V4L2_SEL_TGT_COMPOSE_DEFAULT
:
797 case V4L2_SEL_TGT_COMPOSE_BOUNDS
:
798 case V4L2_SEL_TGT_CROP_BOUNDS
:
799 case V4L2_SEL_TGT_CROP_DEFAULT
:
802 s
->r
.width
= frame
->width
;
803 s
->r
.height
= frame
->height
;
806 case V4L2_SEL_TGT_COMPOSE
:
807 case V4L2_SEL_TGT_CROP
:
808 s
->r
.left
= frame
->crop
.left
;
809 s
->r
.top
= frame
->crop
.top
;
810 s
->r
.width
= frame
->crop
.width
;
811 s
->r
.height
= frame
->crop
.height
;
818 static int mtk_mdp_check_scaler_ratio(struct mtk_mdp_variant
*var
, int src_w
,
819 int src_h
, int dst_w
, int dst_h
, int rot
)
823 if (rot
== 90 || rot
== 270) {
831 if ((src_w
/ tmp_w
) > var
->h_scale_down_max
||
832 (src_h
/ tmp_h
) > var
->v_scale_down_max
||
833 (tmp_w
/ src_w
) > var
->h_scale_up_max
||
834 (tmp_h
/ src_h
) > var
->v_scale_up_max
)
840 static int mtk_mdp_m2m_s_selection(struct file
*file
, void *fh
,
841 struct v4l2_selection
*s
)
843 struct mtk_mdp_frame
*frame
;
844 struct mtk_mdp_ctx
*ctx
= fh_to_ctx(fh
);
845 struct v4l2_rect new_r
;
846 struct mtk_mdp_variant
*variant
= ctx
->mdp_dev
->variant
;
850 if (s
->type
== V4L2_BUF_TYPE_VIDEO_CAPTURE
) {
851 if (s
->target
== V4L2_SEL_TGT_COMPOSE
)
853 } else if (s
->type
== V4L2_BUF_TYPE_VIDEO_OUTPUT
) {
854 if (s
->target
== V4L2_SEL_TGT_CROP
)
858 mtk_mdp_dbg(1, "[%d] invalid type:%d,%u", ctx
->id
, s
->type
,
864 ret
= mtk_mdp_try_crop(ctx
, s
->type
, &new_r
);
868 if (mtk_mdp_is_target_crop(s
->target
))
869 frame
= &ctx
->s_frame
;
871 frame
= &ctx
->d_frame
;
873 /* Check to see if scaling ratio is within supported range */
874 if (V4L2_TYPE_IS_OUTPUT(s
->type
))
875 ret
= mtk_mdp_check_scaler_ratio(variant
, new_r
.width
,
876 new_r
.height
, ctx
->d_frame
.crop
.width
,
877 ctx
->d_frame
.crop
.height
,
878 ctx
->ctrls
.rotate
->val
);
880 ret
= mtk_mdp_check_scaler_ratio(variant
,
881 ctx
->s_frame
.crop
.width
,
882 ctx
->s_frame
.crop
.height
, new_r
.width
,
883 new_r
.height
, ctx
->ctrls
.rotate
->val
);
886 dev_info(&ctx
->mdp_dev
->pdev
->dev
,
887 "Out of scaler range");
897 static const struct v4l2_ioctl_ops mtk_mdp_m2m_ioctl_ops
= {
898 .vidioc_querycap
= mtk_mdp_m2m_querycap
,
899 .vidioc_enum_fmt_vid_cap
= mtk_mdp_m2m_enum_fmt_vid_cap
,
900 .vidioc_enum_fmt_vid_out
= mtk_mdp_m2m_enum_fmt_vid_out
,
901 .vidioc_g_fmt_vid_cap_mplane
= mtk_mdp_m2m_g_fmt_mplane
,
902 .vidioc_g_fmt_vid_out_mplane
= mtk_mdp_m2m_g_fmt_mplane
,
903 .vidioc_try_fmt_vid_cap_mplane
= mtk_mdp_m2m_try_fmt_mplane
,
904 .vidioc_try_fmt_vid_out_mplane
= mtk_mdp_m2m_try_fmt_mplane
,
905 .vidioc_s_fmt_vid_cap_mplane
= mtk_mdp_m2m_s_fmt_mplane
,
906 .vidioc_s_fmt_vid_out_mplane
= mtk_mdp_m2m_s_fmt_mplane
,
907 .vidioc_reqbufs
= mtk_mdp_m2m_reqbufs
,
908 .vidioc_create_bufs
= v4l2_m2m_ioctl_create_bufs
,
909 .vidioc_expbuf
= v4l2_m2m_ioctl_expbuf
,
910 .vidioc_subscribe_event
= v4l2_ctrl_subscribe_event
,
911 .vidioc_unsubscribe_event
= v4l2_event_unsubscribe
,
912 .vidioc_querybuf
= v4l2_m2m_ioctl_querybuf
,
913 .vidioc_qbuf
= v4l2_m2m_ioctl_qbuf
,
914 .vidioc_dqbuf
= v4l2_m2m_ioctl_dqbuf
,
915 .vidioc_streamon
= mtk_mdp_m2m_streamon
,
916 .vidioc_streamoff
= v4l2_m2m_ioctl_streamoff
,
917 .vidioc_g_selection
= mtk_mdp_m2m_g_selection
,
918 .vidioc_s_selection
= mtk_mdp_m2m_s_selection
921 static int mtk_mdp_m2m_queue_init(void *priv
, struct vb2_queue
*src_vq
,
922 struct vb2_queue
*dst_vq
)
924 struct mtk_mdp_ctx
*ctx
= priv
;
927 memset(src_vq
, 0, sizeof(*src_vq
));
928 src_vq
->type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
929 src_vq
->io_modes
= VB2_MMAP
| VB2_DMABUF
;
930 src_vq
->drv_priv
= ctx
;
931 src_vq
->ops
= &mtk_mdp_m2m_qops
;
932 src_vq
->mem_ops
= &vb2_dma_contig_memops
;
933 src_vq
->buf_struct_size
= sizeof(struct v4l2_m2m_buffer
);
934 src_vq
->timestamp_flags
= V4L2_BUF_FLAG_TIMESTAMP_COPY
;
935 src_vq
->dev
= &ctx
->mdp_dev
->pdev
->dev
;
936 src_vq
->lock
= &ctx
->mdp_dev
->lock
;
938 ret
= vb2_queue_init(src_vq
);
942 memset(dst_vq
, 0, sizeof(*dst_vq
));
943 dst_vq
->type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
944 dst_vq
->io_modes
= VB2_MMAP
| VB2_DMABUF
;
945 dst_vq
->drv_priv
= ctx
;
946 dst_vq
->ops
= &mtk_mdp_m2m_qops
;
947 dst_vq
->mem_ops
= &vb2_dma_contig_memops
;
948 dst_vq
->buf_struct_size
= sizeof(struct v4l2_m2m_buffer
);
949 dst_vq
->timestamp_flags
= V4L2_BUF_FLAG_TIMESTAMP_COPY
;
950 dst_vq
->dev
= &ctx
->mdp_dev
->pdev
->dev
;
951 dst_vq
->lock
= &ctx
->mdp_dev
->lock
;
953 return vb2_queue_init(dst_vq
);
956 static int mtk_mdp_s_ctrl(struct v4l2_ctrl
*ctrl
)
958 struct mtk_mdp_ctx
*ctx
= ctrl_to_ctx(ctrl
);
959 struct mtk_mdp_dev
*mdp
= ctx
->mdp_dev
;
960 struct mtk_mdp_variant
*variant
= mdp
->variant
;
963 if (ctrl
->flags
& V4L2_CTRL_FLAG_INACTIVE
)
968 ctx
->hflip
= ctrl
->val
;
971 ctx
->vflip
= ctrl
->val
;
973 case V4L2_CID_ROTATE
:
974 ret
= mtk_mdp_check_scaler_ratio(variant
,
975 ctx
->s_frame
.crop
.width
,
976 ctx
->s_frame
.crop
.height
,
977 ctx
->d_frame
.crop
.width
,
978 ctx
->d_frame
.crop
.height
,
979 ctx
->ctrls
.rotate
->val
);
984 ctx
->rotation
= ctrl
->val
;
986 case V4L2_CID_ALPHA_COMPONENT
:
987 ctx
->d_frame
.alpha
= ctrl
->val
;
994 static const struct v4l2_ctrl_ops mtk_mdp_ctrl_ops
= {
995 .s_ctrl
= mtk_mdp_s_ctrl
,
998 static int mtk_mdp_ctrls_create(struct mtk_mdp_ctx
*ctx
)
1000 v4l2_ctrl_handler_init(&ctx
->ctrl_handler
, MTK_MDP_MAX_CTRL_NUM
);
1002 ctx
->ctrls
.rotate
= v4l2_ctrl_new_std(&ctx
->ctrl_handler
,
1003 &mtk_mdp_ctrl_ops
, V4L2_CID_ROTATE
, 0, 270, 90, 0);
1004 ctx
->ctrls
.hflip
= v4l2_ctrl_new_std(&ctx
->ctrl_handler
,
1008 ctx
->ctrls
.vflip
= v4l2_ctrl_new_std(&ctx
->ctrl_handler
,
1012 ctx
->ctrls
.global_alpha
= v4l2_ctrl_new_std(&ctx
->ctrl_handler
,
1014 V4L2_CID_ALPHA_COMPONENT
,
1016 ctx
->ctrls_rdy
= ctx
->ctrl_handler
.error
== 0;
1018 if (ctx
->ctrl_handler
.error
) {
1019 int err
= ctx
->ctrl_handler
.error
;
1021 v4l2_ctrl_handler_free(&ctx
->ctrl_handler
);
1022 dev_err(&ctx
->mdp_dev
->pdev
->dev
,
1023 "Failed to create control handlers\n");
1030 static void mtk_mdp_set_default_params(struct mtk_mdp_ctx
*ctx
)
1032 struct mtk_mdp_dev
*mdp
= ctx
->mdp_dev
;
1033 struct mtk_mdp_frame
*frame
;
1035 frame
= mtk_mdp_ctx_get_frame(ctx
, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
);
1036 frame
->fmt
= mtk_mdp_find_fmt_by_index(0,
1037 V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
);
1038 frame
->width
= mdp
->variant
->pix_min
->org_w
;
1039 frame
->height
= mdp
->variant
->pix_min
->org_h
;
1040 frame
->payload
[0] = frame
->width
* frame
->height
;
1041 frame
->payload
[1] = frame
->payload
[0] / 2;
1043 frame
= mtk_mdp_ctx_get_frame(ctx
, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
);
1044 frame
->fmt
= mtk_mdp_find_fmt_by_index(0,
1045 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
);
1046 frame
->width
= mdp
->variant
->pix_min
->target_rot_dis_w
;
1047 frame
->height
= mdp
->variant
->pix_min
->target_rot_dis_h
;
1048 frame
->payload
[0] = frame
->width
* frame
->height
;
1049 frame
->payload
[1] = frame
->payload
[0] / 2;
1053 static int mtk_mdp_m2m_open(struct file
*file
)
1055 struct mtk_mdp_dev
*mdp
= video_drvdata(file
);
1056 struct video_device
*vfd
= video_devdata(file
);
1057 struct mtk_mdp_ctx
*ctx
= NULL
;
1059 struct v4l2_format default_format
;
1061 ctx
= kzalloc(sizeof(*ctx
), GFP_KERNEL
);
1065 if (mutex_lock_interruptible(&mdp
->lock
)) {
1070 mutex_init(&ctx
->slock
);
1071 ctx
->id
= mdp
->id_counter
++;
1072 v4l2_fh_init(&ctx
->fh
, vfd
);
1073 file
->private_data
= &ctx
->fh
;
1074 ret
= mtk_mdp_ctrls_create(ctx
);
1078 /* Use separate control handler per file handle */
1079 ctx
->fh
.ctrl_handler
= &ctx
->ctrl_handler
;
1080 v4l2_fh_add(&ctx
->fh
);
1081 INIT_LIST_HEAD(&ctx
->list
);
1084 mtk_mdp_set_default_params(ctx
);
1086 INIT_WORK(&ctx
->work
, mtk_mdp_m2m_worker
);
1087 ctx
->m2m_ctx
= v4l2_m2m_ctx_init(mdp
->m2m_dev
, ctx
,
1088 mtk_mdp_m2m_queue_init
);
1089 if (IS_ERR(ctx
->m2m_ctx
)) {
1090 dev_err(&mdp
->pdev
->dev
, "Failed to initialize m2m context");
1091 ret
= PTR_ERR(ctx
->m2m_ctx
);
1094 ctx
->fh
.m2m_ctx
= ctx
->m2m_ctx
;
1095 if (mdp
->ctx_num
++ == 0) {
1096 ret
= vpu_load_firmware(mdp
->vpu_dev
);
1098 dev_err(&mdp
->pdev
->dev
,
1099 "vpu_load_firmware failed %d\n", ret
);
1103 ret
= mtk_mdp_vpu_register(mdp
->pdev
);
1105 dev_err(&mdp
->pdev
->dev
,
1106 "mdp_vpu register failed %d\n", ret
);
1111 list_add(&ctx
->list
, &mdp
->ctx_list
);
1112 mutex_unlock(&mdp
->lock
);
1114 /* Default format */
1115 memset(&default_format
, 0, sizeof(default_format
));
1116 default_format
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
1117 default_format
.fmt
.pix_mp
.width
= 32;
1118 default_format
.fmt
.pix_mp
.height
= 32;
1119 default_format
.fmt
.pix_mp
.pixelformat
= V4L2_PIX_FMT_YUV420M
;
1120 mtk_mdp_m2m_s_fmt_mplane(file
, &ctx
->fh
, &default_format
);
1121 default_format
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
1122 mtk_mdp_m2m_s_fmt_mplane(file
, &ctx
->fh
, &default_format
);
1124 mtk_mdp_dbg(0, "%s [%d]", dev_name(&mdp
->pdev
->dev
), ctx
->id
);
1130 v4l2_m2m_ctx_release(ctx
->m2m_ctx
);
1132 v4l2_ctrl_handler_free(&ctx
->ctrl_handler
);
1134 v4l2_fh_del(&ctx
->fh
);
1135 v4l2_fh_exit(&ctx
->fh
);
1136 mutex_unlock(&mdp
->lock
);
1143 static int mtk_mdp_m2m_release(struct file
*file
)
1145 struct mtk_mdp_ctx
*ctx
= fh_to_ctx(file
->private_data
);
1146 struct mtk_mdp_dev
*mdp
= ctx
->mdp_dev
;
1148 flush_workqueue(mdp
->job_wq
);
1149 mutex_lock(&mdp
->lock
);
1150 v4l2_m2m_ctx_release(ctx
->m2m_ctx
);
1151 v4l2_ctrl_handler_free(&ctx
->ctrl_handler
);
1152 v4l2_fh_del(&ctx
->fh
);
1153 v4l2_fh_exit(&ctx
->fh
);
1154 mtk_mdp_vpu_deinit(&ctx
->vpu
);
1156 list_del_init(&ctx
->list
);
1158 mtk_mdp_dbg(0, "%s [%d]", dev_name(&mdp
->pdev
->dev
), ctx
->id
);
1160 mutex_unlock(&mdp
->lock
);
1166 static const struct v4l2_file_operations mtk_mdp_m2m_fops
= {
1167 .owner
= THIS_MODULE
,
1168 .open
= mtk_mdp_m2m_open
,
1169 .release
= mtk_mdp_m2m_release
,
1170 .poll
= v4l2_m2m_fop_poll
,
1171 .unlocked_ioctl
= video_ioctl2
,
1172 .mmap
= v4l2_m2m_fop_mmap
,
1175 static const struct v4l2_m2m_ops mtk_mdp_m2m_ops
= {
1176 .device_run
= mtk_mdp_m2m_device_run
,
1179 int mtk_mdp_register_m2m_device(struct mtk_mdp_dev
*mdp
)
1181 struct device
*dev
= &mdp
->pdev
->dev
;
1184 mdp
->variant
= &mtk_mdp_default_variant
;
1185 mdp
->vdev
= video_device_alloc();
1187 dev_err(dev
, "failed to allocate video device\n");
1189 goto err_video_alloc
;
1191 mdp
->vdev
->device_caps
= V4L2_CAP_VIDEO_M2M_MPLANE
| V4L2_CAP_STREAMING
;
1192 mdp
->vdev
->fops
= &mtk_mdp_m2m_fops
;
1193 mdp
->vdev
->ioctl_ops
= &mtk_mdp_m2m_ioctl_ops
;
1194 mdp
->vdev
->release
= video_device_release
;
1195 mdp
->vdev
->lock
= &mdp
->lock
;
1196 mdp
->vdev
->vfl_dir
= VFL_DIR_M2M
;
1197 mdp
->vdev
->v4l2_dev
= &mdp
->v4l2_dev
;
1198 snprintf(mdp
->vdev
->name
, sizeof(mdp
->vdev
->name
), "%s:m2m",
1199 MTK_MDP_MODULE_NAME
);
1200 video_set_drvdata(mdp
->vdev
, mdp
);
1202 mdp
->m2m_dev
= v4l2_m2m_init(&mtk_mdp_m2m_ops
);
1203 if (IS_ERR(mdp
->m2m_dev
)) {
1204 dev_err(dev
, "failed to initialize v4l2-m2m device\n");
1205 ret
= PTR_ERR(mdp
->m2m_dev
);
1209 ret
= video_register_device(mdp
->vdev
, VFL_TYPE_VIDEO
, 2);
1211 dev_err(dev
, "failed to register video device\n");
1212 goto err_vdev_register
;
1215 v4l2_info(&mdp
->v4l2_dev
, "driver registered as /dev/video%d",
1220 v4l2_m2m_release(mdp
->m2m_dev
);
1222 video_device_release(mdp
->vdev
);
1228 void mtk_mdp_unregister_m2m_device(struct mtk_mdp_dev
*mdp
)
1230 video_unregister_device(mdp
->vdev
);
1231 v4l2_m2m_release(mdp
->m2m_dev
);