aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/timblogiw.c
diff options
context:
space:
mode:
authorRichard Röjfors <richard.rojfors@pelagicore.com>2010-11-08 08:45:44 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-12-29 05:16:46 -0500
commit9eae42e5a216059a146b3fbbe24b4fdc0b10c723 (patch)
tree7c06b8dd7b579deb86ffa249ec4b6a3e372fcbf7 /drivers/media/video/timblogiw.c
parent36d89f7de4a4937848de86d9b35cb03a9f0357e1 (diff)
[media] media: Add timberdale video-in driver
This patch adds the timberdale video-in driver. The video IP of timberdale delivers the video data via DMA. The driver uses the DMA api to handle DMA transfers, and make use of the V4L2 video buffers to handle buffers against user space. If available the driver uses an encoder to get/set the video standard Signed-off-by: Richard Röjfors <richard.rojfors@pelagicore.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/timblogiw.c')
-rw-r--r--drivers/media/video/timblogiw.c894
1 files changed, 894 insertions, 0 deletions
diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c
new file mode 100644
index 000000000000..bddcdb6ffef5
--- /dev/null
+++ b/drivers/media/video/timblogiw.c
@@ -0,0 +1,894 @@
1/*
2 * timblogiw.c timberdale FPGA LogiWin Video In driver
3 * Copyright (c) 2009-2010 Intel Corporation
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19/* Supports:
20 * Timberdale FPGA LogiWin Video In
21 */
22
23#include <linux/version.h>
24#include <linux/platform_device.h>
25#include <linux/slab.h>
26#include <linux/dmaengine.h>
27#include <linux/scatterlist.h>
28#include <linux/interrupt.h>
29#include <linux/list.h>
30#include <linux/i2c.h>
31#include <media/v4l2-ioctl.h>
32#include <media/v4l2-device.h>
33#include <media/videobuf-dma-contig.h>
34#include <media/timb_video.h>
35
36#define DRIVER_NAME "timb-video"
37
38#define TIMBLOGIWIN_NAME "Timberdale Video-In"
39#define TIMBLOGIW_VERSION_CODE 0x04
40
41#define TIMBLOGIW_LINES_PER_DESC 44
42#define TIMBLOGIW_MAX_VIDEO_MEM 16
43
44#define TIMBLOGIW_HAS_DECODER(lw) (lw->pdata.encoder.module_name)
45
46
47struct timblogiw {
48 struct video_device video_dev;
49 struct v4l2_device v4l2_dev; /* mutual exclusion */
50 struct mutex lock;
51 struct device *dev;
52 struct timb_video_platform_data pdata;
53 struct v4l2_subdev *sd_enc; /* encoder */
54 bool opened;
55};
56
57struct timblogiw_tvnorm {
58 v4l2_std_id std;
59 u16 width;
60 u16 height;
61 u8 fps;
62};
63
64struct timblogiw_fh {
65 struct videobuf_queue vb_vidq;
66 struct timblogiw_tvnorm const *cur_norm;
67 struct list_head capture;
68 struct dma_chan *chan;
69 spinlock_t queue_lock; /* mutual exclusion */
70 unsigned int frame_count;
71};
72
73struct timblogiw_buffer {
74 /* common v4l buffer stuff -- must be first */
75 struct videobuf_buffer vb;
76 struct scatterlist sg[16];
77 dma_cookie_t cookie;
78 struct timblogiw_fh *fh;
79};
80
81const struct timblogiw_tvnorm timblogiw_tvnorms[] = {
82 {
83 .std = V4L2_STD_PAL,
84 .width = 720,
85 .height = 576,
86 .fps = 25
87 },
88 {
89 .std = V4L2_STD_NTSC,
90 .width = 720,
91 .height = 480,
92 .fps = 30
93 }
94};
95
96static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm)
97{
98 return norm->width * 2;
99}
100
101
102static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm)
103{
104 return norm->height * timblogiw_bytes_per_line(norm);
105}
106
107static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std)
108{
109 int i;
110 for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
111 if (timblogiw_tvnorms[i].std & std)
112 return timblogiw_tvnorms + i;
113
114 /* default to first element */
115 return timblogiw_tvnorms;
116}
117
118static void timblogiw_dma_cb(void *data)
119{
120 struct timblogiw_buffer *buf = data;
121 struct timblogiw_fh *fh = buf->fh;
122 struct videobuf_buffer *vb = &buf->vb;
123
124 spin_lock(&fh->queue_lock);
125
126 /* mark the transfer done */
127 buf->cookie = -1;
128
129 fh->frame_count++;
130
131 if (vb->state != VIDEOBUF_ERROR) {
132 list_del(&vb->queue);
133 do_gettimeofday(&vb->ts);
134 vb->field_count = fh->frame_count * 2;
135 vb->state = VIDEOBUF_DONE;
136
137 wake_up(&vb->done);
138 }
139
140 if (!list_empty(&fh->capture)) {
141 vb = list_entry(fh->capture.next, struct videobuf_buffer,
142 queue);
143 vb->state = VIDEOBUF_ACTIVE;
144 }
145
146 spin_unlock(&fh->queue_lock);
147}
148
149static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param)
150{
151 return chan->chan_id == (int)filter_param;
152}
153
154/* IOCTL functions */
155
156static int timblogiw_g_fmt(struct file *file, void *priv,
157 struct v4l2_format *format)
158{
159 struct video_device *vdev = video_devdata(file);
160 struct timblogiw *lw = video_get_drvdata(vdev);
161 struct timblogiw_fh *fh = priv;
162
163 dev_dbg(&vdev->dev, "%s entry\n", __func__);
164
165 if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
166 return -EINVAL;
167
168 mutex_lock(&lw->lock);
169
170 format->fmt.pix.width = fh->cur_norm->width;
171 format->fmt.pix.height = fh->cur_norm->height;
172 format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
173 format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm);
174 format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm);
175 format->fmt.pix.field = V4L2_FIELD_NONE;
176
177 mutex_unlock(&lw->lock);
178
179 return 0;
180}
181
182static int timblogiw_try_fmt(struct file *file, void *priv,
183 struct v4l2_format *format)
184{
185 struct video_device *vdev = video_devdata(file);
186 struct v4l2_pix_format *pix = &format->fmt.pix;
187
188 dev_dbg(&vdev->dev,
189 "%s - width=%d, height=%d, pixelformat=%d, field=%d\n"
190 "bytes per line %d, size image: %d, colorspace: %d\n",
191 __func__,
192 pix->width, pix->height, pix->pixelformat, pix->field,
193 pix->bytesperline, pix->sizeimage, pix->colorspace);
194
195 if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
196 return -EINVAL;
197
198 if (pix->field != V4L2_FIELD_NONE)
199 return -EINVAL;
200
201 if (pix->pixelformat != V4L2_PIX_FMT_UYVY)
202 return -EINVAL;
203
204 return 0;
205}
206
207static int timblogiw_s_fmt(struct file *file, void *priv,
208 struct v4l2_format *format)
209{
210 struct video_device *vdev = video_devdata(file);
211 struct timblogiw *lw = video_get_drvdata(vdev);
212 struct timblogiw_fh *fh = priv;
213 struct v4l2_pix_format *pix = &format->fmt.pix;
214 int err;
215
216 mutex_lock(&lw->lock);
217
218 err = timblogiw_try_fmt(file, priv, format);
219 if (err)
220 goto out;
221
222 if (videobuf_queue_is_busy(&fh->vb_vidq)) {
223 dev_err(&vdev->dev, "%s queue busy\n", __func__);
224 err = -EBUSY;
225 goto out;
226 }
227
228 pix->width = fh->cur_norm->width;
229 pix->height = fh->cur_norm->height;
230
231out:
232 mutex_unlock(&lw->lock);
233 return err;
234}
235
236static int timblogiw_querycap(struct file *file, void *priv,
237 struct v4l2_capability *cap)
238{
239 struct video_device *vdev = video_devdata(file);
240
241 dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
242 memset(cap, 0, sizeof(*cap));
243 strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1);
244 strncpy(cap->driver, DRIVER_NAME, sizeof(cap->card)-1);
245 strlcpy(cap->bus_info, vdev->name, sizeof(cap->bus_info));
246 cap->version = TIMBLOGIW_VERSION_CODE;
247 cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
248 V4L2_CAP_READWRITE;
249
250 return 0;
251}
252
253static int timblogiw_enum_fmt(struct file *file, void *priv,
254 struct v4l2_fmtdesc *fmt)
255{
256 struct video_device *vdev = video_devdata(file);
257
258 dev_dbg(&vdev->dev, "%s, index: %d\n", __func__, fmt->index);
259
260 if (fmt->index != 0)
261 return -EINVAL;
262 memset(fmt, 0, sizeof(*fmt));
263 fmt->index = 0;
264 fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
265 strncpy(fmt->description, "4:2:2, packed, YUYV",
266 sizeof(fmt->description)-1);
267 fmt->pixelformat = V4L2_PIX_FMT_UYVY;
268
269 return 0;
270}
271
272static int timblogiw_g_parm(struct file *file, void *priv,
273 struct v4l2_streamparm *sp)
274{
275 struct timblogiw_fh *fh = priv;
276 struct v4l2_captureparm *cp = &sp->parm.capture;
277
278 cp->capability = V4L2_CAP_TIMEPERFRAME;
279 cp->timeperframe.numerator = 1;
280 cp->timeperframe.denominator = fh->cur_norm->fps;
281
282 return 0;
283}
284
285static int timblogiw_reqbufs(struct file *file, void *priv,
286 struct v4l2_requestbuffers *rb)
287{
288 struct video_device *vdev = video_devdata(file);
289 struct timblogiw_fh *fh = priv;
290
291 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
292
293 return videobuf_reqbufs(&fh->vb_vidq, rb);
294}
295
296static int timblogiw_querybuf(struct file *file, void *priv,
297 struct v4l2_buffer *b)
298{
299 struct video_device *vdev = video_devdata(file);
300 struct timblogiw_fh *fh = priv;
301
302 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
303
304 return videobuf_querybuf(&fh->vb_vidq, b);
305}
306
307static int timblogiw_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
308{
309 struct video_device *vdev = video_devdata(file);
310 struct timblogiw_fh *fh = priv;
311
312 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
313
314 return videobuf_qbuf(&fh->vb_vidq, b);
315}
316
317static int timblogiw_dqbuf(struct file *file, void *priv,
318 struct v4l2_buffer *b)
319{
320 struct video_device *vdev = video_devdata(file);
321 struct timblogiw_fh *fh = priv;
322
323 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
324
325 return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
326}
327
328static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std)
329{
330 struct video_device *vdev = video_devdata(file);
331 struct timblogiw_fh *fh = priv;
332
333 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
334
335 *std = fh->cur_norm->std;
336 return 0;
337}
338
339static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id *std)
340{
341 struct video_device *vdev = video_devdata(file);
342 struct timblogiw *lw = video_get_drvdata(vdev);
343 struct timblogiw_fh *fh = priv;
344 int err = 0;
345
346 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
347
348 mutex_lock(&lw->lock);
349
350 if (TIMBLOGIW_HAS_DECODER(lw))
351 err = v4l2_subdev_call(lw->sd_enc, core, s_std, *std);
352
353 if (!err)
354 fh->cur_norm = timblogiw_get_norm(*std);
355
356 mutex_unlock(&lw->lock);
357
358 return err;
359}
360
361static int timblogiw_enuminput(struct file *file, void *priv,
362 struct v4l2_input *inp)
363{
364 struct video_device *vdev = video_devdata(file);
365 int i;
366
367 dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
368
369 if (inp->index != 0)
370 return -EINVAL;
371
372 memset(inp, 0, sizeof(*inp));
373 inp->index = 0;
374
375 strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1);
376 inp->type = V4L2_INPUT_TYPE_CAMERA;
377
378 inp->std = 0;
379 for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
380 inp->std |= timblogiw_tvnorms[i].std;
381
382 return 0;
383}
384
385static int timblogiw_g_input(struct file *file, void *priv,
386 unsigned int *input)
387{
388 struct video_device *vdev = video_devdata(file);
389
390 dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
391
392 *input = 0;
393
394 return 0;
395}
396
397static int timblogiw_s_input(struct file *file, void *priv, unsigned int input)
398{
399 struct video_device *vdev = video_devdata(file);
400
401 dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
402
403 if (input != 0)
404 return -EINVAL;
405 return 0;
406}
407
408static int timblogiw_streamon(struct file *file, void *priv, unsigned int type)
409{
410 struct video_device *vdev = video_devdata(file);
411 struct timblogiw_fh *fh = priv;
412
413 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
414
415 if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
416 dev_dbg(&vdev->dev, "%s - No capture device\n", __func__);
417 return -EINVAL;
418 }
419
420 fh->frame_count = 0;
421 return videobuf_streamon(&fh->vb_vidq);
422}
423
424static int timblogiw_streamoff(struct file *file, void *priv,
425 unsigned int type)
426{
427 struct video_device *vdev = video_devdata(file);
428 struct timblogiw_fh *fh = priv;
429
430 dev_dbg(&vdev->dev, "%s entry\n", __func__);
431
432 if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
433 return -EINVAL;
434
435 return videobuf_streamoff(&fh->vb_vidq);
436}
437
438static int timblogiw_querystd(struct file *file, void *priv, v4l2_std_id *std)
439{
440 struct video_device *vdev = video_devdata(file);
441 struct timblogiw *lw = video_get_drvdata(vdev);
442 struct timblogiw_fh *fh = priv;
443
444 dev_dbg(&vdev->dev, "%s entry\n", __func__);
445
446 if (TIMBLOGIW_HAS_DECODER(lw))
447 return v4l2_subdev_call(lw->sd_enc, video, querystd, std);
448 else {
449 *std = fh->cur_norm->std;
450 return 0;
451 }
452}
453
454static int timblogiw_enum_framesizes(struct file *file, void *priv,
455 struct v4l2_frmsizeenum *fsize)
456{
457 struct video_device *vdev = video_devdata(file);
458 struct timblogiw_fh *fh = priv;
459
460 dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n", __func__,
461 fsize->index, fsize->pixel_format);
462
463 if ((fsize->index != 0) ||
464 (fsize->pixel_format != V4L2_PIX_FMT_UYVY))
465 return -EINVAL;
466
467 fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
468 fsize->discrete.width = fh->cur_norm->width;
469 fsize->discrete.height = fh->cur_norm->height;
470
471 return 0;
472}
473
474/* Video buffer functions */
475
476static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
477 unsigned int *size)
478{
479 struct timblogiw_fh *fh = vq->priv_data;
480
481 *size = timblogiw_frame_size(fh->cur_norm);
482
483 if (!*count)
484 *count = 32;
485
486 while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024)
487 (*count)--;
488
489 return 0;
490}
491
492static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
493 enum v4l2_field field)
494{
495 struct timblogiw_fh *fh = vq->priv_data;
496 struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
497 vb);
498 unsigned int data_size = timblogiw_frame_size(fh->cur_norm);
499 int err = 0;
500
501 if (vb->baddr && vb->bsize < data_size)
502 /* User provided buffer, but it is too small */
503 return -ENOMEM;
504
505 vb->size = data_size;
506 vb->width = fh->cur_norm->width;
507 vb->height = fh->cur_norm->height;
508 vb->field = field;
509
510 if (vb->state == VIDEOBUF_NEEDS_INIT) {
511 int i;
512 unsigned int size;
513 unsigned int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
514 timblogiw_bytes_per_line(fh->cur_norm);
515 dma_addr_t addr;
516
517 sg_init_table(buf->sg, ARRAY_SIZE(buf->sg));
518
519 err = videobuf_iolock(vq, vb, NULL);
520 if (err)
521 goto err;
522
523 addr = videobuf_to_dma_contig(vb);
524 for (i = 0, size = 0; size < data_size; i++) {
525 sg_dma_address(buf->sg + i) = addr + size;
526 size += bytes_per_desc;
527 sg_dma_len(buf->sg + i) = (size > data_size) ?
528 (bytes_per_desc - (size - data_size)) :
529 bytes_per_desc;
530 }
531
532 vb->state = VIDEOBUF_PREPARED;
533 buf->cookie = -1;
534 buf->fh = fh;
535 }
536
537 return 0;
538
539err:
540 videobuf_dma_contig_free(vq, vb);
541 vb->state = VIDEOBUF_NEEDS_INIT;
542 return err;
543}
544
545static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
546{
547 struct timblogiw_fh *fh = vq->priv_data;
548 struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
549 vb);
550 struct dma_async_tx_descriptor *desc;
551 int sg_elems;
552 int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
553 timblogiw_bytes_per_line(fh->cur_norm);
554
555 sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc;
556 sg_elems +=
557 (timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0;
558
559 if (list_empty(&fh->capture))
560 vb->state = VIDEOBUF_ACTIVE;
561 else
562 vb->state = VIDEOBUF_QUEUED;
563
564 list_add_tail(&vb->queue, &fh->capture);
565
566 spin_unlock_irq(&fh->queue_lock);
567
568 desc = fh->chan->device->device_prep_slave_sg(fh->chan,
569 buf->sg, sg_elems, DMA_FROM_DEVICE,
570 DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
571 if (!desc) {
572 spin_lock_irq(&fh->queue_lock);
573 list_del_init(&vb->queue);
574 vb->state = VIDEOBUF_PREPARED;
575 return;
576 }
577
578 desc->callback_param = buf;
579 desc->callback = timblogiw_dma_cb;
580
581 buf->cookie = desc->tx_submit(desc);
582
583 spin_lock_irq(&fh->queue_lock);
584}
585
586static void buffer_release(struct videobuf_queue *vq,
587 struct videobuf_buffer *vb)
588{
589 struct timblogiw_fh *fh = vq->priv_data;
590 struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
591 vb);
592
593 videobuf_waiton(vq, vb, 0, 0);
594 if (buf->cookie >= 0)
595 dma_sync_wait(fh->chan, buf->cookie);
596
597 videobuf_dma_contig_free(vq, vb);
598 vb->state = VIDEOBUF_NEEDS_INIT;
599}
600
601static struct videobuf_queue_ops timblogiw_video_qops = {
602 .buf_setup = buffer_setup,
603 .buf_prepare = buffer_prepare,
604 .buf_queue = buffer_queue,
605 .buf_release = buffer_release,
606};
607
608/* Device Operations functions */
609
610static int timblogiw_open(struct file *file)
611{
612 struct video_device *vdev = video_devdata(file);
613 struct timblogiw *lw = video_get_drvdata(vdev);
614 struct timblogiw_fh *fh;
615 v4l2_std_id std;
616 dma_cap_mask_t mask;
617 int err = 0;
618
619 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
620
621 mutex_lock(&lw->lock);
622 if (lw->opened) {
623 err = -EBUSY;
624 goto out;
625 }
626
627 if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) {
628 struct i2c_adapter *adapt;
629
630 /* find the video decoder */
631 adapt = i2c_get_adapter(lw->pdata.i2c_adapter);
632 if (!adapt) {
633 dev_err(&vdev->dev, "No I2C bus #%d\n",
634 lw->pdata.i2c_adapter);
635 err = -ENODEV;
636 goto out;
637 }
638
639 /* now find the encoder */
640 lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt,
641 lw->pdata.encoder.info, NULL);
642
643 i2c_put_adapter(adapt);
644
645 if (!lw->sd_enc) {
646 dev_err(&vdev->dev, "Failed to get encoder: %s\n",
647 lw->pdata.encoder.module_name);
648 err = -ENODEV;
649 goto out;
650 }
651 }
652
653 fh = kzalloc(sizeof(*fh), GFP_KERNEL);
654 if (!fh) {
655 err = -ENOMEM;
656 goto out;
657 }
658
659 fh->cur_norm = timblogiw_tvnorms;
660 timblogiw_querystd(file, fh, &std);
661 fh->cur_norm = timblogiw_get_norm(std);
662
663 INIT_LIST_HEAD(&fh->capture);
664 spin_lock_init(&fh->queue_lock);
665
666 dma_cap_zero(mask);
667 dma_cap_set(DMA_SLAVE, mask);
668 dma_cap_set(DMA_PRIVATE, mask);
669
670 /* find the DMA channel */
671 fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn,
672 (void *)lw->pdata.dma_channel);
673 if (!fh->chan) {
674 dev_err(&vdev->dev, "Failed to get DMA channel\n");
675 kfree(fh);
676 err = -ENODEV;
677 goto out;
678 }
679
680 file->private_data = fh;
681 videobuf_queue_dma_contig_init(&fh->vb_vidq,
682 &timblogiw_video_qops, lw->dev, &fh->queue_lock,
683 V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
684 sizeof(struct timblogiw_buffer), fh, NULL);
685
686 lw->opened = true;
687out:
688 mutex_unlock(&lw->lock);
689
690 return err;
691}
692
693static int timblogiw_close(struct file *file)
694{
695 struct video_device *vdev = video_devdata(file);
696 struct timblogiw *lw = video_get_drvdata(vdev);
697 struct timblogiw_fh *fh = file->private_data;
698
699 dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
700
701 videobuf_stop(&fh->vb_vidq);
702 videobuf_mmap_free(&fh->vb_vidq);
703
704 dma_release_channel(fh->chan);
705
706 kfree(fh);
707
708 mutex_lock(&lw->lock);
709 lw->opened = false;
710 mutex_unlock(&lw->lock);
711 return 0;
712}
713
714static ssize_t timblogiw_read(struct file *file, char __user *data,
715 size_t count, loff_t *ppos)
716{
717 struct video_device *vdev = video_devdata(file);
718 struct timblogiw_fh *fh = file->private_data;
719
720 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
721
722 return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0,
723 file->f_flags & O_NONBLOCK);
724}
725
726static unsigned int timblogiw_poll(struct file *file,
727 struct poll_table_struct *wait)
728{
729 struct video_device *vdev = video_devdata(file);
730 struct timblogiw_fh *fh = file->private_data;
731
732 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
733
734 return videobuf_poll_stream(file, &fh->vb_vidq, wait);
735}
736
737static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma)
738{
739 struct video_device *vdev = video_devdata(file);
740 struct timblogiw_fh *fh = file->private_data;
741
742 dev_dbg(&vdev->dev, "%s: entry\n", __func__);
743
744 return videobuf_mmap_mapper(&fh->vb_vidq, vma);
745}
746
747/* Platform device functions */
748
749static const __devinitdata struct v4l2_ioctl_ops timblogiw_ioctl_ops = {
750 .vidioc_querycap = timblogiw_querycap,
751 .vidioc_enum_fmt_vid_cap = timblogiw_enum_fmt,
752 .vidioc_g_fmt_vid_cap = timblogiw_g_fmt,
753 .vidioc_try_fmt_vid_cap = timblogiw_try_fmt,
754 .vidioc_s_fmt_vid_cap = timblogiw_s_fmt,
755 .vidioc_g_parm = timblogiw_g_parm,
756 .vidioc_reqbufs = timblogiw_reqbufs,
757 .vidioc_querybuf = timblogiw_querybuf,
758 .vidioc_qbuf = timblogiw_qbuf,
759 .vidioc_dqbuf = timblogiw_dqbuf,
760 .vidioc_g_std = timblogiw_g_std,
761 .vidioc_s_std = timblogiw_s_std,
762 .vidioc_enum_input = timblogiw_enuminput,
763 .vidioc_g_input = timblogiw_g_input,
764 .vidioc_s_input = timblogiw_s_input,
765 .vidioc_streamon = timblogiw_streamon,
766 .vidioc_streamoff = timblogiw_streamoff,
767 .vidioc_querystd = timblogiw_querystd,
768 .vidioc_enum_framesizes = timblogiw_enum_framesizes,
769};
770
771static const __devinitdata struct v4l2_file_operations timblogiw_fops = {
772 .owner = THIS_MODULE,
773 .open = timblogiw_open,
774 .release = timblogiw_close,
775 .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
776 .mmap = timblogiw_mmap,
777 .read = timblogiw_read,
778 .poll = timblogiw_poll,
779};
780
781static const __devinitdata struct video_device timblogiw_template = {
782 .name = TIMBLOGIWIN_NAME,
783 .fops = &timblogiw_fops,
784 .ioctl_ops = &timblogiw_ioctl_ops,
785 .release = video_device_release_empty,
786 .minor = -1,
787 .tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC
788};
789
790static int __devinit timblogiw_probe(struct platform_device *pdev)
791{
792 int err;
793 struct timblogiw *lw = NULL;
794 struct timb_video_platform_data *pdata = pdev->dev.platform_data;
795
796 if (!pdata) {
797 dev_err(&pdev->dev, "No platform data\n");
798 err = -EINVAL;
799 goto err;
800 }
801
802 if (!pdata->encoder.module_name)
803 dev_info(&pdev->dev, "Running without decoder\n");
804
805 lw = kzalloc(sizeof(*lw), GFP_KERNEL);
806 if (!lw) {
807 err = -ENOMEM;
808 goto err;
809 }
810
811 if (pdev->dev.parent)
812 lw->dev = pdev->dev.parent;
813 else
814 lw->dev = &pdev->dev;
815
816 memcpy(&lw->pdata, pdata, sizeof(lw->pdata));
817
818 mutex_init(&lw->lock);
819
820 lw->video_dev = timblogiw_template;
821
822 strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name));
823 err = v4l2_device_register(NULL, &lw->v4l2_dev);
824 if (err)
825 goto err_register;
826
827 lw->video_dev.v4l2_dev = &lw->v4l2_dev;
828
829 platform_set_drvdata(pdev, lw);
830 video_set_drvdata(&lw->video_dev, lw);
831
832 err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0);
833 if (err) {
834 dev_err(&pdev->dev, "Error reg video: %d\n", err);
835 goto err_request;
836 }
837
838
839 return 0;
840
841err_request:
842 platform_set_drvdata(pdev, NULL);
843 v4l2_device_unregister(&lw->v4l2_dev);
844err_register:
845 kfree(lw);
846err:
847 dev_err(&pdev->dev, "Failed to register: %d\n", err);
848
849 return err;
850}
851
852static int __devexit timblogiw_remove(struct platform_device *pdev)
853{
854 struct timblogiw *lw = platform_get_drvdata(pdev);
855
856 video_unregister_device(&lw->video_dev);
857
858 v4l2_device_unregister(&lw->v4l2_dev);
859
860 kfree(lw);
861
862 platform_set_drvdata(pdev, NULL);
863
864 return 0;
865}
866
867static struct platform_driver timblogiw_platform_driver = {
868 .driver = {
869 .name = DRIVER_NAME,
870 .owner = THIS_MODULE,
871 },
872 .probe = timblogiw_probe,
873 .remove = __devexit_p(timblogiw_remove),
874};
875
876/* Module functions */
877
878static int __init timblogiw_init(void)
879{
880 return platform_driver_register(&timblogiw_platform_driver);
881}
882
883static void __exit timblogiw_exit(void)
884{
885 platform_driver_unregister(&timblogiw_platform_driver);
886}
887
888module_init(timblogiw_init);
889module_exit(timblogiw_exit);
890
891MODULE_DESCRIPTION(TIMBLOGIWIN_NAME);
892MODULE_AUTHOR("Pelagicore AB <info@pelagicore.com>");
893MODULE_LICENSE("GPL v2");
894MODULE_ALIAS("platform:"DRIVER_NAME);