From 82acdc84cc4acc11389bdc648b23b15426d2038c Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 31 Dec 2009 22:09:51 -0300 Subject: 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 Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.h | 12 +++ drivers/media/video/cx18/cx18-fileops.c | 12 --- drivers/media/video/cx18/cx18-ioctl.c | 135 +++++++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 13 deletions(-) (limited to 'drivers/media/video/cx18') diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index ff34262..930bab6 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -288,6 +288,18 @@ struct cx18_options { #define CX18_SLICED_TYPE_WSS_625 (5) #define CX18_SLICED_TYPE_VPS (7) +/** + * list_entry_is_past_end - check if a previous loop cursor is off list end + * @pos: the type * previously used as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Check if the entry's list_head is the head of the list, thus it's not a + * real entry but was the loop cursor that walked past the end + */ +#define list_entry_is_past_end(pos, head, member) \ + (&pos->member == (head)) + struct cx18_buffer { struct list_head list; dma_addr_t dma_handle; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index b1ad03f6..d522d84 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, return len; } -/** - * list_entry_is_past_end - check if a previous loop cursor is off list end - * @pos: the type * previously used as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Check if the entry's list_head is the head of the list, thus it's not a - * real entry but was the loop cursor that walked past the end - */ -#define list_entry_is_past_end(pos, head, member) \ - (&pos->member == (head)) - static size_t cx18_copy_mdl_to_user(struct cx18_stream *s, struct cx18_mdl *mdl, char __user *ubuf, size_t ucount) { diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 3e4fc19..b81dd0e 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, return 0; } +static int _cx18_process_idx_data(struct cx18_buffer *buf, + struct v4l2_enc_idx *idx) +{ + int consumed, remaining; + struct v4l2_enc_idx_entry *e_idx; + struct cx18_enc_idx_entry *e_buf; + + /* Frame type lookup: 1=I, 2=P, 4=B */ + const int mapping[8] = { + -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, + -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1 + }; + + /* + * Assumption here is that a buf holds an integral number of + * struct cx18_enc_idx_entry objects and is properly aligned. + * This is enforced by the module options on IDX buffer sizes. + */ + remaining = buf->bytesused - buf->readpos; + consumed = 0; + e_idx = &idx->entry[idx->entries]; + e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos]; + + while (remaining >= sizeof(struct cx18_enc_idx_entry) && + idx->entries < V4L2_ENC_IDX_ENTRIES) { + + e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32) + | le32_to_cpu(e_buf->offset_low); + + e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32) + | le32_to_cpu(e_buf->pts_low); + + e_idx->length = le32_to_cpu(e_buf->length); + + e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7]; + + e_idx->reserved[0] = 0; + e_idx->reserved[1] = 0; + + idx->entries++; + e_idx = &idx->entry[idx->entries]; + e_buf++; + + remaining -= sizeof(struct cx18_enc_idx_entry); + consumed += sizeof(struct cx18_enc_idx_entry); + } + + /* Swallow any partial entries at the end, if there are any */ + if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry)) + consumed += remaining; + + buf->readpos += consumed; + return consumed; +} + +static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl, + struct v4l2_enc_idx *idx) +{ + if (s->type != CX18_ENC_STREAM_TYPE_IDX) + return -EINVAL; + + if (mdl->curr_buf == NULL) + mdl->curr_buf = list_first_entry(&mdl->buf_list, + struct cx18_buffer, list); + + if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) { + /* + * For some reason we've exhausted the buffers, but the MDL + * object still said some data was unread. + * Fix that and bail out. + */ + mdl->readpos = mdl->bytesused; + return 0; + } + + list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) { + + /* Skip any empty buffers in the MDL */ + if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused) + continue; + + mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx); + + /* exit when MDL drained or request satisfied */ + if (idx->entries >= V4L2_ENC_IDX_ENTRIES || + mdl->curr_buf->readpos < mdl->curr_buf->bytesused || + mdl->readpos >= mdl->bytesused) + break; + } + return 0; +} + static int cx18_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx) { - return -EINVAL; + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; + s32 tmp; + struct cx18_mdl *mdl; + + if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */ + return -EINVAL; + + /* Compute the best case number of entries we can buffer */ + tmp = s->buffers - + s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN; + if (tmp <= 0) + tmp = 1; + tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry); + + /* Fill out the header of the return structure */ + idx->entries = 0; + idx->entries_cap = tmp; + memset(idx->reserved, 0, sizeof(idx->reserved)); + + /* Pull IDX MDLs and buffers from q_full and populate the entries */ + do { + mdl = cx18_dequeue(s, &s->q_full); + if (mdl == NULL) /* No more IDX data right now */ + break; + + /* Extract the Index entry data from the MDL and buffers */ + cx18_process_idx_data(s, mdl, idx); + if (mdl->readpos < mdl->bytesused) { + /* We finished with data remaining, push the MDL back */ + cx18_push(s, mdl, &s->q_full); + break; + } + + /* We drained this MDL, schedule it to go to the firmware */ + cx18_enqueue(s, mdl, &s->q_free); + + } while (idx->entries < V4L2_ENC_IDX_ENTRIES); + + /* Tell the work handler to send free IDX MDLs to the firmware */ + cx18_stream_load_fw_queue(s); + return 0; } static int cx18_encoder_cmd(struct file *file, void *fh, -- cgit v1.1