diff options
author | Steven Toth <stoth@kernellabs.com> | 2011-04-06 07:32:56 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-05-20 08:28:43 -0400 |
commit | b7101de3fff596b35e45cd9fb7007caa07e97c9a (patch) | |
tree | 1c11baca827c290a1e312180204e8dc0472eabb9 /drivers/media | |
parent | 5ed9bd02444a00bb1e8ecc1baa8ecdb633afc126 (diff) |
[media] cx18: mmap() support for raw YUV video capture
Add support for mmap method streaming of raw YUV video on cx18-based
hardware, in addition to the existing support for read() streaming of
raw YUV and MPEG-2 encoded video.
[simon.farnsworth@onelan.co.uk: I forward-ported this from Steven's original work,
done under contract to ONELAN. The original code is at
http://www.kernellabs.com/hg/~stoth/cx18-videobuf]
Signed-off-by: Steven Toth <stoth@kernellabs.com>
Signed-off-by: Simon Farnsworth <simon.farnsworth@onelan.co.uk>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/cx18/Kconfig | 2 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-driver.h | 25 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-fileops.c | 214 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-fileops.h | 2 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-ioctl.c | 136 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-mailbox.c | 70 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-streams.c | 23 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx23418.h | 6 |
8 files changed, 466 insertions, 12 deletions
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index d788ad6f5c48..9c232022403a 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/drivers/media/video/cx18/Kconfig | |||
@@ -2,6 +2,8 @@ config VIDEO_CX18 | |||
2 | tristate "Conexant cx23418 MPEG encoder support" | 2 | tristate "Conexant cx23418 MPEG encoder support" |
3 | depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL | 3 | depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL |
4 | select I2C_ALGOBIT | 4 | select I2C_ALGOBIT |
5 | select VIDEOBUF_DVB | ||
6 | select VIDEOBUF_VMALLOC | ||
5 | depends on RC_CORE | 7 | depends on RC_CORE |
6 | select VIDEO_TUNER | 8 | select VIDEO_TUNER |
7 | select VIDEO_TVEEPROM | 9 | select VIDEO_TVEEPROM |
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index b86a740c68df..70e1e0401645 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h | |||
@@ -65,6 +65,10 @@ | |||
65 | #include "dvb_net.h" | 65 | #include "dvb_net.h" |
66 | #include "dvbdev.h" | 66 | #include "dvbdev.h" |
67 | 67 | ||
68 | /* Videobuf / YUV support */ | ||
69 | #include <media/videobuf-core.h> | ||
70 | #include <media/videobuf-vmalloc.h> | ||
71 | |||
68 | #ifndef CONFIG_PCI | 72 | #ifndef CONFIG_PCI |
69 | # error "This driver requires kernel PCI support." | 73 | # error "This driver requires kernel PCI support." |
70 | #endif | 74 | #endif |
@@ -403,6 +407,23 @@ struct cx18_stream { | |||
403 | struct cx18_queue q_idle; /* idle - not in rotation */ | 407 | struct cx18_queue q_idle; /* idle - not in rotation */ |
404 | 408 | ||
405 | struct work_struct out_work_order; | 409 | struct work_struct out_work_order; |
410 | |||
411 | /* Videobuf for YUV video */ | ||
412 | u32 pixelformat; | ||
413 | struct list_head vb_capture; /* video capture queue */ | ||
414 | spinlock_t vb_lock; | ||
415 | struct v4l2_framebuffer fbuf; | ||
416 | v4l2_std_id tvnorm; /* selected tv norm */ | ||
417 | struct timer_list vb_timeout; | ||
418 | int vbwidth; | ||
419 | int vbheight; | ||
420 | }; | ||
421 | |||
422 | struct cx18_videobuf_buffer { | ||
423 | /* Common video buffer sub-system struct */ | ||
424 | struct videobuf_buffer vb; | ||
425 | v4l2_std_id tvnorm; /* selected tv norm */ | ||
426 | u32 bytes_used; | ||
406 | }; | 427 | }; |
407 | 428 | ||
408 | struct cx18_open_id { | 429 | struct cx18_open_id { |
@@ -410,6 +431,10 @@ struct cx18_open_id { | |||
410 | u32 open_id; | 431 | u32 open_id; |
411 | int type; | 432 | int type; |
412 | struct cx18 *cx; | 433 | struct cx18 *cx; |
434 | |||
435 | struct videobuf_queue vbuf_q; | ||
436 | spinlock_t s_lock; /* Protect vbuf_q */ | ||
437 | enum v4l2_buf_type vb_type; | ||
413 | }; | 438 | }; |
414 | 439 | ||
415 | static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh) | 440 | static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh) |
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index e9802d99439b..c74eafd67f98 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c | |||
@@ -597,6 +597,13 @@ ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count, | |||
597 | mutex_unlock(&cx->serialize_lock); | 597 | mutex_unlock(&cx->serialize_lock); |
598 | if (rc) | 598 | if (rc) |
599 | return rc; | 599 | return rc; |
600 | |||
601 | if ((id->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
602 | (id->type == CX18_ENC_STREAM_TYPE_YUV)) { | ||
603 | return videobuf_read_stream(&id->vbuf_q, buf, count, pos, 0, | ||
604 | filp->f_flags & O_NONBLOCK); | ||
605 | } | ||
606 | |||
600 | return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); | 607 | return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); |
601 | } | 608 | } |
602 | 609 | ||
@@ -622,6 +629,11 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) | |||
622 | CX18_DEBUG_FILE("Encoder poll started capture\n"); | 629 | CX18_DEBUG_FILE("Encoder poll started capture\n"); |
623 | } | 630 | } |
624 | 631 | ||
632 | if ((id->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
633 | (id->type == CX18_ENC_STREAM_TYPE_YUV)) { | ||
634 | return videobuf_poll_stream(filp, &id->vbuf_q, wait); | ||
635 | } | ||
636 | |||
625 | /* add stream's waitq to the poll list */ | 637 | /* add stream's waitq to the poll list */ |
626 | CX18_DEBUG_HI_FILE("Encoder poll\n"); | 638 | CX18_DEBUG_HI_FILE("Encoder poll\n"); |
627 | poll_wait(filp, &s->waitq, wait); | 639 | poll_wait(filp, &s->waitq, wait); |
@@ -633,6 +645,58 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) | |||
633 | return 0; | 645 | return 0; |
634 | } | 646 | } |
635 | 647 | ||
648 | int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma) | ||
649 | { | ||
650 | struct cx18_open_id *id = file->private_data; | ||
651 | struct cx18 *cx = id->cx; | ||
652 | struct cx18_stream *s = &cx->streams[id->type]; | ||
653 | int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags); | ||
654 | |||
655 | if ((id->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
656 | (id->type == CX18_ENC_STREAM_TYPE_YUV)) { | ||
657 | |||
658 | /* Start a capture if there is none */ | ||
659 | if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) { | ||
660 | int rc; | ||
661 | |||
662 | mutex_lock(&cx->serialize_lock); | ||
663 | rc = cx18_start_capture(id); | ||
664 | mutex_unlock(&cx->serialize_lock); | ||
665 | if (rc) { | ||
666 | CX18_DEBUG_INFO( | ||
667 | "Could not start capture for %s (%d)\n", | ||
668 | s->name, rc); | ||
669 | return -EINVAL; | ||
670 | } | ||
671 | CX18_DEBUG_FILE("Encoder poll started capture\n"); | ||
672 | } | ||
673 | |||
674 | return videobuf_mmap_mapper(&id->vbuf_q, vma); | ||
675 | } | ||
676 | |||
677 | return -EINVAL; | ||
678 | } | ||
679 | |||
680 | void cx18_vb_timeout(unsigned long data) | ||
681 | { | ||
682 | struct cx18_stream *s = (struct cx18_stream *)data; | ||
683 | struct cx18_videobuf_buffer *buf; | ||
684 | unsigned long flags; | ||
685 | |||
686 | /* Return all of the buffers in error state, so the vbi/vid inode | ||
687 | * can return from blocking. | ||
688 | */ | ||
689 | spin_lock_irqsave(&s->vb_lock, flags); | ||
690 | while (!list_empty(&s->vb_capture)) { | ||
691 | buf = list_entry(s->vb_capture.next, | ||
692 | struct cx18_videobuf_buffer, vb.queue); | ||
693 | list_del(&buf->vb.queue); | ||
694 | buf->vb.state = VIDEOBUF_ERROR; | ||
695 | wake_up(&buf->vb.done); | ||
696 | } | ||
697 | spin_unlock_irqrestore(&s->vb_lock, flags); | ||
698 | } | ||
699 | |||
636 | void cx18_stop_capture(struct cx18_open_id *id, int gop_end) | 700 | void cx18_stop_capture(struct cx18_open_id *id, int gop_end) |
637 | { | 701 | { |
638 | struct cx18 *cx = id->cx; | 702 | struct cx18 *cx = id->cx; |
@@ -716,12 +780,150 @@ int cx18_v4l2_close(struct file *filp) | |||
716 | cx18_release_stream(s); | 780 | cx18_release_stream(s); |
717 | } else { | 781 | } else { |
718 | cx18_stop_capture(id, 0); | 782 | cx18_stop_capture(id, 0); |
783 | if (id->type == CX18_ENC_STREAM_TYPE_YUV) | ||
784 | videobuf_mmap_free(&id->vbuf_q); | ||
719 | } | 785 | } |
720 | kfree(id); | 786 | kfree(id); |
721 | mutex_unlock(&cx->serialize_lock); | 787 | mutex_unlock(&cx->serialize_lock); |
722 | return 0; | 788 | return 0; |
723 | } | 789 | } |
724 | 790 | ||
791 | void cx18_dma_free(struct videobuf_queue *q, | ||
792 | struct cx18_stream *s, struct cx18_videobuf_buffer *buf) | ||
793 | { | ||
794 | videobuf_waiton(q, &buf->vb, 0, 0); | ||
795 | videobuf_vmalloc_free(&buf->vb); | ||
796 | buf->vb.state = VIDEOBUF_NEEDS_INIT; | ||
797 | } | ||
798 | |||
799 | static int cx18_prepare_buffer(struct videobuf_queue *q, | ||
800 | struct cx18_stream *s, | ||
801 | struct cx18_videobuf_buffer *buf, | ||
802 | u32 pixelformat, | ||
803 | unsigned int width, unsigned int height, | ||
804 | enum v4l2_field field) | ||
805 | { | ||
806 | int rc = 0; | ||
807 | |||
808 | /* check settings */ | ||
809 | buf->bytes_used = 0; | ||
810 | |||
811 | if ((width < 48) || (height < 32)) | ||
812 | return -EINVAL; | ||
813 | |||
814 | buf->vb.size = (width * height * 16 /*fmt->depth*/) >> 3; | ||
815 | if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size)) | ||
816 | return -EINVAL; | ||
817 | |||
818 | /* alloc + fill struct (if changed) */ | ||
819 | if (buf->vb.width != width || buf->vb.height != height || | ||
820 | buf->vb.field != field || s->pixelformat != pixelformat || | ||
821 | buf->tvnorm != s->tvnorm) { | ||
822 | |||
823 | buf->vb.width = width; | ||
824 | buf->vb.height = height; | ||
825 | buf->vb.field = field; | ||
826 | buf->tvnorm = s->tvnorm; | ||
827 | s->pixelformat = pixelformat; | ||
828 | |||
829 | cx18_dma_free(q, s, buf); | ||
830 | } | ||
831 | |||
832 | if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size)) | ||
833 | return -EINVAL; | ||
834 | |||
835 | if (buf->vb.field == 0) | ||
836 | buf->vb.field = V4L2_FIELD_INTERLACED; | ||
837 | |||
838 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { | ||
839 | buf->vb.width = width; | ||
840 | buf->vb.height = height; | ||
841 | buf->vb.field = field; | ||
842 | buf->tvnorm = s->tvnorm; | ||
843 | s->pixelformat = pixelformat; | ||
844 | |||
845 | rc = videobuf_iolock(q, &buf->vb, &s->fbuf); | ||
846 | if (rc != 0) | ||
847 | goto fail; | ||
848 | } | ||
849 | buf->vb.state = VIDEOBUF_PREPARED; | ||
850 | return 0; | ||
851 | |||
852 | fail: | ||
853 | cx18_dma_free(q, s, buf); | ||
854 | return rc; | ||
855 | |||
856 | } | ||
857 | |||
858 | #define VB_MIN_BUFFERS 32 | ||
859 | #define VB_MIN_BUFSIZE 0x208000 | ||
860 | |||
861 | static int buffer_setup(struct videobuf_queue *q, | ||
862 | unsigned int *count, unsigned int *size) | ||
863 | { | ||
864 | struct cx18_open_id *id = q->priv_data; | ||
865 | struct cx18 *cx = id->cx; | ||
866 | struct cx18_stream *s = &cx->streams[id->type]; | ||
867 | |||
868 | *size = 2 * s->vbwidth * s->vbheight; | ||
869 | if (*count == 0) | ||
870 | *count = VB_MIN_BUFFERS; | ||
871 | |||
872 | while (*size * *count > VB_MIN_BUFFERS * VB_MIN_BUFSIZE) | ||
873 | (*count)--; | ||
874 | |||
875 | q->field = V4L2_FIELD_INTERLACED; | ||
876 | q->last = V4L2_FIELD_INTERLACED; | ||
877 | |||
878 | return 0; | ||
879 | } | ||
880 | |||
881 | static int buffer_prepare(struct videobuf_queue *q, | ||
882 | struct videobuf_buffer *vb, | ||
883 | enum v4l2_field field) | ||
884 | { | ||
885 | struct cx18_videobuf_buffer *buf = | ||
886 | container_of(vb, struct cx18_videobuf_buffer, vb); | ||
887 | struct cx18_open_id *id = q->priv_data; | ||
888 | struct cx18 *cx = id->cx; | ||
889 | struct cx18_stream *s = &cx->streams[id->type]; | ||
890 | |||
891 | return cx18_prepare_buffer(q, s, buf, s->pixelformat, | ||
892 | s->vbwidth, s->vbheight, field); | ||
893 | } | ||
894 | |||
895 | static void buffer_release(struct videobuf_queue *q, | ||
896 | struct videobuf_buffer *vb) | ||
897 | { | ||
898 | struct cx18_videobuf_buffer *buf = | ||
899 | container_of(vb, struct cx18_videobuf_buffer, vb); | ||
900 | struct cx18_open_id *id = q->priv_data; | ||
901 | struct cx18 *cx = id->cx; | ||
902 | struct cx18_stream *s = &cx->streams[id->type]; | ||
903 | |||
904 | cx18_dma_free(q, s, buf); | ||
905 | } | ||
906 | |||
907 | static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
908 | { | ||
909 | struct cx18_videobuf_buffer *buf = | ||
910 | container_of(vb, struct cx18_videobuf_buffer, vb); | ||
911 | struct cx18_open_id *id = q->priv_data; | ||
912 | struct cx18 *cx = id->cx; | ||
913 | struct cx18_stream *s = &cx->streams[id->type]; | ||
914 | |||
915 | buf->vb.state = VIDEOBUF_QUEUED; | ||
916 | |||
917 | list_add_tail(&buf->vb.queue, &s->vb_capture); | ||
918 | } | ||
919 | |||
920 | static struct videobuf_queue_ops cx18_videobuf_qops = { | ||
921 | .buf_setup = buffer_setup, | ||
922 | .buf_prepare = buffer_prepare, | ||
923 | .buf_queue = buffer_queue, | ||
924 | .buf_release = buffer_release, | ||
925 | }; | ||
926 | |||
725 | static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) | 927 | static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) |
726 | { | 928 | { |
727 | struct cx18 *cx = s->cx; | 929 | struct cx18 *cx = s->cx; |
@@ -740,6 +942,9 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) | |||
740 | item->cx = cx; | 942 | item->cx = cx; |
741 | item->type = s->type; | 943 | item->type = s->type; |
742 | 944 | ||
945 | spin_lock_init(&item->s_lock); | ||
946 | item->vb_type = 0; | ||
947 | |||
743 | item->open_id = cx->open_id++; | 948 | item->open_id = cx->open_id++; |
744 | filp->private_data = &item->fh; | 949 | filp->private_data = &item->fh; |
745 | 950 | ||
@@ -774,6 +979,15 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) | |||
774 | /* Done! Unmute and continue. */ | 979 | /* Done! Unmute and continue. */ |
775 | cx18_unmute(cx); | 980 | cx18_unmute(cx); |
776 | } | 981 | } |
982 | if (item->type == CX18_ENC_STREAM_TYPE_YUV) { | ||
983 | item->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
984 | videobuf_queue_vmalloc_init(&item->vbuf_q, &cx18_videobuf_qops, | ||
985 | &cx->pci_dev->dev, &item->s_lock, | ||
986 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
987 | V4L2_FIELD_INTERLACED, | ||
988 | sizeof(struct cx18_videobuf_buffer), | ||
989 | item, &cx->serialize_lock); | ||
990 | } | ||
777 | v4l2_fh_add(&item->fh); | 991 | v4l2_fh_add(&item->fh); |
778 | return 0; | 992 | return 0; |
779 | } | 993 | } |
diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h index 5c8fcb884f0a..b9e5110ad043 100644 --- a/drivers/media/video/cx18/cx18-fileops.h +++ b/drivers/media/video/cx18/cx18-fileops.h | |||
@@ -33,6 +33,8 @@ int cx18_start_capture(struct cx18_open_id *id); | |||
33 | void cx18_stop_capture(struct cx18_open_id *id, int gop_end); | 33 | void cx18_stop_capture(struct cx18_open_id *id, int gop_end); |
34 | void cx18_mute(struct cx18 *cx); | 34 | void cx18_mute(struct cx18 *cx); |
35 | void cx18_unmute(struct cx18 *cx); | 35 | void cx18_unmute(struct cx18 *cx); |
36 | int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma); | ||
37 | void cx18_vb_timeout(unsigned long data); | ||
36 | 38 | ||
37 | /* Shared with cx18-alsa module */ | 39 | /* Shared with cx18-alsa module */ |
38 | int cx18_claim_stream(struct cx18_open_id *id, int type); | 40 | int cx18_claim_stream(struct cx18_open_id *id, int type); |
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 4f041c033c54..777d7265c8a8 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c | |||
@@ -41,6 +41,18 @@ | |||
41 | #include <media/tveeprom.h> | 41 | #include <media/tveeprom.h> |
42 | #include <media/v4l2-chip-ident.h> | 42 | #include <media/v4l2-chip-ident.h> |
43 | 43 | ||
44 | static struct v4l2_fmtdesc formats[] = { | ||
45 | { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, | ||
46 | "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } | ||
47 | }, | ||
48 | { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, | ||
49 | "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 } | ||
50 | }, | ||
51 | { 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, | ||
52 | "YUYV 4:2:2", V4L2_PIX_FMT_YUYV, { 0, 0, 0, 0 } | ||
53 | }, | ||
54 | }; | ||
55 | |||
44 | u16 cx18_service2vbi(int type) | 56 | u16 cx18_service2vbi(int type) |
45 | { | 57 | { |
46 | switch (type) { | 58 | switch (type) { |
@@ -150,6 +162,7 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh, | |||
150 | { | 162 | { |
151 | struct cx18_open_id *id = fh2id(fh); | 163 | struct cx18_open_id *id = fh2id(fh); |
152 | struct cx18 *cx = id->cx; | 164 | struct cx18 *cx = id->cx; |
165 | struct cx18_stream *s = &cx->streams[id->type]; | ||
153 | struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; | 166 | struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; |
154 | 167 | ||
155 | pixfmt->width = cx->cxhdl.width; | 168 | pixfmt->width = cx->cxhdl.width; |
@@ -158,7 +171,7 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh, | |||
158 | pixfmt->field = V4L2_FIELD_INTERLACED; | 171 | pixfmt->field = V4L2_FIELD_INTERLACED; |
159 | pixfmt->priv = 0; | 172 | pixfmt->priv = 0; |
160 | if (id->type == CX18_ENC_STREAM_TYPE_YUV) { | 173 | if (id->type == CX18_ENC_STREAM_TYPE_YUV) { |
161 | pixfmt->pixelformat = V4L2_PIX_FMT_HM12; | 174 | pixfmt->pixelformat = s->pixelformat; |
162 | /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */ | 175 | /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */ |
163 | pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2; | 176 | pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2; |
164 | pixfmt->bytesperline = 720; | 177 | pixfmt->bytesperline = 720; |
@@ -237,7 +250,6 @@ static int cx18_try_fmt_vid_cap(struct file *file, void *fh, | |||
237 | h = min(h, cx->is_50hz ? 576 : 480); | 250 | h = min(h, cx->is_50hz ? 576 : 480); |
238 | h = max(h, min_h); | 251 | h = max(h, min_h); |
239 | 252 | ||
240 | cx18_g_fmt_vid_cap(file, fh, fmt); | ||
241 | fmt->fmt.pix.width = w; | 253 | fmt->fmt.pix.width = w; |
242 | fmt->fmt.pix.height = h; | 254 | fmt->fmt.pix.height = h; |
243 | return 0; | 255 | return 0; |
@@ -274,6 +286,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, | |||
274 | struct cx18_open_id *id = fh2id(fh); | 286 | struct cx18_open_id *id = fh2id(fh); |
275 | struct cx18 *cx = id->cx; | 287 | struct cx18 *cx = id->cx; |
276 | struct v4l2_mbus_framefmt mbus_fmt; | 288 | struct v4l2_mbus_framefmt mbus_fmt; |
289 | struct cx18_stream *s = &cx->streams[id->type]; | ||
277 | int ret; | 290 | int ret; |
278 | int w, h; | 291 | int w, h; |
279 | 292 | ||
@@ -283,6 +296,10 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, | |||
283 | w = fmt->fmt.pix.width; | 296 | w = fmt->fmt.pix.width; |
284 | h = fmt->fmt.pix.height; | 297 | h = fmt->fmt.pix.height; |
285 | 298 | ||
299 | s->pixelformat = fmt->fmt.pix.pixelformat; | ||
300 | s->vbheight = h; | ||
301 | s->vbwidth = w; | ||
302 | |||
286 | if (cx->cxhdl.width == w && cx->cxhdl.height == h) | 303 | if (cx->cxhdl.width == w && cx->cxhdl.height == h) |
287 | return 0; | 304 | return 0; |
288 | 305 | ||
@@ -540,16 +557,7 @@ static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) | |||
540 | static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, | 557 | static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, |
541 | struct v4l2_fmtdesc *fmt) | 558 | struct v4l2_fmtdesc *fmt) |
542 | { | 559 | { |
543 | static struct v4l2_fmtdesc formats[] = { | 560 | if (fmt->index > ARRAY_SIZE(formats) - 1) |
544 | { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, | ||
545 | "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } | ||
546 | }, | ||
547 | { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, | ||
548 | "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 } | ||
549 | } | ||
550 | }; | ||
551 | |||
552 | if (fmt->index > 1) | ||
553 | return -EINVAL; | 561 | return -EINVAL; |
554 | *fmt = formats[fmt->index]; | 562 | *fmt = formats[fmt->index]; |
555 | return 0; | 563 | return 0; |
@@ -863,6 +871,104 @@ static int cx18_g_enc_index(struct file *file, void *fh, | |||
863 | return 0; | 871 | return 0; |
864 | } | 872 | } |
865 | 873 | ||
874 | static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id) | ||
875 | { | ||
876 | struct videobuf_queue *q = NULL; | ||
877 | |||
878 | switch (id->vb_type) { | ||
879 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
880 | q = &id->vbuf_q; | ||
881 | break; | ||
882 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
883 | break; | ||
884 | default: | ||
885 | break; | ||
886 | } | ||
887 | return q; | ||
888 | } | ||
889 | |||
890 | static int cx18_streamon(struct file *file, void *priv, | ||
891 | enum v4l2_buf_type type) | ||
892 | { | ||
893 | struct cx18_open_id *id = file->private_data; | ||
894 | struct cx18 *cx = id->cx; | ||
895 | struct cx18_stream *s = &cx->streams[id->type]; | ||
896 | |||
897 | /* Start the hardware only if we're the video device */ | ||
898 | if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
899 | (id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
900 | return -EINVAL; | ||
901 | |||
902 | if (id->type != CX18_ENC_STREAM_TYPE_YUV) | ||
903 | return -EINVAL; | ||
904 | |||
905 | /* Establish a buffer timeout */ | ||
906 | mod_timer(&s->vb_timeout, jiffies + (HZ * 2)); | ||
907 | |||
908 | return videobuf_streamon(cx18_vb_queue(id)); | ||
909 | } | ||
910 | |||
911 | static int cx18_streamoff(struct file *file, void *priv, | ||
912 | enum v4l2_buf_type type) | ||
913 | { | ||
914 | struct cx18_open_id *id = file->private_data; | ||
915 | |||
916 | /* Start the hardware only if we're the video device */ | ||
917 | if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
918 | (id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
919 | return -EINVAL; | ||
920 | |||
921 | if (id->type != CX18_ENC_STREAM_TYPE_YUV) | ||
922 | return -EINVAL; | ||
923 | |||
924 | return videobuf_streamoff(cx18_vb_queue(id)); | ||
925 | } | ||
926 | |||
927 | static int cx18_reqbufs(struct file *file, void *priv, | ||
928 | struct v4l2_requestbuffers *rb) | ||
929 | { | ||
930 | struct cx18_open_id *id = file->private_data; | ||
931 | |||
932 | if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
933 | (id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
934 | return -EINVAL; | ||
935 | |||
936 | return videobuf_reqbufs(cx18_vb_queue(id), rb); | ||
937 | } | ||
938 | |||
939 | static int cx18_querybuf(struct file *file, void *priv, | ||
940 | struct v4l2_buffer *b) | ||
941 | { | ||
942 | struct cx18_open_id *id = file->private_data; | ||
943 | |||
944 | if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
945 | (id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
946 | return -EINVAL; | ||
947 | |||
948 | return videobuf_querybuf(cx18_vb_queue(id), b); | ||
949 | } | ||
950 | |||
951 | static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) | ||
952 | { | ||
953 | struct cx18_open_id *id = file->private_data; | ||
954 | |||
955 | if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
956 | (id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
957 | return -EINVAL; | ||
958 | |||
959 | return videobuf_qbuf(cx18_vb_queue(id), b); | ||
960 | } | ||
961 | |||
962 | static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) | ||
963 | { | ||
964 | struct cx18_open_id *id = file->private_data; | ||
965 | if ((id->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
966 | (id->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
967 | return -EINVAL; | ||
968 | |||
969 | return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK); | ||
970 | } | ||
971 | |||
866 | static int cx18_encoder_cmd(struct file *file, void *fh, | 972 | static int cx18_encoder_cmd(struct file *file, void *fh, |
867 | struct v4l2_encoder_cmd *enc) | 973 | struct v4l2_encoder_cmd *enc) |
868 | { | 974 | { |
@@ -1081,6 +1187,12 @@ static const struct v4l2_ioctl_ops cx18_ioctl_ops = { | |||
1081 | .vidioc_s_register = cx18_s_register, | 1187 | .vidioc_s_register = cx18_s_register, |
1082 | #endif | 1188 | #endif |
1083 | .vidioc_default = cx18_default, | 1189 | .vidioc_default = cx18_default, |
1190 | .vidioc_streamon = cx18_streamon, | ||
1191 | .vidioc_streamoff = cx18_streamoff, | ||
1192 | .vidioc_reqbufs = cx18_reqbufs, | ||
1193 | .vidioc_querybuf = cx18_querybuf, | ||
1194 | .vidioc_qbuf = cx18_qbuf, | ||
1195 | .vidioc_dqbuf = cx18_dqbuf, | ||
1084 | }; | 1196 | }; |
1085 | 1197 | ||
1086 | void cx18_set_funcs(struct video_device *vdev) | 1198 | void cx18_set_funcs(struct video_device *vdev) |
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 9605d54bd083..d4d88738d893 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c | |||
@@ -81,6 +81,7 @@ static const struct cx18_api_info api_info[] = { | |||
81 | API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0), | 81 | API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0), |
82 | API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0), | 82 | API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0), |
83 | API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), | 83 | API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), |
84 | API_ENTRY(CPU, CX18_CPU_SET_VFC_PARAM, 0), | ||
84 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), | 85 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), |
85 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), | 86 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), |
86 | API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), | 87 | API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), |
@@ -158,6 +159,72 @@ static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl) | |||
158 | } | 159 | } |
159 | } | 160 | } |
160 | 161 | ||
162 | static void cx18_mdl_send_to_videobuf(struct cx18_stream *s, | ||
163 | struct cx18_mdl *mdl) | ||
164 | { | ||
165 | struct cx18_videobuf_buffer *vb_buf; | ||
166 | struct cx18_buffer *buf; | ||
167 | u8 *p, u; | ||
168 | u32 offset = 0; | ||
169 | int dispatch = 0; | ||
170 | int i; | ||
171 | |||
172 | if (mdl->bytesused == 0) | ||
173 | return; | ||
174 | |||
175 | /* Acquire a videobuf buffer, clone to and and release it */ | ||
176 | spin_lock(&s->vb_lock); | ||
177 | if (list_empty(&s->vb_capture)) | ||
178 | goto out; | ||
179 | |||
180 | vb_buf = list_entry(s->vb_capture.next, struct cx18_videobuf_buffer, | ||
181 | vb.queue); | ||
182 | |||
183 | p = videobuf_to_vmalloc(&vb_buf->vb); | ||
184 | if (!p) | ||
185 | goto out; | ||
186 | |||
187 | offset = vb_buf->bytes_used; | ||
188 | list_for_each_entry(buf, &mdl->buf_list, list) { | ||
189 | if (buf->bytesused == 0) | ||
190 | break; | ||
191 | |||
192 | if ((offset + buf->bytesused) <= vb_buf->vb.bsize) { | ||
193 | memcpy(p + offset, buf->buf, buf->bytesused); | ||
194 | offset += buf->bytesused; | ||
195 | vb_buf->bytes_used += buf->bytesused; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /* If we've filled the buffer as per the callers res then dispatch it */ | ||
200 | if (vb_buf->bytes_used >= (vb_buf->vb.width * vb_buf->vb.height * 2)) { | ||
201 | dispatch = 1; | ||
202 | vb_buf->bytes_used = 0; | ||
203 | } | ||
204 | |||
205 | /* */ | ||
206 | if (dispatch) { | ||
207 | |||
208 | if (s->pixelformat == V4L2_PIX_FMT_YUYV) { | ||
209 | /* UYVY to YUYV */ | ||
210 | for (i = 0; i < (720 * 480 * 2); i += 2) { | ||
211 | u = *(p + i); | ||
212 | *(p + i) = *(p + i + 1); | ||
213 | *(p + i + 1) = u; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | do_gettimeofday(&vb_buf->vb.ts); | ||
218 | list_del(&vb_buf->vb.queue); | ||
219 | vb_buf->vb.state = VIDEOBUF_DONE; | ||
220 | wake_up(&vb_buf->vb.done); | ||
221 | } | ||
222 | |||
223 | mod_timer(&s->vb_timeout, jiffies + (HZ / 10)); | ||
224 | |||
225 | out: | ||
226 | spin_unlock(&s->vb_lock); | ||
227 | } | ||
161 | 228 | ||
162 | static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s, | 229 | static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s, |
163 | struct cx18_mdl *mdl) | 230 | struct cx18_mdl *mdl) |
@@ -263,6 +330,9 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) | |||
263 | } else { | 330 | } else { |
264 | cx18_enqueue(s, mdl, &s->q_full); | 331 | cx18_enqueue(s, mdl, &s->q_full); |
265 | } | 332 | } |
333 | } else if (s->type == CX18_ENC_STREAM_TYPE_YUV) { | ||
334 | cx18_mdl_send_to_videobuf(s, mdl); | ||
335 | cx18_enqueue(s, mdl, &s->q_free); | ||
266 | } else { | 336 | } else { |
267 | cx18_enqueue(s, mdl, &s->q_full); | 337 | cx18_enqueue(s, mdl, &s->q_full); |
268 | if (s->type == CX18_ENC_STREAM_TYPE_IDX) | 338 | if (s->type == CX18_ENC_STREAM_TYPE_IDX) |
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 6fbc356113c1..eeb455a8b726 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c | |||
@@ -44,6 +44,7 @@ static struct v4l2_file_operations cx18_v4l2_enc_fops = { | |||
44 | .unlocked_ioctl = cx18_v4l2_ioctl, | 44 | .unlocked_ioctl = cx18_v4l2_ioctl, |
45 | .release = cx18_v4l2_close, | 45 | .release = cx18_v4l2_close, |
46 | .poll = cx18_v4l2_enc_poll, | 46 | .poll = cx18_v4l2_enc_poll, |
47 | .mmap = cx18_v4l2_mmap, | ||
47 | }; | 48 | }; |
48 | 49 | ||
49 | /* offset from 0 to register ts v4l2 minors on */ | 50 | /* offset from 0 to register ts v4l2 minors on */ |
@@ -132,6 +133,15 @@ static void cx18_stream_init(struct cx18 *cx, int type) | |||
132 | cx18_queue_init(&s->q_idle); | 133 | cx18_queue_init(&s->q_idle); |
133 | 134 | ||
134 | INIT_WORK(&s->out_work_order, cx18_out_work_handler); | 135 | INIT_WORK(&s->out_work_order, cx18_out_work_handler); |
136 | |||
137 | INIT_LIST_HEAD(&s->vb_capture); | ||
138 | s->vb_timeout.function = cx18_vb_timeout; | ||
139 | s->vb_timeout.data = (unsigned long)s; | ||
140 | init_timer(&s->vb_timeout); | ||
141 | spin_lock_init(&s->vb_lock); | ||
142 | |||
143 | /* Assume the previous pixel default */ | ||
144 | s->pixelformat = V4L2_PIX_FMT_HM12; | ||
135 | } | 145 | } |
136 | 146 | ||
137 | static int cx18_prep_dev(struct cx18 *cx, int type) | 147 | static int cx18_prep_dev(struct cx18 *cx, int type) |
@@ -729,6 +739,19 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | |||
729 | test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) | 739 | test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) |
730 | cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, | 740 | cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, |
731 | (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1); | 741 | (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1); |
742 | |||
743 | /* Enable the Video Format Converter for UYVY 4:2:2 support, | ||
744 | * rather than the default HM12 Macroblovk 4:2:0 support. | ||
745 | */ | ||
746 | if (captype == CAPTURE_CHANNEL_TYPE_YUV) { | ||
747 | if (s->pixelformat == V4L2_PIX_FMT_YUYV) | ||
748 | cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, | ||
749 | s->handle, 1); | ||
750 | else | ||
751 | /* If in doubt, default to HM12 */ | ||
752 | cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, | ||
753 | s->handle, 0); | ||
754 | } | ||
732 | } | 755 | } |
733 | 756 | ||
734 | if (atomic_read(&cx->tot_capturing) == 0) { | 757 | if (atomic_read(&cx->tot_capturing) == 0) { |
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 935f557acbd0..767a8d23e3f2 100644 --- a/drivers/media/video/cx18/cx23418.h +++ b/drivers/media/video/cx18/cx23418.h | |||
@@ -342,6 +342,12 @@ | |||
342 | ReturnCode */ | 342 | ReturnCode */ |
343 | #define CX18_CPU_GET_ENC_PTS (CPU_CMD_MASK_CAPTURE | 0x0022) | 343 | #define CX18_CPU_GET_ENC_PTS (CPU_CMD_MASK_CAPTURE | 0x0022) |
344 | 344 | ||
345 | /* Description: Set VFC parameters | ||
346 | IN[0] - task handle | ||
347 | IN[1] - VFC enable flag, 1 - enable, 0 - disable | ||
348 | */ | ||
349 | #define CX18_CPU_SET_VFC_PARAM (CPU_CMD_MASK_CAPTURE | 0x0023) | ||
350 | |||
345 | /* Below is the list of commands related to the data exchange */ | 351 | /* Below is the list of commands related to the data exchange */ |
346 | #define CPU_CMD_MASK_DE (CPU_CMD_MASK | 0x040000) | 352 | #define CPU_CMD_MASK_DE (CPU_CMD_MASK | 0x040000) |
347 | 353 | ||