1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2015-2016 MediaTek Inc. |
4 | * Author: Houlong Wei <houlong.wei@mediatek.com> |
5 | * Ming Hsiu Tsai <minghsiu.tsai@mediatek.com> |
6 | */ |
7 | |
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> |
16 | |
17 | #include "mtk_mdp_core.h" |
18 | #include "mtk_mdp_m2m.h" |
19 | #include "mtk_mdp_regs.h" |
20 | #include "mtk_vpu.h" |
21 | |
22 | |
23 | /** |
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 |
31 | */ |
32 | struct mtk_mdp_pix_limit { |
33 | u16 org_w; |
34 | u16 org_h; |
35 | u16 target_rot_dis_w; |
36 | u16 target_rot_dis_h; |
37 | u16 target_rot_en_w; |
38 | u16 target_rot_en_h; |
39 | }; |
40 | |
41 | static struct mtk_mdp_pix_align mtk_mdp_size_align = { |
42 | .org_w = 16, |
43 | .org_h = 16, |
44 | .target_w = 2, |
45 | .target_h = 2, |
46 | }; |
47 | |
48 | static const struct mtk_mdp_fmt mtk_mdp_formats[] = { |
49 | { |
50 | .pixelformat = V4L2_PIX_FMT_MT21C, |
51 | .depth = { 8, 4 }, |
52 | .row_depth = { 8, 8 }, |
53 | .num_planes = 2, |
54 | .num_comp = 2, |
55 | .align = &mtk_mdp_size_align, |
56 | .flags = MTK_MDP_FMT_FLAG_OUTPUT, |
57 | }, { |
58 | .pixelformat = V4L2_PIX_FMT_NV12M, |
59 | .depth = { 8, 4 }, |
60 | .row_depth = { 8, 8 }, |
61 | .num_planes = 2, |
62 | .num_comp = 2, |
63 | .flags = MTK_MDP_FMT_FLAG_OUTPUT | |
64 | MTK_MDP_FMT_FLAG_CAPTURE, |
65 | }, { |
66 | .pixelformat = V4L2_PIX_FMT_YUV420M, |
67 | .depth = { 8, 2, 2 }, |
68 | .row_depth = { 8, 4, 4 }, |
69 | .num_planes = 3, |
70 | .num_comp = 3, |
71 | .flags = MTK_MDP_FMT_FLAG_OUTPUT | |
72 | MTK_MDP_FMT_FLAG_CAPTURE, |
73 | }, { |
74 | .pixelformat = V4L2_PIX_FMT_YVU420, |
75 | .depth = { 12 }, |
76 | .row_depth = { 8 }, |
77 | .num_planes = 1, |
78 | .num_comp = 3, |
79 | .flags = MTK_MDP_FMT_FLAG_OUTPUT | |
80 | MTK_MDP_FMT_FLAG_CAPTURE, |
81 | } |
82 | }; |
83 | |
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, |
89 | }; |
90 | |
91 | static struct mtk_mdp_pix_limit mtk_mdp_size_min = { |
92 | .org_w = 16, |
93 | .org_h = 16, |
94 | .target_rot_dis_w = 16, |
95 | .target_rot_dis_h = 16, |
96 | .target_rot_en_w = 16, |
97 | .target_rot_en_h = 16, |
98 | }; |
99 | |
100 | /* align size for normal raster scan pixel format */ |
101 | static struct mtk_mdp_pix_align mtk_mdp_rs_align = { |
102 | .org_w = 2, |
103 | .org_h = 2, |
104 | .target_w = 2, |
105 | .target_h = 2, |
106 | }; |
107 | |
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, |
116 | }; |
117 | |
118 | static const struct mtk_mdp_fmt *mtk_mdp_find_fmt(u32 pixelformat, u32 type) |
119 | { |
120 | u32 i, flag; |
121 | |
122 | flag = V4L2_TYPE_IS_OUTPUT(type) ? MTK_MDP_FMT_FLAG_OUTPUT : |
123 | MTK_MDP_FMT_FLAG_CAPTURE; |
124 | |
125 | for (i = 0; i < ARRAY_SIZE(mtk_mdp_formats); ++i) { |
126 | if (!(mtk_mdp_formats[i].flags & flag)) |
127 | continue; |
128 | if (mtk_mdp_formats[i].pixelformat == pixelformat) |
129 | return &mtk_mdp_formats[i]; |
130 | } |
131 | return NULL; |
132 | } |
133 | |
134 | static const struct mtk_mdp_fmt *mtk_mdp_find_fmt_by_index(u32 index, u32 type) |
135 | { |
136 | u32 i, flag, num = 0; |
137 | |
138 | flag = V4L2_TYPE_IS_OUTPUT(type) ? MTK_MDP_FMT_FLAG_OUTPUT : |
139 | MTK_MDP_FMT_FLAG_CAPTURE; |
140 | |
141 | for (i = 0; i < ARRAY_SIZE(mtk_mdp_formats); ++i) { |
142 | if (!(mtk_mdp_formats[i].flags & flag)) |
143 | continue; |
144 | if (index == num) |
145 | return &mtk_mdp_formats[i]; |
146 | num++; |
147 | } |
148 | return NULL; |
149 | } |
150 | |
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) |
155 | { |
156 | int org_w, org_h, step_w, step_h; |
157 | int walign, halign; |
158 | |
159 | org_w = *w; |
160 | org_h = *h; |
161 | walign = ffs(align_w) - 1; |
162 | halign = ffs(align_h) - 1; |
163 | v4l_bound_align_image(width: w, wmin, wmax, walign, height: h, hmin, hmax, halign, salign: 0); |
164 | |
165 | step_w = 1 << walign; |
166 | step_h = 1 << halign; |
167 | if (*w < org_w && (*w + step_w) <= wmax) |
168 | *w += step_w; |
169 | if (*h < org_h && (*h + step_h) <= hmax) |
170 | *h += step_h; |
171 | } |
172 | |
173 | static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx, |
174 | struct v4l2_format *f) |
175 | { |
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; |
182 | int i; |
183 | |
184 | fmt = mtk_mdp_find_fmt(pixelformat: pix_mp->pixelformat, type: f->type); |
185 | if (!fmt) |
186 | fmt = mtk_mdp_find_fmt_by_index(index: 0, type: f->type); |
187 | if (!fmt) { |
188 | dev_dbg(&ctx->mdp_dev->pdev->dev, |
189 | "pixelformat format 0x%X invalid\n" , |
190 | pix_mp->pixelformat); |
191 | return NULL; |
192 | } |
193 | |
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; |
201 | } |
202 | |
203 | max_w = variant->pix_max->target_rot_dis_w; |
204 | max_h = variant->pix_max->target_rot_dis_h; |
205 | |
206 | if (fmt->align == NULL) { |
207 | /* use default alignment */ |
208 | align_w = variant->pix_align->org_w; |
209 | align_h = variant->pix_align->org_h; |
210 | } else { |
211 | align_w = fmt->align->org_w; |
212 | align_h = fmt->align->org_h; |
213 | } |
214 | |
215 | if (V4L2_TYPE_IS_OUTPUT(f->type)) { |
216 | min_w = variant->pix_min->org_w; |
217 | min_h = variant->pix_min->org_h; |
218 | } else { |
219 | min_w = variant->pix_min->target_rot_dis_w; |
220 | min_h = variant->pix_min->target_rot_dis_h; |
221 | } |
222 | |
223 | mtk_mdp_dbg(2, "[%d] type:%d, wxh:%ux%u, align:%ux%u, max:%ux%u" , |
224 | ctx->id, f->type, pix_mp->width, pix_mp->height, |
225 | align_w, align_h, max_w, max_h); |
226 | /* |
227 | * To check if image size is modified to adjust parameter against |
228 | * hardware abilities |
229 | */ |
230 | org_w = pix_mp->width; |
231 | org_h = pix_mp->height; |
232 | |
233 | mtk_mdp_bound_align_image(w: &pix_mp->width, wmin: min_w, wmax: max_w, align_w, |
234 | h: &pix_mp->height, hmin: min_h, hmax: max_h, align_h); |
235 | |
236 | if (org_w != pix_mp->width || org_h != pix_mp->height) |
237 | mtk_mdp_dbg(1, "[%d] size change:%ux%u to %ux%u" , ctx->id, |
238 | org_w, org_h, pix_mp->width, pix_mp->height); |
239 | pix_mp->num_planes = fmt->num_planes; |
240 | |
241 | for (i = 0; i < pix_mp->num_planes; ++i) { |
242 | int bpl = (pix_mp->width * fmt->row_depth[i]) / 8; |
243 | int sizeimage = (pix_mp->width * pix_mp->height * |
244 | fmt->depth[i]) / 8; |
245 | |
246 | pix_mp->plane_fmt[i].bytesperline = bpl; |
247 | if (pix_mp->plane_fmt[i].sizeimage < sizeimage) |
248 | pix_mp->plane_fmt[i].sizeimage = sizeimage; |
249 | mtk_mdp_dbg(2, "[%d] p%d, bpl:%d, sizeimage:%u (%u)" , ctx->id, |
250 | i, bpl, pix_mp->plane_fmt[i].sizeimage, sizeimage); |
251 | } |
252 | |
253 | return fmt; |
254 | } |
255 | |
256 | static struct mtk_mdp_frame *mtk_mdp_ctx_get_frame(struct mtk_mdp_ctx *ctx, |
257 | enum v4l2_buf_type type) |
258 | { |
259 | if (V4L2_TYPE_IS_OUTPUT(type)) |
260 | return &ctx->s_frame; |
261 | return &ctx->d_frame; |
262 | } |
263 | |
264 | static void mtk_mdp_check_crop_change(u32 new_w, u32 new_h, u32 *w, u32 *h) |
265 | { |
266 | if (new_w != *w || new_h != *h) { |
267 | mtk_mdp_dbg(1, "size change:%dx%d to %dx%d" , |
268 | *w, *h, new_w, new_h); |
269 | |
270 | *w = new_w; |
271 | *h = new_h; |
272 | } |
273 | } |
274 | |
275 | static int mtk_mdp_try_crop(struct mtk_mdp_ctx *ctx, u32 type, |
276 | struct v4l2_rect *r) |
277 | { |
278 | struct mtk_mdp_frame *frame; |
279 | struct mtk_mdp_dev *mdp = ctx->mdp_dev; |
280 | struct mtk_mdp_variant *variant = mdp->variant; |
281 | u32 align_w, align_h, new_w, new_h; |
282 | u32 min_w, min_h, max_w, max_h; |
283 | |
284 | if (r->top < 0 || r->left < 0) { |
285 | dev_err(&ctx->mdp_dev->pdev->dev, |
286 | "doesn't support negative values for top & left\n" ); |
287 | return -EINVAL; |
288 | } |
289 | |
290 | mtk_mdp_dbg(2, "[%d] type:%d, set wxh:%dx%d" , ctx->id, type, |
291 | r->width, r->height); |
292 | |
293 | frame = mtk_mdp_ctx_get_frame(ctx, type); |
294 | max_w = frame->width; |
295 | max_h = frame->height; |
296 | new_w = r->width; |
297 | new_h = r->height; |
298 | |
299 | if (V4L2_TYPE_IS_OUTPUT(type)) { |
300 | align_w = 1; |
301 | align_h = 1; |
302 | min_w = 64; |
303 | min_h = 32; |
304 | } else { |
305 | align_w = variant->pix_align->target_w; |
306 | align_h = variant->pix_align->target_h; |
307 | if (ctx->ctrls.rotate->val == 90 || |
308 | ctx->ctrls.rotate->val == 270) { |
309 | max_w = frame->height; |
310 | max_h = frame->width; |
311 | min_w = variant->pix_min->target_rot_en_w; |
312 | min_h = variant->pix_min->target_rot_en_h; |
313 | new_w = r->height; |
314 | new_h = r->width; |
315 | } else { |
316 | min_w = variant->pix_min->target_rot_dis_w; |
317 | min_h = variant->pix_min->target_rot_dis_h; |
318 | } |
319 | } |
320 | |
321 | mtk_mdp_dbg(2, "[%d] align:%dx%d, min:%dx%d, new:%dx%d" , ctx->id, |
322 | align_w, align_h, min_w, min_h, new_w, new_h); |
323 | |
324 | mtk_mdp_bound_align_image(w: &new_w, wmin: min_w, wmax: max_w, align_w, |
325 | h: &new_h, hmin: min_h, hmax: max_h, align_h); |
326 | |
327 | if (V4L2_TYPE_IS_CAPTURE(type) && |
328 | (ctx->ctrls.rotate->val == 90 || ctx->ctrls.rotate->val == 270)) |
329 | mtk_mdp_check_crop_change(new_w: new_h, new_h: new_w, |
330 | w: &r->width, h: &r->height); |
331 | else |
332 | mtk_mdp_check_crop_change(new_w, new_h, |
333 | w: &r->width, h: &r->height); |
334 | |
335 | /* adjust left/top if cropping rectangle is out of bounds */ |
336 | /* Need to add code to algin left value with 2's multiple */ |
337 | if (r->left + new_w > max_w) |
338 | r->left = max_w - new_w; |
339 | if (r->top + new_h > max_h) |
340 | r->top = max_h - new_h; |
341 | |
342 | if (r->left & 1) |
343 | r->left -= 1; |
344 | |
345 | mtk_mdp_dbg(2, "[%d] crop l,t,w,h:%d,%d,%d,%d, max:%dx%d" , ctx->id, |
346 | r->left, r->top, r->width, |
347 | r->height, max_w, max_h); |
348 | return 0; |
349 | } |
350 | |
351 | static inline struct mtk_mdp_ctx *fh_to_ctx(struct v4l2_fh *fh) |
352 | { |
353 | return container_of(fh, struct mtk_mdp_ctx, fh); |
354 | } |
355 | |
356 | static inline struct mtk_mdp_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) |
357 | { |
358 | return container_of(ctrl->handler, struct mtk_mdp_ctx, ctrl_handler); |
359 | } |
360 | |
361 | void mtk_mdp_ctx_state_lock_set(struct mtk_mdp_ctx *ctx, u32 state) |
362 | { |
363 | mutex_lock(&ctx->slock); |
364 | ctx->state |= state; |
365 | mutex_unlock(lock: &ctx->slock); |
366 | } |
367 | |
368 | static bool mtk_mdp_ctx_state_is_set(struct mtk_mdp_ctx *ctx, u32 mask) |
369 | { |
370 | bool ret; |
371 | |
372 | mutex_lock(&ctx->slock); |
373 | ret = (ctx->state & mask) == mask; |
374 | mutex_unlock(lock: &ctx->slock); |
375 | return ret; |
376 | } |
377 | |
378 | static void mtk_mdp_set_frame_size(struct mtk_mdp_frame *frame, int width, |
379 | int height) |
380 | { |
381 | frame->width = width; |
382 | frame->height = height; |
383 | frame->crop.width = width; |
384 | frame->crop.height = height; |
385 | frame->crop.left = 0; |
386 | frame->crop.top = 0; |
387 | } |
388 | |
389 | static int mtk_mdp_m2m_start_streaming(struct vb2_queue *q, unsigned int count) |
390 | { |
391 | struct mtk_mdp_ctx *ctx = q->drv_priv; |
392 | int ret; |
393 | |
394 | ret = pm_runtime_resume_and_get(dev: &ctx->mdp_dev->pdev->dev); |
395 | if (ret < 0) |
396 | mtk_mdp_dbg(1, "[%d] pm_runtime_resume_and_get failed:%d" , |
397 | ctx->id, ret); |
398 | |
399 | return ret; |
400 | } |
401 | |
402 | static void *mtk_mdp_m2m_buf_remove(struct mtk_mdp_ctx *ctx, |
403 | enum v4l2_buf_type type) |
404 | { |
405 | if (V4L2_TYPE_IS_OUTPUT(type)) |
406 | return v4l2_m2m_src_buf_remove(m2m_ctx: ctx->m2m_ctx); |
407 | else |
408 | return v4l2_m2m_dst_buf_remove(m2m_ctx: ctx->m2m_ctx); |
409 | } |
410 | |
411 | static void mtk_mdp_m2m_stop_streaming(struct vb2_queue *q) |
412 | { |
413 | struct mtk_mdp_ctx *ctx = q->drv_priv; |
414 | struct vb2_buffer *vb; |
415 | |
416 | vb = mtk_mdp_m2m_buf_remove(ctx, type: q->type); |
417 | while (vb != NULL) { |
418 | v4l2_m2m_buf_done(to_vb2_v4l2_buffer(vb), state: VB2_BUF_STATE_ERROR); |
419 | vb = mtk_mdp_m2m_buf_remove(ctx, type: q->type); |
420 | } |
421 | |
422 | pm_runtime_put(dev: &ctx->mdp_dev->pdev->dev); |
423 | } |
424 | |
425 | /* The color format (num_planes) must be already configured. */ |
426 | static void mtk_mdp_prepare_addr(struct mtk_mdp_ctx *ctx, |
427 | struct vb2_buffer *vb, |
428 | struct mtk_mdp_frame *frame, |
429 | struct mtk_mdp_addr *addr) |
430 | { |
431 | u32 pix_size, planes, i; |
432 | |
433 | pix_size = frame->width * frame->height; |
434 | planes = min_t(u32, frame->fmt->num_planes, ARRAY_SIZE(addr->addr)); |
435 | for (i = 0; i < planes; i++) |
436 | addr->addr[i] = vb2_dma_contig_plane_dma_addr(vb, plane_no: i); |
437 | |
438 | if (planes == 1) { |
439 | if (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) { |
440 | addr->addr[1] = (dma_addr_t)(addr->addr[0] + pix_size); |
441 | addr->addr[2] = (dma_addr_t)(addr->addr[1] + |
442 | (pix_size >> 2)); |
443 | } else { |
444 | dev_err(&ctx->mdp_dev->pdev->dev, |
445 | "Invalid pixelformat:0x%x\n" , |
446 | frame->fmt->pixelformat); |
447 | } |
448 | } |
449 | mtk_mdp_dbg(3, "[%d] planes:%d, size:%d, addr:%p,%p,%p" , |
450 | ctx->id, planes, pix_size, (void *)addr->addr[0], |
451 | (void *)addr->addr[1], (void *)addr->addr[2]); |
452 | } |
453 | |
454 | static void mtk_mdp_m2m_get_bufs(struct mtk_mdp_ctx *ctx) |
455 | { |
456 | struct mtk_mdp_frame *s_frame, *d_frame; |
457 | struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; |
458 | |
459 | s_frame = &ctx->s_frame; |
460 | d_frame = &ctx->d_frame; |
461 | |
462 | src_vbuf = v4l2_m2m_next_src_buf(m2m_ctx: ctx->m2m_ctx); |
463 | mtk_mdp_prepare_addr(ctx, vb: &src_vbuf->vb2_buf, frame: s_frame, addr: &s_frame->addr); |
464 | |
465 | dst_vbuf = v4l2_m2m_next_dst_buf(m2m_ctx: ctx->m2m_ctx); |
466 | mtk_mdp_prepare_addr(ctx, vb: &dst_vbuf->vb2_buf, frame: d_frame, addr: &d_frame->addr); |
467 | |
468 | dst_vbuf->vb2_buf.timestamp = src_vbuf->vb2_buf.timestamp; |
469 | } |
470 | |
471 | static void mtk_mdp_process_done(void *priv, int vb_state) |
472 | { |
473 | struct mtk_mdp_dev *mdp = priv; |
474 | struct mtk_mdp_ctx *ctx; |
475 | struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; |
476 | |
477 | ctx = v4l2_m2m_get_curr_priv(m2m_dev: mdp->m2m_dev); |
478 | if (!ctx) |
479 | return; |
480 | |
481 | src_vbuf = v4l2_m2m_src_buf_remove(m2m_ctx: ctx->m2m_ctx); |
482 | dst_vbuf = v4l2_m2m_dst_buf_remove(m2m_ctx: ctx->m2m_ctx); |
483 | |
484 | dst_vbuf->vb2_buf.timestamp = src_vbuf->vb2_buf.timestamp; |
485 | dst_vbuf->timecode = src_vbuf->timecode; |
486 | dst_vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; |
487 | dst_vbuf->flags |= src_vbuf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; |
488 | |
489 | v4l2_m2m_buf_done(buf: src_vbuf, state: vb_state); |
490 | v4l2_m2m_buf_done(buf: dst_vbuf, state: vb_state); |
491 | v4l2_m2m_job_finish(m2m_dev: ctx->mdp_dev->m2m_dev, m2m_ctx: ctx->m2m_ctx); |
492 | } |
493 | |
494 | static void mtk_mdp_m2m_worker(struct work_struct *work) |
495 | { |
496 | struct mtk_mdp_ctx *ctx = |
497 | container_of(work, struct mtk_mdp_ctx, work); |
498 | struct mtk_mdp_dev *mdp = ctx->mdp_dev; |
499 | enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR; |
500 | int ret; |
501 | |
502 | if (mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_CTX_ERROR)) { |
503 | dev_err(&mdp->pdev->dev, "ctx is in error state" ); |
504 | goto worker_end; |
505 | } |
506 | |
507 | mtk_mdp_m2m_get_bufs(ctx); |
508 | |
509 | mtk_mdp_hw_set_input_addr(ctx, addr: &ctx->s_frame.addr); |
510 | mtk_mdp_hw_set_output_addr(ctx, addr: &ctx->d_frame.addr); |
511 | |
512 | mtk_mdp_hw_set_in_size(ctx); |
513 | mtk_mdp_hw_set_in_image_format(ctx); |
514 | |
515 | mtk_mdp_hw_set_out_size(ctx); |
516 | mtk_mdp_hw_set_out_image_format(ctx); |
517 | |
518 | mtk_mdp_hw_set_rotation(ctx); |
519 | mtk_mdp_hw_set_global_alpha(ctx); |
520 | |
521 | ret = mtk_mdp_vpu_process(vpu: &ctx->vpu); |
522 | if (ret) { |
523 | dev_err(&mdp->pdev->dev, "processing failed: %d" , ret); |
524 | goto worker_end; |
525 | } |
526 | |
527 | buf_state = VB2_BUF_STATE_DONE; |
528 | |
529 | worker_end: |
530 | mtk_mdp_process_done(priv: mdp, vb_state: buf_state); |
531 | } |
532 | |
533 | static void mtk_mdp_m2m_device_run(void *priv) |
534 | { |
535 | struct mtk_mdp_ctx *ctx = priv; |
536 | |
537 | queue_work(wq: ctx->mdp_dev->job_wq, work: &ctx->work); |
538 | } |
539 | |
540 | static int mtk_mdp_m2m_queue_setup(struct vb2_queue *vq, |
541 | unsigned int *num_buffers, unsigned int *num_planes, |
542 | unsigned int sizes[], struct device *alloc_devs[]) |
543 | { |
544 | struct mtk_mdp_ctx *ctx = vb2_get_drv_priv(q: vq); |
545 | struct mtk_mdp_frame *frame; |
546 | int i; |
547 | |
548 | frame = mtk_mdp_ctx_get_frame(ctx, type: vq->type); |
549 | *num_planes = frame->fmt->num_planes; |
550 | for (i = 0; i < frame->fmt->num_planes; i++) |
551 | sizes[i] = frame->payload[i]; |
552 | mtk_mdp_dbg(2, "[%d] type:%d, planes:%d, buffers:%d, size:%u,%u" , |
553 | ctx->id, vq->type, *num_planes, *num_buffers, |
554 | sizes[0], sizes[1]); |
555 | return 0; |
556 | } |
557 | |
558 | static int mtk_mdp_m2m_buf_prepare(struct vb2_buffer *vb) |
559 | { |
560 | struct mtk_mdp_ctx *ctx = vb2_get_drv_priv(q: vb->vb2_queue); |
561 | struct mtk_mdp_frame *frame; |
562 | int i; |
563 | |
564 | frame = mtk_mdp_ctx_get_frame(ctx, type: vb->vb2_queue->type); |
565 | |
566 | if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { |
567 | for (i = 0; i < frame->fmt->num_planes; i++) |
568 | vb2_set_plane_payload(vb, plane_no: i, size: frame->payload[i]); |
569 | } |
570 | |
571 | return 0; |
572 | } |
573 | |
574 | static void mtk_mdp_m2m_buf_queue(struct vb2_buffer *vb) |
575 | { |
576 | struct mtk_mdp_ctx *ctx = vb2_get_drv_priv(q: vb->vb2_queue); |
577 | |
578 | v4l2_m2m_buf_queue(m2m_ctx: ctx->m2m_ctx, to_vb2_v4l2_buffer(vb)); |
579 | } |
580 | |
581 | static const struct vb2_ops mtk_mdp_m2m_qops = { |
582 | .queue_setup = mtk_mdp_m2m_queue_setup, |
583 | .buf_prepare = mtk_mdp_m2m_buf_prepare, |
584 | .buf_queue = mtk_mdp_m2m_buf_queue, |
585 | .stop_streaming = mtk_mdp_m2m_stop_streaming, |
586 | .start_streaming = mtk_mdp_m2m_start_streaming, |
587 | .wait_prepare = vb2_ops_wait_prepare, |
588 | .wait_finish = vb2_ops_wait_finish, |
589 | }; |
590 | |
591 | static int mtk_mdp_m2m_querycap(struct file *file, void *fh, |
592 | struct v4l2_capability *cap) |
593 | { |
594 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); |
595 | struct mtk_mdp_dev *mdp = ctx->mdp_dev; |
596 | |
597 | strscpy(cap->driver, MTK_MDP_MODULE_NAME, sizeof(cap->driver)); |
598 | strscpy(cap->card, mdp->pdev->name, sizeof(cap->card)); |
599 | strscpy(cap->bus_info, "platform:mt8173" , sizeof(cap->bus_info)); |
600 | |
601 | return 0; |
602 | } |
603 | |
604 | static int mtk_mdp_enum_fmt(struct v4l2_fmtdesc *f, u32 type) |
605 | { |
606 | const struct mtk_mdp_fmt *fmt; |
607 | |
608 | fmt = mtk_mdp_find_fmt_by_index(index: f->index, type); |
609 | if (!fmt) |
610 | return -EINVAL; |
611 | |
612 | f->pixelformat = fmt->pixelformat; |
613 | |
614 | return 0; |
615 | } |
616 | |
617 | static int mtk_mdp_m2m_enum_fmt_vid_cap(struct file *file, void *priv, |
618 | struct v4l2_fmtdesc *f) |
619 | { |
620 | return mtk_mdp_enum_fmt(f, type: V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); |
621 | } |
622 | |
623 | static int mtk_mdp_m2m_enum_fmt_vid_out(struct file *file, void *priv, |
624 | struct v4l2_fmtdesc *f) |
625 | { |
626 | return mtk_mdp_enum_fmt(f, type: V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); |
627 | } |
628 | |
629 | static int mtk_mdp_m2m_g_fmt_mplane(struct file *file, void *fh, |
630 | struct v4l2_format *f) |
631 | { |
632 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); |
633 | struct mtk_mdp_frame *frame; |
634 | struct v4l2_pix_format_mplane *pix_mp; |
635 | int i; |
636 | |
637 | mtk_mdp_dbg(2, "[%d] type:%d" , ctx->id, f->type); |
638 | |
639 | frame = mtk_mdp_ctx_get_frame(ctx, type: f->type); |
640 | pix_mp = &f->fmt.pix_mp; |
641 | |
642 | pix_mp->width = frame->width; |
643 | pix_mp->height = frame->height; |
644 | pix_mp->field = V4L2_FIELD_NONE; |
645 | pix_mp->pixelformat = frame->fmt->pixelformat; |
646 | pix_mp->num_planes = frame->fmt->num_planes; |
647 | pix_mp->colorspace = ctx->colorspace; |
648 | pix_mp->xfer_func = ctx->xfer_func; |
649 | pix_mp->ycbcr_enc = ctx->ycbcr_enc; |
650 | pix_mp->quantization = ctx->quant; |
651 | mtk_mdp_dbg(2, "[%d] wxh:%dx%d" , ctx->id, |
652 | pix_mp->width, pix_mp->height); |
653 | |
654 | for (i = 0; i < pix_mp->num_planes; ++i) { |
655 | pix_mp->plane_fmt[i].bytesperline = (frame->width * |
656 | frame->fmt->row_depth[i]) / 8; |
657 | pix_mp->plane_fmt[i].sizeimage = (frame->width * |
658 | frame->height * frame->fmt->depth[i]) / 8; |
659 | |
660 | mtk_mdp_dbg(2, "[%d] p%d, bpl:%d, sizeimage:%d" , ctx->id, i, |
661 | pix_mp->plane_fmt[i].bytesperline, |
662 | pix_mp->plane_fmt[i].sizeimage); |
663 | } |
664 | |
665 | return 0; |
666 | } |
667 | |
668 | static int mtk_mdp_m2m_try_fmt_mplane(struct file *file, void *fh, |
669 | struct v4l2_format *f) |
670 | { |
671 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); |
672 | |
673 | if (!mtk_mdp_try_fmt_mplane(ctx, f)) |
674 | return -EINVAL; |
675 | return 0; |
676 | } |
677 | |
678 | static int mtk_mdp_m2m_s_fmt_mplane(struct file *file, void *fh, |
679 | struct v4l2_format *f) |
680 | { |
681 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); |
682 | struct vb2_queue *vq; |
683 | struct mtk_mdp_frame *frame; |
684 | struct v4l2_pix_format_mplane *pix_mp; |
685 | const struct mtk_mdp_fmt *fmt; |
686 | int i; |
687 | |
688 | mtk_mdp_dbg(2, "[%d] type:%d" , ctx->id, f->type); |
689 | |
690 | frame = mtk_mdp_ctx_get_frame(ctx, type: f->type); |
691 | fmt = mtk_mdp_try_fmt_mplane(ctx, f); |
692 | if (!fmt) { |
693 | mtk_mdp_err("[%d] try_fmt failed, type:%d" , ctx->id, f->type); |
694 | return -EINVAL; |
695 | } |
696 | frame->fmt = fmt; |
697 | |
698 | vq = v4l2_m2m_get_vq(m2m_ctx: ctx->m2m_ctx, type: f->type); |
699 | if (vb2_is_streaming(q: vq)) { |
700 | dev_info(&ctx->mdp_dev->pdev->dev, "queue %d busy" , f->type); |
701 | return -EBUSY; |
702 | } |
703 | |
704 | pix_mp = &f->fmt.pix_mp; |
705 | for (i = 0; i < frame->fmt->num_planes; i++) { |
706 | frame->payload[i] = pix_mp->plane_fmt[i].sizeimage; |
707 | frame->pitch[i] = pix_mp->plane_fmt[i].bytesperline; |
708 | } |
709 | |
710 | mtk_mdp_set_frame_size(frame, width: pix_mp->width, height: pix_mp->height); |
711 | if (V4L2_TYPE_IS_OUTPUT(f->type)) { |
712 | ctx->colorspace = pix_mp->colorspace; |
713 | ctx->xfer_func = pix_mp->xfer_func; |
714 | ctx->ycbcr_enc = pix_mp->ycbcr_enc; |
715 | ctx->quant = pix_mp->quantization; |
716 | } |
717 | |
718 | mtk_mdp_dbg(2, "[%d] type:%d, frame:%dx%d" , ctx->id, f->type, |
719 | frame->width, frame->height); |
720 | |
721 | return 0; |
722 | } |
723 | |
724 | static int mtk_mdp_m2m_reqbufs(struct file *file, void *fh, |
725 | struct v4l2_requestbuffers *reqbufs) |
726 | { |
727 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); |
728 | |
729 | return v4l2_m2m_reqbufs(file, m2m_ctx: ctx->m2m_ctx, reqbufs); |
730 | } |
731 | |
732 | static int mtk_mdp_m2m_streamon(struct file *file, void *fh, |
733 | enum v4l2_buf_type type) |
734 | { |
735 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); |
736 | int ret; |
737 | |
738 | if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_VPU_INIT)) { |
739 | ret = mtk_mdp_vpu_init(vpu: &ctx->vpu); |
740 | if (ret < 0) { |
741 | dev_err(&ctx->mdp_dev->pdev->dev, |
742 | "vpu init failed %d\n" , |
743 | ret); |
744 | return -EINVAL; |
745 | } |
746 | mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_VPU_INIT); |
747 | } |
748 | |
749 | return v4l2_m2m_streamon(file, m2m_ctx: ctx->m2m_ctx, type); |
750 | } |
751 | |
752 | static inline bool mtk_mdp_is_target_compose(u32 target) |
753 | { |
754 | if (target == V4L2_SEL_TGT_COMPOSE_DEFAULT |
755 | || target == V4L2_SEL_TGT_COMPOSE_BOUNDS |
756 | || target == V4L2_SEL_TGT_COMPOSE) |
757 | return true; |
758 | return false; |
759 | } |
760 | |
761 | static inline bool mtk_mdp_is_target_crop(u32 target) |
762 | { |
763 | if (target == V4L2_SEL_TGT_CROP_DEFAULT |
764 | || target == V4L2_SEL_TGT_CROP_BOUNDS |
765 | || target == V4L2_SEL_TGT_CROP) |
766 | return true; |
767 | return false; |
768 | } |
769 | |
770 | static int mtk_mdp_m2m_g_selection(struct file *file, void *fh, |
771 | struct v4l2_selection *s) |
772 | { |
773 | struct mtk_mdp_frame *frame; |
774 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); |
775 | bool valid = false; |
776 | |
777 | if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { |
778 | if (mtk_mdp_is_target_compose(target: s->target)) |
779 | valid = true; |
780 | } else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { |
781 | if (mtk_mdp_is_target_crop(target: s->target)) |
782 | valid = true; |
783 | } |
784 | if (!valid) { |
785 | mtk_mdp_dbg(1, "[%d] invalid type:%d,%u" , ctx->id, s->type, |
786 | s->target); |
787 | return -EINVAL; |
788 | } |
789 | |
790 | frame = mtk_mdp_ctx_get_frame(ctx, type: s->type); |
791 | |
792 | switch (s->target) { |
793 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: |
794 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: |
795 | case V4L2_SEL_TGT_CROP_BOUNDS: |
796 | case V4L2_SEL_TGT_CROP_DEFAULT: |
797 | s->r.left = 0; |
798 | s->r.top = 0; |
799 | s->r.width = frame->width; |
800 | s->r.height = frame->height; |
801 | return 0; |
802 | |
803 | case V4L2_SEL_TGT_COMPOSE: |
804 | case V4L2_SEL_TGT_CROP: |
805 | s->r.left = frame->crop.left; |
806 | s->r.top = frame->crop.top; |
807 | s->r.width = frame->crop.width; |
808 | s->r.height = frame->crop.height; |
809 | return 0; |
810 | } |
811 | |
812 | return -EINVAL; |
813 | } |
814 | |
815 | static int mtk_mdp_check_scaler_ratio(struct mtk_mdp_variant *var, int src_w, |
816 | int src_h, int dst_w, int dst_h, int rot) |
817 | { |
818 | int tmp_w, tmp_h; |
819 | |
820 | if (rot == 90 || rot == 270) { |
821 | tmp_w = dst_h; |
822 | tmp_h = dst_w; |
823 | } else { |
824 | tmp_w = dst_w; |
825 | tmp_h = dst_h; |
826 | } |
827 | |
828 | if ((src_w / tmp_w) > var->h_scale_down_max || |
829 | (src_h / tmp_h) > var->v_scale_down_max || |
830 | (tmp_w / src_w) > var->h_scale_up_max || |
831 | (tmp_h / src_h) > var->v_scale_up_max) |
832 | return -EINVAL; |
833 | |
834 | return 0; |
835 | } |
836 | |
837 | static int mtk_mdp_m2m_s_selection(struct file *file, void *fh, |
838 | struct v4l2_selection *s) |
839 | { |
840 | struct mtk_mdp_frame *frame; |
841 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); |
842 | struct v4l2_rect new_r; |
843 | struct mtk_mdp_variant *variant = ctx->mdp_dev->variant; |
844 | int ret; |
845 | bool valid = false; |
846 | |
847 | if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { |
848 | if (s->target == V4L2_SEL_TGT_COMPOSE) |
849 | valid = true; |
850 | } else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { |
851 | if (s->target == V4L2_SEL_TGT_CROP) |
852 | valid = true; |
853 | } |
854 | if (!valid) { |
855 | mtk_mdp_dbg(1, "[%d] invalid type:%d,%u" , ctx->id, s->type, |
856 | s->target); |
857 | return -EINVAL; |
858 | } |
859 | |
860 | new_r = s->r; |
861 | ret = mtk_mdp_try_crop(ctx, type: s->type, r: &new_r); |
862 | if (ret) |
863 | return ret; |
864 | |
865 | if (mtk_mdp_is_target_crop(target: s->target)) |
866 | frame = &ctx->s_frame; |
867 | else |
868 | frame = &ctx->d_frame; |
869 | |
870 | /* Check to see if scaling ratio is within supported range */ |
871 | if (V4L2_TYPE_IS_OUTPUT(s->type)) |
872 | ret = mtk_mdp_check_scaler_ratio(var: variant, src_w: new_r.width, |
873 | src_h: new_r.height, dst_w: ctx->d_frame.crop.width, |
874 | dst_h: ctx->d_frame.crop.height, |
875 | rot: ctx->ctrls.rotate->val); |
876 | else |
877 | ret = mtk_mdp_check_scaler_ratio(var: variant, |
878 | src_w: ctx->s_frame.crop.width, |
879 | src_h: ctx->s_frame.crop.height, dst_w: new_r.width, |
880 | dst_h: new_r.height, rot: ctx->ctrls.rotate->val); |
881 | |
882 | if (ret) { |
883 | dev_info(&ctx->mdp_dev->pdev->dev, |
884 | "Out of scaler range" ); |
885 | return -EINVAL; |
886 | } |
887 | |
888 | s->r = new_r; |
889 | frame->crop = new_r; |
890 | |
891 | return 0; |
892 | } |
893 | |
894 | static const struct v4l2_ioctl_ops mtk_mdp_m2m_ioctl_ops = { |
895 | .vidioc_querycap = mtk_mdp_m2m_querycap, |
896 | .vidioc_enum_fmt_vid_cap = mtk_mdp_m2m_enum_fmt_vid_cap, |
897 | .vidioc_enum_fmt_vid_out = mtk_mdp_m2m_enum_fmt_vid_out, |
898 | .vidioc_g_fmt_vid_cap_mplane = mtk_mdp_m2m_g_fmt_mplane, |
899 | .vidioc_g_fmt_vid_out_mplane = mtk_mdp_m2m_g_fmt_mplane, |
900 | .vidioc_try_fmt_vid_cap_mplane = mtk_mdp_m2m_try_fmt_mplane, |
901 | .vidioc_try_fmt_vid_out_mplane = mtk_mdp_m2m_try_fmt_mplane, |
902 | .vidioc_s_fmt_vid_cap_mplane = mtk_mdp_m2m_s_fmt_mplane, |
903 | .vidioc_s_fmt_vid_out_mplane = mtk_mdp_m2m_s_fmt_mplane, |
904 | .vidioc_reqbufs = mtk_mdp_m2m_reqbufs, |
905 | .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, |
906 | .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, |
907 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, |
908 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
909 | .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, |
910 | .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, |
911 | .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, |
912 | .vidioc_streamon = mtk_mdp_m2m_streamon, |
913 | .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, |
914 | .vidioc_g_selection = mtk_mdp_m2m_g_selection, |
915 | .vidioc_s_selection = mtk_mdp_m2m_s_selection |
916 | }; |
917 | |
918 | static int mtk_mdp_m2m_queue_init(void *priv, struct vb2_queue *src_vq, |
919 | struct vb2_queue *dst_vq) |
920 | { |
921 | struct mtk_mdp_ctx *ctx = priv; |
922 | int ret; |
923 | |
924 | memset(src_vq, 0, sizeof(*src_vq)); |
925 | src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
926 | src_vq->io_modes = VB2_MMAP | VB2_DMABUF; |
927 | src_vq->drv_priv = ctx; |
928 | src_vq->ops = &mtk_mdp_m2m_qops; |
929 | src_vq->mem_ops = &vb2_dma_contig_memops; |
930 | src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
931 | src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; |
932 | src_vq->dev = &ctx->mdp_dev->pdev->dev; |
933 | src_vq->lock = &ctx->mdp_dev->lock; |
934 | |
935 | ret = vb2_queue_init(q: src_vq); |
936 | if (ret) |
937 | return ret; |
938 | |
939 | memset(dst_vq, 0, sizeof(*dst_vq)); |
940 | dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
941 | dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; |
942 | dst_vq->drv_priv = ctx; |
943 | dst_vq->ops = &mtk_mdp_m2m_qops; |
944 | dst_vq->mem_ops = &vb2_dma_contig_memops; |
945 | dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
946 | dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; |
947 | dst_vq->dev = &ctx->mdp_dev->pdev->dev; |
948 | dst_vq->lock = &ctx->mdp_dev->lock; |
949 | |
950 | return vb2_queue_init(q: dst_vq); |
951 | } |
952 | |
953 | static int mtk_mdp_s_ctrl(struct v4l2_ctrl *ctrl) |
954 | { |
955 | struct mtk_mdp_ctx *ctx = ctrl_to_ctx(ctrl); |
956 | struct mtk_mdp_dev *mdp = ctx->mdp_dev; |
957 | struct mtk_mdp_variant *variant = mdp->variant; |
958 | int ret = 0; |
959 | |
960 | if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) |
961 | return 0; |
962 | |
963 | switch (ctrl->id) { |
964 | case V4L2_CID_HFLIP: |
965 | ctx->hflip = ctrl->val; |
966 | break; |
967 | case V4L2_CID_VFLIP: |
968 | ctx->vflip = ctrl->val; |
969 | break; |
970 | case V4L2_CID_ROTATE: |
971 | ret = mtk_mdp_check_scaler_ratio(var: variant, |
972 | src_w: ctx->s_frame.crop.width, |
973 | src_h: ctx->s_frame.crop.height, |
974 | dst_w: ctx->d_frame.crop.width, |
975 | dst_h: ctx->d_frame.crop.height, |
976 | rot: ctx->ctrls.rotate->val); |
977 | |
978 | if (ret) |
979 | return -EINVAL; |
980 | |
981 | ctx->rotation = ctrl->val; |
982 | break; |
983 | case V4L2_CID_ALPHA_COMPONENT: |
984 | ctx->d_frame.alpha = ctrl->val; |
985 | break; |
986 | } |
987 | |
988 | return 0; |
989 | } |
990 | |
991 | static const struct v4l2_ctrl_ops mtk_mdp_ctrl_ops = { |
992 | .s_ctrl = mtk_mdp_s_ctrl, |
993 | }; |
994 | |
995 | static int mtk_mdp_ctrls_create(struct mtk_mdp_ctx *ctx) |
996 | { |
997 | v4l2_ctrl_handler_init(&ctx->ctrl_handler, MTK_MDP_MAX_CTRL_NUM); |
998 | |
999 | ctx->ctrls.rotate = v4l2_ctrl_new_std(hdl: &ctx->ctrl_handler, |
1000 | ops: &mtk_mdp_ctrl_ops, V4L2_CID_ROTATE, min: 0, max: 270, step: 90, def: 0); |
1001 | ctx->ctrls.hflip = v4l2_ctrl_new_std(hdl: &ctx->ctrl_handler, |
1002 | ops: &mtk_mdp_ctrl_ops, |
1003 | V4L2_CID_HFLIP, |
1004 | min: 0, max: 1, step: 1, def: 0); |
1005 | ctx->ctrls.vflip = v4l2_ctrl_new_std(hdl: &ctx->ctrl_handler, |
1006 | ops: &mtk_mdp_ctrl_ops, |
1007 | V4L2_CID_VFLIP, |
1008 | min: 0, max: 1, step: 1, def: 0); |
1009 | ctx->ctrls.global_alpha = v4l2_ctrl_new_std(hdl: &ctx->ctrl_handler, |
1010 | ops: &mtk_mdp_ctrl_ops, |
1011 | V4L2_CID_ALPHA_COMPONENT, |
1012 | min: 0, max: 255, step: 1, def: 0); |
1013 | ctx->ctrls_rdy = ctx->ctrl_handler.error == 0; |
1014 | |
1015 | if (ctx->ctrl_handler.error) { |
1016 | int err = ctx->ctrl_handler.error; |
1017 | |
1018 | v4l2_ctrl_handler_free(hdl: &ctx->ctrl_handler); |
1019 | dev_err(&ctx->mdp_dev->pdev->dev, |
1020 | "Failed to create control handlers\n" ); |
1021 | return err; |
1022 | } |
1023 | |
1024 | return 0; |
1025 | } |
1026 | |
1027 | static void mtk_mdp_set_default_params(struct mtk_mdp_ctx *ctx) |
1028 | { |
1029 | struct mtk_mdp_dev *mdp = ctx->mdp_dev; |
1030 | struct mtk_mdp_frame *frame; |
1031 | |
1032 | frame = mtk_mdp_ctx_get_frame(ctx, type: V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); |
1033 | frame->fmt = mtk_mdp_find_fmt_by_index(index: 0, |
1034 | type: V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); |
1035 | frame->width = mdp->variant->pix_min->org_w; |
1036 | frame->height = mdp->variant->pix_min->org_h; |
1037 | frame->payload[0] = frame->width * frame->height; |
1038 | frame->payload[1] = frame->payload[0] / 2; |
1039 | |
1040 | frame = mtk_mdp_ctx_get_frame(ctx, type: V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); |
1041 | frame->fmt = mtk_mdp_find_fmt_by_index(index: 0, |
1042 | type: V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); |
1043 | frame->width = mdp->variant->pix_min->target_rot_dis_w; |
1044 | frame->height = mdp->variant->pix_min->target_rot_dis_h; |
1045 | frame->payload[0] = frame->width * frame->height; |
1046 | frame->payload[1] = frame->payload[0] / 2; |
1047 | |
1048 | } |
1049 | |
1050 | static int mtk_mdp_m2m_open(struct file *file) |
1051 | { |
1052 | struct mtk_mdp_dev *mdp = video_drvdata(file); |
1053 | struct video_device *vfd = video_devdata(file); |
1054 | struct mtk_mdp_ctx *ctx = NULL; |
1055 | int ret; |
1056 | struct v4l2_format default_format; |
1057 | |
1058 | ctx = kzalloc(size: sizeof(*ctx), GFP_KERNEL); |
1059 | if (!ctx) |
1060 | return -ENOMEM; |
1061 | |
1062 | if (mutex_lock_interruptible(&mdp->lock)) { |
1063 | ret = -ERESTARTSYS; |
1064 | goto err_lock; |
1065 | } |
1066 | |
1067 | mutex_init(&ctx->slock); |
1068 | ctx->id = mdp->id_counter++; |
1069 | v4l2_fh_init(fh: &ctx->fh, vdev: vfd); |
1070 | file->private_data = &ctx->fh; |
1071 | ret = mtk_mdp_ctrls_create(ctx); |
1072 | if (ret) |
1073 | goto error_ctrls; |
1074 | |
1075 | /* Use separate control handler per file handle */ |
1076 | ctx->fh.ctrl_handler = &ctx->ctrl_handler; |
1077 | v4l2_fh_add(fh: &ctx->fh); |
1078 | INIT_LIST_HEAD(list: &ctx->list); |
1079 | |
1080 | ctx->mdp_dev = mdp; |
1081 | mtk_mdp_set_default_params(ctx); |
1082 | |
1083 | INIT_WORK(&ctx->work, mtk_mdp_m2m_worker); |
1084 | ctx->m2m_ctx = v4l2_m2m_ctx_init(m2m_dev: mdp->m2m_dev, drv_priv: ctx, |
1085 | queue_init: mtk_mdp_m2m_queue_init); |
1086 | if (IS_ERR(ptr: ctx->m2m_ctx)) { |
1087 | dev_err(&mdp->pdev->dev, "Failed to initialize m2m context" ); |
1088 | ret = PTR_ERR(ptr: ctx->m2m_ctx); |
1089 | goto error_m2m_ctx; |
1090 | } |
1091 | ctx->fh.m2m_ctx = ctx->m2m_ctx; |
1092 | if (mdp->ctx_num++ == 0) { |
1093 | ret = vpu_load_firmware(pdev: mdp->vpu_dev); |
1094 | if (ret < 0) { |
1095 | dev_err(&mdp->pdev->dev, |
1096 | "vpu_load_firmware failed %d\n" , ret); |
1097 | goto err_load_vpu; |
1098 | } |
1099 | |
1100 | ret = mtk_mdp_vpu_register(pdev: mdp->pdev); |
1101 | if (ret < 0) { |
1102 | dev_err(&mdp->pdev->dev, |
1103 | "mdp_vpu register failed %d\n" , ret); |
1104 | goto err_load_vpu; |
1105 | } |
1106 | } |
1107 | |
1108 | list_add(new: &ctx->list, head: &mdp->ctx_list); |
1109 | mutex_unlock(lock: &mdp->lock); |
1110 | |
1111 | /* Default format */ |
1112 | memset(&default_format, 0, sizeof(default_format)); |
1113 | default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
1114 | default_format.fmt.pix_mp.width = 32; |
1115 | default_format.fmt.pix_mp.height = 32; |
1116 | default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M; |
1117 | mtk_mdp_m2m_s_fmt_mplane(file, fh: &ctx->fh, f: &default_format); |
1118 | default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
1119 | mtk_mdp_m2m_s_fmt_mplane(file, fh: &ctx->fh, f: &default_format); |
1120 | |
1121 | mtk_mdp_dbg(0, "%s [%d]" , dev_name(&mdp->pdev->dev), ctx->id); |
1122 | |
1123 | return 0; |
1124 | |
1125 | err_load_vpu: |
1126 | mdp->ctx_num--; |
1127 | v4l2_m2m_ctx_release(m2m_ctx: ctx->m2m_ctx); |
1128 | error_m2m_ctx: |
1129 | v4l2_ctrl_handler_free(hdl: &ctx->ctrl_handler); |
1130 | error_ctrls: |
1131 | v4l2_fh_del(fh: &ctx->fh); |
1132 | v4l2_fh_exit(fh: &ctx->fh); |
1133 | mutex_unlock(lock: &mdp->lock); |
1134 | err_lock: |
1135 | kfree(objp: ctx); |
1136 | |
1137 | return ret; |
1138 | } |
1139 | |
1140 | static int mtk_mdp_m2m_release(struct file *file) |
1141 | { |
1142 | struct mtk_mdp_ctx *ctx = fh_to_ctx(fh: file->private_data); |
1143 | struct mtk_mdp_dev *mdp = ctx->mdp_dev; |
1144 | |
1145 | flush_workqueue(mdp->job_wq); |
1146 | mutex_lock(&mdp->lock); |
1147 | v4l2_m2m_ctx_release(m2m_ctx: ctx->m2m_ctx); |
1148 | v4l2_ctrl_handler_free(hdl: &ctx->ctrl_handler); |
1149 | v4l2_fh_del(fh: &ctx->fh); |
1150 | v4l2_fh_exit(fh: &ctx->fh); |
1151 | mtk_mdp_vpu_deinit(vpu: &ctx->vpu); |
1152 | mdp->ctx_num--; |
1153 | list_del_init(entry: &ctx->list); |
1154 | |
1155 | mtk_mdp_dbg(0, "%s [%d]" , dev_name(&mdp->pdev->dev), ctx->id); |
1156 | |
1157 | mutex_unlock(lock: &mdp->lock); |
1158 | kfree(objp: ctx); |
1159 | |
1160 | return 0; |
1161 | } |
1162 | |
1163 | static const struct v4l2_file_operations mtk_mdp_m2m_fops = { |
1164 | .owner = THIS_MODULE, |
1165 | .open = mtk_mdp_m2m_open, |
1166 | .release = mtk_mdp_m2m_release, |
1167 | .poll = v4l2_m2m_fop_poll, |
1168 | .unlocked_ioctl = video_ioctl2, |
1169 | .mmap = v4l2_m2m_fop_mmap, |
1170 | }; |
1171 | |
1172 | static const struct v4l2_m2m_ops mtk_mdp_m2m_ops = { |
1173 | .device_run = mtk_mdp_m2m_device_run, |
1174 | }; |
1175 | |
1176 | int mtk_mdp_register_m2m_device(struct mtk_mdp_dev *mdp) |
1177 | { |
1178 | struct device *dev = &mdp->pdev->dev; |
1179 | int ret; |
1180 | |
1181 | mdp->variant = &mtk_mdp_default_variant; |
1182 | mdp->vdev = video_device_alloc(); |
1183 | if (!mdp->vdev) { |
1184 | dev_err(dev, "failed to allocate video device\n" ); |
1185 | ret = -ENOMEM; |
1186 | goto err_video_alloc; |
1187 | } |
1188 | mdp->vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; |
1189 | mdp->vdev->fops = &mtk_mdp_m2m_fops; |
1190 | mdp->vdev->ioctl_ops = &mtk_mdp_m2m_ioctl_ops; |
1191 | mdp->vdev->release = video_device_release; |
1192 | mdp->vdev->lock = &mdp->lock; |
1193 | mdp->vdev->vfl_dir = VFL_DIR_M2M; |
1194 | mdp->vdev->v4l2_dev = &mdp->v4l2_dev; |
1195 | snprintf(buf: mdp->vdev->name, size: sizeof(mdp->vdev->name), fmt: "%s:m2m" , |
1196 | MTK_MDP_MODULE_NAME); |
1197 | video_set_drvdata(vdev: mdp->vdev, data: mdp); |
1198 | |
1199 | mdp->m2m_dev = v4l2_m2m_init(m2m_ops: &mtk_mdp_m2m_ops); |
1200 | if (IS_ERR(ptr: mdp->m2m_dev)) { |
1201 | dev_err(dev, "failed to initialize v4l2-m2m device\n" ); |
1202 | ret = PTR_ERR(ptr: mdp->m2m_dev); |
1203 | goto err_m2m_init; |
1204 | } |
1205 | |
1206 | ret = video_register_device(vdev: mdp->vdev, type: VFL_TYPE_VIDEO, nr: 2); |
1207 | if (ret) { |
1208 | dev_err(dev, "failed to register video device\n" ); |
1209 | goto err_vdev_register; |
1210 | } |
1211 | |
1212 | v4l2_info(&mdp->v4l2_dev, "driver registered as /dev/video%d" , |
1213 | mdp->vdev->num); |
1214 | return 0; |
1215 | |
1216 | err_vdev_register: |
1217 | v4l2_m2m_release(m2m_dev: mdp->m2m_dev); |
1218 | err_m2m_init: |
1219 | video_device_release(vdev: mdp->vdev); |
1220 | err_video_alloc: |
1221 | |
1222 | return ret; |
1223 | } |
1224 | |
1225 | void mtk_mdp_unregister_m2m_device(struct mtk_mdp_dev *mdp) |
1226 | { |
1227 | video_unregister_device(vdev: mdp->vdev); |
1228 | v4l2_m2m_release(m2m_dev: mdp->m2m_dev); |
1229 | } |
1230 | |