1*cf21f328SLaurent Pinchart // SPDX-License-Identifier: GPL-2.0
2*cf21f328SLaurent Pinchart /*
3*cf21f328SLaurent Pinchart  * ISI V4L2 memory to memory driver for i.MX8QXP/QM platform
4*cf21f328SLaurent Pinchart  *
5*cf21f328SLaurent Pinchart  * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which
6*cf21f328SLaurent Pinchart  * used to process image from camera sensor or memory to memory or DC
7*cf21f328SLaurent Pinchart  *
8*cf21f328SLaurent Pinchart  * Copyright (c) 2019 NXP Semiconductor
9*cf21f328SLaurent Pinchart  */
10*cf21f328SLaurent Pinchart 
11*cf21f328SLaurent Pinchart #include <linux/container_of.h>
12*cf21f328SLaurent Pinchart #include <linux/device.h>
13*cf21f328SLaurent Pinchart #include <linux/errno.h>
14*cf21f328SLaurent Pinchart #include <linux/kernel.h>
15*cf21f328SLaurent Pinchart #include <linux/limits.h>
16*cf21f328SLaurent Pinchart #include <linux/minmax.h>
17*cf21f328SLaurent Pinchart #include <linux/mutex.h>
18*cf21f328SLaurent Pinchart #include <linux/pm_runtime.h>
19*cf21f328SLaurent Pinchart #include <linux/slab.h>
20*cf21f328SLaurent Pinchart #include <linux/spinlock.h>
21*cf21f328SLaurent Pinchart #include <linux/string.h>
22*cf21f328SLaurent Pinchart #include <linux/types.h>
23*cf21f328SLaurent Pinchart #include <linux/videodev2.h>
24*cf21f328SLaurent Pinchart 
25*cf21f328SLaurent Pinchart #include <media/media-entity.h>
26*cf21f328SLaurent Pinchart #include <media/v4l2-ctrls.h>
27*cf21f328SLaurent Pinchart #include <media/v4l2-device.h>
28*cf21f328SLaurent Pinchart #include <media/v4l2-event.h>
29*cf21f328SLaurent Pinchart #include <media/v4l2-fh.h>
30*cf21f328SLaurent Pinchart #include <media/v4l2-ioctl.h>
31*cf21f328SLaurent Pinchart #include <media/v4l2-mem2mem.h>
32*cf21f328SLaurent Pinchart #include <media/videobuf2-core.h>
33*cf21f328SLaurent Pinchart #include <media/videobuf2-dma-contig.h>
34*cf21f328SLaurent Pinchart 
35*cf21f328SLaurent Pinchart #include "imx8-isi-core.h"
36*cf21f328SLaurent Pinchart 
37*cf21f328SLaurent Pinchart struct mxc_isi_m2m_buffer {
38*cf21f328SLaurent Pinchart 	struct v4l2_m2m_buffer buf;
39*cf21f328SLaurent Pinchart 	dma_addr_t dma_addrs[3];
40*cf21f328SLaurent Pinchart };
41*cf21f328SLaurent Pinchart 
42*cf21f328SLaurent Pinchart struct mxc_isi_m2m_ctx_queue_data {
43*cf21f328SLaurent Pinchart 	struct v4l2_pix_format_mplane format;
44*cf21f328SLaurent Pinchart 	const struct mxc_isi_format_info *info;
45*cf21f328SLaurent Pinchart 	u32 sequence;
46*cf21f328SLaurent Pinchart };
47*cf21f328SLaurent Pinchart 
48*cf21f328SLaurent Pinchart struct mxc_isi_m2m_ctx {
49*cf21f328SLaurent Pinchart 	struct v4l2_fh fh;
50*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m *m2m;
51*cf21f328SLaurent Pinchart 
52*cf21f328SLaurent Pinchart 	/* Protects the m2m vb2 queues */
53*cf21f328SLaurent Pinchart 	struct mutex vb2_lock;
54*cf21f328SLaurent Pinchart 
55*cf21f328SLaurent Pinchart 	struct {
56*cf21f328SLaurent Pinchart 		struct mxc_isi_m2m_ctx_queue_data out;
57*cf21f328SLaurent Pinchart 		struct mxc_isi_m2m_ctx_queue_data cap;
58*cf21f328SLaurent Pinchart 	} queues;
59*cf21f328SLaurent Pinchart 
60*cf21f328SLaurent Pinchart 	struct {
61*cf21f328SLaurent Pinchart 		struct v4l2_ctrl_handler handler;
62*cf21f328SLaurent Pinchart 		unsigned int alpha;
63*cf21f328SLaurent Pinchart 		bool hflip;
64*cf21f328SLaurent Pinchart 		bool vflip;
65*cf21f328SLaurent Pinchart 	} ctrls;
66*cf21f328SLaurent Pinchart 
67*cf21f328SLaurent Pinchart 	bool chained;
68*cf21f328SLaurent Pinchart };
69*cf21f328SLaurent Pinchart 
70*cf21f328SLaurent Pinchart static inline struct mxc_isi_m2m_buffer *
to_isi_m2m_buffer(struct vb2_v4l2_buffer * buf)71*cf21f328SLaurent Pinchart to_isi_m2m_buffer(struct vb2_v4l2_buffer *buf)
72*cf21f328SLaurent Pinchart {
73*cf21f328SLaurent Pinchart 	return container_of(buf, struct mxc_isi_m2m_buffer, buf.vb);
74*cf21f328SLaurent Pinchart }
75*cf21f328SLaurent Pinchart 
to_isi_m2m_ctx(struct v4l2_fh * fh)76*cf21f328SLaurent Pinchart static inline struct mxc_isi_m2m_ctx *to_isi_m2m_ctx(struct v4l2_fh *fh)
77*cf21f328SLaurent Pinchart {
78*cf21f328SLaurent Pinchart 	return container_of(fh, struct mxc_isi_m2m_ctx, fh);
79*cf21f328SLaurent Pinchart }
80*cf21f328SLaurent Pinchart 
81*cf21f328SLaurent Pinchart static inline struct mxc_isi_m2m_ctx_queue_data *
mxc_isi_m2m_ctx_qdata(struct mxc_isi_m2m_ctx * ctx,enum v4l2_buf_type type)82*cf21f328SLaurent Pinchart mxc_isi_m2m_ctx_qdata(struct mxc_isi_m2m_ctx *ctx, enum v4l2_buf_type type)
83*cf21f328SLaurent Pinchart {
84*cf21f328SLaurent Pinchart 	if (V4L2_TYPE_IS_OUTPUT(type))
85*cf21f328SLaurent Pinchart 		return &ctx->queues.out;
86*cf21f328SLaurent Pinchart 	else
87*cf21f328SLaurent Pinchart 		return &ctx->queues.cap;
88*cf21f328SLaurent Pinchart }
89*cf21f328SLaurent Pinchart 
90*cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
91*cf21f328SLaurent Pinchart  * V4L2 M2M device operations
92*cf21f328SLaurent Pinchart  */
93*cf21f328SLaurent Pinchart 
mxc_isi_m2m_frame_write_done(struct mxc_isi_pipe * pipe,u32 status)94*cf21f328SLaurent Pinchart static void mxc_isi_m2m_frame_write_done(struct mxc_isi_pipe *pipe, u32 status)
95*cf21f328SLaurent Pinchart {
96*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m *m2m = &pipe->isi->m2m;
97*cf21f328SLaurent Pinchart 	struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
98*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx;
99*cf21f328SLaurent Pinchart 
100*cf21f328SLaurent Pinchart 	ctx = v4l2_m2m_get_curr_priv(m2m->m2m_dev);
101*cf21f328SLaurent Pinchart 	if (!ctx) {
102*cf21f328SLaurent Pinchart 		dev_err(m2m->isi->dev,
103*cf21f328SLaurent Pinchart 			"Instance released before the end of transaction\n");
104*cf21f328SLaurent Pinchart 		return;
105*cf21f328SLaurent Pinchart 	}
106*cf21f328SLaurent Pinchart 
107*cf21f328SLaurent Pinchart 	src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
108*cf21f328SLaurent Pinchart 	dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
109*cf21f328SLaurent Pinchart 
110*cf21f328SLaurent Pinchart 	v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, false);
111*cf21f328SLaurent Pinchart 
112*cf21f328SLaurent Pinchart 	src_vbuf->sequence = ctx->queues.out.sequence++;
113*cf21f328SLaurent Pinchart 	dst_vbuf->sequence = ctx->queues.cap.sequence++;
114*cf21f328SLaurent Pinchart 
115*cf21f328SLaurent Pinchart 	v4l2_m2m_buf_done(src_vbuf, VB2_BUF_STATE_DONE);
116*cf21f328SLaurent Pinchart 	v4l2_m2m_buf_done(dst_vbuf, VB2_BUF_STATE_DONE);
117*cf21f328SLaurent Pinchart 
118*cf21f328SLaurent Pinchart 	v4l2_m2m_job_finish(m2m->m2m_dev, ctx->fh.m2m_ctx);
119*cf21f328SLaurent Pinchart }
120*cf21f328SLaurent Pinchart 
mxc_isi_m2m_device_run(void * priv)121*cf21f328SLaurent Pinchart static void mxc_isi_m2m_device_run(void *priv)
122*cf21f328SLaurent Pinchart {
123*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = priv;
124*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m *m2m = ctx->m2m;
125*cf21f328SLaurent Pinchart 	struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
126*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_buffer *src_buf, *dst_buf;
127*cf21f328SLaurent Pinchart 
128*cf21f328SLaurent Pinchart 	mxc_isi_channel_disable(m2m->pipe);
129*cf21f328SLaurent Pinchart 
130*cf21f328SLaurent Pinchart 	mutex_lock(&m2m->lock);
131*cf21f328SLaurent Pinchart 
132*cf21f328SLaurent Pinchart 	/* If the context has changed, reconfigure the channel. */
133*cf21f328SLaurent Pinchart 	if (m2m->last_ctx != ctx) {
134*cf21f328SLaurent Pinchart 		const struct v4l2_area in_size = {
135*cf21f328SLaurent Pinchart 			.width = ctx->queues.out.format.width,
136*cf21f328SLaurent Pinchart 			.height = ctx->queues.out.format.height,
137*cf21f328SLaurent Pinchart 		};
138*cf21f328SLaurent Pinchart 		const struct v4l2_area scale = {
139*cf21f328SLaurent Pinchart 			.width = ctx->queues.cap.format.width,
140*cf21f328SLaurent Pinchart 			.height = ctx->queues.cap.format.height,
141*cf21f328SLaurent Pinchart 		};
142*cf21f328SLaurent Pinchart 		const struct v4l2_rect crop = {
143*cf21f328SLaurent Pinchart 			.width = ctx->queues.cap.format.width,
144*cf21f328SLaurent Pinchart 			.height = ctx->queues.cap.format.height,
145*cf21f328SLaurent Pinchart 		};
146*cf21f328SLaurent Pinchart 
147*cf21f328SLaurent Pinchart 		mxc_isi_channel_config(m2m->pipe, MXC_ISI_INPUT_MEM,
148*cf21f328SLaurent Pinchart 				       &in_size, &scale, &crop,
149*cf21f328SLaurent Pinchart 				       ctx->queues.out.info->encoding,
150*cf21f328SLaurent Pinchart 				       ctx->queues.cap.info->encoding);
151*cf21f328SLaurent Pinchart 		mxc_isi_channel_set_input_format(m2m->pipe,
152*cf21f328SLaurent Pinchart 						 ctx->queues.out.info,
153*cf21f328SLaurent Pinchart 						 &ctx->queues.out.format);
154*cf21f328SLaurent Pinchart 		mxc_isi_channel_set_output_format(m2m->pipe,
155*cf21f328SLaurent Pinchart 						  ctx->queues.cap.info,
156*cf21f328SLaurent Pinchart 						  &ctx->queues.cap.format);
157*cf21f328SLaurent Pinchart 
158*cf21f328SLaurent Pinchart 		m2m->last_ctx = ctx;
159*cf21f328SLaurent Pinchart 	}
160*cf21f328SLaurent Pinchart 
161*cf21f328SLaurent Pinchart 	mutex_unlock(&m2m->lock);
162*cf21f328SLaurent Pinchart 
163*cf21f328SLaurent Pinchart 	mutex_lock(ctx->ctrls.handler.lock);
164*cf21f328SLaurent Pinchart 	mxc_isi_channel_set_alpha(m2m->pipe, ctx->ctrls.alpha);
165*cf21f328SLaurent Pinchart 	mxc_isi_channel_set_flip(m2m->pipe, ctx->ctrls.hflip, ctx->ctrls.vflip);
166*cf21f328SLaurent Pinchart 	mutex_unlock(ctx->ctrls.handler.lock);
167*cf21f328SLaurent Pinchart 
168*cf21f328SLaurent Pinchart 	src_vbuf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
169*cf21f328SLaurent Pinchart 	dst_vbuf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
170*cf21f328SLaurent Pinchart 
171*cf21f328SLaurent Pinchart 	src_buf = to_isi_m2m_buffer(src_vbuf);
172*cf21f328SLaurent Pinchart 	dst_buf = to_isi_m2m_buffer(dst_vbuf);
173*cf21f328SLaurent Pinchart 
174*cf21f328SLaurent Pinchart 	mxc_isi_channel_set_inbuf(m2m->pipe, src_buf->dma_addrs[0]);
175*cf21f328SLaurent Pinchart 	mxc_isi_channel_set_outbuf(m2m->pipe, dst_buf->dma_addrs, MXC_ISI_BUF1);
176*cf21f328SLaurent Pinchart 	mxc_isi_channel_set_outbuf(m2m->pipe, dst_buf->dma_addrs, MXC_ISI_BUF2);
177*cf21f328SLaurent Pinchart 
178*cf21f328SLaurent Pinchart 	mxc_isi_channel_enable(m2m->pipe);
179*cf21f328SLaurent Pinchart 
180*cf21f328SLaurent Pinchart 	mxc_isi_channel_m2m_start(m2m->pipe);
181*cf21f328SLaurent Pinchart }
182*cf21f328SLaurent Pinchart 
183*cf21f328SLaurent Pinchart static const struct v4l2_m2m_ops mxc_isi_m2m_ops = {
184*cf21f328SLaurent Pinchart 	.device_run = mxc_isi_m2m_device_run,
185*cf21f328SLaurent Pinchart };
186*cf21f328SLaurent Pinchart 
187*cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
188*cf21f328SLaurent Pinchart  * videobuf2 queue operations
189*cf21f328SLaurent Pinchart  */
190*cf21f328SLaurent Pinchart 
mxc_isi_m2m_vb2_queue_setup(struct vb2_queue * q,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])191*cf21f328SLaurent Pinchart static int mxc_isi_m2m_vb2_queue_setup(struct vb2_queue *q,
192*cf21f328SLaurent Pinchart 				       unsigned int *num_buffers,
193*cf21f328SLaurent Pinchart 				       unsigned int *num_planes,
194*cf21f328SLaurent Pinchart 				       unsigned int sizes[],
195*cf21f328SLaurent Pinchart 				       struct device *alloc_devs[])
196*cf21f328SLaurent Pinchart {
197*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);
198*cf21f328SLaurent Pinchart 	const struct mxc_isi_m2m_ctx_queue_data *qdata =
199*cf21f328SLaurent Pinchart 		mxc_isi_m2m_ctx_qdata(ctx, q->type);
200*cf21f328SLaurent Pinchart 
201*cf21f328SLaurent Pinchart 	return mxc_isi_video_queue_setup(&qdata->format, qdata->info,
202*cf21f328SLaurent Pinchart 					 num_buffers, num_planes, sizes);
203*cf21f328SLaurent Pinchart }
204*cf21f328SLaurent Pinchart 
mxc_isi_m2m_vb2_buffer_init(struct vb2_buffer * vb2)205*cf21f328SLaurent Pinchart static int mxc_isi_m2m_vb2_buffer_init(struct vb2_buffer *vb2)
206*cf21f328SLaurent Pinchart {
207*cf21f328SLaurent Pinchart 	struct vb2_queue *vq = vb2->vb2_queue;
208*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_buffer *buf = to_isi_m2m_buffer(to_vb2_v4l2_buffer(vb2));
209*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vb2->vb2_queue);
210*cf21f328SLaurent Pinchart 	const struct mxc_isi_m2m_ctx_queue_data *qdata =
211*cf21f328SLaurent Pinchart 		mxc_isi_m2m_ctx_qdata(ctx, vq->type);
212*cf21f328SLaurent Pinchart 
213*cf21f328SLaurent Pinchart 	mxc_isi_video_buffer_init(vb2, buf->dma_addrs, qdata->info,
214*cf21f328SLaurent Pinchart 				  &qdata->format);
215*cf21f328SLaurent Pinchart 
216*cf21f328SLaurent Pinchart 	return 0;
217*cf21f328SLaurent Pinchart }
218*cf21f328SLaurent Pinchart 
mxc_isi_m2m_vb2_buffer_prepare(struct vb2_buffer * vb2)219*cf21f328SLaurent Pinchart static int mxc_isi_m2m_vb2_buffer_prepare(struct vb2_buffer *vb2)
220*cf21f328SLaurent Pinchart {
221*cf21f328SLaurent Pinchart 	struct vb2_queue *vq = vb2->vb2_queue;
222*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vq);
223*cf21f328SLaurent Pinchart 	const struct mxc_isi_m2m_ctx_queue_data *qdata =
224*cf21f328SLaurent Pinchart 		mxc_isi_m2m_ctx_qdata(ctx, vq->type);
225*cf21f328SLaurent Pinchart 
226*cf21f328SLaurent Pinchart 	return mxc_isi_video_buffer_prepare(ctx->m2m->isi, vb2, qdata->info,
227*cf21f328SLaurent Pinchart 					    &qdata->format);
228*cf21f328SLaurent Pinchart }
229*cf21f328SLaurent Pinchart 
mxc_isi_m2m_vb2_buffer_queue(struct vb2_buffer * vb2)230*cf21f328SLaurent Pinchart static void mxc_isi_m2m_vb2_buffer_queue(struct vb2_buffer *vb2)
231*cf21f328SLaurent Pinchart {
232*cf21f328SLaurent Pinchart 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
233*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(vb2->vb2_queue);
234*cf21f328SLaurent Pinchart 
235*cf21f328SLaurent Pinchart 	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
236*cf21f328SLaurent Pinchart }
237*cf21f328SLaurent Pinchart 
mxc_isi_m2m_vb2_start_streaming(struct vb2_queue * q,unsigned int count)238*cf21f328SLaurent Pinchart static int mxc_isi_m2m_vb2_start_streaming(struct vb2_queue *q,
239*cf21f328SLaurent Pinchart 					   unsigned int count)
240*cf21f328SLaurent Pinchart {
241*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);
242*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx_queue_data *qdata =
243*cf21f328SLaurent Pinchart 		mxc_isi_m2m_ctx_qdata(ctx, q->type);
244*cf21f328SLaurent Pinchart 
245*cf21f328SLaurent Pinchart 	qdata->sequence = 0;
246*cf21f328SLaurent Pinchart 
247*cf21f328SLaurent Pinchart 	return 0;
248*cf21f328SLaurent Pinchart }
249*cf21f328SLaurent Pinchart 
mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue * q)250*cf21f328SLaurent Pinchart static void mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue *q)
251*cf21f328SLaurent Pinchart {
252*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);
253*cf21f328SLaurent Pinchart 	struct vb2_v4l2_buffer *vbuf;
254*cf21f328SLaurent Pinchart 
255*cf21f328SLaurent Pinchart 	for (;;) {
256*cf21f328SLaurent Pinchart 		if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
257*cf21f328SLaurent Pinchart 			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
258*cf21f328SLaurent Pinchart 		else
259*cf21f328SLaurent Pinchart 			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
260*cf21f328SLaurent Pinchart 		if (!vbuf)
261*cf21f328SLaurent Pinchart 			break;
262*cf21f328SLaurent Pinchart 
263*cf21f328SLaurent Pinchart 		v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
264*cf21f328SLaurent Pinchart 	}
265*cf21f328SLaurent Pinchart }
266*cf21f328SLaurent Pinchart 
267*cf21f328SLaurent Pinchart static const struct vb2_ops mxc_isi_m2m_vb2_qops = {
268*cf21f328SLaurent Pinchart 	.queue_setup		= mxc_isi_m2m_vb2_queue_setup,
269*cf21f328SLaurent Pinchart 	.buf_init		= mxc_isi_m2m_vb2_buffer_init,
270*cf21f328SLaurent Pinchart 	.buf_prepare		= mxc_isi_m2m_vb2_buffer_prepare,
271*cf21f328SLaurent Pinchart 	.buf_queue		= mxc_isi_m2m_vb2_buffer_queue,
272*cf21f328SLaurent Pinchart 	.wait_prepare		= vb2_ops_wait_prepare,
273*cf21f328SLaurent Pinchart 	.wait_finish		= vb2_ops_wait_finish,
274*cf21f328SLaurent Pinchart 	.start_streaming	= mxc_isi_m2m_vb2_start_streaming,
275*cf21f328SLaurent Pinchart 	.stop_streaming		= mxc_isi_m2m_vb2_stop_streaming,
276*cf21f328SLaurent Pinchart };
277*cf21f328SLaurent Pinchart 
mxc_isi_m2m_queue_init(void * priv,struct vb2_queue * src_vq,struct vb2_queue * dst_vq)278*cf21f328SLaurent Pinchart static int mxc_isi_m2m_queue_init(void *priv, struct vb2_queue *src_vq,
279*cf21f328SLaurent Pinchart 				  struct vb2_queue *dst_vq)
280*cf21f328SLaurent Pinchart {
281*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = priv;
282*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m *m2m = ctx->m2m;
283*cf21f328SLaurent Pinchart 	int ret;
284*cf21f328SLaurent Pinchart 
285*cf21f328SLaurent Pinchart 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
286*cf21f328SLaurent Pinchart 	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
287*cf21f328SLaurent Pinchart 	src_vq->drv_priv = ctx;
288*cf21f328SLaurent Pinchart 	src_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer);
289*cf21f328SLaurent Pinchart 	src_vq->ops = &mxc_isi_m2m_vb2_qops;
290*cf21f328SLaurent Pinchart 	src_vq->mem_ops = &vb2_dma_contig_memops;
291*cf21f328SLaurent Pinchart 	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
292*cf21f328SLaurent Pinchart 	src_vq->lock = &ctx->vb2_lock;
293*cf21f328SLaurent Pinchart 	src_vq->dev = m2m->isi->dev;
294*cf21f328SLaurent Pinchart 
295*cf21f328SLaurent Pinchart 	ret = vb2_queue_init(src_vq);
296*cf21f328SLaurent Pinchart 	if (ret)
297*cf21f328SLaurent Pinchart 		return ret;
298*cf21f328SLaurent Pinchart 
299*cf21f328SLaurent Pinchart 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
300*cf21f328SLaurent Pinchart 	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
301*cf21f328SLaurent Pinchart 	dst_vq->drv_priv = ctx;
302*cf21f328SLaurent Pinchart 	dst_vq->buf_struct_size = sizeof(struct mxc_isi_m2m_buffer);
303*cf21f328SLaurent Pinchart 	dst_vq->ops = &mxc_isi_m2m_vb2_qops;
304*cf21f328SLaurent Pinchart 	dst_vq->mem_ops = &vb2_dma_contig_memops;
305*cf21f328SLaurent Pinchart 	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
306*cf21f328SLaurent Pinchart 	dst_vq->lock = &ctx->vb2_lock;
307*cf21f328SLaurent Pinchart 	dst_vq->dev = m2m->isi->dev;
308*cf21f328SLaurent Pinchart 
309*cf21f328SLaurent Pinchart 	return vb2_queue_init(dst_vq);
310*cf21f328SLaurent Pinchart }
311*cf21f328SLaurent Pinchart 
312*cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
313*cf21f328SLaurent Pinchart  * V4L2 controls
314*cf21f328SLaurent Pinchart  */
315*cf21f328SLaurent Pinchart 
316*cf21f328SLaurent Pinchart static inline struct mxc_isi_m2m_ctx *
ctrl_to_mxc_isi_m2m_ctx(struct v4l2_ctrl * ctrl)317*cf21f328SLaurent Pinchart ctrl_to_mxc_isi_m2m_ctx(struct v4l2_ctrl *ctrl)
318*cf21f328SLaurent Pinchart {
319*cf21f328SLaurent Pinchart 	return container_of(ctrl->handler, struct mxc_isi_m2m_ctx, ctrls.handler);
320*cf21f328SLaurent Pinchart }
321*cf21f328SLaurent Pinchart 
mxc_isi_m2m_ctx_s_ctrl(struct v4l2_ctrl * ctrl)322*cf21f328SLaurent Pinchart static int mxc_isi_m2m_ctx_s_ctrl(struct v4l2_ctrl *ctrl)
323*cf21f328SLaurent Pinchart {
324*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = ctrl_to_mxc_isi_m2m_ctx(ctrl);
325*cf21f328SLaurent Pinchart 
326*cf21f328SLaurent Pinchart 	switch (ctrl->id) {
327*cf21f328SLaurent Pinchart 	case V4L2_CID_HFLIP:
328*cf21f328SLaurent Pinchart 		ctx->ctrls.hflip = ctrl->val;
329*cf21f328SLaurent Pinchart 		break;
330*cf21f328SLaurent Pinchart 
331*cf21f328SLaurent Pinchart 	case V4L2_CID_VFLIP:
332*cf21f328SLaurent Pinchart 		ctx->ctrls.vflip = ctrl->val;
333*cf21f328SLaurent Pinchart 		break;
334*cf21f328SLaurent Pinchart 
335*cf21f328SLaurent Pinchart 	case V4L2_CID_ALPHA_COMPONENT:
336*cf21f328SLaurent Pinchart 		ctx->ctrls.alpha = ctrl->val;
337*cf21f328SLaurent Pinchart 		break;
338*cf21f328SLaurent Pinchart 	}
339*cf21f328SLaurent Pinchart 
340*cf21f328SLaurent Pinchart 	return 0;
341*cf21f328SLaurent Pinchart }
342*cf21f328SLaurent Pinchart 
343*cf21f328SLaurent Pinchart static const struct v4l2_ctrl_ops mxc_isi_m2m_ctx_ctrl_ops = {
344*cf21f328SLaurent Pinchart 	.s_ctrl = mxc_isi_m2m_ctx_s_ctrl,
345*cf21f328SLaurent Pinchart };
346*cf21f328SLaurent Pinchart 
mxc_isi_m2m_ctx_ctrls_create(struct mxc_isi_m2m_ctx * ctx)347*cf21f328SLaurent Pinchart static int mxc_isi_m2m_ctx_ctrls_create(struct mxc_isi_m2m_ctx *ctx)
348*cf21f328SLaurent Pinchart {
349*cf21f328SLaurent Pinchart 	struct v4l2_ctrl_handler *handler = &ctx->ctrls.handler;
350*cf21f328SLaurent Pinchart 	int ret;
351*cf21f328SLaurent Pinchart 
352*cf21f328SLaurent Pinchart 	v4l2_ctrl_handler_init(handler, 3);
353*cf21f328SLaurent Pinchart 
354*cf21f328SLaurent Pinchart 	v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops,
355*cf21f328SLaurent Pinchart 			  V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
356*cf21f328SLaurent Pinchart 	v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops,
357*cf21f328SLaurent Pinchart 			  V4L2_CID_HFLIP, 0, 1, 1, 0);
358*cf21f328SLaurent Pinchart 	v4l2_ctrl_new_std(handler, &mxc_isi_m2m_ctx_ctrl_ops,
359*cf21f328SLaurent Pinchart 			  V4L2_CID_VFLIP, 0, 1, 1, 0);
360*cf21f328SLaurent Pinchart 
361*cf21f328SLaurent Pinchart 	if (handler->error) {
362*cf21f328SLaurent Pinchart 		ret = handler->error;
363*cf21f328SLaurent Pinchart 		v4l2_ctrl_handler_free(handler);
364*cf21f328SLaurent Pinchart 		return ret;
365*cf21f328SLaurent Pinchart 	}
366*cf21f328SLaurent Pinchart 
367*cf21f328SLaurent Pinchart 	ctx->fh.ctrl_handler = handler;
368*cf21f328SLaurent Pinchart 
369*cf21f328SLaurent Pinchart 	return 0;
370*cf21f328SLaurent Pinchart }
371*cf21f328SLaurent Pinchart 
mxc_isi_m2m_ctx_ctrls_delete(struct mxc_isi_m2m_ctx * ctx)372*cf21f328SLaurent Pinchart static void mxc_isi_m2m_ctx_ctrls_delete(struct mxc_isi_m2m_ctx *ctx)
373*cf21f328SLaurent Pinchart {
374*cf21f328SLaurent Pinchart 	v4l2_ctrl_handler_free(&ctx->ctrls.handler);
375*cf21f328SLaurent Pinchart }
376*cf21f328SLaurent Pinchart 
377*cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
378*cf21f328SLaurent Pinchart  * V4L2 ioctls
379*cf21f328SLaurent Pinchart  */
380*cf21f328SLaurent Pinchart 
mxc_isi_m2m_querycap(struct file * file,void * fh,struct v4l2_capability * cap)381*cf21f328SLaurent Pinchart static int mxc_isi_m2m_querycap(struct file *file, void *fh,
382*cf21f328SLaurent Pinchart 				struct v4l2_capability *cap)
383*cf21f328SLaurent Pinchart {
384*cf21f328SLaurent Pinchart 	strscpy(cap->driver, MXC_ISI_DRIVER_NAME, sizeof(cap->driver));
385*cf21f328SLaurent Pinchart 	strscpy(cap->card, MXC_ISI_M2M, sizeof(cap->card));
386*cf21f328SLaurent Pinchart 	cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
387*cf21f328SLaurent Pinchart 	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
388*cf21f328SLaurent Pinchart 
389*cf21f328SLaurent Pinchart 	return 0;
390*cf21f328SLaurent Pinchart }
391*cf21f328SLaurent Pinchart 
mxc_isi_m2m_enum_fmt_vid(struct file * file,void * fh,struct v4l2_fmtdesc * f)392*cf21f328SLaurent Pinchart static int mxc_isi_m2m_enum_fmt_vid(struct file *file, void *fh,
393*cf21f328SLaurent Pinchart 				    struct v4l2_fmtdesc *f)
394*cf21f328SLaurent Pinchart {
395*cf21f328SLaurent Pinchart 	const enum mxc_isi_video_type type =
396*cf21f328SLaurent Pinchart 		f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ?
397*cf21f328SLaurent Pinchart 		MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP;
398*cf21f328SLaurent Pinchart 	const struct mxc_isi_format_info *info;
399*cf21f328SLaurent Pinchart 
400*cf21f328SLaurent Pinchart 	info = mxc_isi_format_enum(f->index, type);
401*cf21f328SLaurent Pinchart 	if (!info)
402*cf21f328SLaurent Pinchart 		return -EINVAL;
403*cf21f328SLaurent Pinchart 
404*cf21f328SLaurent Pinchart 	f->pixelformat = info->fourcc;
405*cf21f328SLaurent Pinchart 	f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE | V4L2_FMT_FLAG_CSC_YCBCR_ENC
406*cf21f328SLaurent Pinchart 		 |  V4L2_FMT_FLAG_CSC_QUANTIZATION | V4L2_FMT_FLAG_CSC_XFER_FUNC;
407*cf21f328SLaurent Pinchart 
408*cf21f328SLaurent Pinchart 	return 0;
409*cf21f328SLaurent Pinchart }
410*cf21f328SLaurent Pinchart 
411*cf21f328SLaurent Pinchart static const struct mxc_isi_format_info *
__mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx * ctx,struct v4l2_pix_format_mplane * pix,const enum mxc_isi_video_type type)412*cf21f328SLaurent Pinchart __mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx,
413*cf21f328SLaurent Pinchart 			  struct v4l2_pix_format_mplane *pix,
414*cf21f328SLaurent Pinchart 			  const enum mxc_isi_video_type type)
415*cf21f328SLaurent Pinchart {
416*cf21f328SLaurent Pinchart 	if (type == MXC_ISI_VIDEO_M2M_CAP) {
417*cf21f328SLaurent Pinchart 		/* Downscaling only  */
418*cf21f328SLaurent Pinchart 		pix->width = min(pix->width, ctx->queues.out.format.width);
419*cf21f328SLaurent Pinchart 		pix->height = min(pix->height, ctx->queues.out.format.height);
420*cf21f328SLaurent Pinchart 	}
421*cf21f328SLaurent Pinchart 
422*cf21f328SLaurent Pinchart 	return mxc_isi_format_try(ctx->m2m->pipe, pix, type);
423*cf21f328SLaurent Pinchart }
424*cf21f328SLaurent Pinchart 
mxc_isi_m2m_try_fmt_vid(struct file * file,void * fh,struct v4l2_format * f)425*cf21f328SLaurent Pinchart static int mxc_isi_m2m_try_fmt_vid(struct file *file, void *fh,
426*cf21f328SLaurent Pinchart 				   struct v4l2_format *f)
427*cf21f328SLaurent Pinchart {
428*cf21f328SLaurent Pinchart 	const enum mxc_isi_video_type type =
429*cf21f328SLaurent Pinchart 		f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ?
430*cf21f328SLaurent Pinchart 		MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP;
431*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
432*cf21f328SLaurent Pinchart 
433*cf21f328SLaurent Pinchart 	__mxc_isi_m2m_try_fmt_vid(ctx, &f->fmt.pix_mp, type);
434*cf21f328SLaurent Pinchart 
435*cf21f328SLaurent Pinchart 	return 0;
436*cf21f328SLaurent Pinchart }
437*cf21f328SLaurent Pinchart 
mxc_isi_m2m_g_fmt_vid(struct file * file,void * fh,struct v4l2_format * f)438*cf21f328SLaurent Pinchart static int mxc_isi_m2m_g_fmt_vid(struct file *file, void *fh,
439*cf21f328SLaurent Pinchart 				 struct v4l2_format *f)
440*cf21f328SLaurent Pinchart {
441*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
442*cf21f328SLaurent Pinchart 	const struct mxc_isi_m2m_ctx_queue_data *qdata =
443*cf21f328SLaurent Pinchart 		mxc_isi_m2m_ctx_qdata(ctx, f->type);
444*cf21f328SLaurent Pinchart 
445*cf21f328SLaurent Pinchart 	f->fmt.pix_mp = qdata->format;
446*cf21f328SLaurent Pinchart 
447*cf21f328SLaurent Pinchart 	return 0;
448*cf21f328SLaurent Pinchart }
449*cf21f328SLaurent Pinchart 
mxc_isi_m2m_s_fmt_vid(struct file * file,void * fh,struct v4l2_format * f)450*cf21f328SLaurent Pinchart static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh,
451*cf21f328SLaurent Pinchart 				 struct v4l2_format *f)
452*cf21f328SLaurent Pinchart {
453*cf21f328SLaurent Pinchart 	const enum mxc_isi_video_type type =
454*cf21f328SLaurent Pinchart 		f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ?
455*cf21f328SLaurent Pinchart 		MXC_ISI_VIDEO_M2M_OUT : MXC_ISI_VIDEO_M2M_CAP;
456*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
457*cf21f328SLaurent Pinchart 	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
458*cf21f328SLaurent Pinchart 	const struct mxc_isi_format_info *info;
459*cf21f328SLaurent Pinchart 	struct vb2_queue *vq;
460*cf21f328SLaurent Pinchart 
461*cf21f328SLaurent Pinchart 	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
462*cf21f328SLaurent Pinchart 	if (!vq)
463*cf21f328SLaurent Pinchart 		return -EINVAL;
464*cf21f328SLaurent Pinchart 
465*cf21f328SLaurent Pinchart 	if (vb2_is_busy(vq))
466*cf21f328SLaurent Pinchart 		return -EBUSY;
467*cf21f328SLaurent Pinchart 
468*cf21f328SLaurent Pinchart 	info = __mxc_isi_m2m_try_fmt_vid(ctx, pix, type);
469*cf21f328SLaurent Pinchart 
470*cf21f328SLaurent Pinchart 	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
471*cf21f328SLaurent Pinchart 		ctx->queues.out.format = *pix;
472*cf21f328SLaurent Pinchart 		ctx->queues.out.info = info;
473*cf21f328SLaurent Pinchart 	}
474*cf21f328SLaurent Pinchart 
475*cf21f328SLaurent Pinchart 	/*
476*cf21f328SLaurent Pinchart 	 * Always set the format on the capture side, due to either format
477*cf21f328SLaurent Pinchart 	 * propagation or direct setting.
478*cf21f328SLaurent Pinchart 	 */
479*cf21f328SLaurent Pinchart 	ctx->queues.cap.format = *pix;
480*cf21f328SLaurent Pinchart 	ctx->queues.cap.info = info;
481*cf21f328SLaurent Pinchart 
482*cf21f328SLaurent Pinchart 	return 0;
483*cf21f328SLaurent Pinchart }
484*cf21f328SLaurent Pinchart 
mxc_isi_m2m_streamon(struct file * file,void * fh,enum v4l2_buf_type type)485*cf21f328SLaurent Pinchart static int mxc_isi_m2m_streamon(struct file *file, void *fh,
486*cf21f328SLaurent Pinchart 				enum v4l2_buf_type type)
487*cf21f328SLaurent Pinchart {
488*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
489*cf21f328SLaurent Pinchart 	const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format;
490*cf21f328SLaurent Pinchart 	const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format;
491*cf21f328SLaurent Pinchart 	const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info;
492*cf21f328SLaurent Pinchart 	const struct mxc_isi_format_info *out_info = ctx->queues.out.info;
493*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m *m2m = ctx->m2m;
494*cf21f328SLaurent Pinchart 	bool bypass;
495*cf21f328SLaurent Pinchart 
496*cf21f328SLaurent Pinchart 	int ret;
497*cf21f328SLaurent Pinchart 
498*cf21f328SLaurent Pinchart 	mutex_lock(&m2m->lock);
499*cf21f328SLaurent Pinchart 
500*cf21f328SLaurent Pinchart 	if (m2m->usage_count == INT_MAX) {
501*cf21f328SLaurent Pinchart 		ret = -EOVERFLOW;
502*cf21f328SLaurent Pinchart 		goto unlock;
503*cf21f328SLaurent Pinchart 	}
504*cf21f328SLaurent Pinchart 
505*cf21f328SLaurent Pinchart 	bypass = cap_pix->width == out_pix->width &&
506*cf21f328SLaurent Pinchart 		 cap_pix->height == out_pix->height &&
507*cf21f328SLaurent Pinchart 		 cap_info->encoding == out_info->encoding;
508*cf21f328SLaurent Pinchart 
509*cf21f328SLaurent Pinchart 	/*
510*cf21f328SLaurent Pinchart 	 * Acquire the pipe and initialize the channel with the first user of
511*cf21f328SLaurent Pinchart 	 * the M2M device.
512*cf21f328SLaurent Pinchart 	 */
513*cf21f328SLaurent Pinchart 	if (m2m->usage_count == 0) {
514*cf21f328SLaurent Pinchart 		ret = mxc_isi_channel_acquire(m2m->pipe,
515*cf21f328SLaurent Pinchart 					      &mxc_isi_m2m_frame_write_done,
516*cf21f328SLaurent Pinchart 					      bypass);
517*cf21f328SLaurent Pinchart 		if (ret)
518*cf21f328SLaurent Pinchart 			goto unlock;
519*cf21f328SLaurent Pinchart 
520*cf21f328SLaurent Pinchart 		mxc_isi_channel_get(m2m->pipe);
521*cf21f328SLaurent Pinchart 	}
522*cf21f328SLaurent Pinchart 
523*cf21f328SLaurent Pinchart 	m2m->usage_count++;
524*cf21f328SLaurent Pinchart 
525*cf21f328SLaurent Pinchart 	/*
526*cf21f328SLaurent Pinchart 	 * Allocate resources for the channel, counting how many users require
527*cf21f328SLaurent Pinchart 	 * buffer chaining.
528*cf21f328SLaurent Pinchart 	 */
529*cf21f328SLaurent Pinchart 	if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) {
530*cf21f328SLaurent Pinchart 		ret = mxc_isi_channel_chain(m2m->pipe, bypass);
531*cf21f328SLaurent Pinchart 		if (ret)
532*cf21f328SLaurent Pinchart 			goto deinit;
533*cf21f328SLaurent Pinchart 
534*cf21f328SLaurent Pinchart 		m2m->chained_count++;
535*cf21f328SLaurent Pinchart 		ctx->chained = true;
536*cf21f328SLaurent Pinchart 	}
537*cf21f328SLaurent Pinchart 
538*cf21f328SLaurent Pinchart 	/*
539*cf21f328SLaurent Pinchart 	 * Drop the lock to start the stream, as the .device_run() operation
540*cf21f328SLaurent Pinchart 	 * needs to acquire it.
541*cf21f328SLaurent Pinchart 	 */
542*cf21f328SLaurent Pinchart 	mutex_unlock(&m2m->lock);
543*cf21f328SLaurent Pinchart 	ret = v4l2_m2m_ioctl_streamon(file, fh, type);
544*cf21f328SLaurent Pinchart 	if (ret) {
545*cf21f328SLaurent Pinchart 		/* Reacquire the lock for the cleanup path. */
546*cf21f328SLaurent Pinchart 		mutex_lock(&m2m->lock);
547*cf21f328SLaurent Pinchart 		goto unchain;
548*cf21f328SLaurent Pinchart 	}
549*cf21f328SLaurent Pinchart 
550*cf21f328SLaurent Pinchart 	return 0;
551*cf21f328SLaurent Pinchart 
552*cf21f328SLaurent Pinchart unchain:
553*cf21f328SLaurent Pinchart 	if (ctx->chained && --m2m->chained_count == 0)
554*cf21f328SLaurent Pinchart 		mxc_isi_channel_unchain(m2m->pipe);
555*cf21f328SLaurent Pinchart 	ctx->chained = false;
556*cf21f328SLaurent Pinchart 
557*cf21f328SLaurent Pinchart deinit:
558*cf21f328SLaurent Pinchart 	if (--m2m->usage_count == 0) {
559*cf21f328SLaurent Pinchart 		mxc_isi_channel_put(m2m->pipe);
560*cf21f328SLaurent Pinchart 		mxc_isi_channel_release(m2m->pipe);
561*cf21f328SLaurent Pinchart 	}
562*cf21f328SLaurent Pinchart 
563*cf21f328SLaurent Pinchart unlock:
564*cf21f328SLaurent Pinchart 	mutex_unlock(&m2m->lock);
565*cf21f328SLaurent Pinchart 	return ret;
566*cf21f328SLaurent Pinchart }
567*cf21f328SLaurent Pinchart 
mxc_isi_m2m_streamoff(struct file * file,void * fh,enum v4l2_buf_type type)568*cf21f328SLaurent Pinchart static int mxc_isi_m2m_streamoff(struct file *file, void *fh,
569*cf21f328SLaurent Pinchart 				 enum v4l2_buf_type type)
570*cf21f328SLaurent Pinchart {
571*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
572*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m *m2m = ctx->m2m;
573*cf21f328SLaurent Pinchart 
574*cf21f328SLaurent Pinchart 	v4l2_m2m_ioctl_streamoff(file, fh, type);
575*cf21f328SLaurent Pinchart 
576*cf21f328SLaurent Pinchart 	mutex_lock(&m2m->lock);
577*cf21f328SLaurent Pinchart 
578*cf21f328SLaurent Pinchart 	/*
579*cf21f328SLaurent Pinchart 	 * If the last context is this one, reset it to make sure the device
580*cf21f328SLaurent Pinchart 	 * will be reconfigured when streaming is restarted.
581*cf21f328SLaurent Pinchart 	 */
582*cf21f328SLaurent Pinchart 	if (m2m->last_ctx == ctx)
583*cf21f328SLaurent Pinchart 		m2m->last_ctx = NULL;
584*cf21f328SLaurent Pinchart 
585*cf21f328SLaurent Pinchart 	/* Free the channel resources if this is the last chained context. */
586*cf21f328SLaurent Pinchart 	if (ctx->chained && --m2m->chained_count == 0)
587*cf21f328SLaurent Pinchart 		mxc_isi_channel_unchain(m2m->pipe);
588*cf21f328SLaurent Pinchart 	ctx->chained = false;
589*cf21f328SLaurent Pinchart 
590*cf21f328SLaurent Pinchart 	/* Turn off the light with the last user. */
591*cf21f328SLaurent Pinchart 	if (--m2m->usage_count == 0) {
592*cf21f328SLaurent Pinchart 		mxc_isi_channel_disable(m2m->pipe);
593*cf21f328SLaurent Pinchart 		mxc_isi_channel_put(m2m->pipe);
594*cf21f328SLaurent Pinchart 		mxc_isi_channel_release(m2m->pipe);
595*cf21f328SLaurent Pinchart 	}
596*cf21f328SLaurent Pinchart 
597*cf21f328SLaurent Pinchart 	WARN_ON(m2m->usage_count < 0);
598*cf21f328SLaurent Pinchart 
599*cf21f328SLaurent Pinchart 	mutex_unlock(&m2m->lock);
600*cf21f328SLaurent Pinchart 
601*cf21f328SLaurent Pinchart 	return 0;
602*cf21f328SLaurent Pinchart }
603*cf21f328SLaurent Pinchart 
604*cf21f328SLaurent Pinchart static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = {
605*cf21f328SLaurent Pinchart 	.vidioc_querycap		= mxc_isi_m2m_querycap,
606*cf21f328SLaurent Pinchart 
607*cf21f328SLaurent Pinchart 	.vidioc_enum_fmt_vid_cap	= mxc_isi_m2m_enum_fmt_vid,
608*cf21f328SLaurent Pinchart 	.vidioc_enum_fmt_vid_out	= mxc_isi_m2m_enum_fmt_vid,
609*cf21f328SLaurent Pinchart 	.vidioc_g_fmt_vid_cap_mplane	= mxc_isi_m2m_g_fmt_vid,
610*cf21f328SLaurent Pinchart 	.vidioc_g_fmt_vid_out_mplane	= mxc_isi_m2m_g_fmt_vid,
611*cf21f328SLaurent Pinchart 	.vidioc_s_fmt_vid_cap_mplane	= mxc_isi_m2m_s_fmt_vid,
612*cf21f328SLaurent Pinchart 	.vidioc_s_fmt_vid_out_mplane	= mxc_isi_m2m_s_fmt_vid,
613*cf21f328SLaurent Pinchart 	.vidioc_try_fmt_vid_cap_mplane	= mxc_isi_m2m_try_fmt_vid,
614*cf21f328SLaurent Pinchart 	.vidioc_try_fmt_vid_out_mplane	= mxc_isi_m2m_try_fmt_vid,
615*cf21f328SLaurent Pinchart 
616*cf21f328SLaurent Pinchart 	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
617*cf21f328SLaurent Pinchart 	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
618*cf21f328SLaurent Pinchart 	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
619*cf21f328SLaurent Pinchart 	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
620*cf21f328SLaurent Pinchart 	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
621*cf21f328SLaurent Pinchart 	.vidioc_prepare_buf		= v4l2_m2m_ioctl_prepare_buf,
622*cf21f328SLaurent Pinchart 	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
623*cf21f328SLaurent Pinchart 
624*cf21f328SLaurent Pinchart 	.vidioc_streamon		= mxc_isi_m2m_streamon,
625*cf21f328SLaurent Pinchart 	.vidioc_streamoff		= mxc_isi_m2m_streamoff,
626*cf21f328SLaurent Pinchart 
627*cf21f328SLaurent Pinchart 	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
628*cf21f328SLaurent Pinchart 	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
629*cf21f328SLaurent Pinchart };
630*cf21f328SLaurent Pinchart 
631*cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
632*cf21f328SLaurent Pinchart  * Video device file operations
633*cf21f328SLaurent Pinchart  */
634*cf21f328SLaurent Pinchart 
mxc_isi_m2m_init_format(struct mxc_isi_m2m_ctx * ctx,struct mxc_isi_m2m_ctx_queue_data * qdata,enum mxc_isi_video_type type)635*cf21f328SLaurent Pinchart static void mxc_isi_m2m_init_format(struct mxc_isi_m2m_ctx *ctx,
636*cf21f328SLaurent Pinchart 				    struct mxc_isi_m2m_ctx_queue_data *qdata,
637*cf21f328SLaurent Pinchart 				    enum mxc_isi_video_type type)
638*cf21f328SLaurent Pinchart {
639*cf21f328SLaurent Pinchart 	qdata->format.width = MXC_ISI_DEF_WIDTH;
640*cf21f328SLaurent Pinchart 	qdata->format.height = MXC_ISI_DEF_HEIGHT;
641*cf21f328SLaurent Pinchart 	qdata->format.pixelformat = MXC_ISI_DEF_PIXEL_FORMAT;
642*cf21f328SLaurent Pinchart 
643*cf21f328SLaurent Pinchart 	qdata->info = mxc_isi_format_try(ctx->m2m->pipe, &qdata->format, type);
644*cf21f328SLaurent Pinchart }
645*cf21f328SLaurent Pinchart 
mxc_isi_m2m_open(struct file * file)646*cf21f328SLaurent Pinchart static int mxc_isi_m2m_open(struct file *file)
647*cf21f328SLaurent Pinchart {
648*cf21f328SLaurent Pinchart 	struct video_device *vdev = video_devdata(file);
649*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m *m2m = video_drvdata(file);
650*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx;
651*cf21f328SLaurent Pinchart 	int ret;
652*cf21f328SLaurent Pinchart 
653*cf21f328SLaurent Pinchart 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
654*cf21f328SLaurent Pinchart 	if (!ctx)
655*cf21f328SLaurent Pinchart 		return -ENOMEM;
656*cf21f328SLaurent Pinchart 
657*cf21f328SLaurent Pinchart 	ctx->m2m = m2m;
658*cf21f328SLaurent Pinchart 	mutex_init(&ctx->vb2_lock);
659*cf21f328SLaurent Pinchart 
660*cf21f328SLaurent Pinchart 	v4l2_fh_init(&ctx->fh, vdev);
661*cf21f328SLaurent Pinchart 	file->private_data = &ctx->fh;
662*cf21f328SLaurent Pinchart 
663*cf21f328SLaurent Pinchart 	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(m2m->m2m_dev, ctx,
664*cf21f328SLaurent Pinchart 					    &mxc_isi_m2m_queue_init);
665*cf21f328SLaurent Pinchart 	if (IS_ERR(ctx->fh.m2m_ctx)) {
666*cf21f328SLaurent Pinchart 		ret = PTR_ERR(ctx->fh.m2m_ctx);
667*cf21f328SLaurent Pinchart 		ctx->fh.m2m_ctx = NULL;
668*cf21f328SLaurent Pinchart 		goto err_fh;
669*cf21f328SLaurent Pinchart 	}
670*cf21f328SLaurent Pinchart 
671*cf21f328SLaurent Pinchart 	mxc_isi_m2m_init_format(ctx, &ctx->queues.out, MXC_ISI_VIDEO_M2M_OUT);
672*cf21f328SLaurent Pinchart 	mxc_isi_m2m_init_format(ctx, &ctx->queues.cap, MXC_ISI_VIDEO_M2M_CAP);
673*cf21f328SLaurent Pinchart 
674*cf21f328SLaurent Pinchart 	ret = mxc_isi_m2m_ctx_ctrls_create(ctx);
675*cf21f328SLaurent Pinchart 	if (ret)
676*cf21f328SLaurent Pinchart 		goto err_ctx;
677*cf21f328SLaurent Pinchart 
678*cf21f328SLaurent Pinchart 	ret = pm_runtime_resume_and_get(m2m->isi->dev);
679*cf21f328SLaurent Pinchart 	if (ret)
680*cf21f328SLaurent Pinchart 		goto err_ctrls;
681*cf21f328SLaurent Pinchart 
682*cf21f328SLaurent Pinchart 	v4l2_fh_add(&ctx->fh);
683*cf21f328SLaurent Pinchart 
684*cf21f328SLaurent Pinchart 	return 0;
685*cf21f328SLaurent Pinchart 
686*cf21f328SLaurent Pinchart err_ctrls:
687*cf21f328SLaurent Pinchart 	mxc_isi_m2m_ctx_ctrls_delete(ctx);
688*cf21f328SLaurent Pinchart err_ctx:
689*cf21f328SLaurent Pinchart 	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
690*cf21f328SLaurent Pinchart err_fh:
691*cf21f328SLaurent Pinchart 	v4l2_fh_exit(&ctx->fh);
692*cf21f328SLaurent Pinchart 	mutex_destroy(&ctx->vb2_lock);
693*cf21f328SLaurent Pinchart 	kfree(ctx);
694*cf21f328SLaurent Pinchart 	return ret;
695*cf21f328SLaurent Pinchart }
696*cf21f328SLaurent Pinchart 
mxc_isi_m2m_release(struct file * file)697*cf21f328SLaurent Pinchart static int mxc_isi_m2m_release(struct file *file)
698*cf21f328SLaurent Pinchart {
699*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m *m2m = video_drvdata(file);
700*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(file->private_data);
701*cf21f328SLaurent Pinchart 
702*cf21f328SLaurent Pinchart 	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
703*cf21f328SLaurent Pinchart 	mxc_isi_m2m_ctx_ctrls_delete(ctx);
704*cf21f328SLaurent Pinchart 
705*cf21f328SLaurent Pinchart 	v4l2_fh_del(&ctx->fh);
706*cf21f328SLaurent Pinchart 	v4l2_fh_exit(&ctx->fh);
707*cf21f328SLaurent Pinchart 
708*cf21f328SLaurent Pinchart 	mutex_destroy(&ctx->vb2_lock);
709*cf21f328SLaurent Pinchart 	kfree(ctx);
710*cf21f328SLaurent Pinchart 
711*cf21f328SLaurent Pinchart 	pm_runtime_put(m2m->isi->dev);
712*cf21f328SLaurent Pinchart 
713*cf21f328SLaurent Pinchart 	return 0;
714*cf21f328SLaurent Pinchart }
715*cf21f328SLaurent Pinchart 
716*cf21f328SLaurent Pinchart static const struct v4l2_file_operations mxc_isi_m2m_fops = {
717*cf21f328SLaurent Pinchart 	.owner		= THIS_MODULE,
718*cf21f328SLaurent Pinchart 	.open		= mxc_isi_m2m_open,
719*cf21f328SLaurent Pinchart 	.release	= mxc_isi_m2m_release,
720*cf21f328SLaurent Pinchart 	.poll		= v4l2_m2m_fop_poll,
721*cf21f328SLaurent Pinchart 	.unlocked_ioctl	= video_ioctl2,
722*cf21f328SLaurent Pinchart 	.mmap		= v4l2_m2m_fop_mmap,
723*cf21f328SLaurent Pinchart };
724*cf21f328SLaurent Pinchart 
725*cf21f328SLaurent Pinchart /* -----------------------------------------------------------------------------
726*cf21f328SLaurent Pinchart  * Registration
727*cf21f328SLaurent Pinchart  */
728*cf21f328SLaurent Pinchart 
mxc_isi_m2m_register(struct mxc_isi_dev * isi,struct v4l2_device * v4l2_dev)729*cf21f328SLaurent Pinchart int mxc_isi_m2m_register(struct mxc_isi_dev *isi, struct v4l2_device *v4l2_dev)
730*cf21f328SLaurent Pinchart {
731*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m *m2m = &isi->m2m;
732*cf21f328SLaurent Pinchart 	struct video_device *vdev = &m2m->vdev;
733*cf21f328SLaurent Pinchart 	struct media_link *link;
734*cf21f328SLaurent Pinchart 	int ret;
735*cf21f328SLaurent Pinchart 
736*cf21f328SLaurent Pinchart 	m2m->isi = isi;
737*cf21f328SLaurent Pinchart 	m2m->pipe = &isi->pipes[0];
738*cf21f328SLaurent Pinchart 
739*cf21f328SLaurent Pinchart 	mutex_init(&m2m->lock);
740*cf21f328SLaurent Pinchart 
741*cf21f328SLaurent Pinchart 	/* Initialize the video device and create controls. */
742*cf21f328SLaurent Pinchart 	snprintf(vdev->name, sizeof(vdev->name), "mxc_isi.m2m");
743*cf21f328SLaurent Pinchart 
744*cf21f328SLaurent Pinchart 	vdev->fops	= &mxc_isi_m2m_fops;
745*cf21f328SLaurent Pinchart 	vdev->ioctl_ops	= &mxc_isi_m2m_ioctl_ops;
746*cf21f328SLaurent Pinchart 	vdev->v4l2_dev	= v4l2_dev;
747*cf21f328SLaurent Pinchart 	vdev->minor	= -1;
748*cf21f328SLaurent Pinchart 	vdev->release	= video_device_release_empty;
749*cf21f328SLaurent Pinchart 	vdev->vfl_dir	= VFL_DIR_M2M;
750*cf21f328SLaurent Pinchart 
751*cf21f328SLaurent Pinchart 	vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
752*cf21f328SLaurent Pinchart 	video_set_drvdata(vdev, m2m);
753*cf21f328SLaurent Pinchart 
754*cf21f328SLaurent Pinchart 	/* Create the M2M device. */
755*cf21f328SLaurent Pinchart 	m2m->m2m_dev = v4l2_m2m_init(&mxc_isi_m2m_ops);
756*cf21f328SLaurent Pinchart 	if (IS_ERR(m2m->m2m_dev)) {
757*cf21f328SLaurent Pinchart 		dev_err(isi->dev, "failed to initialize m2m device\n");
758*cf21f328SLaurent Pinchart 		ret = PTR_ERR(m2m->m2m_dev);
759*cf21f328SLaurent Pinchart 		goto err_mutex;
760*cf21f328SLaurent Pinchart 	}
761*cf21f328SLaurent Pinchart 
762*cf21f328SLaurent Pinchart 	/* Register the video device. */
763*cf21f328SLaurent Pinchart 	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
764*cf21f328SLaurent Pinchart 	if (ret < 0) {
765*cf21f328SLaurent Pinchart 		dev_err(isi->dev, "failed to register m2m device\n");
766*cf21f328SLaurent Pinchart 		goto err_m2m;
767*cf21f328SLaurent Pinchart 	}
768*cf21f328SLaurent Pinchart 
769*cf21f328SLaurent Pinchart 	/*
770*cf21f328SLaurent Pinchart 	 * Populate the media graph. We can't use the mem2mem helper
771*cf21f328SLaurent Pinchart 	 * v4l2_m2m_register_media_controller() as the M2M interface needs to
772*cf21f328SLaurent Pinchart 	 * be connected to the existing entities in the graph, so we have to
773*cf21f328SLaurent Pinchart 	 * wire things up manually:
774*cf21f328SLaurent Pinchart 	 *
775*cf21f328SLaurent Pinchart 	 * - The entity in the video_device, which isn't touched by the V4L2
776*cf21f328SLaurent Pinchart 	 *   core for M2M devices, is used as the source I/O entity in the
777*cf21f328SLaurent Pinchart 	 *   graph, connected to the crossbar switch.
778*cf21f328SLaurent Pinchart 	 *
779*cf21f328SLaurent Pinchart 	 * - The video device at the end of the pipeline provides the sink
780*cf21f328SLaurent Pinchart 	 *   entity, and is already wired up in the graph.
781*cf21f328SLaurent Pinchart 	 *
782*cf21f328SLaurent Pinchart 	 * - A new interface is created, pointing at both entities. The sink
783*cf21f328SLaurent Pinchart 	 *   entity will thus have two interfaces pointing to it.
784*cf21f328SLaurent Pinchart 	 */
785*cf21f328SLaurent Pinchart 	m2m->pad.flags = MEDIA_PAD_FL_SOURCE;
786*cf21f328SLaurent Pinchart 	vdev->entity.name = "mxc_isi.output";
787*cf21f328SLaurent Pinchart 	vdev->entity.function = MEDIA_ENT_F_IO_V4L;
788*cf21f328SLaurent Pinchart 	ret = media_entity_pads_init(&vdev->entity, 1, &m2m->pad);
789*cf21f328SLaurent Pinchart 	if (ret)
790*cf21f328SLaurent Pinchart 		goto err_video;
791*cf21f328SLaurent Pinchart 
792*cf21f328SLaurent Pinchart 	ret = media_device_register_entity(v4l2_dev->mdev, &vdev->entity);
793*cf21f328SLaurent Pinchart 	if (ret)
794*cf21f328SLaurent Pinchart 		goto err_entity_cleanup;
795*cf21f328SLaurent Pinchart 
796*cf21f328SLaurent Pinchart 	ret = media_create_pad_link(&vdev->entity, 0,
797*cf21f328SLaurent Pinchart 				    &m2m->isi->crossbar.sd.entity,
798*cf21f328SLaurent Pinchart 				    m2m->isi->crossbar.num_sinks - 1,
799*cf21f328SLaurent Pinchart 				    MEDIA_LNK_FL_IMMUTABLE |
800*cf21f328SLaurent Pinchart 				    MEDIA_LNK_FL_ENABLED);
801*cf21f328SLaurent Pinchart 	if (ret)
802*cf21f328SLaurent Pinchart 		goto err_entity_unreg;
803*cf21f328SLaurent Pinchart 
804*cf21f328SLaurent Pinchart 	m2m->intf = media_devnode_create(v4l2_dev->mdev, MEDIA_INTF_T_V4L_VIDEO,
805*cf21f328SLaurent Pinchart 					 0, VIDEO_MAJOR, vdev->minor);
806*cf21f328SLaurent Pinchart 	if (!m2m->intf) {
807*cf21f328SLaurent Pinchart 		ret = -ENOMEM;
808*cf21f328SLaurent Pinchart 		goto err_entity_unreg;
809*cf21f328SLaurent Pinchart 	}
810*cf21f328SLaurent Pinchart 
811*cf21f328SLaurent Pinchart 	link = media_create_intf_link(&vdev->entity, &m2m->intf->intf,
812*cf21f328SLaurent Pinchart 				      MEDIA_LNK_FL_IMMUTABLE |
813*cf21f328SLaurent Pinchart 				      MEDIA_LNK_FL_ENABLED);
814*cf21f328SLaurent Pinchart 	if (!link) {
815*cf21f328SLaurent Pinchart 		ret = -ENOMEM;
816*cf21f328SLaurent Pinchart 		goto err_devnode;
817*cf21f328SLaurent Pinchart 	}
818*cf21f328SLaurent Pinchart 
819*cf21f328SLaurent Pinchart 	link = media_create_intf_link(&m2m->pipe->video.vdev.entity,
820*cf21f328SLaurent Pinchart 				      &m2m->intf->intf,
821*cf21f328SLaurent Pinchart 				      MEDIA_LNK_FL_IMMUTABLE |
822*cf21f328SLaurent Pinchart 				      MEDIA_LNK_FL_ENABLED);
823*cf21f328SLaurent Pinchart 	if (!link) {
824*cf21f328SLaurent Pinchart 		ret = -ENOMEM;
825*cf21f328SLaurent Pinchart 		goto err_devnode;
826*cf21f328SLaurent Pinchart 	}
827*cf21f328SLaurent Pinchart 
828*cf21f328SLaurent Pinchart 	return 0;
829*cf21f328SLaurent Pinchart 
830*cf21f328SLaurent Pinchart err_devnode:
831*cf21f328SLaurent Pinchart 	media_devnode_remove(m2m->intf);
832*cf21f328SLaurent Pinchart err_entity_unreg:
833*cf21f328SLaurent Pinchart 	media_device_unregister_entity(&vdev->entity);
834*cf21f328SLaurent Pinchart err_entity_cleanup:
835*cf21f328SLaurent Pinchart 	media_entity_cleanup(&vdev->entity);
836*cf21f328SLaurent Pinchart err_video:
837*cf21f328SLaurent Pinchart 	video_unregister_device(vdev);
838*cf21f328SLaurent Pinchart err_m2m:
839*cf21f328SLaurent Pinchart 	v4l2_m2m_release(m2m->m2m_dev);
840*cf21f328SLaurent Pinchart err_mutex:
841*cf21f328SLaurent Pinchart 	mutex_destroy(&m2m->lock);
842*cf21f328SLaurent Pinchart 	return ret;
843*cf21f328SLaurent Pinchart }
844*cf21f328SLaurent Pinchart 
mxc_isi_m2m_unregister(struct mxc_isi_dev * isi)845*cf21f328SLaurent Pinchart int mxc_isi_m2m_unregister(struct mxc_isi_dev *isi)
846*cf21f328SLaurent Pinchart {
847*cf21f328SLaurent Pinchart 	struct mxc_isi_m2m *m2m = &isi->m2m;
848*cf21f328SLaurent Pinchart 	struct video_device *vdev = &m2m->vdev;
849*cf21f328SLaurent Pinchart 
850*cf21f328SLaurent Pinchart 	video_unregister_device(vdev);
851*cf21f328SLaurent Pinchart 
852*cf21f328SLaurent Pinchart 	v4l2_m2m_release(m2m->m2m_dev);
853*cf21f328SLaurent Pinchart 	media_devnode_remove(m2m->intf);
854*cf21f328SLaurent Pinchart 	media_entity_cleanup(&vdev->entity);
855*cf21f328SLaurent Pinchart 	mutex_destroy(&m2m->lock);
856*cf21f328SLaurent Pinchart 
857*cf21f328SLaurent Pinchart 	return 0;
858*cf21f328SLaurent Pinchart }
859