diff options
author | Andy Walls <awalls@radix.net> | 2009-12-31 20:09:51 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-02-26 13:10:30 -0500 |
commit | 82acdc84cc4acc11389bdc648b23b15426d2038c (patch) | |
tree | 17f9bc1f4f20611a17f6d1063270e44fc932de2c /drivers/media | |
parent | ef99179710d6ec04d6783afdf8387523c7087920 (diff) |
V4L/DVB (13908): cx18: Add initial working VIDIOC_G_ENC_INDEX ioctl() support
VIDIOC_G_ENC_INDEX support see the light of day.
Some notes:
1. With default capture parameters, the CX23418 seems to transfer 192 index
entries (4.5 kB worth) at 10 second intervals.
2. Index streams don't seem to be supported for MPEG 2 TS streams
3. The index entries seem to claim every frame is a B-Frame. Possible
firmware bug.
4. The cx18 driver does not try to capture an index stream when inserting
sliced VBI into the MPEg stream as the offsets would need fixup.
Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/cx18/cx18-driver.h | 12 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-fileops.c | 12 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-ioctl.c | 135 |
3 files changed, 146 insertions, 13 deletions
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index ff3426206765..930bab647467 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h | |||
@@ -288,6 +288,18 @@ struct cx18_options { | |||
288 | #define CX18_SLICED_TYPE_WSS_625 (5) | 288 | #define CX18_SLICED_TYPE_WSS_625 (5) |
289 | #define CX18_SLICED_TYPE_VPS (7) | 289 | #define CX18_SLICED_TYPE_VPS (7) |
290 | 290 | ||
291 | /** | ||
292 | * list_entry_is_past_end - check if a previous loop cursor is off list end | ||
293 | * @pos: the type * previously used as a loop cursor. | ||
294 | * @head: the head for your list. | ||
295 | * @member: the name of the list_struct within the struct. | ||
296 | * | ||
297 | * Check if the entry's list_head is the head of the list, thus it's not a | ||
298 | * real entry but was the loop cursor that walked past the end | ||
299 | */ | ||
300 | #define list_entry_is_past_end(pos, head, member) \ | ||
301 | (&pos->member == (head)) | ||
302 | |||
291 | struct cx18_buffer { | 303 | struct cx18_buffer { |
292 | struct list_head list; | 304 | struct list_head list; |
293 | dma_addr_t dma_handle; | 305 | dma_addr_t dma_handle; |
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index b1ad03f61019..d522d84906e1 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c | |||
@@ -392,18 +392,6 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, | |||
392 | return len; | 392 | return len; |
393 | } | 393 | } |
394 | 394 | ||
395 | /** | ||
396 | * list_entry_is_past_end - check if a previous loop cursor is off list end | ||
397 | * @pos: the type * previously used as a loop cursor. | ||
398 | * @head: the head for your list. | ||
399 | * @member: the name of the list_struct within the struct. | ||
400 | * | ||
401 | * Check if the entry's list_head is the head of the list, thus it's not a | ||
402 | * real entry but was the loop cursor that walked past the end | ||
403 | */ | ||
404 | #define list_entry_is_past_end(pos, head, member) \ | ||
405 | (&pos->member == (head)) | ||
406 | |||
407 | static size_t cx18_copy_mdl_to_user(struct cx18_stream *s, | 395 | static size_t cx18_copy_mdl_to_user(struct cx18_stream *s, |
408 | struct cx18_mdl *mdl, char __user *ubuf, size_t ucount) | 396 | struct cx18_mdl *mdl, char __user *ubuf, size_t ucount) |
409 | { | 397 | { |
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 3e4fc192fdec..b81dd0ea8eb9 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c | |||
@@ -775,10 +775,143 @@ static int cx18_g_sliced_vbi_cap(struct file *file, void *fh, | |||
775 | return 0; | 775 | return 0; |
776 | } | 776 | } |
777 | 777 | ||
778 | static int _cx18_process_idx_data(struct cx18_buffer *buf, | ||
779 | struct v4l2_enc_idx *idx) | ||
780 | { | ||
781 | int consumed, remaining; | ||
782 | struct v4l2_enc_idx_entry *e_idx; | ||
783 | struct cx18_enc_idx_entry *e_buf; | ||
784 | |||
785 | /* Frame type lookup: 1=I, 2=P, 4=B */ | ||
786 | const int mapping[8] = { | ||
787 | -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, | ||
788 | -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1 | ||
789 | }; | ||
790 | |||
791 | /* | ||
792 | * Assumption here is that a buf holds an integral number of | ||
793 | * struct cx18_enc_idx_entry objects and is properly aligned. | ||
794 | * This is enforced by the module options on IDX buffer sizes. | ||
795 | */ | ||
796 | remaining = buf->bytesused - buf->readpos; | ||
797 | consumed = 0; | ||
798 | e_idx = &idx->entry[idx->entries]; | ||
799 | e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos]; | ||
800 | |||
801 | while (remaining >= sizeof(struct cx18_enc_idx_entry) && | ||
802 | idx->entries < V4L2_ENC_IDX_ENTRIES) { | ||
803 | |||
804 | e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32) | ||
805 | | le32_to_cpu(e_buf->offset_low); | ||
806 | |||
807 | e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32) | ||
808 | | le32_to_cpu(e_buf->pts_low); | ||
809 | |||
810 | e_idx->length = le32_to_cpu(e_buf->length); | ||
811 | |||
812 | e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7]; | ||
813 | |||
814 | e_idx->reserved[0] = 0; | ||
815 | e_idx->reserved[1] = 0; | ||
816 | |||
817 | idx->entries++; | ||
818 | e_idx = &idx->entry[idx->entries]; | ||
819 | e_buf++; | ||
820 | |||
821 | remaining -= sizeof(struct cx18_enc_idx_entry); | ||
822 | consumed += sizeof(struct cx18_enc_idx_entry); | ||
823 | } | ||
824 | |||
825 | /* Swallow any partial entries at the end, if there are any */ | ||
826 | if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry)) | ||
827 | consumed += remaining; | ||
828 | |||
829 | buf->readpos += consumed; | ||
830 | return consumed; | ||
831 | } | ||
832 | |||
833 | static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl, | ||
834 | struct v4l2_enc_idx *idx) | ||
835 | { | ||
836 | if (s->type != CX18_ENC_STREAM_TYPE_IDX) | ||
837 | return -EINVAL; | ||
838 | |||
839 | if (mdl->curr_buf == NULL) | ||
840 | mdl->curr_buf = list_first_entry(&mdl->buf_list, | ||
841 | struct cx18_buffer, list); | ||
842 | |||
843 | if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) { | ||
844 | /* | ||
845 | * For some reason we've exhausted the buffers, but the MDL | ||
846 | * object still said some data was unread. | ||
847 | * Fix that and bail out. | ||
848 | */ | ||
849 | mdl->readpos = mdl->bytesused; | ||
850 | return 0; | ||
851 | } | ||
852 | |||
853 | list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) { | ||
854 | |||
855 | /* Skip any empty buffers in the MDL */ | ||
856 | if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused) | ||
857 | continue; | ||
858 | |||
859 | mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx); | ||
860 | |||
861 | /* exit when MDL drained or request satisfied */ | ||
862 | if (idx->entries >= V4L2_ENC_IDX_ENTRIES || | ||
863 | mdl->curr_buf->readpos < mdl->curr_buf->bytesused || | ||
864 | mdl->readpos >= mdl->bytesused) | ||
865 | break; | ||
866 | } | ||
867 | return 0; | ||
868 | } | ||
869 | |||
778 | static int cx18_g_enc_index(struct file *file, void *fh, | 870 | static int cx18_g_enc_index(struct file *file, void *fh, |
779 | struct v4l2_enc_idx *idx) | 871 | struct v4l2_enc_idx *idx) |
780 | { | 872 | { |
781 | return -EINVAL; | 873 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
874 | struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; | ||
875 | s32 tmp; | ||
876 | struct cx18_mdl *mdl; | ||
877 | |||
878 | if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */ | ||
879 | return -EINVAL; | ||
880 | |||
881 | /* Compute the best case number of entries we can buffer */ | ||
882 | tmp = s->buffers - | ||
883 | s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN; | ||
884 | if (tmp <= 0) | ||
885 | tmp = 1; | ||
886 | tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry); | ||
887 | |||
888 | /* Fill out the header of the return structure */ | ||
889 | idx->entries = 0; | ||
890 | idx->entries_cap = tmp; | ||
891 | memset(idx->reserved, 0, sizeof(idx->reserved)); | ||
892 | |||
893 | /* Pull IDX MDLs and buffers from q_full and populate the entries */ | ||
894 | do { | ||
895 | mdl = cx18_dequeue(s, &s->q_full); | ||
896 | if (mdl == NULL) /* No more IDX data right now */ | ||
897 | break; | ||
898 | |||
899 | /* Extract the Index entry data from the MDL and buffers */ | ||
900 | cx18_process_idx_data(s, mdl, idx); | ||
901 | if (mdl->readpos < mdl->bytesused) { | ||
902 | /* We finished with data remaining, push the MDL back */ | ||
903 | cx18_push(s, mdl, &s->q_full); | ||
904 | break; | ||
905 | } | ||
906 | |||
907 | /* We drained this MDL, schedule it to go to the firmware */ | ||
908 | cx18_enqueue(s, mdl, &s->q_free); | ||
909 | |||
910 | } while (idx->entries < V4L2_ENC_IDX_ENTRIES); | ||
911 | |||
912 | /* Tell the work handler to send free IDX MDLs to the firmware */ | ||
913 | cx18_stream_load_fw_queue(s); | ||
914 | return 0; | ||
782 | } | 915 | } |
783 | 916 | ||
784 | static int cx18_encoder_cmd(struct file *file, void *fh, | 917 | static int cx18_encoder_cmd(struct file *file, void *fh, |