File Index
Files and directories
Sign in
FILE

fimc-m2m.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver
  4 *
  5 * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
  6 * Sylwester Nawrocki <s.nawrocki@samsung.com>
  7 */
  8
  9#include <linux/module.h>
 10#include <linux/kernel.h>
 11#include <linux/types.h>
 12#include <linux/errno.h>
 13#include <linux/bug.h>
 14#include <linux/interrupt.h>
 15#include <linux/device.h>
 16#include <linux/platform_device.h>
 17#include <linux/pm_runtime.h>
 18#include <linux/list.h>
 19#include <linux/io.h>
 20#include <linux/slab.h>
 21#include <linux/clk.h>
 22#include <media/v4l2-ioctl.h>
 23#include <media/videobuf2-v4l2.h>
 24#include <media/videobuf2-dma-contig.h>
 25
 26#include "common.h"
 27#include "fimc-core.h"
 28#include "fimc-reg.h"
 29#include "media-dev.h"
 30
 31static unsigned int get_m2m_fmt_flags(unsigned int stream_type)
 32{
 33	if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
 34		return FMT_FLAGS_M2M_IN;
 35	else
 36		return FMT_FLAGS_M2M_OUT;
 37}
 38
 39void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
 40{
 41	struct vb2_v4l2_buffer *src_vb, *dst_vb;
 42
 43	if (!ctx || !ctx->fh.m2m_ctx)
 44		return;
 45
 46	src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
 47	dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
 48
 49	if (src_vb)
 50		v4l2_m2m_buf_done(src_vb, vb_state);
 51	if (dst_vb)
 52		v4l2_m2m_buf_done(dst_vb, vb_state);
 53	if (src_vb && dst_vb)
 54		v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
 55				    ctx->fh.m2m_ctx);
 56}
 57
 58/* Complete the transaction which has been scheduled for execution. */
 59static void fimc_m2m_shutdown(struct fimc_ctx *ctx)
 60{
 61	struct fimc_dev *fimc = ctx->fimc_dev;
 62
 63	if (!fimc_m2m_pending(fimc))
 64		return;
 65
 66	fimc_ctx_state_set(FIMC_CTX_SHUT, ctx);
 67
 68	wait_event_timeout(fimc->irq_queue,
 69			!fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
 70			FIMC_SHUTDOWN_TIMEOUT);
 71}
 72
 73static int start_streaming(struct vb2_queue *q, unsigned int count)
 74{
 75	struct fimc_ctx *ctx = q->drv_priv;
 76
 77	return pm_runtime_resume_and_get(&ctx->fimc_dev->pdev->dev);
 78}
 79
 80static void stop_streaming(struct vb2_queue *q)
 81{
 82	struct fimc_ctx *ctx = q->drv_priv;
 83
 84	fimc_m2m_shutdown(ctx);
 85	fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
 86	pm_runtime_put(&ctx->fimc_dev->pdev->dev);
 87}
 88
 89static void fimc_device_run(void *priv)
 90{
 91	struct vb2_v4l2_buffer *src_vb, *dst_vb;
 92	struct fimc_ctx *ctx = priv;
 93	struct fimc_frame *sf, *df;
 94	struct fimc_dev *fimc;
 95	unsigned long flags;
 96	int ret;
 97
 98	if (WARN(!ctx, "Null context\n"))
 99		return;
100
101	fimc = ctx->fimc_dev;
102	spin_lock_irqsave(&fimc->slock, flags);
103
104	set_bit(ST_M2M_PEND, &fimc->state);
105	sf = &ctx->s_frame;
106	df = &ctx->d_frame;
107
108	if (ctx->state & FIMC_PARAMS) {
109		/* Prepare the DMA offsets for scaler */
110		fimc_prepare_dma_offset(ctx, sf);
111		fimc_prepare_dma_offset(ctx, df);
112	}
113
114	src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
115	ret = fimc_prepare_addr(ctx, &src_vb->vb2_buf, sf, &sf->addr);
116	if (ret)
117		goto dma_unlock;
118
119	dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
120	ret = fimc_prepare_addr(ctx, &dst_vb->vb2_buf, df, &df->addr);
121	if (ret)
122		goto dma_unlock;
123
124	dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
125	dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
126	dst_vb->flags |=
127		src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
128
129	/* Reconfigure hardware if the context has changed. */
130	if (fimc->m2m.ctx != ctx) {
131		ctx->state |= FIMC_PARAMS;
132		fimc->m2m.ctx = ctx;
133	}
134
135	if (ctx->state & FIMC_PARAMS) {
136		fimc_set_yuv_order(ctx);
137		fimc_hw_set_input_path(ctx);
138		fimc_hw_set_in_dma(ctx);
139		ret = fimc_set_scaler_info(ctx);
140		if (ret)
141			goto dma_unlock;
142		fimc_hw_set_prescaler(ctx);
143		fimc_hw_set_mainscaler(ctx);
144		fimc_hw_set_target_format(ctx);
145		fimc_hw_set_rotation(ctx);
146		fimc_hw_set_effect(ctx);
147		fimc_hw_set_out_dma(ctx);
148		if (fimc->drv_data->alpha_color)
149			fimc_hw_set_rgb_alpha(ctx);
150		fimc_hw_set_output_path(ctx);
151	}
152	fimc_hw_set_input_addr(fimc, &sf->addr);
153	fimc_hw_set_output_addr(fimc, &df->addr, -1);
154
155	fimc_activate_capture(ctx);
156	ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP);
157	fimc_hw_activate_input_dma(fimc, true);
158
159dma_unlock:
160	spin_unlock_irqrestore(&fimc->slock, flags);
161}
162
163static void fimc_job_abort(void *priv)
164{
165	fimc_m2m_shutdown(priv);
166}
167
168static int fimc_queue_setup(struct vb2_queue *vq,
169			    unsigned int *num_buffers, unsigned int *num_planes,
170			    unsigned int sizes[], struct device *alloc_devs[])
171{
172	struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
173	const struct fimc_frame *f;
174	int i;
175
176	f = ctx_get_frame(ctx, vq->type);
177	if (IS_ERR(f))
178		return PTR_ERR(f);
179	/*
180	 * Return number of non-contiguous planes (plane buffers)
181	 * depending on the configured color format.
182	 */
183	if (!f->fmt)
184		return -EINVAL;
185
186	*num_planes = f->fmt->memplanes;
187	for (i = 0; i < f->fmt->memplanes; i++)
188		sizes[i] = f->payload[i];
189	return 0;
190}
191
192static int fimc_buf_prepare(struct vb2_buffer *vb)
193{
194	struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
195	const struct fimc_frame *frame;
196	int i;
197
198	frame = ctx_get_frame(ctx, vb->vb2_queue->type);
199	if (IS_ERR(frame))
200		return PTR_ERR(frame);
201
202	for (i = 0; i < frame->fmt->memplanes; i++)
203		vb2_set_plane_payload(vb, i, frame->payload[i]);
204
205	return 0;
206}
207
208static void fimc_buf_queue(struct vb2_buffer *vb)
209{
210	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
211	struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
212	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
213}
214
215static const struct vb2_ops fimc_qops = {
216	.queue_setup	 = fimc_queue_setup,
217	.buf_prepare	 = fimc_buf_prepare,
218	.buf_queue	 = fimc_buf_queue,
219	.stop_streaming	 = stop_streaming,
220	.start_streaming = start_streaming,
221};
222
223/*
224 * V4L2 ioctl handlers
225 */
226static int fimc_m2m_querycap(struct file *file, void *fh,
227				     struct v4l2_capability *cap)
228{
229	struct fimc_dev *fimc = video_drvdata(file);
230
231	__fimc_vidioc_querycap(&fimc->pdev->dev, cap);
232	return 0;
233}
234
235static int fimc_m2m_enum_fmt(struct file *file, void *priv,
236			     struct v4l2_fmtdesc *f)
237{
238	const struct fimc_fmt *fmt;
239
240	fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type),
241			       f->index);
242	if (!fmt)
243		return -EINVAL;
244
245	f->pixelformat = fmt->fourcc;
246	return 0;
247}
248
249static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
250				 struct v4l2_format *f)
251{
252	struct fimc_ctx *ctx = file_to_ctx(file);
253	const struct fimc_frame *frame = ctx_get_frame(ctx, f->type);
254
255	if (IS_ERR(frame))
256		return PTR_ERR(frame);
257
258	__fimc_get_format(frame, f);
259	return 0;
260}
261
262static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
263{
264	struct fimc_dev *fimc = ctx->fimc_dev;
265	const struct fimc_variant *variant = fimc->variant;
266	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
267	const struct fimc_fmt *fmt;
268	u32 max_w, mod_x, mod_y;
269
270	if (!IS_M2M(f->type))
271		return -EINVAL;
272
273	fmt = fimc_find_format(&pix->pixelformat, NULL,
274			       get_m2m_fmt_flags(f->type), 0);
275	if (WARN(fmt == NULL, "Pixel format lookup failed"))
276		return -EINVAL;
277
278	if (pix->field == V4L2_FIELD_ANY)
279		pix->field = V4L2_FIELD_NONE;
280	else if (pix->field != V4L2_FIELD_NONE)
281		return -EINVAL;
282
283	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
284		max_w = variant->pix_limit->scaler_dis_w;
285		mod_x = ffs(variant->min_inp_pixsize) - 1;
286	} else {
287		max_w = variant->pix_limit->out_rot_dis_w;
288		mod_x = ffs(variant->min_out_pixsize) - 1;
289	}
290
291	if (tiled_fmt(fmt)) {
292		mod_x = 6; /* 64 x 32 pixels tile */
293		mod_y = 5;
294	} else {
295		if (variant->min_vsize_align == 1)
296			mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1;
297		else
298			mod_y = ffs(variant->min_vsize_align) - 1;
299	}
300
301	v4l_bound_align_image(&pix->width, 16, max_w, mod_x,
302		&pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
303
304	fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp);
305	return 0;
306}
307
308static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
309				   struct v4l2_format *f)
310{
311	struct fimc_ctx *ctx = file_to_ctx(file);
312	return fimc_try_fmt_mplane(ctx, f);
313}
314
315static void __set_frame_format(struct fimc_frame *frame,
316			       const struct fimc_fmt *fmt,
317			       const struct v4l2_pix_format_mplane *pixm)
318{
319	int i;
320
321	for (i = 0; i < fmt->memplanes; i++) {
322		frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline;
323		frame->payload[i] = pixm->plane_fmt[i].sizeimage;
324	}
325
326	frame->f_width = pixm->width;
327	frame->f_height	= pixm->height;
328	frame->o_width = pixm->width;
329	frame->o_height = pixm->height;
330	frame->width = pixm->width;
331	frame->height = pixm->height;
332	frame->offs_h = 0;
333	frame->offs_v = 0;
334	frame->fmt = fmt;
335}
336
337static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
338				 struct v4l2_format *f)
339{
340	struct fimc_ctx *ctx = file_to_ctx(file);
341	struct fimc_dev *fimc = ctx->fimc_dev;
342	const struct fimc_fmt *fmt;
343	struct vb2_queue *vq;
344	struct fimc_frame *frame;
345	int ret;
346
347	ret = fimc_try_fmt_mplane(ctx, f);
348	if (ret)
349		return ret;
350
351	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
352
353	if (vb2_is_busy(vq)) {
354		v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type);
355		return -EBUSY;
356	}
357
358	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
359		frame = &ctx->s_frame;
360	else
361		frame = &ctx->d_frame;
362
363	fmt = fimc_find_format(&f->fmt.pix_mp.pixelformat, NULL,
364			       get_m2m_fmt_flags(f->type), 0);
365	if (!fmt)
366		return -EINVAL;
367
368	__set_frame_format(frame, fmt, &f->fmt.pix_mp);
369
370	/* Update RGB Alpha control state and value range */
371	fimc_alpha_ctrl_update(ctx);
372
373	return 0;
374}
375
376static int fimc_m2m_g_selection(struct file *file, void *fh,
377				struct v4l2_selection *s)
378{
379	struct fimc_ctx *ctx = file_to_ctx(file);
380	const struct fimc_frame *frame;
381
382	frame = ctx_get_frame(ctx, s->type);
383	if (IS_ERR(frame))
384		return PTR_ERR(frame);
385
386	switch (s->target) {
387	case V4L2_SEL_TGT_CROP:
388	case V4L2_SEL_TGT_CROP_DEFAULT:
389	case V4L2_SEL_TGT_CROP_BOUNDS:
390		if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
391			return -EINVAL;
392		break;
393	case V4L2_SEL_TGT_COMPOSE:
394	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
395	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
396		if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
397			return -EINVAL;
398		break;
399	default:
400		return -EINVAL;
401	}
402
403	switch (s->target) {
404	case V4L2_SEL_TGT_CROP:
405	case V4L2_SEL_TGT_COMPOSE:
406		s->r.left = frame->offs_h;
407		s->r.top = frame->offs_v;
408		s->r.width = frame->width;
409		s->r.height = frame->height;
410		break;
411	case V4L2_SEL_TGT_CROP_DEFAULT:
412	case V4L2_SEL_TGT_CROP_BOUNDS:
413	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
414	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
415		s->r.left = 0;
416		s->r.top = 0;
417		s->r.width = frame->o_width;
418		s->r.height = frame->o_height;
419		break;
420	default:
421		return -EINVAL;
422	}
423	return 0;
424}
425
426static int fimc_m2m_try_selection(struct fimc_ctx *ctx,
427				  struct v4l2_selection *s)
428{
429	struct fimc_dev *fimc = ctx->fimc_dev;
430	const struct fimc_frame *f;
431	u32 min_size, halign, depth = 0;
432	int i;
433
434	if (s->r.top < 0 || s->r.left < 0) {
435		v4l2_err(&fimc->m2m.vfd,
436			"doesn't support negative values for top & left\n");
437		return -EINVAL;
438	}
439	if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
440		f = &ctx->d_frame;
441		if (s->target != V4L2_SEL_TGT_COMPOSE)
442			return -EINVAL;
443	} else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
444		f = &ctx->s_frame;
445		if (s->target != V4L2_SEL_TGT_CROP)
446			return -EINVAL;
447	} else {
448		return -EINVAL;
449	}
450
451	min_size = (f == &ctx->s_frame) ?
452		fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
453
454	/* Get pixel alignment constraints. */
455	if (fimc->variant->min_vsize_align == 1)
456		halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
457	else
458		halign = ffs(fimc->variant->min_vsize_align) - 1;
459
460	for (i = 0; i < f->fmt->memplanes; i++)
461		depth += f->fmt->depth[i];
462
463	v4l_bound_align_image(&s->r.width, min_size, f->o_width,
464			      ffs(min_size) - 1,
465			      &s->r.height, min_size, f->o_height,
466			      halign, 64/(ALIGN(depth, 8)));
467
468	/* adjust left/top if cropping rectangle is out of bounds */
469	if (s->r.left + s->r.width > f->o_width)
470		s->r.left = f->o_width - s->r.width;
471	if (s->r.top + s->r.height > f->o_height)
472		s->r.top = f->o_height - s->r.height;
473
474	s->r.left = round_down(s->r.left, min_size);
475	s->r.top  = round_down(s->r.top, fimc->variant->hor_offs_align);
476
477	dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
478	    s->r.left, s->r.top, s->r.width, s->r.height,
479	    f->f_width, f->f_height);
480
481	return 0;
482}
483
484static int fimc_m2m_s_selection(struct file *file, void *fh,
485				struct v4l2_selection *s)
486{
487	struct fimc_ctx *ctx = file_to_ctx(file);
488	struct fimc_dev *fimc = ctx->fimc_dev;
489	struct fimc_frame *f;
490	int ret;
491
492	ret = fimc_m2m_try_selection(ctx, s);
493	if (ret)
494		return ret;
495
496	f = (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?
497		&ctx->s_frame : &ctx->d_frame;
498
499	/* Check to see if scaling ratio is within supported range */
500	if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
501		ret = fimc_check_scaler_ratio(ctx, s->r.width,
502				s->r.height, ctx->d_frame.width,
503				ctx->d_frame.height, ctx->rotation);
504	} else {
505		ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
506				ctx->s_frame.height, s->r.width,
507				s->r.height, ctx->rotation);
508	}
509	if (ret) {
510		v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n");
511		return -EINVAL;
512	}
513
514	f->offs_h = s->r.left;
515	f->offs_v = s->r.top;
516	f->width  = s->r.width;
517	f->height = s->r.height;
518
519	fimc_ctx_state_set(FIMC_PARAMS, ctx);
520
521	return 0;
522}
523
524static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
525	.vidioc_querycap		= fimc_m2m_querycap,
526	.vidioc_enum_fmt_vid_cap	= fimc_m2m_enum_fmt,
527	.vidioc_enum_fmt_vid_out	= fimc_m2m_enum_fmt,
528	.vidioc_g_fmt_vid_cap_mplane	= fimc_m2m_g_fmt_mplane,
529	.vidioc_g_fmt_vid_out_mplane	= fimc_m2m_g_fmt_mplane,
530	.vidioc_try_fmt_vid_cap_mplane	= fimc_m2m_try_fmt_mplane,
531	.vidioc_try_fmt_vid_out_mplane	= fimc_m2m_try_fmt_mplane,
532	.vidioc_s_fmt_vid_cap_mplane	= fimc_m2m_s_fmt_mplane,
533	.vidioc_s_fmt_vid_out_mplane	= fimc_m2m_s_fmt_mplane,
534	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
535	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
536	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
537	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
538	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
539	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
540	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
541	.vidioc_g_selection		= fimc_m2m_g_selection,
542	.vidioc_s_selection		= fimc_m2m_s_selection,
543
544};
545
546static int queue_init(void *priv, struct vb2_queue *src_vq,
547		      struct vb2_queue *dst_vq)
548{
549	struct fimc_ctx *ctx = priv;
550	int ret;
551
552	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
553	src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
554	src_vq->drv_priv = ctx;
555	src_vq->ops = &fimc_qops;
556	src_vq->mem_ops = &vb2_dma_contig_memops;
557	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
558	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
559	src_vq->lock = &ctx->fimc_dev->lock;
560	src_vq->dev = &ctx->fimc_dev->pdev->dev;
561
562	ret = vb2_queue_init(src_vq);
563	if (ret)
564		return ret;
565
566	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
567	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
568	dst_vq->drv_priv = ctx;
569	dst_vq->ops = &fimc_qops;
570	dst_vq->mem_ops = &vb2_dma_contig_memops;
571	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
572	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
573	dst_vq->lock = &ctx->fimc_dev->lock;
574	dst_vq->dev = &ctx->fimc_dev->pdev->dev;
575
576	return vb2_queue_init(dst_vq);
577}
578
579static int fimc_m2m_set_default_format(struct fimc_ctx *ctx)
580{
581	struct v4l2_pix_format_mplane pixm = {
582		.pixelformat	= V4L2_PIX_FMT_RGB32,
583		.width		= 800,
584		.height		= 600,
585		.plane_fmt[0]	= {
586			.bytesperline = 800 * 4,
587			.sizeimage = 800 * 4 * 600,
588		},
589	};
590	const struct fimc_fmt *fmt;
591
592	fmt = fimc_find_format(&pixm.pixelformat, NULL, FMT_FLAGS_M2M, 0);
593	if (!fmt)
594		return -EINVAL;
595
596	__set_frame_format(&ctx->s_frame, fmt, &pixm);
597	__set_frame_format(&ctx->d_frame, fmt, &pixm);
598
599	return 0;
600}
601
602static int fimc_m2m_open(struct file *file)
603{
604	struct fimc_dev *fimc = video_drvdata(file);
605	struct fimc_ctx *ctx;
606	int ret = -EBUSY;
607
608	pr_debug("pid: %d, state: %#lx\n", task_pid_nr(current), fimc->state);
609
610	if (mutex_lock_interruptible(&fimc->lock))
611		return -ERESTARTSYS;
612	/*
613	 * Don't allow simultaneous open() of the mem-to-mem and the
614	 * capture video node that belong to same FIMC IP instance.
615	 */
616	if (test_bit(ST_CAPT_BUSY, &fimc->state))
617		goto unlock;
618
619	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
620	if (!ctx) {
621		ret = -ENOMEM;
622		goto unlock;
623	}
624	v4l2_fh_init(&ctx->fh, &fimc->m2m.vfd);
625	ctx->fimc_dev = fimc;
626
627	/* Default color format */
628	ctx->s_frame.fmt = fimc_get_format(0);
629	ctx->d_frame.fmt = fimc_get_format(0);
630
631	ret = fimc_ctrls_create(ctx);
632	if (ret)
633		goto error_fh;
634
635	/* Use separate control handler per file handle */
636	ctx->fh.ctrl_handler = &ctx->ctrls.handler;
637	v4l2_fh_add(&ctx->fh, file);
638
639	/* Setup the device context for memory-to-memory mode */
640	ctx->state = FIMC_CTX_M2M;
641	ctx->flags = 0;
642	ctx->in_path = FIMC_IO_DMA;
643	ctx->out_path = FIMC_IO_DMA;
644	ctx->scaler.enabled = 1;
645
646	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
647	if (IS_ERR(ctx->fh.m2m_ctx)) {
648		ret = PTR_ERR(ctx->fh.m2m_ctx);
649		goto error_c;
650	}
651
652	if (fimc->m2m.refcnt++ == 0)
653		set_bit(ST_M2M_RUN, &fimc->state);
654
655	ret = fimc_m2m_set_default_format(ctx);
656	if (ret < 0)
657		goto error_m2m_ctx;
658
659	mutex_unlock(&fimc->lock);
660	return 0;
661
662error_m2m_ctx:
663	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
664error_c:
665	fimc_ctrls_delete(ctx);
666	v4l2_fh_del(&ctx->fh, file);
667error_fh:
668	v4l2_fh_exit(&ctx->fh);
669	kfree(ctx);
670unlock:
671	mutex_unlock(&fimc->lock);
672	return ret;
673}
674
675static int fimc_m2m_release(struct file *file)
676{
677	struct fimc_ctx *ctx = file_to_ctx(file);
678	struct fimc_dev *fimc = ctx->fimc_dev;
679
680	dbg("pid: %d, state: 0x%lx, refcnt= %d",
681		task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
682
683	mutex_lock(&fimc->lock);
684
685	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
686	fimc_ctrls_delete(ctx);
687	v4l2_fh_del(&ctx->fh, file);
688	v4l2_fh_exit(&ctx->fh);
689
690	if (--fimc->m2m.refcnt <= 0)
691		clear_bit(ST_M2M_RUN, &fimc->state);
692	kfree(ctx);
693
694	mutex_unlock(&fimc->lock);
695	return 0;
696}
697
698static const struct v4l2_file_operations fimc_m2m_fops = {
699	.owner		= THIS_MODULE,
700	.open		= fimc_m2m_open,
701	.release	= fimc_m2m_release,
702	.poll		= v4l2_m2m_fop_poll,
703	.unlocked_ioctl	= video_ioctl2,
704	.mmap		= v4l2_m2m_fop_mmap,
705};
706
707static const struct v4l2_m2m_ops m2m_ops = {
708	.device_run	= fimc_device_run,
709	.job_abort	= fimc_job_abort,
710};
711
712int fimc_register_m2m_device(struct fimc_dev *fimc,
713			     struct v4l2_device *v4l2_dev)
714{
715	struct video_device *vfd = &fimc->m2m.vfd;
716	int ret;
717
718	fimc->v4l2_dev = v4l2_dev;
719
720	memset(vfd, 0, sizeof(*vfd));
721	vfd->fops = &fimc_m2m_fops;
722	vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
723	vfd->v4l2_dev = v4l2_dev;
724	vfd->minor = -1;
725	vfd->release = video_device_release_empty;
726	vfd->lock = &fimc->lock;
727	vfd->vfl_dir = VFL_DIR_M2M;
728	vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
729	set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags);
730
731	snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id);
732	video_set_drvdata(vfd, fimc);
733
734	fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
735	if (IS_ERR(fimc->m2m.m2m_dev)) {
736		v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
737		return PTR_ERR(fimc->m2m.m2m_dev);
738	}
739
740	ret = media_entity_pads_init(&vfd->entity, 0, NULL);
741	if (ret)
742		goto err_me;
743
744	ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
745	if (ret)
746		goto err_vd;
747
748	v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
749		  vfd->name, video_device_node_name(vfd));
750	return 0;
751
752err_vd:
753	media_entity_cleanup(&vfd->entity);
754err_me:
755	v4l2_m2m_release(fimc->m2m.m2m_dev);
756	return ret;
757}
758
759void fimc_unregister_m2m_device(struct fimc_dev *fimc)
760{
761	if (!fimc)
762		return;
763
764	if (fimc->m2m.m2m_dev)
765		v4l2_m2m_release(fimc->m2m.m2m_dev);
766
767	if (video_is_registered(&fimc->m2m.vfd)) {
768		video_unregister_device(&fimc->m2m.vfd);
769		media_entity_cleanup(&fimc->m2m.vfd.entity);
770	}
771}
~/ Read-only file preview.