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