1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * V4L2 deinterlacing support.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2012 Vista Silicon S.L.
6*4882a593Smuzhiyun * Javier Martin <javier.martin@vista-silicon.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun #include <linux/interrupt.h>
12*4882a593Smuzhiyun #include <linux/dmaengine.h>
13*4882a593Smuzhiyun #include <linux/platform_device.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <media/v4l2-mem2mem.h>
16*4882a593Smuzhiyun #include <media/v4l2-device.h>
17*4882a593Smuzhiyun #include <media/v4l2-ioctl.h>
18*4882a593Smuzhiyun #include <media/videobuf2-dma-contig.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define MEM2MEM_TEST_MODULE_NAME "mem2mem-deinterlace"
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun MODULE_DESCRIPTION("mem2mem device which supports deinterlacing using dmaengine");
23*4882a593Smuzhiyun MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com");
24*4882a593Smuzhiyun MODULE_LICENSE("GPL");
25*4882a593Smuzhiyun MODULE_VERSION("0.0.1");
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun static bool debug;
28*4882a593Smuzhiyun module_param(debug, bool, 0644);
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun /* Flags that indicate a format can be used for capture/output */
31*4882a593Smuzhiyun #define MEM2MEM_CAPTURE (1 << 0)
32*4882a593Smuzhiyun #define MEM2MEM_OUTPUT (1 << 1)
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define MEM2MEM_NAME "m2m-deinterlace"
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define dprintk(dev, fmt, arg...) \
37*4882a593Smuzhiyun v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun struct deinterlace_fmt {
40*4882a593Smuzhiyun u32 fourcc;
41*4882a593Smuzhiyun /* Types the format can be used for */
42*4882a593Smuzhiyun u32 types;
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun static struct deinterlace_fmt formats[] = {
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_YUV420,
48*4882a593Smuzhiyun .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
49*4882a593Smuzhiyun },
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_YUYV,
52*4882a593Smuzhiyun .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
53*4882a593Smuzhiyun },
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun #define NUM_FORMATS ARRAY_SIZE(formats)
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun /* Per-queue, driver-specific private data */
59*4882a593Smuzhiyun struct deinterlace_q_data {
60*4882a593Smuzhiyun unsigned int width;
61*4882a593Smuzhiyun unsigned int height;
62*4882a593Smuzhiyun unsigned int sizeimage;
63*4882a593Smuzhiyun struct deinterlace_fmt *fmt;
64*4882a593Smuzhiyun enum v4l2_field field;
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun enum {
68*4882a593Smuzhiyun V4L2_M2M_SRC = 0,
69*4882a593Smuzhiyun V4L2_M2M_DST = 1,
70*4882a593Smuzhiyun };
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun enum {
73*4882a593Smuzhiyun YUV420_DMA_Y_ODD,
74*4882a593Smuzhiyun YUV420_DMA_Y_EVEN,
75*4882a593Smuzhiyun YUV420_DMA_U_ODD,
76*4882a593Smuzhiyun YUV420_DMA_U_EVEN,
77*4882a593Smuzhiyun YUV420_DMA_V_ODD,
78*4882a593Smuzhiyun YUV420_DMA_V_EVEN,
79*4882a593Smuzhiyun YUV420_DMA_Y_ODD_DOUBLING,
80*4882a593Smuzhiyun YUV420_DMA_U_ODD_DOUBLING,
81*4882a593Smuzhiyun YUV420_DMA_V_ODD_DOUBLING,
82*4882a593Smuzhiyun YUYV_DMA_ODD,
83*4882a593Smuzhiyun YUYV_DMA_EVEN,
84*4882a593Smuzhiyun YUYV_DMA_EVEN_DOUBLING,
85*4882a593Smuzhiyun };
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun /* Source and destination queue data */
88*4882a593Smuzhiyun static struct deinterlace_q_data q_data[2];
89*4882a593Smuzhiyun
get_q_data(enum v4l2_buf_type type)90*4882a593Smuzhiyun static struct deinterlace_q_data *get_q_data(enum v4l2_buf_type type)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun switch (type) {
93*4882a593Smuzhiyun case V4L2_BUF_TYPE_VIDEO_OUTPUT:
94*4882a593Smuzhiyun return &q_data[V4L2_M2M_SRC];
95*4882a593Smuzhiyun case V4L2_BUF_TYPE_VIDEO_CAPTURE:
96*4882a593Smuzhiyun return &q_data[V4L2_M2M_DST];
97*4882a593Smuzhiyun default:
98*4882a593Smuzhiyun BUG();
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun return NULL;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
find_format(struct v4l2_format * f)103*4882a593Smuzhiyun static struct deinterlace_fmt *find_format(struct v4l2_format *f)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun struct deinterlace_fmt *fmt;
106*4882a593Smuzhiyun unsigned int k;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun for (k = 0; k < NUM_FORMATS; k++) {
109*4882a593Smuzhiyun fmt = &formats[k];
110*4882a593Smuzhiyun if ((fmt->types & f->type) &&
111*4882a593Smuzhiyun (fmt->fourcc == f->fmt.pix.pixelformat))
112*4882a593Smuzhiyun break;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun if (k == NUM_FORMATS)
116*4882a593Smuzhiyun return NULL;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun return &formats[k];
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun struct deinterlace_dev {
122*4882a593Smuzhiyun struct v4l2_device v4l2_dev;
123*4882a593Smuzhiyun struct video_device vfd;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun atomic_t busy;
126*4882a593Smuzhiyun struct mutex dev_mutex;
127*4882a593Smuzhiyun spinlock_t irqlock;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun struct dma_chan *dma_chan;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun struct v4l2_m2m_dev *m2m_dev;
132*4882a593Smuzhiyun };
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun struct deinterlace_ctx {
135*4882a593Smuzhiyun struct v4l2_fh fh;
136*4882a593Smuzhiyun struct deinterlace_dev *dev;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun /* Abort requested by m2m */
139*4882a593Smuzhiyun int aborting;
140*4882a593Smuzhiyun enum v4l2_colorspace colorspace;
141*4882a593Smuzhiyun dma_cookie_t cookie;
142*4882a593Smuzhiyun struct dma_interleaved_template *xt;
143*4882a593Smuzhiyun };
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun /*
146*4882a593Smuzhiyun * mem2mem callbacks
147*4882a593Smuzhiyun */
deinterlace_job_ready(void * priv)148*4882a593Smuzhiyun static int deinterlace_job_ready(void *priv)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun struct deinterlace_ctx *ctx = priv;
151*4882a593Smuzhiyun struct deinterlace_dev *pcdev = ctx->dev;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0 &&
154*4882a593Smuzhiyun v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0 &&
155*4882a593Smuzhiyun !atomic_read(&ctx->dev->busy)) {
156*4882a593Smuzhiyun dprintk(pcdev, "Task ready\n");
157*4882a593Smuzhiyun return 1;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun dprintk(pcdev, "Task not ready to run\n");
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun return 0;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
deinterlace_job_abort(void * priv)165*4882a593Smuzhiyun static void deinterlace_job_abort(void *priv)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun struct deinterlace_ctx *ctx = priv;
168*4882a593Smuzhiyun struct deinterlace_dev *pcdev = ctx->dev;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun ctx->aborting = 1;
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun dprintk(pcdev, "Aborting task\n");
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->fh.m2m_ctx);
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun
dma_callback(void * data)177*4882a593Smuzhiyun static void dma_callback(void *data)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun struct deinterlace_ctx *curr_ctx = data;
180*4882a593Smuzhiyun struct deinterlace_dev *pcdev = curr_ctx->dev;
181*4882a593Smuzhiyun struct vb2_v4l2_buffer *src_vb, *dst_vb;
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun atomic_set(&pcdev->busy, 0);
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
186*4882a593Smuzhiyun dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
189*4882a593Smuzhiyun dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
190*4882a593Smuzhiyun dst_vb->flags |=
191*4882a593Smuzhiyun src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
192*4882a593Smuzhiyun dst_vb->timecode = src_vb->timecode;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
195*4882a593Smuzhiyun v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->fh.m2m_ctx);
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun dprintk(pcdev, "dma transfers completed.\n");
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
deinterlace_issue_dma(struct deinterlace_ctx * ctx,int op,int do_callback)202*4882a593Smuzhiyun static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op,
203*4882a593Smuzhiyun int do_callback)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun struct deinterlace_q_data *s_q_data;
206*4882a593Smuzhiyun struct vb2_v4l2_buffer *src_buf, *dst_buf;
207*4882a593Smuzhiyun struct deinterlace_dev *pcdev = ctx->dev;
208*4882a593Smuzhiyun struct dma_chan *chan = pcdev->dma_chan;
209*4882a593Smuzhiyun struct dma_device *dmadev = chan->device;
210*4882a593Smuzhiyun struct dma_async_tx_descriptor *tx;
211*4882a593Smuzhiyun unsigned int s_width, s_height;
212*4882a593Smuzhiyun unsigned int s_size;
213*4882a593Smuzhiyun dma_addr_t p_in, p_out;
214*4882a593Smuzhiyun enum dma_ctrl_flags flags;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
217*4882a593Smuzhiyun dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun s_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT);
220*4882a593Smuzhiyun s_width = s_q_data->width;
221*4882a593Smuzhiyun s_height = s_q_data->height;
222*4882a593Smuzhiyun s_size = s_width * s_height;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun p_in = (dma_addr_t)vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
225*4882a593Smuzhiyun p_out = (dma_addr_t)vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf,
226*4882a593Smuzhiyun 0);
227*4882a593Smuzhiyun if (!p_in || !p_out) {
228*4882a593Smuzhiyun v4l2_err(&pcdev->v4l2_dev,
229*4882a593Smuzhiyun "Acquiring kernel pointers to buffers failed\n");
230*4882a593Smuzhiyun return;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun switch (op) {
234*4882a593Smuzhiyun case YUV420_DMA_Y_ODD:
235*4882a593Smuzhiyun ctx->xt->numf = s_height / 2;
236*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width;
237*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width;
238*4882a593Smuzhiyun ctx->xt->src_start = p_in;
239*4882a593Smuzhiyun ctx->xt->dst_start = p_out;
240*4882a593Smuzhiyun break;
241*4882a593Smuzhiyun case YUV420_DMA_Y_EVEN:
242*4882a593Smuzhiyun ctx->xt->numf = s_height / 2;
243*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width;
244*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width;
245*4882a593Smuzhiyun ctx->xt->src_start = p_in + s_size / 2;
246*4882a593Smuzhiyun ctx->xt->dst_start = p_out + s_width;
247*4882a593Smuzhiyun break;
248*4882a593Smuzhiyun case YUV420_DMA_U_ODD:
249*4882a593Smuzhiyun ctx->xt->numf = s_height / 4;
250*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width / 2;
251*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width / 2;
252*4882a593Smuzhiyun ctx->xt->src_start = p_in + s_size;
253*4882a593Smuzhiyun ctx->xt->dst_start = p_out + s_size;
254*4882a593Smuzhiyun break;
255*4882a593Smuzhiyun case YUV420_DMA_U_EVEN:
256*4882a593Smuzhiyun ctx->xt->numf = s_height / 4;
257*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width / 2;
258*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width / 2;
259*4882a593Smuzhiyun ctx->xt->src_start = p_in + (9 * s_size) / 8;
260*4882a593Smuzhiyun ctx->xt->dst_start = p_out + s_size + s_width / 2;
261*4882a593Smuzhiyun break;
262*4882a593Smuzhiyun case YUV420_DMA_V_ODD:
263*4882a593Smuzhiyun ctx->xt->numf = s_height / 4;
264*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width / 2;
265*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width / 2;
266*4882a593Smuzhiyun ctx->xt->src_start = p_in + (5 * s_size) / 4;
267*4882a593Smuzhiyun ctx->xt->dst_start = p_out + (5 * s_size) / 4;
268*4882a593Smuzhiyun break;
269*4882a593Smuzhiyun case YUV420_DMA_V_EVEN:
270*4882a593Smuzhiyun ctx->xt->numf = s_height / 4;
271*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width / 2;
272*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width / 2;
273*4882a593Smuzhiyun ctx->xt->src_start = p_in + (11 * s_size) / 8;
274*4882a593Smuzhiyun ctx->xt->dst_start = p_out + (5 * s_size) / 4 + s_width / 2;
275*4882a593Smuzhiyun break;
276*4882a593Smuzhiyun case YUV420_DMA_Y_ODD_DOUBLING:
277*4882a593Smuzhiyun ctx->xt->numf = s_height / 2;
278*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width;
279*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width;
280*4882a593Smuzhiyun ctx->xt->src_start = p_in;
281*4882a593Smuzhiyun ctx->xt->dst_start = p_out + s_width;
282*4882a593Smuzhiyun break;
283*4882a593Smuzhiyun case YUV420_DMA_U_ODD_DOUBLING:
284*4882a593Smuzhiyun ctx->xt->numf = s_height / 4;
285*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width / 2;
286*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width / 2;
287*4882a593Smuzhiyun ctx->xt->src_start = p_in + s_size;
288*4882a593Smuzhiyun ctx->xt->dst_start = p_out + s_size + s_width / 2;
289*4882a593Smuzhiyun break;
290*4882a593Smuzhiyun case YUV420_DMA_V_ODD_DOUBLING:
291*4882a593Smuzhiyun ctx->xt->numf = s_height / 4;
292*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width / 2;
293*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width / 2;
294*4882a593Smuzhiyun ctx->xt->src_start = p_in + (5 * s_size) / 4;
295*4882a593Smuzhiyun ctx->xt->dst_start = p_out + (5 * s_size) / 4 + s_width / 2;
296*4882a593Smuzhiyun break;
297*4882a593Smuzhiyun case YUYV_DMA_ODD:
298*4882a593Smuzhiyun ctx->xt->numf = s_height / 2;
299*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width * 2;
300*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width * 2;
301*4882a593Smuzhiyun ctx->xt->src_start = p_in;
302*4882a593Smuzhiyun ctx->xt->dst_start = p_out;
303*4882a593Smuzhiyun break;
304*4882a593Smuzhiyun case YUYV_DMA_EVEN:
305*4882a593Smuzhiyun ctx->xt->numf = s_height / 2;
306*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width * 2;
307*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width * 2;
308*4882a593Smuzhiyun ctx->xt->src_start = p_in + s_size;
309*4882a593Smuzhiyun ctx->xt->dst_start = p_out + s_width * 2;
310*4882a593Smuzhiyun break;
311*4882a593Smuzhiyun case YUYV_DMA_EVEN_DOUBLING:
312*4882a593Smuzhiyun default:
313*4882a593Smuzhiyun ctx->xt->numf = s_height / 2;
314*4882a593Smuzhiyun ctx->xt->sgl[0].size = s_width * 2;
315*4882a593Smuzhiyun ctx->xt->sgl[0].icg = s_width * 2;
316*4882a593Smuzhiyun ctx->xt->src_start = p_in;
317*4882a593Smuzhiyun ctx->xt->dst_start = p_out + s_width * 2;
318*4882a593Smuzhiyun break;
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun /* Common parameters for al transfers */
322*4882a593Smuzhiyun ctx->xt->frame_size = 1;
323*4882a593Smuzhiyun ctx->xt->dir = DMA_MEM_TO_MEM;
324*4882a593Smuzhiyun ctx->xt->src_sgl = false;
325*4882a593Smuzhiyun ctx->xt->dst_sgl = true;
326*4882a593Smuzhiyun flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun tx = dmadev->device_prep_interleaved_dma(chan, ctx->xt, flags);
329*4882a593Smuzhiyun if (tx == NULL) {
330*4882a593Smuzhiyun v4l2_warn(&pcdev->v4l2_dev, "DMA interleaved prep error\n");
331*4882a593Smuzhiyun return;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun if (do_callback) {
335*4882a593Smuzhiyun tx->callback = dma_callback;
336*4882a593Smuzhiyun tx->callback_param = ctx;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun ctx->cookie = dmaengine_submit(tx);
340*4882a593Smuzhiyun if (dma_submit_error(ctx->cookie)) {
341*4882a593Smuzhiyun v4l2_warn(&pcdev->v4l2_dev,
342*4882a593Smuzhiyun "DMA submit error %d with src=0x%x dst=0x%x len=0x%x\n",
343*4882a593Smuzhiyun ctx->cookie, (unsigned)p_in, (unsigned)p_out,
344*4882a593Smuzhiyun s_size * 3/2);
345*4882a593Smuzhiyun return;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun dma_async_issue_pending(chan);
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun
deinterlace_device_run(void * priv)351*4882a593Smuzhiyun static void deinterlace_device_run(void *priv)
352*4882a593Smuzhiyun {
353*4882a593Smuzhiyun struct deinterlace_ctx *ctx = priv;
354*4882a593Smuzhiyun struct deinterlace_q_data *dst_q_data;
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun atomic_set(&ctx->dev->busy, 1);
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun dprintk(ctx->dev, "%s: DMA try issue.\n", __func__);
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun dst_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_CAPTURE);
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun /*
363*4882a593Smuzhiyun * 4 possible field conversions are possible at the moment:
364*4882a593Smuzhiyun * V4L2_FIELD_SEQ_TB --> V4L2_FIELD_INTERLACED_TB:
365*4882a593Smuzhiyun * two separate fields in the same input buffer are interlaced
366*4882a593Smuzhiyun * in the output buffer using weaving. Top field comes first.
367*4882a593Smuzhiyun * V4L2_FIELD_SEQ_TB --> V4L2_FIELD_NONE:
368*4882a593Smuzhiyun * top field from the input buffer is copied to the output buffer
369*4882a593Smuzhiyun * using line doubling. Bottom field from the input buffer is discarded.
370*4882a593Smuzhiyun * V4L2_FIELD_SEQ_BT --> V4L2_FIELD_INTERLACED_BT:
371*4882a593Smuzhiyun * two separate fields in the same input buffer are interlaced
372*4882a593Smuzhiyun * in the output buffer using weaving. Bottom field comes first.
373*4882a593Smuzhiyun * V4L2_FIELD_SEQ_BT --> V4L2_FIELD_NONE:
374*4882a593Smuzhiyun * bottom field from the input buffer is copied to the output buffer
375*4882a593Smuzhiyun * using line doubling. Top field from the input buffer is discarded.
376*4882a593Smuzhiyun */
377*4882a593Smuzhiyun switch (dst_q_data->fmt->fourcc) {
378*4882a593Smuzhiyun case V4L2_PIX_FMT_YUV420:
379*4882a593Smuzhiyun switch (dst_q_data->field) {
380*4882a593Smuzhiyun case V4L2_FIELD_INTERLACED_TB:
381*4882a593Smuzhiyun case V4L2_FIELD_INTERLACED_BT:
382*4882a593Smuzhiyun dprintk(ctx->dev, "%s: yuv420 interlaced tb.\n",
383*4882a593Smuzhiyun __func__);
384*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_Y_ODD, 0);
385*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_Y_EVEN, 0);
386*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_U_ODD, 0);
387*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_U_EVEN, 0);
388*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_V_ODD, 0);
389*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_V_EVEN, 1);
390*4882a593Smuzhiyun break;
391*4882a593Smuzhiyun case V4L2_FIELD_NONE:
392*4882a593Smuzhiyun default:
393*4882a593Smuzhiyun dprintk(ctx->dev, "%s: yuv420 interlaced line doubling.\n",
394*4882a593Smuzhiyun __func__);
395*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_Y_ODD, 0);
396*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_Y_ODD_DOUBLING, 0);
397*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_U_ODD, 0);
398*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_U_ODD_DOUBLING, 0);
399*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_V_ODD, 0);
400*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUV420_DMA_V_ODD_DOUBLING, 1);
401*4882a593Smuzhiyun break;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun break;
404*4882a593Smuzhiyun case V4L2_PIX_FMT_YUYV:
405*4882a593Smuzhiyun default:
406*4882a593Smuzhiyun switch (dst_q_data->field) {
407*4882a593Smuzhiyun case V4L2_FIELD_INTERLACED_TB:
408*4882a593Smuzhiyun case V4L2_FIELD_INTERLACED_BT:
409*4882a593Smuzhiyun dprintk(ctx->dev, "%s: yuyv interlaced_tb.\n",
410*4882a593Smuzhiyun __func__);
411*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUYV_DMA_ODD, 0);
412*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUYV_DMA_EVEN, 1);
413*4882a593Smuzhiyun break;
414*4882a593Smuzhiyun case V4L2_FIELD_NONE:
415*4882a593Smuzhiyun default:
416*4882a593Smuzhiyun dprintk(ctx->dev, "%s: yuyv interlaced line doubling.\n",
417*4882a593Smuzhiyun __func__);
418*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUYV_DMA_ODD, 0);
419*4882a593Smuzhiyun deinterlace_issue_dma(ctx, YUYV_DMA_EVEN_DOUBLING, 1);
420*4882a593Smuzhiyun break;
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun break;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun dprintk(ctx->dev, "%s: DMA issue done.\n", __func__);
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun /*
429*4882a593Smuzhiyun * video ioctls
430*4882a593Smuzhiyun */
vidioc_querycap(struct file * file,void * priv,struct v4l2_capability * cap)431*4882a593Smuzhiyun static int vidioc_querycap(struct file *file, void *priv,
432*4882a593Smuzhiyun struct v4l2_capability *cap)
433*4882a593Smuzhiyun {
434*4882a593Smuzhiyun strscpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver));
435*4882a593Smuzhiyun strscpy(cap->card, MEM2MEM_NAME, sizeof(cap->card));
436*4882a593Smuzhiyun strscpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->bus_info));
437*4882a593Smuzhiyun return 0;
438*4882a593Smuzhiyun }
439*4882a593Smuzhiyun
enum_fmt(struct v4l2_fmtdesc * f,u32 type)440*4882a593Smuzhiyun static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
441*4882a593Smuzhiyun {
442*4882a593Smuzhiyun int i, num;
443*4882a593Smuzhiyun struct deinterlace_fmt *fmt;
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun num = 0;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun for (i = 0; i < NUM_FORMATS; ++i) {
448*4882a593Smuzhiyun if (formats[i].types & type) {
449*4882a593Smuzhiyun /* index-th format of type type found ? */
450*4882a593Smuzhiyun if (num == f->index)
451*4882a593Smuzhiyun break;
452*4882a593Smuzhiyun /* Correct type but haven't reached our index yet,
453*4882a593Smuzhiyun * just increment per-type index */
454*4882a593Smuzhiyun ++num;
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun }
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun if (i < NUM_FORMATS) {
459*4882a593Smuzhiyun /* Format found */
460*4882a593Smuzhiyun fmt = &formats[i];
461*4882a593Smuzhiyun f->pixelformat = fmt->fourcc;
462*4882a593Smuzhiyun return 0;
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun /* Format not found */
466*4882a593Smuzhiyun return -EINVAL;
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun
vidioc_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)469*4882a593Smuzhiyun static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
470*4882a593Smuzhiyun struct v4l2_fmtdesc *f)
471*4882a593Smuzhiyun {
472*4882a593Smuzhiyun return enum_fmt(f, MEM2MEM_CAPTURE);
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun
vidioc_enum_fmt_vid_out(struct file * file,void * priv,struct v4l2_fmtdesc * f)475*4882a593Smuzhiyun static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
476*4882a593Smuzhiyun struct v4l2_fmtdesc *f)
477*4882a593Smuzhiyun {
478*4882a593Smuzhiyun return enum_fmt(f, MEM2MEM_OUTPUT);
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun
vidioc_g_fmt(struct deinterlace_ctx * ctx,struct v4l2_format * f)481*4882a593Smuzhiyun static int vidioc_g_fmt(struct deinterlace_ctx *ctx, struct v4l2_format *f)
482*4882a593Smuzhiyun {
483*4882a593Smuzhiyun struct vb2_queue *vq;
484*4882a593Smuzhiyun struct deinterlace_q_data *q_data;
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
487*4882a593Smuzhiyun if (!vq)
488*4882a593Smuzhiyun return -EINVAL;
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun q_data = get_q_data(f->type);
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun f->fmt.pix.width = q_data->width;
493*4882a593Smuzhiyun f->fmt.pix.height = q_data->height;
494*4882a593Smuzhiyun f->fmt.pix.field = q_data->field;
495*4882a593Smuzhiyun f->fmt.pix.pixelformat = q_data->fmt->fourcc;
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun switch (q_data->fmt->fourcc) {
498*4882a593Smuzhiyun case V4L2_PIX_FMT_YUV420:
499*4882a593Smuzhiyun f->fmt.pix.bytesperline = q_data->width * 3 / 2;
500*4882a593Smuzhiyun break;
501*4882a593Smuzhiyun case V4L2_PIX_FMT_YUYV:
502*4882a593Smuzhiyun default:
503*4882a593Smuzhiyun f->fmt.pix.bytesperline = q_data->width * 2;
504*4882a593Smuzhiyun }
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun f->fmt.pix.sizeimage = q_data->sizeimage;
507*4882a593Smuzhiyun f->fmt.pix.colorspace = ctx->colorspace;
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun return 0;
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun
vidioc_g_fmt_vid_out(struct file * file,void * priv,struct v4l2_format * f)512*4882a593Smuzhiyun static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
513*4882a593Smuzhiyun struct v4l2_format *f)
514*4882a593Smuzhiyun {
515*4882a593Smuzhiyun return vidioc_g_fmt(priv, f);
516*4882a593Smuzhiyun }
517*4882a593Smuzhiyun
vidioc_g_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)518*4882a593Smuzhiyun static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
519*4882a593Smuzhiyun struct v4l2_format *f)
520*4882a593Smuzhiyun {
521*4882a593Smuzhiyun return vidioc_g_fmt(priv, f);
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun
vidioc_try_fmt(struct v4l2_format * f,struct deinterlace_fmt * fmt)524*4882a593Smuzhiyun static int vidioc_try_fmt(struct v4l2_format *f, struct deinterlace_fmt *fmt)
525*4882a593Smuzhiyun {
526*4882a593Smuzhiyun switch (f->fmt.pix.pixelformat) {
527*4882a593Smuzhiyun case V4L2_PIX_FMT_YUV420:
528*4882a593Smuzhiyun f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2;
529*4882a593Smuzhiyun break;
530*4882a593Smuzhiyun case V4L2_PIX_FMT_YUYV:
531*4882a593Smuzhiyun default:
532*4882a593Smuzhiyun f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun return 0;
537*4882a593Smuzhiyun }
538*4882a593Smuzhiyun
vidioc_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)539*4882a593Smuzhiyun static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
540*4882a593Smuzhiyun struct v4l2_format *f)
541*4882a593Smuzhiyun {
542*4882a593Smuzhiyun struct deinterlace_fmt *fmt;
543*4882a593Smuzhiyun struct deinterlace_ctx *ctx = priv;
544*4882a593Smuzhiyun
545*4882a593Smuzhiyun fmt = find_format(f);
546*4882a593Smuzhiyun if (!fmt || !(fmt->types & MEM2MEM_CAPTURE))
547*4882a593Smuzhiyun f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun f->fmt.pix.colorspace = ctx->colorspace;
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun if (f->fmt.pix.field != V4L2_FIELD_INTERLACED_TB &&
552*4882a593Smuzhiyun f->fmt.pix.field != V4L2_FIELD_INTERLACED_BT &&
553*4882a593Smuzhiyun f->fmt.pix.field != V4L2_FIELD_NONE)
554*4882a593Smuzhiyun f->fmt.pix.field = V4L2_FIELD_INTERLACED_TB;
555*4882a593Smuzhiyun
556*4882a593Smuzhiyun return vidioc_try_fmt(f, fmt);
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun
vidioc_try_fmt_vid_out(struct file * file,void * priv,struct v4l2_format * f)559*4882a593Smuzhiyun static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
560*4882a593Smuzhiyun struct v4l2_format *f)
561*4882a593Smuzhiyun {
562*4882a593Smuzhiyun struct deinterlace_fmt *fmt;
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun fmt = find_format(f);
565*4882a593Smuzhiyun if (!fmt || !(fmt->types & MEM2MEM_OUTPUT))
566*4882a593Smuzhiyun f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun if (!f->fmt.pix.colorspace)
569*4882a593Smuzhiyun f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun if (f->fmt.pix.field != V4L2_FIELD_SEQ_TB &&
572*4882a593Smuzhiyun f->fmt.pix.field != V4L2_FIELD_SEQ_BT)
573*4882a593Smuzhiyun f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
574*4882a593Smuzhiyun
575*4882a593Smuzhiyun return vidioc_try_fmt(f, fmt);
576*4882a593Smuzhiyun }
577*4882a593Smuzhiyun
vidioc_s_fmt(struct deinterlace_ctx * ctx,struct v4l2_format * f)578*4882a593Smuzhiyun static int vidioc_s_fmt(struct deinterlace_ctx *ctx, struct v4l2_format *f)
579*4882a593Smuzhiyun {
580*4882a593Smuzhiyun struct deinterlace_q_data *q_data;
581*4882a593Smuzhiyun struct vb2_queue *vq;
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
584*4882a593Smuzhiyun if (!vq)
585*4882a593Smuzhiyun return -EINVAL;
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun q_data = get_q_data(f->type);
588*4882a593Smuzhiyun if (!q_data)
589*4882a593Smuzhiyun return -EINVAL;
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun if (vb2_is_busy(vq)) {
592*4882a593Smuzhiyun v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
593*4882a593Smuzhiyun return -EBUSY;
594*4882a593Smuzhiyun }
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun q_data->fmt = find_format(f);
597*4882a593Smuzhiyun if (!q_data->fmt) {
598*4882a593Smuzhiyun v4l2_err(&ctx->dev->v4l2_dev,
599*4882a593Smuzhiyun "Couldn't set format type %d, wxh: %dx%d. fmt: %d, field: %d\n",
600*4882a593Smuzhiyun f->type, f->fmt.pix.width, f->fmt.pix.height,
601*4882a593Smuzhiyun f->fmt.pix.pixelformat, f->fmt.pix.field);
602*4882a593Smuzhiyun return -EINVAL;
603*4882a593Smuzhiyun }
604*4882a593Smuzhiyun
605*4882a593Smuzhiyun q_data->width = f->fmt.pix.width;
606*4882a593Smuzhiyun q_data->height = f->fmt.pix.height;
607*4882a593Smuzhiyun q_data->field = f->fmt.pix.field;
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun switch (f->fmt.pix.pixelformat) {
610*4882a593Smuzhiyun case V4L2_PIX_FMT_YUV420:
611*4882a593Smuzhiyun f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2;
612*4882a593Smuzhiyun q_data->sizeimage = (q_data->width * q_data->height * 3) / 2;
613*4882a593Smuzhiyun break;
614*4882a593Smuzhiyun case V4L2_PIX_FMT_YUYV:
615*4882a593Smuzhiyun default:
616*4882a593Smuzhiyun f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
617*4882a593Smuzhiyun q_data->sizeimage = q_data->width * q_data->height * 2;
618*4882a593Smuzhiyun }
619*4882a593Smuzhiyun
620*4882a593Smuzhiyun dprintk(ctx->dev,
621*4882a593Smuzhiyun "Setting format for type %d, wxh: %dx%d, fmt: %d, field: %d\n",
622*4882a593Smuzhiyun f->type, q_data->width, q_data->height, q_data->fmt->fourcc,
623*4882a593Smuzhiyun q_data->field);
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun return 0;
626*4882a593Smuzhiyun }
627*4882a593Smuzhiyun
vidioc_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)628*4882a593Smuzhiyun static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
629*4882a593Smuzhiyun struct v4l2_format *f)
630*4882a593Smuzhiyun {
631*4882a593Smuzhiyun int ret;
632*4882a593Smuzhiyun
633*4882a593Smuzhiyun ret = vidioc_try_fmt_vid_cap(file, priv, f);
634*4882a593Smuzhiyun if (ret)
635*4882a593Smuzhiyun return ret;
636*4882a593Smuzhiyun return vidioc_s_fmt(priv, f);
637*4882a593Smuzhiyun }
638*4882a593Smuzhiyun
vidioc_s_fmt_vid_out(struct file * file,void * priv,struct v4l2_format * f)639*4882a593Smuzhiyun static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
640*4882a593Smuzhiyun struct v4l2_format *f)
641*4882a593Smuzhiyun {
642*4882a593Smuzhiyun struct deinterlace_ctx *ctx = priv;
643*4882a593Smuzhiyun int ret;
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun ret = vidioc_try_fmt_vid_out(file, priv, f);
646*4882a593Smuzhiyun if (ret)
647*4882a593Smuzhiyun return ret;
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun ret = vidioc_s_fmt(priv, f);
650*4882a593Smuzhiyun if (!ret)
651*4882a593Smuzhiyun ctx->colorspace = f->fmt.pix.colorspace;
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun return ret;
654*4882a593Smuzhiyun }
655*4882a593Smuzhiyun
vidioc_streamon(struct file * file,void * priv,enum v4l2_buf_type type)656*4882a593Smuzhiyun static int vidioc_streamon(struct file *file, void *priv,
657*4882a593Smuzhiyun enum v4l2_buf_type type)
658*4882a593Smuzhiyun {
659*4882a593Smuzhiyun struct deinterlace_q_data *s_q_data, *d_q_data;
660*4882a593Smuzhiyun struct deinterlace_ctx *ctx = priv;
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun s_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT);
663*4882a593Smuzhiyun d_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_CAPTURE);
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun /* Check that src and dst queues have the same pix format */
666*4882a593Smuzhiyun if (s_q_data->fmt->fourcc != d_q_data->fmt->fourcc) {
667*4882a593Smuzhiyun v4l2_err(&ctx->dev->v4l2_dev,
668*4882a593Smuzhiyun "src and dst formats don't match.\n");
669*4882a593Smuzhiyun return -EINVAL;
670*4882a593Smuzhiyun }
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun /* Check that input and output deinterlacing types are compatible */
673*4882a593Smuzhiyun switch (s_q_data->field) {
674*4882a593Smuzhiyun case V4L2_FIELD_SEQ_BT:
675*4882a593Smuzhiyun if (d_q_data->field != V4L2_FIELD_NONE &&
676*4882a593Smuzhiyun d_q_data->field != V4L2_FIELD_INTERLACED_BT) {
677*4882a593Smuzhiyun v4l2_err(&ctx->dev->v4l2_dev,
678*4882a593Smuzhiyun "src and dst field conversion [(%d)->(%d)] not supported.\n",
679*4882a593Smuzhiyun s_q_data->field, d_q_data->field);
680*4882a593Smuzhiyun return -EINVAL;
681*4882a593Smuzhiyun }
682*4882a593Smuzhiyun break;
683*4882a593Smuzhiyun case V4L2_FIELD_SEQ_TB:
684*4882a593Smuzhiyun if (d_q_data->field != V4L2_FIELD_NONE &&
685*4882a593Smuzhiyun d_q_data->field != V4L2_FIELD_INTERLACED_TB) {
686*4882a593Smuzhiyun v4l2_err(&ctx->dev->v4l2_dev,
687*4882a593Smuzhiyun "src and dst field conversion [(%d)->(%d)] not supported.\n",
688*4882a593Smuzhiyun s_q_data->field, d_q_data->field);
689*4882a593Smuzhiyun return -EINVAL;
690*4882a593Smuzhiyun }
691*4882a593Smuzhiyun break;
692*4882a593Smuzhiyun default:
693*4882a593Smuzhiyun return -EINVAL;
694*4882a593Smuzhiyun }
695*4882a593Smuzhiyun
696*4882a593Smuzhiyun return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type);
697*4882a593Smuzhiyun }
698*4882a593Smuzhiyun
699*4882a593Smuzhiyun static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = {
700*4882a593Smuzhiyun .vidioc_querycap = vidioc_querycap,
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
703*4882a593Smuzhiyun .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
704*4882a593Smuzhiyun .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
705*4882a593Smuzhiyun .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
706*4882a593Smuzhiyun
707*4882a593Smuzhiyun .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
708*4882a593Smuzhiyun .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
709*4882a593Smuzhiyun .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
710*4882a593Smuzhiyun .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
711*4882a593Smuzhiyun
712*4882a593Smuzhiyun .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
713*4882a593Smuzhiyun .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
714*4882a593Smuzhiyun .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
715*4882a593Smuzhiyun .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
716*4882a593Smuzhiyun .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
717*4882a593Smuzhiyun .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun .vidioc_streamon = vidioc_streamon,
720*4882a593Smuzhiyun .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
721*4882a593Smuzhiyun };
722*4882a593Smuzhiyun
723*4882a593Smuzhiyun
724*4882a593Smuzhiyun /*
725*4882a593Smuzhiyun * Queue operations
726*4882a593Smuzhiyun */
727*4882a593Smuzhiyun struct vb2_dc_conf {
728*4882a593Smuzhiyun struct device *dev;
729*4882a593Smuzhiyun };
730*4882a593Smuzhiyun
deinterlace_queue_setup(struct vb2_queue * vq,unsigned int * nbuffers,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])731*4882a593Smuzhiyun static int deinterlace_queue_setup(struct vb2_queue *vq,
732*4882a593Smuzhiyun unsigned int *nbuffers, unsigned int *nplanes,
733*4882a593Smuzhiyun unsigned int sizes[], struct device *alloc_devs[])
734*4882a593Smuzhiyun {
735*4882a593Smuzhiyun struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
736*4882a593Smuzhiyun struct deinterlace_q_data *q_data;
737*4882a593Smuzhiyun unsigned int size, count = *nbuffers;
738*4882a593Smuzhiyun
739*4882a593Smuzhiyun q_data = get_q_data(vq->type);
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun switch (q_data->fmt->fourcc) {
742*4882a593Smuzhiyun case V4L2_PIX_FMT_YUV420:
743*4882a593Smuzhiyun size = q_data->width * q_data->height * 3 / 2;
744*4882a593Smuzhiyun break;
745*4882a593Smuzhiyun case V4L2_PIX_FMT_YUYV:
746*4882a593Smuzhiyun default:
747*4882a593Smuzhiyun size = q_data->width * q_data->height * 2;
748*4882a593Smuzhiyun }
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun *nplanes = 1;
751*4882a593Smuzhiyun *nbuffers = count;
752*4882a593Smuzhiyun sizes[0] = size;
753*4882a593Smuzhiyun
754*4882a593Smuzhiyun dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun return 0;
757*4882a593Smuzhiyun }
758*4882a593Smuzhiyun
deinterlace_buf_prepare(struct vb2_buffer * vb)759*4882a593Smuzhiyun static int deinterlace_buf_prepare(struct vb2_buffer *vb)
760*4882a593Smuzhiyun {
761*4882a593Smuzhiyun struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
762*4882a593Smuzhiyun struct deinterlace_q_data *q_data;
763*4882a593Smuzhiyun
764*4882a593Smuzhiyun dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun q_data = get_q_data(vb->vb2_queue->type);
767*4882a593Smuzhiyun
768*4882a593Smuzhiyun if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
769*4882a593Smuzhiyun dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n",
770*4882a593Smuzhiyun __func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage);
771*4882a593Smuzhiyun return -EINVAL;
772*4882a593Smuzhiyun }
773*4882a593Smuzhiyun
774*4882a593Smuzhiyun vb2_set_plane_payload(vb, 0, q_data->sizeimage);
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun return 0;
777*4882a593Smuzhiyun }
778*4882a593Smuzhiyun
deinterlace_buf_queue(struct vb2_buffer * vb)779*4882a593Smuzhiyun static void deinterlace_buf_queue(struct vb2_buffer *vb)
780*4882a593Smuzhiyun {
781*4882a593Smuzhiyun struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
782*4882a593Smuzhiyun struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
783*4882a593Smuzhiyun
784*4882a593Smuzhiyun v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
785*4882a593Smuzhiyun }
786*4882a593Smuzhiyun
787*4882a593Smuzhiyun static const struct vb2_ops deinterlace_qops = {
788*4882a593Smuzhiyun .queue_setup = deinterlace_queue_setup,
789*4882a593Smuzhiyun .buf_prepare = deinterlace_buf_prepare,
790*4882a593Smuzhiyun .buf_queue = deinterlace_buf_queue,
791*4882a593Smuzhiyun .wait_prepare = vb2_ops_wait_prepare,
792*4882a593Smuzhiyun .wait_finish = vb2_ops_wait_finish,
793*4882a593Smuzhiyun };
794*4882a593Smuzhiyun
queue_init(void * priv,struct vb2_queue * src_vq,struct vb2_queue * dst_vq)795*4882a593Smuzhiyun static int queue_init(void *priv, struct vb2_queue *src_vq,
796*4882a593Smuzhiyun struct vb2_queue *dst_vq)
797*4882a593Smuzhiyun {
798*4882a593Smuzhiyun struct deinterlace_ctx *ctx = priv;
799*4882a593Smuzhiyun int ret;
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
802*4882a593Smuzhiyun src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
803*4882a593Smuzhiyun src_vq->drv_priv = ctx;
804*4882a593Smuzhiyun src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
805*4882a593Smuzhiyun src_vq->ops = &deinterlace_qops;
806*4882a593Smuzhiyun src_vq->mem_ops = &vb2_dma_contig_memops;
807*4882a593Smuzhiyun src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
808*4882a593Smuzhiyun src_vq->dev = ctx->dev->v4l2_dev.dev;
809*4882a593Smuzhiyun src_vq->lock = &ctx->dev->dev_mutex;
810*4882a593Smuzhiyun q_data[V4L2_M2M_SRC].fmt = &formats[0];
811*4882a593Smuzhiyun q_data[V4L2_M2M_SRC].width = 640;
812*4882a593Smuzhiyun q_data[V4L2_M2M_SRC].height = 480;
813*4882a593Smuzhiyun q_data[V4L2_M2M_SRC].sizeimage = (640 * 480 * 3) / 2;
814*4882a593Smuzhiyun q_data[V4L2_M2M_SRC].field = V4L2_FIELD_SEQ_TB;
815*4882a593Smuzhiyun
816*4882a593Smuzhiyun ret = vb2_queue_init(src_vq);
817*4882a593Smuzhiyun if (ret)
818*4882a593Smuzhiyun return ret;
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
821*4882a593Smuzhiyun dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
822*4882a593Smuzhiyun dst_vq->drv_priv = ctx;
823*4882a593Smuzhiyun dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
824*4882a593Smuzhiyun dst_vq->ops = &deinterlace_qops;
825*4882a593Smuzhiyun dst_vq->mem_ops = &vb2_dma_contig_memops;
826*4882a593Smuzhiyun dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
827*4882a593Smuzhiyun dst_vq->dev = ctx->dev->v4l2_dev.dev;
828*4882a593Smuzhiyun dst_vq->lock = &ctx->dev->dev_mutex;
829*4882a593Smuzhiyun q_data[V4L2_M2M_DST].fmt = &formats[0];
830*4882a593Smuzhiyun q_data[V4L2_M2M_DST].width = 640;
831*4882a593Smuzhiyun q_data[V4L2_M2M_DST].height = 480;
832*4882a593Smuzhiyun q_data[V4L2_M2M_DST].sizeimage = (640 * 480 * 3) / 2;
833*4882a593Smuzhiyun q_data[V4L2_M2M_SRC].field = V4L2_FIELD_INTERLACED_TB;
834*4882a593Smuzhiyun
835*4882a593Smuzhiyun return vb2_queue_init(dst_vq);
836*4882a593Smuzhiyun }
837*4882a593Smuzhiyun
838*4882a593Smuzhiyun /*
839*4882a593Smuzhiyun * File operations
840*4882a593Smuzhiyun */
deinterlace_open(struct file * file)841*4882a593Smuzhiyun static int deinterlace_open(struct file *file)
842*4882a593Smuzhiyun {
843*4882a593Smuzhiyun struct deinterlace_dev *pcdev = video_drvdata(file);
844*4882a593Smuzhiyun struct deinterlace_ctx *ctx = NULL;
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
847*4882a593Smuzhiyun if (!ctx)
848*4882a593Smuzhiyun return -ENOMEM;
849*4882a593Smuzhiyun
850*4882a593Smuzhiyun v4l2_fh_init(&ctx->fh, video_devdata(file));
851*4882a593Smuzhiyun file->private_data = &ctx->fh;
852*4882a593Smuzhiyun ctx->dev = pcdev;
853*4882a593Smuzhiyun
854*4882a593Smuzhiyun ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init);
855*4882a593Smuzhiyun if (IS_ERR(ctx->fh.m2m_ctx)) {
856*4882a593Smuzhiyun int ret = PTR_ERR(ctx->fh.m2m_ctx);
857*4882a593Smuzhiyun
858*4882a593Smuzhiyun kfree(ctx);
859*4882a593Smuzhiyun return ret;
860*4882a593Smuzhiyun }
861*4882a593Smuzhiyun
862*4882a593Smuzhiyun ctx->xt = kzalloc(sizeof(struct dma_interleaved_template) +
863*4882a593Smuzhiyun sizeof(struct data_chunk), GFP_KERNEL);
864*4882a593Smuzhiyun if (!ctx->xt) {
865*4882a593Smuzhiyun kfree(ctx);
866*4882a593Smuzhiyun return -ENOMEM;
867*4882a593Smuzhiyun }
868*4882a593Smuzhiyun
869*4882a593Smuzhiyun ctx->colorspace = V4L2_COLORSPACE_REC709;
870*4882a593Smuzhiyun v4l2_fh_add(&ctx->fh);
871*4882a593Smuzhiyun
872*4882a593Smuzhiyun dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n",
873*4882a593Smuzhiyun ctx, ctx->fh.m2m_ctx);
874*4882a593Smuzhiyun
875*4882a593Smuzhiyun return 0;
876*4882a593Smuzhiyun }
877*4882a593Smuzhiyun
deinterlace_release(struct file * file)878*4882a593Smuzhiyun static int deinterlace_release(struct file *file)
879*4882a593Smuzhiyun {
880*4882a593Smuzhiyun struct deinterlace_dev *pcdev = video_drvdata(file);
881*4882a593Smuzhiyun struct deinterlace_ctx *ctx = file->private_data;
882*4882a593Smuzhiyun
883*4882a593Smuzhiyun dprintk(pcdev, "Releasing instance %p\n", ctx);
884*4882a593Smuzhiyun
885*4882a593Smuzhiyun v4l2_fh_del(&ctx->fh);
886*4882a593Smuzhiyun v4l2_fh_exit(&ctx->fh);
887*4882a593Smuzhiyun v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
888*4882a593Smuzhiyun kfree(ctx->xt);
889*4882a593Smuzhiyun kfree(ctx);
890*4882a593Smuzhiyun
891*4882a593Smuzhiyun return 0;
892*4882a593Smuzhiyun }
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun static const struct v4l2_file_operations deinterlace_fops = {
895*4882a593Smuzhiyun .owner = THIS_MODULE,
896*4882a593Smuzhiyun .open = deinterlace_open,
897*4882a593Smuzhiyun .release = deinterlace_release,
898*4882a593Smuzhiyun .poll = v4l2_m2m_fop_poll,
899*4882a593Smuzhiyun .unlocked_ioctl = video_ioctl2,
900*4882a593Smuzhiyun .mmap = v4l2_m2m_fop_mmap,
901*4882a593Smuzhiyun };
902*4882a593Smuzhiyun
903*4882a593Smuzhiyun static const struct video_device deinterlace_videodev = {
904*4882a593Smuzhiyun .name = MEM2MEM_NAME,
905*4882a593Smuzhiyun .fops = &deinterlace_fops,
906*4882a593Smuzhiyun .ioctl_ops = &deinterlace_ioctl_ops,
907*4882a593Smuzhiyun .minor = -1,
908*4882a593Smuzhiyun .release = video_device_release_empty,
909*4882a593Smuzhiyun .vfl_dir = VFL_DIR_M2M,
910*4882a593Smuzhiyun .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
911*4882a593Smuzhiyun };
912*4882a593Smuzhiyun
913*4882a593Smuzhiyun static const struct v4l2_m2m_ops m2m_ops = {
914*4882a593Smuzhiyun .device_run = deinterlace_device_run,
915*4882a593Smuzhiyun .job_ready = deinterlace_job_ready,
916*4882a593Smuzhiyun .job_abort = deinterlace_job_abort,
917*4882a593Smuzhiyun };
918*4882a593Smuzhiyun
deinterlace_probe(struct platform_device * pdev)919*4882a593Smuzhiyun static int deinterlace_probe(struct platform_device *pdev)
920*4882a593Smuzhiyun {
921*4882a593Smuzhiyun struct deinterlace_dev *pcdev;
922*4882a593Smuzhiyun struct video_device *vfd;
923*4882a593Smuzhiyun dma_cap_mask_t mask;
924*4882a593Smuzhiyun int ret = 0;
925*4882a593Smuzhiyun
926*4882a593Smuzhiyun pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
927*4882a593Smuzhiyun if (!pcdev)
928*4882a593Smuzhiyun return -ENOMEM;
929*4882a593Smuzhiyun
930*4882a593Smuzhiyun spin_lock_init(&pcdev->irqlock);
931*4882a593Smuzhiyun
932*4882a593Smuzhiyun dma_cap_zero(mask);
933*4882a593Smuzhiyun dma_cap_set(DMA_INTERLEAVE, mask);
934*4882a593Smuzhiyun pcdev->dma_chan = dma_request_channel(mask, NULL, pcdev);
935*4882a593Smuzhiyun if (!pcdev->dma_chan)
936*4882a593Smuzhiyun return -ENODEV;
937*4882a593Smuzhiyun
938*4882a593Smuzhiyun if (!dma_has_cap(DMA_INTERLEAVE, pcdev->dma_chan->device->cap_mask)) {
939*4882a593Smuzhiyun dev_err(&pdev->dev, "DMA does not support INTERLEAVE\n");
940*4882a593Smuzhiyun ret = -ENODEV;
941*4882a593Smuzhiyun goto rel_dma;
942*4882a593Smuzhiyun }
943*4882a593Smuzhiyun
944*4882a593Smuzhiyun ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
945*4882a593Smuzhiyun if (ret)
946*4882a593Smuzhiyun goto rel_dma;
947*4882a593Smuzhiyun
948*4882a593Smuzhiyun atomic_set(&pcdev->busy, 0);
949*4882a593Smuzhiyun mutex_init(&pcdev->dev_mutex);
950*4882a593Smuzhiyun
951*4882a593Smuzhiyun vfd = &pcdev->vfd;
952*4882a593Smuzhiyun *vfd = deinterlace_videodev;
953*4882a593Smuzhiyun vfd->lock = &pcdev->dev_mutex;
954*4882a593Smuzhiyun vfd->v4l2_dev = &pcdev->v4l2_dev;
955*4882a593Smuzhiyun
956*4882a593Smuzhiyun ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
957*4882a593Smuzhiyun if (ret) {
958*4882a593Smuzhiyun v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
959*4882a593Smuzhiyun goto unreg_dev;
960*4882a593Smuzhiyun }
961*4882a593Smuzhiyun
962*4882a593Smuzhiyun video_set_drvdata(vfd, pcdev);
963*4882a593Smuzhiyun v4l2_info(&pcdev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME
964*4882a593Smuzhiyun " Device registered as /dev/video%d\n", vfd->num);
965*4882a593Smuzhiyun
966*4882a593Smuzhiyun platform_set_drvdata(pdev, pcdev);
967*4882a593Smuzhiyun
968*4882a593Smuzhiyun pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops);
969*4882a593Smuzhiyun if (IS_ERR(pcdev->m2m_dev)) {
970*4882a593Smuzhiyun v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n");
971*4882a593Smuzhiyun ret = PTR_ERR(pcdev->m2m_dev);
972*4882a593Smuzhiyun goto err_m2m;
973*4882a593Smuzhiyun }
974*4882a593Smuzhiyun
975*4882a593Smuzhiyun return 0;
976*4882a593Smuzhiyun
977*4882a593Smuzhiyun err_m2m:
978*4882a593Smuzhiyun video_unregister_device(&pcdev->vfd);
979*4882a593Smuzhiyun unreg_dev:
980*4882a593Smuzhiyun v4l2_device_unregister(&pcdev->v4l2_dev);
981*4882a593Smuzhiyun rel_dma:
982*4882a593Smuzhiyun dma_release_channel(pcdev->dma_chan);
983*4882a593Smuzhiyun
984*4882a593Smuzhiyun return ret;
985*4882a593Smuzhiyun }
986*4882a593Smuzhiyun
deinterlace_remove(struct platform_device * pdev)987*4882a593Smuzhiyun static int deinterlace_remove(struct platform_device *pdev)
988*4882a593Smuzhiyun {
989*4882a593Smuzhiyun struct deinterlace_dev *pcdev = platform_get_drvdata(pdev);
990*4882a593Smuzhiyun
991*4882a593Smuzhiyun v4l2_info(&pcdev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
992*4882a593Smuzhiyun v4l2_m2m_release(pcdev->m2m_dev);
993*4882a593Smuzhiyun video_unregister_device(&pcdev->vfd);
994*4882a593Smuzhiyun v4l2_device_unregister(&pcdev->v4l2_dev);
995*4882a593Smuzhiyun dma_release_channel(pcdev->dma_chan);
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun return 0;
998*4882a593Smuzhiyun }
999*4882a593Smuzhiyun
1000*4882a593Smuzhiyun static struct platform_driver deinterlace_pdrv = {
1001*4882a593Smuzhiyun .probe = deinterlace_probe,
1002*4882a593Smuzhiyun .remove = deinterlace_remove,
1003*4882a593Smuzhiyun .driver = {
1004*4882a593Smuzhiyun .name = MEM2MEM_NAME,
1005*4882a593Smuzhiyun },
1006*4882a593Smuzhiyun };
1007*4882a593Smuzhiyun module_platform_driver(deinterlace_pdrv);
1008*4882a593Smuzhiyun
1009