File Index
Files and directories
Sign in
FILE

mtk-mdp3-m2m.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  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 *file_to_ctx(struct file *filp)
 14{
 15	return container_of(file_to_v4l2_fh(filp), 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	else
 29		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);
 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(&param.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(&param.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, &param);
 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 = &param;
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	else
186		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	.buf_prepare	= mdp_m2m_buf_prepare,
270	.start_streaming = mdp_m2m_start_streaming,
271	.stop_streaming	= mdp_m2m_stop_streaming,
272	.buf_queue	= mdp_m2m_buf_queue,
273	.buf_out_validate = mdp_m2m_buf_out_validate,
274};
275
276static int mdp_m2m_querycap(struct file *file, void *fh,
277			    struct v4l2_capability *cap)
278{
279	strscpy(cap->driver, MDP_MODULE_NAME, sizeof(cap->driver));
280	strscpy(cap->card, MDP_DEVICE_NAME, sizeof(cap->card));
281
282	return 0;
283}
284
285static int mdp_m2m_enum_fmt_mplane(struct file *file, void *fh,
286				   struct v4l2_fmtdesc *f)
287{
288	struct mdp_m2m_ctx *ctx = file_to_ctx(file);
289
290	return mdp_enum_fmt_mplane(ctx->mdp_dev, f);
291}
292
293static int mdp_m2m_g_fmt_mplane(struct file *file, void *fh,
294				struct v4l2_format *f)
295{
296	struct mdp_m2m_ctx *ctx = file_to_ctx(file);
297	struct mdp_frame *frame;
298	struct v4l2_pix_format_mplane *pix_mp;
299
300	frame = ctx_get_frame(ctx, f->type);
301	*f = frame->format;
302	pix_mp = &f->fmt.pix_mp;
303	pix_mp->colorspace = ctx->curr_param.colorspace;
304	pix_mp->xfer_func = ctx->curr_param.xfer_func;
305	pix_mp->ycbcr_enc = ctx->curr_param.ycbcr_enc;
306	pix_mp->quantization = ctx->curr_param.quant;
307
308	return 0;
309}
310
311static int mdp_m2m_s_fmt_mplane(struct file *file, void *fh,
312				struct v4l2_format *f)
313{
314	struct mdp_m2m_ctx *ctx = file_to_ctx(file);
315	struct mdp_frame *frame = ctx_get_frame(ctx, f->type);
316	struct mdp_frame *capture;
317	const struct mdp_format *fmt;
318	struct vb2_queue *vq;
319
320	fmt = mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id);
321	if (!fmt)
322		return -EINVAL;
323
324	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
325	if (vb2_is_busy(vq))
326		return -EBUSY;
327
328	frame->format = *f;
329	frame->mdp_fmt = fmt;
330	frame->ycbcr_prof = mdp_map_ycbcr_prof_mplane(f, fmt->mdp_color);
331	frame->usage = V4L2_TYPE_IS_OUTPUT(f->type) ?
332		MDP_BUFFER_USAGE_HW_READ : MDP_BUFFER_USAGE_MDP;
333
334	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
335	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
336		capture->crop.c.left = 0;
337		capture->crop.c.top = 0;
338		capture->crop.c.width = f->fmt.pix_mp.width;
339		capture->crop.c.height = f->fmt.pix_mp.height;
340		ctx->curr_param.colorspace = f->fmt.pix_mp.colorspace;
341		ctx->curr_param.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
342		ctx->curr_param.quant = f->fmt.pix_mp.quantization;
343		ctx->curr_param.xfer_func = f->fmt.pix_mp.xfer_func;
344	} else {
345		capture->compose.left = 0;
346		capture->compose.top = 0;
347		capture->compose.width = f->fmt.pix_mp.width;
348		capture->compose.height = f->fmt.pix_mp.height;
349	}
350
351	return 0;
352}
353
354static int mdp_m2m_try_fmt_mplane(struct file *file, void *fh,
355				  struct v4l2_format *f)
356{
357	struct mdp_m2m_ctx *ctx = file_to_ctx(file);
358
359	if (!mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id))
360		return -EINVAL;
361
362	return 0;
363}
364
365static int mdp_m2m_g_selection(struct file *file, void *fh,
366			       struct v4l2_selection *s)
367{
368	struct mdp_m2m_ctx *ctx = file_to_ctx(file);
369	struct mdp_frame *frame;
370	bool valid = false;
371
372	if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
373		valid = mdp_target_is_crop(s->target);
374	else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
375		valid = mdp_target_is_compose(s->target);
376
377	if (!valid)
378		return -EINVAL;
379
380	switch (s->target) {
381	case V4L2_SEL_TGT_CROP:
382		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
383			return -EINVAL;
384		frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
385		s->r = frame->crop.c;
386		return 0;
387	case V4L2_SEL_TGT_COMPOSE:
388		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
389			return -EINVAL;
390		frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
391		s->r = frame->compose;
392		return 0;
393	case V4L2_SEL_TGT_CROP_DEFAULT:
394	case V4L2_SEL_TGT_CROP_BOUNDS:
395		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
396			return -EINVAL;
397		frame = ctx_get_frame(ctx, s->type);
398		s->r.left = 0;
399		s->r.top = 0;
400		s->r.width = frame->format.fmt.pix_mp.width;
401		s->r.height = frame->format.fmt.pix_mp.height;
402		return 0;
403	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
404	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
405		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
406			return -EINVAL;
407		frame = ctx_get_frame(ctx, s->type);
408		s->r.left = 0;
409		s->r.top = 0;
410		s->r.width = frame->format.fmt.pix_mp.width;
411		s->r.height = frame->format.fmt.pix_mp.height;
412		return 0;
413	}
414	return -EINVAL;
415}
416
417static int mdp_m2m_s_selection(struct file *file, void *fh,
418			       struct v4l2_selection *s)
419{
420	struct mdp_m2m_ctx *ctx = file_to_ctx(file);
421	struct mdp_frame *frame = ctx_get_frame(ctx, s->type);
422	struct mdp_frame *capture;
423	struct v4l2_rect r;
424	struct device *dev = &ctx->mdp_dev->pdev->dev;
425	bool valid = false;
426	int ret;
427
428	if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
429		valid = (s->target == V4L2_SEL_TGT_CROP);
430	else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
431		valid = (s->target == V4L2_SEL_TGT_COMPOSE);
432
433	if (!valid) {
434		dev_dbg(dev, "[%s:%d] invalid type:%u target:%u", __func__,
435			ctx->id, s->type, s->target);
436		return -EINVAL;
437	}
438
439	ret = mdp_try_crop(ctx, &r, s, frame);
440	if (ret)
441		return ret;
442	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
443
444	if (mdp_target_is_crop(s->target))
445		capture->crop.c = r;
446	else
447		capture->compose = r;
448
449	s->r = r;
450
451	return 0;
452}
453
454static const struct v4l2_ioctl_ops mdp_m2m_ioctl_ops = {
455	.vidioc_querycap		= mdp_m2m_querycap,
456	.vidioc_enum_fmt_vid_cap	= mdp_m2m_enum_fmt_mplane,
457	.vidioc_enum_fmt_vid_out	= mdp_m2m_enum_fmt_mplane,
458	.vidioc_g_fmt_vid_cap_mplane	= mdp_m2m_g_fmt_mplane,
459	.vidioc_g_fmt_vid_out_mplane	= mdp_m2m_g_fmt_mplane,
460	.vidioc_s_fmt_vid_cap_mplane	= mdp_m2m_s_fmt_mplane,
461	.vidioc_s_fmt_vid_out_mplane	= mdp_m2m_s_fmt_mplane,
462	.vidioc_try_fmt_vid_cap_mplane	= mdp_m2m_try_fmt_mplane,
463	.vidioc_try_fmt_vid_out_mplane	= mdp_m2m_try_fmt_mplane,
464	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
465	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
466	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
467	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
468	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
469	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
470	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
471	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
472	.vidioc_g_selection		= mdp_m2m_g_selection,
473	.vidioc_s_selection		= mdp_m2m_s_selection,
474	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
475	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
476};
477
478static int mdp_m2m_queue_init(void *priv,
479			      struct vb2_queue *src_vq,
480			      struct vb2_queue *dst_vq)
481{
482	struct mdp_m2m_ctx *ctx = priv;
483	int ret;
484
485	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
486	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
487	src_vq->ops = &mdp_m2m_qops;
488	src_vq->mem_ops = &vb2_dma_contig_memops;
489	src_vq->drv_priv = ctx;
490	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
491	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
492	src_vq->dev = &ctx->mdp_dev->pdev->dev;
493	src_vq->lock = &ctx->ctx_lock;
494
495	ret = vb2_queue_init(src_vq);
496	if (ret)
497		return ret;
498
499	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
500	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
501	dst_vq->ops = &mdp_m2m_qops;
502	dst_vq->mem_ops = &vb2_dma_contig_memops;
503	dst_vq->drv_priv = ctx;
504	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
505	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
506	dst_vq->dev = &ctx->mdp_dev->pdev->dev;
507	dst_vq->lock = &ctx->ctx_lock;
508
509	return vb2_queue_init(dst_vq);
510}
511
512static int mdp_m2m_s_ctrl(struct v4l2_ctrl *ctrl)
513{
514	struct mdp_m2m_ctx *ctx = ctrl_to_ctx(ctrl);
515	struct mdp_frame *capture;
516
517	capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
518	switch (ctrl->id) {
519	case V4L2_CID_HFLIP:
520		capture->hflip = ctrl->val;
521		break;
522	case V4L2_CID_VFLIP:
523		capture->vflip = ctrl->val;
524		break;
525	case V4L2_CID_ROTATE:
526		capture->rotation = ctrl->val;
527		break;
528	}
529
530	return 0;
531}
532
533static const struct v4l2_ctrl_ops mdp_m2m_ctrl_ops = {
534	.s_ctrl	= mdp_m2m_s_ctrl,
535};
536
537static int mdp_m2m_ctrls_create(struct mdp_m2m_ctx *ctx)
538{
539	v4l2_ctrl_handler_init(&ctx->ctrl_handler, MDP_MAX_CTRLS);
540	ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
541					     &mdp_m2m_ctrl_ops, V4L2_CID_HFLIP,
542					     0, 1, 1, 0);
543	ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
544					     &mdp_m2m_ctrl_ops, V4L2_CID_VFLIP,
545					     0, 1, 1, 0);
546	ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
547					      &mdp_m2m_ctrl_ops,
548					      V4L2_CID_ROTATE, 0, 270, 90, 0);
549
550	if (ctx->ctrl_handler.error) {
551		int err = ctx->ctrl_handler.error;
552
553		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
554		dev_err(&ctx->mdp_dev->pdev->dev,
555			"Failed to register controls\n");
556		return err;
557	}
558	return 0;
559}
560
561static int mdp_m2m_open(struct file *file)
562{
563	struct video_device *vdev = video_devdata(file);
564	struct mdp_dev *mdp = video_get_drvdata(vdev);
565	struct mdp_m2m_ctx *ctx;
566	struct device *dev = &mdp->pdev->dev;
567	int ret;
568	struct v4l2_format default_format = {};
569	const struct mdp_limit *limit = mdp->mdp_data->def_limit;
570
571	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
572	if (!ctx)
573		return -ENOMEM;
574
575	if (mutex_lock_interruptible(&mdp->m2m_lock)) {
576		ret = -ERESTARTSYS;
577		goto err_free_ctx;
578	}
579
580	ret = ida_alloc(&mdp->mdp_ida, GFP_KERNEL);
581	if (ret < 0)
582		goto err_unlock_mutex;
583	ctx->id = ret;
584
585	ctx->mdp_dev = mdp;
586
587	v4l2_fh_init(&ctx->fh, vdev);
588	ret = mdp_m2m_ctrls_create(ctx);
589	if (ret)
590		goto err_exit_fh;
591
592	/* Use separate control handler per file handle */
593	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
594	v4l2_fh_add(&ctx->fh, file);
595
596	mutex_init(&ctx->ctx_lock);
597	ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx, mdp_m2m_queue_init);
598	if (IS_ERR(ctx->m2m_ctx)) {
599		dev_err(dev, "Failed to initialize m2m context\n");
600		ret = PTR_ERR(ctx->m2m_ctx);
601		goto err_release_handler;
602	}
603	ctx->fh.m2m_ctx = ctx->m2m_ctx;
604
605	ctx->curr_param.ctx = ctx;
606	ret = mdp_frameparam_init(mdp, &ctx->curr_param);
607	if (ret) {
608		dev_err(dev, "Failed to initialize mdp parameter\n");
609		goto err_release_m2m_ctx;
610	}
611
612	mutex_unlock(&mdp->m2m_lock);
613
614	/* Default format */
615	default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
616	default_format.fmt.pix_mp.width = limit->out_limit.wmin;
617	default_format.fmt.pix_mp.height = limit->out_limit.hmin;
618	default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
619	mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
620	default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
621	mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
622
623	dev_dbg(dev, "%s:[%d]", __func__, ctx->id);
624
625	return 0;
626
627err_release_m2m_ctx:
628	v4l2_m2m_ctx_release(ctx->m2m_ctx);
629err_release_handler:
630	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
631	v4l2_fh_del(&ctx->fh, file);
632err_exit_fh:
633	v4l2_fh_exit(&ctx->fh);
634	ida_free(&mdp->mdp_ida, ctx->id);
635err_unlock_mutex:
636	mutex_unlock(&mdp->m2m_lock);
637err_free_ctx:
638	kfree(ctx);
639
640	return ret;
641}
642
643static int mdp_m2m_release(struct file *file)
644{
645	struct mdp_m2m_ctx *ctx = file_to_ctx(file);
646	struct mdp_dev *mdp = video_drvdata(file);
647	struct device *dev = &mdp->pdev->dev;
648
649	mutex_lock(&mdp->m2m_lock);
650	v4l2_m2m_ctx_release(ctx->m2m_ctx);
651	if (mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT))
652		mdp_vpu_put_locked(mdp);
653
654	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
655	v4l2_fh_del(&ctx->fh, file);
656	v4l2_fh_exit(&ctx->fh);
657	ida_free(&mdp->mdp_ida, ctx->id);
658	mutex_unlock(&mdp->m2m_lock);
659
660	dev_dbg(dev, "%s:[%d]", __func__, ctx->id);
661	kfree(ctx);
662
663	return 0;
664}
665
666static const struct v4l2_file_operations mdp_m2m_fops = {
667	.owner		= THIS_MODULE,
668	.poll		= v4l2_m2m_fop_poll,
669	.unlocked_ioctl	= video_ioctl2,
670	.mmap		= v4l2_m2m_fop_mmap,
671	.open		= mdp_m2m_open,
672	.release	= mdp_m2m_release,
673};
674
675static const struct v4l2_m2m_ops mdp_m2m_ops = {
676	.device_run	= mdp_m2m_device_run,
677};
678
679int mdp_m2m_device_register(struct mdp_dev *mdp)
680{
681	struct device *dev = &mdp->pdev->dev;
682	int ret = 0;
683
684	mdp->m2m_vdev = video_device_alloc();
685	if (!mdp->m2m_vdev) {
686		dev_err(dev, "Failed to allocate video device\n");
687		ret = -ENOMEM;
688		goto err_video_alloc;
689	}
690	mdp->m2m_vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE |
691		V4L2_CAP_STREAMING;
692	mdp->m2m_vdev->fops = &mdp_m2m_fops;
693	mdp->m2m_vdev->ioctl_ops = &mdp_m2m_ioctl_ops;
694	mdp->m2m_vdev->release = mdp_video_device_release;
695	mdp->m2m_vdev->lock = &mdp->m2m_lock;
696	mdp->m2m_vdev->vfl_dir = VFL_DIR_M2M;
697	mdp->m2m_vdev->v4l2_dev = &mdp->v4l2_dev;
698	snprintf(mdp->m2m_vdev->name, sizeof(mdp->m2m_vdev->name), "%s:m2m",
699		 MDP_MODULE_NAME);
700	video_set_drvdata(mdp->m2m_vdev, mdp);
701
702	mdp->m2m_dev = v4l2_m2m_init(&mdp_m2m_ops);
703	if (IS_ERR(mdp->m2m_dev)) {
704		dev_err(dev, "Failed to initialize v4l2-m2m device\n");
705		ret = PTR_ERR(mdp->m2m_dev);
706		goto err_m2m_init;
707	}
708
709	ret = video_register_device(mdp->m2m_vdev, VFL_TYPE_VIDEO, -1);
710	if (ret) {
711		dev_err(dev, "Failed to register video device\n");
712		goto err_video_register;
713	}
714
715	v4l2_info(&mdp->v4l2_dev, "Driver registered as /dev/video%d",
716		  mdp->m2m_vdev->num);
717	return 0;
718
719err_video_register:
720	v4l2_m2m_release(mdp->m2m_dev);
721err_m2m_init:
722	video_device_release(mdp->m2m_vdev);
723err_video_alloc:
724
725	return ret;
726}
727
728void mdp_m2m_device_unregister(struct mdp_dev *mdp)
729{
730	video_unregister_device(mdp->m2m_vdev);
731}
732
733void mdp_m2m_job_finish(struct mdp_m2m_ctx *ctx)
734{
735	enum vb2_buffer_state vb_state = VB2_BUF_STATE_DONE;
736
737	mdp_m2m_process_done(ctx, vb_state);
738}
~/ Read-only file preview.