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