From 1f93daa4570eb74f94fff23dbe9fb4b29af53291 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 16 Oct 2007 08:21:46 +0200 Subject: ivtv: ivtv yuv stream handling change From: Ian Armstrong Currently the yuv output stream buffer is divided into blocks whose size depend on the broadcast standard selected during the driver init phase. However, the standard can be changed after the init phase. This effectively breaks the yuv output stream handler, since it relies on the different yuv planes being block aligned. This patch changes the setup, so that the block size is always the same. The decoder dma function has been modified to cope with the fact that the second yuv plane may no longer be block aligned. The start of the yuv frame must still be at the beginning of a block, so the stream write function has also been modified to ensure this is always true. Also, the stream write function will now initiate a yuv dma transfer as soon as a full frame is ready. It will not wait until the current write request has completed, or the stream buffer becomes full. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ivtv/ivtv-driver.c | 8 ++----- linux/drivers/media/video/ivtv/ivtv-driver.h | 2 ++ linux/drivers/media/video/ivtv/ivtv-fileops.c | 31 +++++++++++++++++++++++---- linux/drivers/media/video/ivtv/ivtv-irq.c | 25 +++++++++++++++------ 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index 0a00c023c..c3afac631 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -974,7 +974,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) { int retval = 0; - int yuv_buf_size; int vbi_buf_size; struct ivtv *itv; @@ -1126,11 +1125,8 @@ static int __devinit ivtv_probe(struct pci_dev *dev, itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000; itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200; itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000; - - /* 0x15180 == 720 * 480 / 4, 0x19500 == 720 * 576 / 4 */ - yuv_buf_size = itv->is_60hz ? 0x15180 : 0x19500; - itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = yuv_buf_size / 2; - itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = yuv_buf_size / 8; + itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = 0x10000; + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = 0x08000; /* Setup VBI Raw Size. Should be big enough to hold PAL. It is possible to switch between PAL and NTSC, so we need to diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.h b/linux/drivers/media/video/ivtv/ivtv-driver.h index b1511cbf4..e2faed6b0 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.h +++ b/linux/drivers/media/video/ivtv/ivtv-driver.h @@ -495,6 +495,8 @@ struct yuv_playback_info void *blanking_ptr; dma_addr_t blanking_dmaptr; + + int stream_size; }; #define IVTV_VBI_FRAMES 32 diff --git a/linux/drivers/media/video/ivtv/ivtv-fileops.c b/linux/drivers/media/video/ivtv/ivtv-fileops.c index a200a8a95..58ad0d9c6 100644 --- a/linux/drivers/media/video/ivtv/ivtv-fileops.c +++ b/linux/drivers/media/video/ivtv/ivtv-fileops.c @@ -542,6 +542,7 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c struct ivtv_open_id *id = filp->private_data; struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; + struct yuv_playback_info *yi = &itv->yuv_info; struct ivtv_buffer *buf; struct ivtv_queue q; int bytes_written = 0; @@ -604,9 +605,16 @@ retry: /* copy user data into buffers */ while ((buf = ivtv_dequeue(s, &q))) { - /* Make sure we really got all the user data */ - rc = ivtv_buf_copy_from_user(s, buf, user_buf, count); + /* yuv is a pain. Don't copy more data than needed for a single + frame, otherwise we lose sync with the incoming stream */ + if (s->type == IVTV_DEC_STREAM_TYPE_YUV && + yi->stream_size + count > itv->dma_data_req_size) + rc = ivtv_buf_copy_from_user(s, buf, user_buf, + itv->dma_data_req_size - yi->stream_size); + else + rc = ivtv_buf_copy_from_user(s, buf, user_buf, count); + /* Make sure we really got all the user data */ if (rc < 0) { ivtv_queue_move(s, &q, NULL, &s->q_free, 0); return rc; @@ -615,6 +623,16 @@ retry: count -= rc; bytes_written += rc; + if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { + yi->stream_size += rc; + /* If we have a complete yuv frame, break loop now */ + if (yi->stream_size == itv->dma_data_req_size) { + ivtv_enqueue(s, buf, &s->q_full); + yi->stream_size = 0; + break; + } + } + if (buf->bytesused != s->buf_size) { /* incomplete, leave in q_io for next time */ ivtv_enqueue(s, buf, &s->q_io); @@ -922,10 +940,15 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) } /* YUV or MPG Decoding Mode? */ - if (s->type == IVTV_DEC_STREAM_TYPE_MPG) + if (s->type == IVTV_DEC_STREAM_TYPE_MPG) { clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); - else if (s->type == IVTV_DEC_STREAM_TYPE_YUV) + } else if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); + /* For yuv, we need to know the dma size before we start */ + itv->dma_data_req_size = + itv->params.width * itv->params.height * 3 / 2; + itv->yuv_info.stream_size = 0; + } return 0; } diff --git a/linux/drivers/media/video/ivtv/ivtv-irq.c b/linux/drivers/media/video/ivtv/ivtv-irq.c index 3db780eb7..353610a97 100644 --- a/linux/drivers/media/video/ivtv/ivtv-irq.c +++ b/linux/drivers/media/video/ivtv/ivtv-irq.c @@ -319,15 +319,28 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); list_for_each_entry(buf, &s->q_predma.list, list) { /* YUV UV Offset from Y Buffer */ - if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) { + if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && + (bytes_written + buf->bytesused) >= y_size) { + s->sg_pending[idx].src = buf->dma_handle; + s->sg_pending[idx].dst = offset; + s->sg_pending[idx].size = y_size - bytes_written; offset = uv_offset; + if (s->sg_pending[idx].size != buf->bytesused) { + idx++; + s->sg_pending[idx].src = + buf->dma_handle + s->sg_pending[idx - 1].size; + s->sg_pending[idx].dst = offset; + s->sg_pending[idx].size = + buf->bytesused - s->sg_pending[idx - 1].size; + offset += s->sg_pending[idx].size; + } y_done = 1; + } else { + s->sg_pending[idx].src = buf->dma_handle; + s->sg_pending[idx].dst = offset; + s->sg_pending[idx].size = buf->bytesused; + offset += buf->bytesused; } - s->sg_pending[idx].src = buf->dma_handle; - s->sg_pending[idx].dst = offset; - s->sg_pending[idx].size = buf->bytesused; - - offset += buf->bytesused; bytes_written += buf->bytesused; /* Sync SG buffers */ -- cgit v1.2.3 From 3917eec08ffe6fe8ba15fc6df349f6d1777b5086 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Oct 2007 19:52:55 +0200 Subject: ivtv: ivtv_yuv_prep_frame breakup and yuv hardware buffer changes From: Ian Armstrong ivtv_yuv_prep_frame is split in smaller code blocks. Modified yuv buffer handling on the PVR350 itself. We now cycle through all 8 hardware buffers. With this patch in place, driver behaviour should remain unchanged from the existing release. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ivtv/ivtv-driver.c | 1 + linux/drivers/media/video/ivtv/ivtv-driver.h | 11 +- linux/drivers/media/video/ivtv/ivtv-irq.c | 31 +++--- linux/drivers/media/video/ivtv/ivtv-yuv.c | 149 ++++++++++++++++----------- linux/drivers/media/video/ivtv/ivtv-yuv.h | 7 +- 5 files changed, 117 insertions(+), 82 deletions(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index c3afac631..194809010 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -701,6 +701,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) atomic_set(&itv->yuv_info.next_dma_frame, -1); itv->yuv_info.lace_mode = ivtv_yuv_mode; itv->yuv_info.lace_threshold = ivtv_yuv_threshold; + itv->yuv_info.max_frames_buffered = 3; return 0; } diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.h b/linux/drivers/media/video/ivtv/ivtv-driver.h index e2faed6b0..36cf07869 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.h +++ b/linux/drivers/media/video/ivtv/ivtv-driver.h @@ -413,6 +413,8 @@ struct yuv_frame_info #define IVTV_YUV_SYNC_ODD 0x04 #define IVTV_YUV_SYNC_MASK 0x04 +#define IVTV_YUV_BUFFERS 8 + struct yuv_playback_info { u32 reg_2834; @@ -485,11 +487,11 @@ struct yuv_playback_info u32 yuv_forced_update; int update_frame; - int sync_field[4]; /* Field to sync on */ - int field_delay[4]; /* Flag to extend duration of previous frame */ + int sync_field[IVTV_YUV_BUFFERS]; /* Field to sync on */ + int field_delay[IVTV_YUV_BUFFERS]; /* Flag to extend duration of previous frame */ u8 fields_lapsed; /* Counter used when delaying a frame */ - struct yuv_frame_info new_frame_info[4]; + struct yuv_frame_info new_frame_info[IVTV_YUV_BUFFERS]; struct yuv_frame_info old_frame_info; struct yuv_frame_info old_frame_info_args; @@ -497,6 +499,9 @@ struct yuv_playback_info dma_addr_t blanking_dmaptr; int stream_size; + + u8 draw_frame; /* PVR350 buffer to draw into */ + u8 max_frames_buffered; /* Maximum number of frames to buffer */ }; #define IVTV_VBI_FRAMES 32 diff --git a/linux/drivers/media/video/ivtv/ivtv-irq.c b/linux/drivers/media/video/ivtv/ivtv-irq.c index 353610a97..cc82ce028 100644 --- a/linux/drivers/media/video/ivtv/ivtv-irq.c +++ b/linux/drivers/media/video/ivtv/ivtv-irq.c @@ -750,24 +750,25 @@ static void ivtv_irq_vsync(struct ivtv *itv) * one vsync per frame. */ unsigned int frame = read_reg(0x28c0) & 1; + struct yuv_playback_info *yi = &itv->yuv_info; int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame); if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n"); - if (((frame ^ itv->yuv_info.sync_field[last_dma_frame]) == 0 && - ((itv->last_vsync_field & 1) ^ itv->yuv_info.sync_field[last_dma_frame])) || - (frame != (itv->last_vsync_field & 1) && !itv->yuv_info.frame_interlaced)) { + if (((frame ^ yi->sync_field[last_dma_frame]) == 0 && + ((itv->last_vsync_field & 1) ^ yi->sync_field[last_dma_frame])) || + (frame != (itv->last_vsync_field & 1) && !yi->frame_interlaced)) { int next_dma_frame = last_dma_frame; - if (!(itv->yuv_info.frame_interlaced && itv->yuv_info.field_delay[next_dma_frame] && itv->yuv_info.fields_lapsed < 1)) { - if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) { + if (!(yi->frame_interlaced && yi->field_delay[next_dma_frame] && yi->fields_lapsed < 1)) { + if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&yi->next_fill_frame)) { write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c); write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); write_reg(yuv_offset[next_dma_frame] >> 4, 0x834); write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838); - next_dma_frame = (next_dma_frame + 1) & 0x3; - atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame); - itv->yuv_info.fields_lapsed = -1; + next_dma_frame = (next_dma_frame + 1) % IVTV_YUV_BUFFERS; + atomic_set(&yi->next_dma_frame, next_dma_frame); + yi->fields_lapsed = -1; } } } @@ -800,20 +801,20 @@ static void ivtv_irq_vsync(struct ivtv *itv) } /* Check if we need to update the yuv registers */ - if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) { - if (!itv->yuv_info.new_frame_info[last_dma_frame].update) + if ((yi->yuv_forced_update || yi->new_frame_info[last_dma_frame].update) && last_dma_frame != -1) { + if (!yi->new_frame_info[last_dma_frame].update) last_dma_frame = (last_dma_frame - 1) & 3; - if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) { - itv->yuv_info.update_frame = last_dma_frame; - itv->yuv_info.new_frame_info[last_dma_frame].update = 0; - itv->yuv_info.yuv_forced_update = 0; + if (yi->new_frame_info[last_dma_frame].src_w) { + yi->update_frame = last_dma_frame; + yi->new_frame_info[last_dma_frame].update = 0; + yi->yuv_forced_update = 0; set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags); set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags); } } - itv->yuv_info.fields_lapsed ++; + yi->fields_lapsed++; } } diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.c b/linux/drivers/media/video/ivtv/ivtv-yuv.c index 9091c4837..15e9bd248 100644 --- a/linux/drivers/media/video/ivtv/ivtv-yuv.c +++ b/linux/drivers/media/video/ivtv/ivtv-yuv.c @@ -22,11 +22,16 @@ #include "ivtv-udma.h" #include "ivtv-yuv.h" -const u32 yuv_offset[4] = { - IVTV_YUV_BUFFER_OFFSET, - IVTV_YUV_BUFFER_OFFSET_1, - IVTV_YUV_BUFFER_OFFSET_2, - IVTV_YUV_BUFFER_OFFSET_3 +/* YUV buffer offsets */ +const u32 yuv_offset[IVTV_YUV_BUFFERS] = { + 0x001a8600, + 0x00240400, + 0x002d8200, + 0x00370000, + 0x00029000, + 0x000C0E00, + 0x006B0400, + 0x00748200 }; static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, @@ -37,10 +42,9 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, int i; int y_pages, uv_pages; - + u8 frame = itv->yuv_info.draw_frame; unsigned long y_buffer_offset, uv_buffer_offset; int y_decode_height, uv_decode_height, y_size; - int frame = atomic_read(&itv->yuv_info.next_fill_frame); y_buffer_offset = IVTV_DECODER_OFFSET + yuv_offset[frame]; uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET; @@ -954,76 +958,105 @@ static void ivtv_yuv_init (struct ivtv *itv) atomic_set(&yi->next_dma_frame, 0); } -int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) +/* Get next available yuv buffer on PVR350 */ +void ivtv_yuv_next_free(struct ivtv *itv) { - DEFINE_WAIT(wait); - int rc = 0; - int got_sig = 0; - int frame, next_fill_frame, last_fill_frame; - int register_update = 0; + int draw, display; + struct yuv_playback_info *yi = &itv->yuv_info; - IVTV_DEBUG_INFO("yuv_prep_frame\n"); + if (atomic_read(&yi->next_dma_frame) == -1) + ivtv_yuv_init(itv); - if (atomic_read(&itv->yuv_info.next_dma_frame) == -1) ivtv_yuv_init(itv); + draw = atomic_read(&yi->next_fill_frame); + display = atomic_read(&yi->next_dma_frame); - frame = atomic_read(&itv->yuv_info.next_fill_frame); - next_fill_frame = (frame + 1) & 0x3; - last_fill_frame = (atomic_read(&itv->yuv_info.next_dma_frame)+1) & 0x3; + if (display > draw) + display -= IVTV_YUV_BUFFERS; - if (next_fill_frame != last_fill_frame && last_fill_frame != frame) { - /* Buffers are full - Overwrite the last frame */ - next_fill_frame = frame; - frame = (frame - 1) & 3; - register_update = itv->yuv_info.new_frame_info[frame].update; - } + if (draw - display >= yi->max_frames_buffered) + draw = (u8)(draw - 1) % IVTV_YUV_BUFFERS; + else + yi->new_frame_info[draw].update = 0; + + yi->draw_frame = draw; +} + +/* Set up frame according to ivtv_dma_frame parameters */ +void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args) +{ + struct yuv_playback_info *yi = &itv->yuv_info; + u8 frame = yi->draw_frame; + + /* Preserve old update flag in case we're overwriting a queued frame */ + int register_update = yi->new_frame_info[frame].update; /* Take a snapshot of the yuv coordinate information */ - itv->yuv_info.new_frame_info[frame].src_x = args->src.left; - itv->yuv_info.new_frame_info[frame].src_y = args->src.top; - itv->yuv_info.new_frame_info[frame].src_w = args->src.width; - itv->yuv_info.new_frame_info[frame].src_h = args->src.height; - itv->yuv_info.new_frame_info[frame].dst_x = args->dst.left; - itv->yuv_info.new_frame_info[frame].dst_y = args->dst.top; - itv->yuv_info.new_frame_info[frame].dst_w = args->dst.width; - itv->yuv_info.new_frame_info[frame].dst_h = args->dst.height; - itv->yuv_info.new_frame_info[frame].tru_x = args->dst.left; - itv->yuv_info.new_frame_info[frame].tru_w = args->src_width; - itv->yuv_info.new_frame_info[frame].tru_h = args->src_height; + yi->new_frame_info[frame].src_x = args->src.left; + yi->new_frame_info[frame].src_y = args->src.top; + yi->new_frame_info[frame].src_w = args->src.width; + yi->new_frame_info[frame].src_h = args->src.height; + yi->new_frame_info[frame].dst_x = args->dst.left; + yi->new_frame_info[frame].dst_y = args->dst.top; + yi->new_frame_info[frame].dst_w = args->dst.width; + yi->new_frame_info[frame].dst_h = args->dst.height; + yi->new_frame_info[frame].tru_x = args->dst.left; + yi->new_frame_info[frame].tru_w = args->src_width; + yi->new_frame_info[frame].tru_h = args->src_height; /* Snapshot field order */ - itv->yuv_info.sync_field[frame] = itv->yuv_info.lace_sync_field; + yi->sync_field[frame] = yi->lace_sync_field; /* Are we going to offset the Y plane */ if (args->src.height + args->src.top < 512-16) - itv->yuv_info.new_frame_info[frame].offset_y = 1; + yi->new_frame_info[frame].offset_y = 1; else - itv->yuv_info.new_frame_info[frame].offset_y = 0; + yi->new_frame_info[frame].offset_y = 0; /* Snapshot the osd pan info */ - itv->yuv_info.new_frame_info[frame].pan_x = itv->yuv_info.osd_x_pan; - itv->yuv_info.new_frame_info[frame].pan_y = itv->yuv_info.osd_y_pan; - itv->yuv_info.new_frame_info[frame].vis_w = itv->yuv_info.osd_vis_w; - itv->yuv_info.new_frame_info[frame].vis_h = itv->yuv_info.osd_vis_h; - - itv->yuv_info.new_frame_info[frame].update = 0; - itv->yuv_info.new_frame_info[frame].interlaced_y = 0; - itv->yuv_info.new_frame_info[frame].interlaced_uv = 0; - itv->yuv_info.new_frame_info[frame].lace_mode = itv->yuv_info.lace_mode; - - if (memcmp (&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], - sizeof (itv->yuv_info.new_frame_info[frame]))) { - memcpy(&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], sizeof (itv->yuv_info.old_frame_info_args)); - itv->yuv_info.new_frame_info[frame].update = 1; -/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */ + yi->new_frame_info[frame].pan_x = yi->osd_x_pan; + yi->new_frame_info[frame].pan_y = yi->osd_y_pan; + yi->new_frame_info[frame].vis_w = yi->osd_vis_w; + yi->new_frame_info[frame].vis_h = yi->osd_vis_h; + + yi->new_frame_info[frame].update = 0; + yi->new_frame_info[frame].interlaced_y = 0; + yi->new_frame_info[frame].interlaced_uv = 0; + yi->new_frame_info[frame].lace_mode = yi->lace_mode; + + if (memcmp(&yi->old_frame_info_args, &yi->new_frame_info[frame], + sizeof(yi->new_frame_info[frame]))) { + yi->old_frame_info_args = yi->new_frame_info[frame]; + yi->new_frame_info[frame].update = 1; +/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */ } - itv->yuv_info.new_frame_info[frame].update |= register_update; + yi->new_frame_info[frame].update |= register_update; /* Should this frame be delayed ? */ - if (itv->yuv_info.sync_field[frame] != itv->yuv_info.sync_field[(frame - 1) & 3]) - itv->yuv_info.field_delay[frame] = 1; + if (yi->sync_field[frame] != + yi->sync_field[(frame - 1) % IVTV_YUV_BUFFERS]) + yi->field_delay[frame] = 1; else - itv->yuv_info.field_delay[frame] = 0; + yi->field_delay[frame] = 0; +} + +/* Frame is complete & ready for display */ +void ivtv_yuv_frame_complete(struct ivtv *itv) +{ + atomic_set(&itv->yuv_info.next_fill_frame, + (itv->yuv_info.draw_frame + 1) % IVTV_YUV_BUFFERS); +} + +int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) +{ + DEFINE_WAIT(wait); + int rc = 0; + int got_sig = 0; + + IVTV_DEBUG_INFO("yuv_prep_frame\n"); + + ivtv_yuv_next_free(itv); + ivtv_yuv_setup_frame(itv, args); /* DMA the frame */ mutex_lock(&itv->udma.lock); @@ -1057,7 +1090,7 @@ int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) return -EINTR; } - atomic_set(&itv->yuv_info.next_fill_frame, next_fill_frame); + ivtv_yuv_frame_complete(itv); mutex_unlock(&itv->udma.lock); return rc; diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.h b/linux/drivers/media/video/ivtv/ivtv-yuv.h index 3b966f0a2..3b290927d 100644 --- a/linux/drivers/media/video/ivtv/ivtv-yuv.h +++ b/linux/drivers/media/video/ivtv/ivtv-yuv.h @@ -21,11 +21,6 @@ #ifndef IVTV_YUV_H #define IVTV_YUV_H -/* Buffers on hardware offsets */ -#define IVTV_YUV_BUFFER_OFFSET 0x001a8600 /* First YUV Buffer */ -#define IVTV_YUV_BUFFER_OFFSET_1 0x00240400 /* Second YUV Buffer */ -#define IVTV_YUV_BUFFER_OFFSET_2 0x002d8200 /* Third YUV Buffer */ -#define IVTV_YUV_BUFFER_OFFSET_3 0x00370000 /* Fourth YUV Buffer */ #define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */ /* Offset to filter table in firmware */ @@ -36,7 +31,7 @@ #define IVTV_YUV_UPDATE_VERTICAL 0x02 #define IVTV_YUV_UPDATE_INVALID 0x04 -extern const u32 yuv_offset[4]; +extern const u32 yuv_offset[IVTV_YUV_BUFFERS]; int ivtv_yuv_filter_check(struct ivtv *itv); int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args); -- cgit v1.2.3 From ab95acf7bb7b3e5cddaabdaa8497ec7fb2a97c99 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 21 Oct 2007 13:09:10 +0200 Subject: ivtv: yuv frame parameter fix From: Ian Armstrong Inadvertently missed a line when converting code to new hardware buffering method. In some circumstances, this would lead to a frame being displayed using parameters belonging to another frame. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ivtv/ivtv-irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-irq.c b/linux/drivers/media/video/ivtv/ivtv-irq.c index cc82ce028..ac174fc0d 100644 --- a/linux/drivers/media/video/ivtv/ivtv-irq.c +++ b/linux/drivers/media/video/ivtv/ivtv-irq.c @@ -803,7 +803,7 @@ static void ivtv_irq_vsync(struct ivtv *itv) /* Check if we need to update the yuv registers */ if ((yi->yuv_forced_update || yi->new_frame_info[last_dma_frame].update) && last_dma_frame != -1) { if (!yi->new_frame_info[last_dma_frame].update) - last_dma_frame = (last_dma_frame - 1) & 3; + last_dma_frame = (u8)(last_dma_frame - 1) % IVTV_YUV_BUFFERS; if (yi->new_frame_info[last_dma_frame].src_w) { yi->update_frame = last_dma_frame; -- cgit v1.2.3 From 8e998e00f380ac98c3ef522b5736072266c79ada Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 21 Oct 2007 13:33:59 +0200 Subject: ivtv: Remove unnecessary register update From: Ian Armstrong To reduce the number of display register accesses, the yuv code keeps track of the current video settings. Should there be a change in any single parameter, it will update the associated display registers to ensure everything is displayed correctly. The existing check also looks at the field order for the video. This is not required, since field reversal does not require any display register changes. This patch removes the field order from the check. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ivtv/ivtv-yuv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.c b/linux/drivers/media/video/ivtv/ivtv-yuv.c index 15e9bd248..a23108fae 100644 --- a/linux/drivers/media/video/ivtv/ivtv-yuv.c +++ b/linux/drivers/media/video/ivtv/ivtv-yuv.c @@ -1021,7 +1021,7 @@ void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args) yi->new_frame_info[frame].update = 0; yi->new_frame_info[frame].interlaced_y = 0; yi->new_frame_info[frame].interlaced_uv = 0; - yi->new_frame_info[frame].lace_mode = yi->lace_mode; + yi->new_frame_info[frame].lace_mode = yi->lace_mode & IVTV_YUV_MODE_MASK; if (memcmp(&yi->old_frame_info_args, &yi->new_frame_info[frame], sizeof(yi->new_frame_info[frame]))) { -- cgit v1.2.3 From 7c15dc889ef55b898fd541cfffd37b7e2badaf92 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 22 Oct 2007 19:24:26 +0200 Subject: ivtv: yuv interlace mode change From: Ian Armstrong Interlace mode selection code moved into the frame setup phase, so it's now run before the frame is loaded into a hardware buffer. Given that it can affect how a new frame is displayed, it was a bit stupid running it after the frame was already visible. A few stray interlace related variables which were linked to individual frames have now been moved into the yuv_frame_info struct. This means that all variables linked to a specific frame are in the same place & not scattered. Minor code reformatting in areas touched by the above changes. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ivtv/ivtv-driver.h | 7 +- linux/drivers/media/video/ivtv/ivtv-irq.c | 19 +- linux/drivers/media/video/ivtv/ivtv-yuv.c | 382 +++++++++++++-------------- 3 files changed, 199 insertions(+), 209 deletions(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.h b/linux/drivers/media/video/ivtv/ivtv-driver.h index 36cf07869..1ad96a665 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.h +++ b/linux/drivers/media/video/ivtv/ivtv-driver.h @@ -402,6 +402,9 @@ struct yuv_frame_info u32 tru_h; u32 offset_y; s32 lace_mode; + u32 sync_field; + u32 delay; + u32 interlaced; }; #define IVTV_YUV_MODE_INTERLACED 0x00 @@ -475,8 +478,6 @@ struct yuv_playback_info int decode_height; - int frame_interlaced; - int lace_mode; int lace_threshold; int lace_sync_field; @@ -487,8 +488,6 @@ struct yuv_playback_info u32 yuv_forced_update; int update_frame; - int sync_field[IVTV_YUV_BUFFERS]; /* Field to sync on */ - int field_delay[IVTV_YUV_BUFFERS]; /* Flag to extend duration of previous frame */ u8 fields_lapsed; /* Counter used when delaying a frame */ struct yuv_frame_info new_frame_info[IVTV_YUV_BUFFERS]; diff --git a/linux/drivers/media/video/ivtv/ivtv-irq.c b/linux/drivers/media/video/ivtv/ivtv-irq.c index ac174fc0d..88fcb6517 100644 --- a/linux/drivers/media/video/ivtv/ivtv-irq.c +++ b/linux/drivers/media/video/ivtv/ivtv-irq.c @@ -752,15 +752,16 @@ static void ivtv_irq_vsync(struct ivtv *itv) unsigned int frame = read_reg(0x28c0) & 1; struct yuv_playback_info *yi = &itv->yuv_info; int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame); + struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame]; if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n"); - if (((frame ^ yi->sync_field[last_dma_frame]) == 0 && - ((itv->last_vsync_field & 1) ^ yi->sync_field[last_dma_frame])) || - (frame != (itv->last_vsync_field & 1) && !yi->frame_interlaced)) { + if (((frame ^ f->sync_field) == 0 && + ((itv->last_vsync_field & 1) ^ f->sync_field)) || + (frame != (itv->last_vsync_field & 1) && !f->interlaced)) { int next_dma_frame = last_dma_frame; - if (!(yi->frame_interlaced && yi->field_delay[next_dma_frame] && yi->fields_lapsed < 1)) { + if (!(f->interlaced && f->delay && yi->fields_lapsed < 1)) { if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&yi->next_fill_frame)) { write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c); write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); @@ -801,13 +802,15 @@ static void ivtv_irq_vsync(struct ivtv *itv) } /* Check if we need to update the yuv registers */ - if ((yi->yuv_forced_update || yi->new_frame_info[last_dma_frame].update) && last_dma_frame != -1) { - if (!yi->new_frame_info[last_dma_frame].update) + if ((yi->yuv_forced_update || f->update) && last_dma_frame != -1) { + if (!f->update) { last_dma_frame = (u8)(last_dma_frame - 1) % IVTV_YUV_BUFFERS; + f = &yi->new_frame_info[last_dma_frame]; + } - if (yi->new_frame_info[last_dma_frame].src_w) { + if (f->src_w) { yi->update_frame = last_dma_frame; - yi->new_frame_info[last_dma_frame].update = 0; + f->update = 0; yi->yuv_forced_update = 0; set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags); set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags); diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.c b/linux/drivers/media/video/ivtv/ivtv-yuv.c index a23108fae..cd42db9b5 100644 --- a/linux/drivers/media/video/ivtv/ivtv-yuv.c +++ b/linux/drivers/media/video/ivtv/ivtv-yuv.c @@ -39,19 +39,20 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, { struct ivtv_dma_page_info y_dma; struct ivtv_dma_page_info uv_dma; - + struct yuv_playback_info *yi = &itv->yuv_info; + u8 frame = yi->draw_frame; + struct yuv_frame_info *f = &yi->new_frame_info[frame]; int i; int y_pages, uv_pages; - u8 frame = itv->yuv_info.draw_frame; unsigned long y_buffer_offset, uv_buffer_offset; int y_decode_height, uv_decode_height, y_size; y_buffer_offset = IVTV_DECODER_OFFSET + yuv_offset[frame]; uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET; - y_decode_height = uv_decode_height = args->src.height + args->src.top; + y_decode_height = uv_decode_height = f->src_h + f->src_x; - if (y_decode_height < 512-16) + if (f->offset_y) y_buffer_offset += 720 * 16; if (y_decode_height & 15) @@ -106,13 +107,11 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, ivtv_udma_fill_sg_array (dma, y_buffer_offset, uv_buffer_offset, y_size); /* If we've offset the y plane, ensure top area is blanked */ - if (args->src.height + args->src.top < 512-16) { - if (itv->yuv_info.blanking_dmaptr) { - dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16); - dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr); - dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]); - dma->SG_length++; - } + if (f->offset_y && itv->yuv_info.blanking_dmaptr) { + dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16); + dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr); + dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]); + dma->SG_length++; } /* Tag SG Array with Interrupt Bit */ @@ -387,7 +386,7 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi } /* What is the source video being treated as... */ - if (itv->yuv_info.frame_interlaced) { + if (window->interlaced) { IVTV_DEBUG_WARN("Source video: Interlaced\n"); } else { @@ -631,192 +630,142 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi } /* Modify the supplied coordinate information to fit the visible osd area */ -static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *window) +static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f) { - int osd_crop, lace_threshold; + struct yuv_frame_info *of = &itv->yuv_info.old_frame_info; + int osd_crop; u32 osd_scale; u32 yuv_update = 0; - lace_threshold = itv->yuv_info.lace_threshold; - if (lace_threshold < 0) - lace_threshold = itv->yuv_info.decode_height - 1; - - /* Work out the lace settings */ - switch (itv->yuv_info.lace_mode) { - case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */ - itv->yuv_info.frame_interlaced = 0; - if (window->tru_h < 512 || (window->tru_h > 576 && window->tru_h < 1021)) - window->interlaced_y = 0; - else - window->interlaced_y = 1; - - if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2)) - window->interlaced_uv = 0; - else - window->interlaced_uv = 1; - break; - - case IVTV_YUV_MODE_AUTO: - if (window->tru_h <= lace_threshold || window->tru_h > 576 || window->tru_w > 720){ - itv->yuv_info.frame_interlaced = 0; - if ((window->tru_h < 512) || - (window->tru_h > 576 && window->tru_h < 1021) || - (window->tru_w > 720 && window->tru_h < 1021)) - window->interlaced_y = 0; - else - window->interlaced_y = 1; - - if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2)) - window->interlaced_uv = 0; - else - window->interlaced_uv = 1; - } - else { - itv->yuv_info.frame_interlaced = 1; - window->interlaced_y = 1; - window->interlaced_uv = 1; - } - break; - - case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */ - default: - itv->yuv_info.frame_interlaced = 1; - window->interlaced_y = 1; - window->interlaced_uv = 1; - break; - } - /* Sorry, but no negative coords for src */ - if (window->src_x < 0) window->src_x = 0; - if (window->src_y < 0) window->src_y = 0; + if (f->src_x < 0) + f->src_x = 0; + if (f->src_y < 0) + f->src_y = 0; /* Can only reduce width down to 1/4 original size */ - if ((osd_crop = window->src_w - ( 4 * window->dst_w )) > 0) { - window->src_x += osd_crop / 2; - window->src_w = (window->src_w - osd_crop) & ~3; - window->dst_w = window->src_w / 4; - window->dst_w += window->dst_w & 1; + if ((osd_crop = f->src_w - 4 * f->dst_w) > 0) { + f->src_x += osd_crop / 2; + f->src_w = (f->src_w - osd_crop) & ~3; + f->dst_w = f->src_w / 4; + f->dst_w += f->dst_w & 1; } /* Can only reduce height down to 1/4 original size */ - if (window->src_h / window->dst_h >= 2) { - /* Overflow may be because we're running progressive, so force mode switch */ - window->interlaced_y = 1; + if (f->src_h / f->dst_h >= 2) { + /* Overflow may be because we're running progressive, + so force mode switch */ + f->interlaced_y = 1; /* Make sure we're still within limits for interlace */ - if ((osd_crop = window->src_h - ( 4 * window->dst_h )) > 0) { + if ((osd_crop = f->src_h - 4 * f->dst_h) > 0) { /* If we reach here we'll have to force the height. */ - window->src_y += osd_crop / 2; - window->src_h = (window->src_h - osd_crop) & ~3; - window->dst_h = window->src_h / 4; - window->dst_h += window->dst_h & 1; + f->src_y += osd_crop / 2; + f->src_h = (f->src_h - osd_crop) & ~3; + f->dst_h = f->src_h / 4; + f->dst_h += f->dst_h & 1; } } /* If there's nothing to safe to display, we may as well stop now */ - if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) { + if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 || + (int)f->src_w <= 2 || (int)f->src_h <= 2) { return IVTV_YUV_UPDATE_INVALID; } /* Ensure video remains inside OSD area */ - osd_scale = (window->src_h << 16) / window->dst_h; + osd_scale = (f->src_h << 16) / f->dst_h; - if ((osd_crop = window->pan_y - window->dst_y) > 0) { + if ((osd_crop = f->pan_y - f->dst_y) > 0) { /* Falls off the upper edge - crop */ - window->src_y += (osd_scale * osd_crop) >> 16; - window->src_h -= (osd_scale * osd_crop) >> 16; - window->dst_h -= osd_crop; - window->dst_y = 0; - } - else { - window->dst_y -= window->pan_y; + f->src_y += (osd_scale * osd_crop) >> 16; + f->src_h -= (osd_scale * osd_crop) >> 16; + f->dst_h -= osd_crop; + f->dst_y = 0; + } else { + f->dst_y -= f->pan_y; } - if ((osd_crop = window->dst_h + window->dst_y - window->vis_h) > 0) { + if ((osd_crop = f->dst_h + f->dst_y - f->vis_h) > 0) { /* Falls off the lower edge - crop */ - window->dst_h -= osd_crop; - window->src_h -= (osd_scale * osd_crop) >> 16; + f->dst_h -= osd_crop; + f->src_h -= (osd_scale * osd_crop) >> 16; } - osd_scale = (window->src_w << 16) / window->dst_w; + osd_scale = (f->src_w << 16) / f->dst_w; - if ((osd_crop = window->pan_x - window->dst_x) > 0) { + if ((osd_crop = f->pan_x - f->dst_x) > 0) { /* Fall off the left edge - crop */ - window->src_x += (osd_scale * osd_crop) >> 16; - window->src_w -= (osd_scale * osd_crop) >> 16; - window->dst_w -= osd_crop; - window->dst_x = 0; - } - else { - window->dst_x -= window->pan_x; + f->src_x += (osd_scale * osd_crop) >> 16; + f->src_w -= (osd_scale * osd_crop) >> 16; + f->dst_w -= osd_crop; + f->dst_x = 0; + } else { + f->dst_x -= f->pan_x; } - if ((osd_crop = window->dst_w + window->dst_x - window->vis_w) > 0) { + if ((osd_crop = f->dst_w + f->dst_x - f->vis_w) > 0) { /* Falls off the right edge - crop */ - window->dst_w -= osd_crop; - window->src_w -= (osd_scale * osd_crop) >> 16; + f->dst_w -= osd_crop; + f->src_w -= (osd_scale * osd_crop) >> 16; } /* The OSD can be moved. Track to it */ - window->dst_x += itv->yuv_info.osd_x_offset; - window->dst_y += itv->yuv_info.osd_y_offset; + f->dst_x += itv->yuv_info.osd_x_offset; + f->dst_y += itv->yuv_info.osd_y_offset; /* Width & height for both src & dst must be even. Same for coordinates. */ - window->dst_w &= ~1; - window->dst_x &= ~1; + f->dst_w &= ~1; + f->dst_x &= ~1; - window->src_w += window->src_x & 1; - window->src_x &= ~1; + f->src_w += f->src_x & 1; + f->src_x &= ~1; - window->src_w &= ~1; - window->dst_w &= ~1; + f->src_w &= ~1; + f->dst_w &= ~1; - window->dst_h &= ~1; - window->dst_y &= ~1; + f->dst_h &= ~1; + f->dst_y &= ~1; - window->src_h += window->src_y & 1; - window->src_y &= ~1; + f->src_h += f->src_y & 1; + f->src_y &= ~1; - window->src_h &= ~1; - window->dst_h &= ~1; + f->src_h &= ~1; + f->dst_h &= ~1; - /* Due to rounding, we may have reduced the output size to <1/4 of the source - Check again, but this time just resize. Don't change source coordinates */ - if (window->dst_w < window->src_w / 4) { - window->src_w &= ~3; - window->dst_w = window->src_w / 4; - window->dst_w += window->dst_w & 1; + /* Due to rounding, we may have reduced the output size to <1/4 of + the source. Check again, but this time just resize. Don't change + source coordinates */ + if (f->dst_w < f->src_w / 4) { + f->src_w &= ~3; + f->dst_w = f->src_w / 4; + f->dst_w += f->dst_w & 1; } - if (window->dst_h < window->src_h / 4) { - window->src_h &= ~3; - window->dst_h = window->src_h / 4; - window->dst_h += window->dst_h & 1; + if (f->dst_h < f->src_h / 4) { + f->src_h &= ~3; + f->dst_h = f->src_h / 4; + f->dst_h += f->dst_h & 1; } /* Check again. If there's nothing to safe to display, stop now */ - if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) { + if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 || + (int)f->src_w <= 2 || (int)f->src_h <= 2) { return IVTV_YUV_UPDATE_INVALID; } /* Both x offset & width are linked, so they have to be done together */ - if ((itv->yuv_info.old_frame_info.dst_w != window->dst_w) || - (itv->yuv_info.old_frame_info.src_w != window->src_w) || - (itv->yuv_info.old_frame_info.dst_x != window->dst_x) || - (itv->yuv_info.old_frame_info.src_x != window->src_x) || - (itv->yuv_info.old_frame_info.pan_x != window->pan_x) || - (itv->yuv_info.old_frame_info.vis_w != window->vis_w)) { + if ((of->dst_w != f->dst_w) || (of->src_w != f->src_w) || + (of->dst_x != f->dst_x) || (of->src_x != f->src_x) || + (of->pan_x != f->pan_x) || (of->vis_w != f->vis_w)) { yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL; } - if ((itv->yuv_info.old_frame_info.src_h != window->src_h) || - (itv->yuv_info.old_frame_info.dst_h != window->dst_h) || - (itv->yuv_info.old_frame_info.dst_y != window->dst_y) || - (itv->yuv_info.old_frame_info.src_y != window->src_y) || - (itv->yuv_info.old_frame_info.pan_y != window->pan_y) || - (itv->yuv_info.old_frame_info.vis_h != window->vis_h) || - (itv->yuv_info.old_frame_info.lace_mode != window->lace_mode) || - (itv->yuv_info.old_frame_info.interlaced_y != window->interlaced_y) || - (itv->yuv_info.old_frame_info.interlaced_uv != window->interlaced_uv)) { + if ((of->src_h != f->src_h) || (of->dst_h != f->dst_h) || + (of->dst_y != f->dst_y) || (of->src_y != f->src_y) || + (of->pan_y != f->pan_y) || (of->vis_h != f->vis_h) || + (of->lace_mode != f->lace_mode) || + (of->interlaced_y != f->interlaced_y) || + (of->interlaced_uv != f->interlaced_uv)) { yuv_update |= IVTV_YUV_UPDATE_VERTICAL; } @@ -826,22 +775,22 @@ static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *windo /* Update the scaling register to the requested value */ void ivtv_yuv_work_handler (struct ivtv *itv) { - struct yuv_frame_info window; + struct yuv_playback_info *yi = &itv->yuv_info; + struct yuv_frame_info f; + int frame = yi->update_frame; u32 yuv_update; - int frame = itv->yuv_info.update_frame; - /* IVTV_DEBUG_YUV("Update yuv registers for frame %d\n",frame); */ - memcpy(&window, &itv->yuv_info.new_frame_info[frame], sizeof (window)); + f = yi->new_frame_info[frame]; /* Update the osd pan info */ - window.pan_x = itv->yuv_info.osd_x_pan; - window.pan_y = itv->yuv_info.osd_y_pan; - window.vis_w = itv->yuv_info.osd_vis_w; - window.vis_h = itv->yuv_info.osd_vis_h; + f.pan_x = itv->yuv_info.osd_x_pan; + f.pan_y = itv->yuv_info.osd_y_pan; + f.vis_w = itv->yuv_info.osd_vis_w; + f.vis_h = itv->yuv_info.osd_vis_h; /* Calculate the display window coordinates. Exit if nothing left */ - if (!(yuv_update = ivtv_yuv_window_setup (itv, &window))) + if (!(yuv_update = ivtv_yuv_window_setup (itv, &f))) return; if (yuv_update & IVTV_YUV_UPDATE_INVALID) { @@ -850,13 +799,12 @@ void ivtv_yuv_work_handler (struct ivtv *itv) write_reg(0x00108080, 0x2898); if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL) - ivtv_yuv_handle_horizontal(itv, &window); + ivtv_yuv_handle_horizontal(itv, &f); if (yuv_update & IVTV_YUV_UPDATE_VERTICAL) - ivtv_yuv_handle_vertical(itv, &window); + ivtv_yuv_handle_vertical(itv, &f); } - - memcpy(&itv->yuv_info.old_frame_info, &window, sizeof (itv->yuv_info.old_frame_info)); + yi->old_frame_info = f; } static void ivtv_yuv_init (struct ivtv *itv) @@ -986,58 +934,98 @@ void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args) { struct yuv_playback_info *yi = &itv->yuv_info; u8 frame = yi->draw_frame; + u8 last_frame = (u8)(frame - 1) % IVTV_YUV_BUFFERS; + struct yuv_frame_info *nf = &yi->new_frame_info[frame]; + struct yuv_frame_info *of = &yi->new_frame_info[last_frame]; + int lace_threshold = yi->lace_threshold; /* Preserve old update flag in case we're overwriting a queued frame */ - int register_update = yi->new_frame_info[frame].update; + int update = nf->update; /* Take a snapshot of the yuv coordinate information */ - yi->new_frame_info[frame].src_x = args->src.left; - yi->new_frame_info[frame].src_y = args->src.top; - yi->new_frame_info[frame].src_w = args->src.width; - yi->new_frame_info[frame].src_h = args->src.height; - yi->new_frame_info[frame].dst_x = args->dst.left; - yi->new_frame_info[frame].dst_y = args->dst.top; - yi->new_frame_info[frame].dst_w = args->dst.width; - yi->new_frame_info[frame].dst_h = args->dst.height; - yi->new_frame_info[frame].tru_x = args->dst.left; - yi->new_frame_info[frame].tru_w = args->src_width; - yi->new_frame_info[frame].tru_h = args->src_height; - - /* Snapshot field order */ - yi->sync_field[frame] = yi->lace_sync_field; + nf->src_x = args->src.left; + nf->src_y = args->src.top; + nf->src_w = args->src.width; + nf->src_h = args->src.height; + nf->dst_x = args->dst.left; + nf->dst_y = args->dst.top; + nf->dst_w = args->dst.width; + nf->dst_h = args->dst.height; + nf->tru_x = args->dst.left; + nf->tru_w = args->src_width; + nf->tru_h = args->src_height; /* Are we going to offset the Y plane */ - if (args->src.height + args->src.top < 512-16) - yi->new_frame_info[frame].offset_y = 1; - else - yi->new_frame_info[frame].offset_y = 0; + nf->offset_y = (nf->tru_h + nf->src_x < 512 - 16) ? 1 : 0; /* Snapshot the osd pan info */ - yi->new_frame_info[frame].pan_x = yi->osd_x_pan; - yi->new_frame_info[frame].pan_y = yi->osd_y_pan; - yi->new_frame_info[frame].vis_w = yi->osd_vis_w; - yi->new_frame_info[frame].vis_h = yi->osd_vis_h; - - yi->new_frame_info[frame].update = 0; - yi->new_frame_info[frame].interlaced_y = 0; - yi->new_frame_info[frame].interlaced_uv = 0; - yi->new_frame_info[frame].lace_mode = yi->lace_mode & IVTV_YUV_MODE_MASK; - - if (memcmp(&yi->old_frame_info_args, &yi->new_frame_info[frame], - sizeof(yi->new_frame_info[frame]))) { - yi->old_frame_info_args = yi->new_frame_info[frame]; - yi->new_frame_info[frame].update = 1; -/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */ + nf->pan_x = yi->osd_x_pan; + nf->pan_y = yi->osd_y_pan; + nf->vis_w = yi->osd_vis_w; + nf->vis_h = yi->osd_vis_h; + + nf->update = 0; + nf->interlaced_y = 0; + nf->interlaced_uv = 0; + nf->delay = 0; + nf->sync_field = 0; + nf->lace_mode = yi->lace_mode & IVTV_YUV_MODE_MASK; + + if (lace_threshold < 0) + lace_threshold = yi->decode_height - 1; + + /* Work out the lace settings */ + switch (nf->lace_mode) { + case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */ + nf->interlaced = 0; + if (nf->tru_h < 512 || (nf->tru_h > 576 && nf->tru_h < 1021)) + nf->interlaced_y = 0; + else + nf->interlaced_y = 1; + + if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2)) + nf->interlaced_uv = 0; + else + nf->interlaced_uv = 1; + break; + + case IVTV_YUV_MODE_AUTO: + if (nf->tru_h <= lace_threshold || nf->tru_h > 576 || nf->tru_w > 720) { + nf->interlaced = 0; + if ((nf->tru_h < 512) || + (nf->tru_h > 576 && nf->tru_h < 1021) || + (nf->tru_w > 720 && nf->tru_h < 1021)) + nf->interlaced_y = 0; + else + nf->interlaced_y = 1; + if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2)) + nf->interlaced_uv = 0; + else + nf->interlaced_uv = 1; + } else { + nf->interlaced = 1; + nf->interlaced_y = 1; + nf->interlaced_uv = 1; + } + break; + + case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */ + default: + nf->interlaced = 1; + nf->interlaced_y = 1; + nf->interlaced_uv = 1; + break; } - yi->new_frame_info[frame].update |= register_update; + if (memcmp(&yi->old_frame_info_args, nf, sizeof(*nf))) { + yi->old_frame_info_args = *nf; + nf->update = 1; +/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */ + } - /* Should this frame be delayed ? */ - if (yi->sync_field[frame] != - yi->sync_field[(frame - 1) % IVTV_YUV_BUFFERS]) - yi->field_delay[frame] = 1; - else - yi->field_delay[frame] = 0; + nf->update |= update; + nf->sync_field = yi->lace_sync_field; + nf->delay = nf->sync_field != of->sync_field; } /* Frame is complete & ready for display */ -- cgit v1.2.3 From 39446d1c76c5ebb42b424060c502ba0a57494476 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 5 Nov 2007 18:27:09 +0100 Subject: ivtv: Initial merge of video48 yuv handling into the IVTV_IOC_DMA_FRAME framework From: Ian Armstrong Previously, all yuv data written to /dev/video48 had only basic support with no double buffering to avoid display tearing. With this patch, yuv frames written to video48 are now handled by the existing IVTV_IOC_DMA_FRAME framework. As such, the frames are hardware buffered to avoid tearing, and honour scaling mode & field order options. Unlike the proprietary IVTV_IOC_DMA_FRAME ioctl, all parameters are controlled by the V4L2 API. Due to mpeg & yuv output restrictions being different, their V4L2 output controls have been separated. To control the yuv output, the V4L2 calls must be done via video48. If the ivtvfb module is loaded, there will be one side effect to this merge. The yuv output window will be constrained to the visible framebuffer area. In the event that a virtual framebuffer size is being used, the limit to the output size will be the virtual dimensions, but only the portion that falls within the currently visible area of the framebuffer will be shown. Like the IVTV_IOC_DMA_FRAME ioctl, the supplied frames must be padded to 720 pixels wide. However the height must only be padded up the nearest multiple of 32. This would mean an image of 102 lines must be padded to 128. As long as the true source image size is given, the padding will not be visible in the final output. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ivtv/ivtv-driver.c | 6 ++ linux/drivers/media/video/ivtv/ivtv-driver.h | 7 ++ linux/drivers/media/video/ivtv/ivtv-fileops.c | 23 ++++- linux/drivers/media/video/ivtv/ivtv-ioctl.c | 120 +++++++++++++++++--------- linux/drivers/media/video/ivtv/ivtv-irq.c | 26 +++++- linux/drivers/media/video/ivtv/ivtv-streams.c | 17 +--- linux/drivers/media/video/ivtv/ivtv-yuv.c | 58 +++++++++++-- linux/drivers/media/video/ivtv/ivtv-yuv.h | 5 +- linux/drivers/media/video/ivtv/ivtvfb.c | 4 + 9 files changed, 196 insertions(+), 70 deletions(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index 194809010..5a5634ee0 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -1121,6 +1121,12 @@ static int __devinit ivtv_probe(struct pci_dev *dev, itv->is_50hz = 1; itv->is_out_50hz = 1; } + + itv->yuv_info.osd_full_w = 720; + itv->yuv_info.osd_full_h = itv->is_out_50hz ? 576 : 480; + itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w; + itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h; + itv->params.video_gop_size = itv->is_60hz ? 15 : 12; itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000; diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.h b/linux/drivers/media/video/ivtv/ivtv-driver.h index 1ad96a665..8b0074624 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.h +++ b/linux/drivers/media/video/ivtv/ivtv-driver.h @@ -476,6 +476,9 @@ struct yuv_playback_info u32 osd_vis_w; u32 osd_vis_h; + u32 osd_full_w; + u32 osd_full_h; + int decode_height; int lace_mode; @@ -501,6 +504,10 @@ struct yuv_playback_info u8 draw_frame; /* PVR350 buffer to draw into */ u8 max_frames_buffered; /* Maximum number of frames to buffer */ + + struct v4l2_rect main_rect; + u32 v4l2_src_w; + u32 v4l2_src_h; }; #define IVTV_VBI_FRAMES 32 diff --git a/linux/drivers/media/video/ivtv/ivtv-fileops.c b/linux/drivers/media/video/ivtv/ivtv-fileops.c index 58ad0d9c6..6fb96f19a 100644 --- a/linux/drivers/media/video/ivtv/ivtv-fileops.c +++ b/linux/drivers/media/video/ivtv/ivtv-fileops.c @@ -581,6 +581,24 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c set_bit(IVTV_F_S_APPL_IO, &s->s_flags); retry: + /* If possible, just DMA the entire frame - Check the data transfer size + since we may get here before the stream has been fully set-up */ + if (mode == OUT_YUV && s->q_full.length == 0 && itv->dma_data_req_size) { + while (count >= itv->dma_data_req_size) { + if (!ivtv_yuv_udma_stream_frame (itv, (void *)user_buf)) { + bytes_written += itv->dma_data_req_size; + user_buf += itv->dma_data_req_size; + count -= itv->dma_data_req_size; + } else { + break; + } + } + if (count == 0) { + IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused); + return bytes_written; + } + } + for (;;) { /* Gather buffers */ while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io))) @@ -660,6 +678,9 @@ retry: if (s->q_full.length >= itv->dma_data_req_size) { int got_sig; + if (mode == OUT_YUV) + ivtv_yuv_setup_stream_frame(itv); + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); while (!(got_sig = signal_pending(current)) && test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) { @@ -946,7 +967,7 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); /* For yuv, we need to know the dma size before we start */ itv->dma_data_req_size = - itv->params.width * itv->params.height * 3 / 2; + 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31); itv->yuv_info.stream_size = 0; } return 0; diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c index b01aaa752..e571cbaef 100644 --- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c @@ -372,7 +372,7 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm fmt->fmt.pix.height = itv->main_rect.height; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - if (itv->output_mode == OUT_UDMA_YUV) { + if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) { case IVTV_YUV_MODE_INTERLACED: fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ? @@ -386,14 +386,13 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm break; } fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + fmt->fmt.pix.bytesperline = 720; + fmt->fmt.pix.width = itv->yuv_info.v4l2_src_w; + fmt->fmt.pix.height = itv->yuv_info.v4l2_src_h; /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ fmt->fmt.pix.sizeimage = - fmt->fmt.pix.height * fmt->fmt.pix.width + - fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); - } - else if (itv->output_mode == OUT_YUV || - streamtype == IVTV_ENC_STREAM_TYPE_YUV || - streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + 1080 * ((fmt->fmt.pix.height + 31) & ~31); + } else if (streamtype == IVTV_ENC_STREAM_TYPE_YUV) { fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ fmt->fmt.pix.sizeimage = @@ -490,6 +489,7 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fmt, int set_fmt) { + struct yuv_playback_info *yi = &itv->yuv_info; struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; u16 set; @@ -505,39 +505,52 @@ static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype, r.width = fmt->fmt.pix.width; r.height = fmt->fmt.pix.height; ivtv_get_fmt(itv, streamtype, fmt); - if (itv->output_mode != OUT_UDMA_YUV) { - /* TODO: would setting the rect also be valid for this mode? */ - fmt->fmt.pix.width = r.width; - fmt->fmt.pix.height = r.height; - } - if (itv->output_mode == OUT_UDMA_YUV) { - /* TODO: add checks for validity */ + fmt->fmt.pix.width = r.width; + fmt->fmt.pix.height = r.height; + if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { fmt->fmt.pix.field = field; + if (fmt->fmt.pix.width < 2) + fmt->fmt.pix.width = 2; + if (fmt->fmt.pix.width > 720) + fmt->fmt.pix.width = 720; + if (fmt->fmt.pix.height < 2) + fmt->fmt.pix.height = 2; + if (fmt->fmt.pix.height > 576) + fmt->fmt.pix.height = 576; } - if (set_fmt) { - if (itv->output_mode == OUT_UDMA_YUV) { - switch (field) { - case V4L2_FIELD_NONE: - itv->yuv_info.lace_mode = IVTV_YUV_MODE_PROGRESSIVE; - break; - case V4L2_FIELD_ANY: - itv->yuv_info.lace_mode = IVTV_YUV_MODE_AUTO; - break; - case V4L2_FIELD_INTERLACED_BT: - itv->yuv_info.lace_mode = - IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD; - break; - case V4L2_FIELD_INTERLACED_TB: - default: - itv->yuv_info.lace_mode = IVTV_YUV_MODE_INTERLACED; - break; - } - itv->yuv_info.lace_sync_field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1; + if (set_fmt && streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + /* Return now if we already have some frame data */ + if (yi->stream_size) + return -EBUSY; - /* Force update of yuv registers */ - itv->yuv_info.yuv_forced_update = 1; - return 0; + yi->v4l2_src_w = r.width; + yi->v4l2_src_h = r.height; + + switch (field) { + case V4L2_FIELD_NONE: + yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE; + break; + case V4L2_FIELD_ANY: + yi->lace_mode = IVTV_YUV_MODE_AUTO; + break; + case V4L2_FIELD_INTERLACED_BT: + yi->lace_mode = + IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD; + break; + case V4L2_FIELD_INTERLACED_TB: + default: + yi->lace_mode = IVTV_YUV_MODE_INTERLACED; + break; } + yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1; + + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) + itv->dma_data_req_size = + 1080 * ((yi->v4l2_src_h + 31) & ~31); + + /* Force update of yuv registers */ + yi->yuv_forced_update = 1; + return 0; } return 0; } @@ -709,8 +722,12 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void { struct ivtv_open_id *id = NULL; u32 data[CX2341X_MBOX_MAX_DATA]; + int streamtype = 0; - if (filp) id = (struct ivtv_open_id *)filp->private_data; + if (filp) { + id = (struct ivtv_open_id *)filp->private_data; + streamtype = id->type; + } switch (cmd) { case VIDIOC_G_PRIORITY: @@ -828,6 +845,11 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void cropcap->bounds.height = itv->is_50hz ? 576 : 480; cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10; cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11; + } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + cropcap->bounds.width = itv->yuv_info.osd_full_w; + cropcap->bounds.height = itv->yuv_info.osd_full_h; + cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10; + cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11; } else { cropcap->bounds.height = itv->is_out_50hz ? 576 : 480; cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10; @@ -842,10 +864,15 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { - if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, - crop->c.width, crop->c.height, crop->c.left, crop->c.top)) { - itv->main_rect = crop->c; + if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + itv->yuv_info.main_rect = crop->c; return 0; + } else { + if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, + crop->c.width, crop->c.height, crop->c.left, crop->c.top)) { + itv->main_rect = crop->c; + return 0; + } } return -EINVAL; } @@ -859,7 +886,10 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { - crop->c = itv->main_rect; + if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) + crop->c = itv->yuv_info.main_rect; + else + crop->c = itv->main_rect; return 0; } if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -870,7 +900,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void case VIDIOC_ENUM_FMT: { static struct v4l2_fmtdesc formats[] = { { 0, 0, 0, - "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, + "HM12 (YUV 4:2:2)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } }, { 1, 0, V4L2_FMT_FLAG_COMPRESSED, @@ -1049,6 +1079,12 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void itv->main_rect.height = itv->params.height; ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, 720, itv->main_rect.height, 0, 0); + itv->yuv_info.main_rect = itv->main_rect; + if (!itv->osd_info) { + itv->yuv_info.osd_full_w = 720; + itv->yuv_info.osd_full_h = + itv->is_out_50hz ? 576 : 480; + } } break; } diff --git a/linux/drivers/media/video/ivtv/ivtv-irq.c b/linux/drivers/media/video/ivtv/ivtv-irq.c index 88fcb6517..ab5a4e9b0 100644 --- a/linux/drivers/media/video/ivtv/ivtv-irq.c +++ b/linux/drivers/media/video/ivtv/ivtv-irq.c @@ -308,8 +308,11 @@ static void dma_post(struct ivtv_stream *s) void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) { struct ivtv *itv = s->itv; + struct yuv_playback_info *yi = &itv->yuv_info; + u8 frame = yi->draw_frame; + struct yuv_frame_info *f = &yi->new_frame_info[frame]; struct ivtv_buffer *buf; - u32 y_size = itv->params.height * itv->params.width; + u32 y_size = 720 * ((f->src_h + 31) & ~31); u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET; int y_done = 0; int bytes_written = 0; @@ -317,6 +320,18 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) int idx = 0; IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); + + /* Insert buffer block for YUV if needed */ + if (s->type == IVTV_DEC_STREAM_TYPE_YUV && f->offset_y) { + if (yi->blanking_dmaptr) { + s->sg_pending[idx].src = yi->blanking_dmaptr; + s->sg_pending[idx].dst = offset; + s->sg_pending[idx].size = 720 * 16; + } + offset += 720 * 16; + idx++; + } + list_for_each_entry(buf, &s->q_predma.list, list) { /* YUV UV Offset from Y Buffer */ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && @@ -719,8 +734,11 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv) ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data); if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) { - itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2; - itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0]; + itv->dma_data_req_size = + 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31); + itv->dma_data_req_offset = data[1]; + if (atomic_read(&itv->yuv_info.next_dma_frame) >= 0) + ivtv_yuv_frame_complete(itv); s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; } else { @@ -734,6 +752,8 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv) set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); } else { + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) + ivtv_yuv_setup_stream_frame(itv); clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size); ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0); diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c index a5bfbd98a..3ca2a1a62 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.c +++ b/linux/drivers/media/video/ivtv/ivtv-streams.c @@ -661,27 +661,12 @@ int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset); - /* Clear Streamoff */ - if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { - /* Initialize Decoder */ - /* Reprogram Decoder YUV Buffers for YUV */ - write_reg(yuv_offset[0] >> 4, 0x82c); - write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); - write_reg(yuv_offset[0] >> 4, 0x834); - write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838); - - write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24); - - write_reg_sync(0x00108080, 0x2898); - /* Enable YUV decoder output */ - write_reg_sync(0x01, IVTV_REG_VDM); - } - ivtv_setup_v4l2_decode_stream(s); /* set dma size to 65536 bytes */ ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536); + /* Clear Streamoff */ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); /* Zero out decoder counters */ diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.c b/linux/drivers/media/video/ivtv/ivtv-yuv.c index cd42db9b5..711ce5b5a 100644 --- a/linux/drivers/media/video/ivtv/ivtv-yuv.c +++ b/linux/drivers/media/video/ivtv/ivtv-yuv.c @@ -1035,17 +1035,11 @@ void ivtv_yuv_frame_complete(struct ivtv *itv) (itv->yuv_info.draw_frame + 1) % IVTV_YUV_BUFFERS); } -int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) +int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args) { DEFINE_WAIT(wait); int rc = 0; int got_sig = 0; - - IVTV_DEBUG_INFO("yuv_prep_frame\n"); - - ivtv_yuv_next_free(itv); - ivtv_yuv_setup_frame(itv, args); - /* DMA the frame */ mutex_lock(&itv->udma.lock); @@ -1084,6 +1078,56 @@ int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) return rc; } +/* Setup frame according to V4L2 parameters */ +void ivtv_yuv_setup_stream_frame(struct ivtv *itv) +{ + struct yuv_playback_info *yi = &itv->yuv_info; + struct ivtv_dma_frame dma_args; + + ivtv_yuv_next_free(itv); + + /* Copy V4L2 parameters to an ivtv_dma_frame struct... */ + dma_args.y_source = 0L; + dma_args.uv_source = 0L; + dma_args.src.left = 0; + dma_args.src.top = 0; + dma_args.src.width = yi->v4l2_src_w; + dma_args.src.height = yi->v4l2_src_h; + dma_args.dst = yi->main_rect; + dma_args.src_width = yi->v4l2_src_w; + dma_args.src_height = yi->v4l2_src_h; + + /* ... and use the same setup routine as ivtv_yuv_prep_frame */ + ivtv_yuv_setup_frame(itv, &dma_args); + + if (!itv->dma_data_req_offset) + itv->dma_data_req_offset = yuv_offset[yi->draw_frame]; +} + +/* Attempt to dma a frame from a user buffer */ +int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void *src) +{ + struct yuv_playback_info *yi = &itv->yuv_info; + struct ivtv_dma_frame dma_args; + + ivtv_yuv_setup_stream_frame(itv); + + /* We only need to supply source addresses for this */ + dma_args.y_source = src; + dma_args.uv_source = src + 720 * ((yi->v4l2_src_h + 31) & ~31); + return ivtv_yuv_udma_frame(itv, &dma_args); +} + +/* IVTV_IOC_DMA_FRAME ioctl handler */ +int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) +{ + IVTV_DEBUG_INFO("yuv_prep_frame\n"); + + ivtv_yuv_next_free(itv); + ivtv_yuv_setup_frame(itv, args); + return ivtv_yuv_udma_frame(itv, args); +} + void ivtv_yuv_close(struct ivtv *itv) { int h_filter, v_filter_1, v_filter_2; diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.h b/linux/drivers/media/video/ivtv/ivtv-yuv.h index 3b290927d..2fe5f1250 100644 --- a/linux/drivers/media/video/ivtv/ivtv-yuv.h +++ b/linux/drivers/media/video/ivtv/ivtv-yuv.h @@ -34,8 +34,11 @@ extern const u32 yuv_offset[IVTV_YUV_BUFFERS]; int ivtv_yuv_filter_check(struct ivtv *itv); +void ivtv_yuv_setup_stream_frame(struct ivtv *itv); +int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void *src); +void ivtv_yuv_frame_complete(struct ivtv *itv); int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args); void ivtv_yuv_close(struct ivtv *itv); -void ivtv_yuv_work_handler (struct ivtv *itv); +void ivtv_yuv_work_handler(struct ivtv *itv); #endif diff --git a/linux/drivers/media/video/ivtv/ivtvfb.c b/linux/drivers/media/video/ivtv/ivtvfb.c index 52ffd154a..1a73038ea 100644 --- a/linux/drivers/media/video/ivtv/ivtvfb.c +++ b/linux/drivers/media/video/ivtv/ivtvfb.c @@ -504,6 +504,10 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) ivtvfb_set_display_window(itv, &ivtv_window); + /* Pass screen size back to yuv handler */ + itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride; + itv->yuv_info.osd_full_h = ivtv_osd.lines; + /* Force update of yuv registers */ itv->yuv_info.yuv_forced_update = 1; -- cgit v1.2.3 From 9058aa0fb66f57fae9e5cd1084410d94f1565fbc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 5 Nov 2007 18:30:03 +0100 Subject: ivtv: ivtv yuv format description correction From: Ian Armstrong The driver was incorrectly reporting that it supported YUV 4:2:2 output, when it is actually YUV 4:2:0. Though I believe the hardware can be pushed to 4:2:2, we don't currently support that. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ivtv/ivtv-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c index e571cbaef..30cc3b290 100644 --- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c @@ -900,7 +900,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void case VIDIOC_ENUM_FMT: { static struct v4l2_fmtdesc formats[] = { { 0, 0, 0, - "HM12 (YUV 4:2:2)", V4L2_PIX_FMT_HM12, + "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } }, { 1, 0, V4L2_FMT_FLAG_COMPRESSED, -- cgit v1.2.3 From a674794ef023a935a0b34c2de89e2e069231bcc5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 13 Nov 2007 23:15:25 +0100 Subject: ivtv: ivtv-yuv clean-up + source cropping bug-fix From: Ian Armstrong ivtv-yuv code clean up & reformat. Includes minor changes to some debug lines. Also fixes a bug found during the reformatting, which would cause the incorrect amount of yuv data to be sent to the card if source cropping coordinates were used. Apart from the bug-fix, there should be no functional difference to the previous version. Signed-off-by: Ian Armstrong Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ivtv/ivtv-yuv.c | 635 +++++++++++++++--------------- 1 file changed, 320 insertions(+), 315 deletions(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.c b/linux/drivers/media/video/ivtv/ivtv-yuv.c index 711ce5b5a..85183480a 100644 --- a/linux/drivers/media/video/ivtv/ivtv-yuv.c +++ b/linux/drivers/media/video/ivtv/ivtv-yuv.c @@ -35,7 +35,7 @@ const u32 yuv_offset[IVTV_YUV_BUFFERS] = { }; static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, - struct ivtv_dma_frame *args) + struct ivtv_dma_frame *args) { struct ivtv_dma_page_info y_dma; struct ivtv_dma_page_info uv_dma; @@ -50,7 +50,7 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, y_buffer_offset = IVTV_DECODER_OFFSET + yuv_offset[frame]; uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET; - y_decode_height = uv_decode_height = f->src_h + f->src_x; + y_decode_height = uv_decode_height = f->src_h + f->src_y; if (f->offset_y) y_buffer_offset += 720 * 16; @@ -65,8 +65,9 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, /* Still in USE */ if (dma->SG_length || dma->page_count) { - IVTV_DEBUG_WARN("prep_user_dma: SG_length %d page_count %d still full?\n", - dma->SG_length, dma->page_count); + IVTV_DEBUG_WARN + ("prep_user_dma: SG_length %d page_count %d still full?\n", + dma->SG_length, dma->page_count); return -EBUSY; } @@ -82,8 +83,9 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, dma->page_count = y_dma.page_count + uv_dma.page_count; if (y_pages + uv_pages != dma->page_count) { - IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", - y_pages + uv_pages, dma->page_count); + IVTV_DEBUG_WARN + ("failed to map user pages, returned %d instead of %d\n", + y_pages + uv_pages, dma->page_count); for (i = 0; i < dma->page_count; i++) { put_page(dma->map[i]); @@ -104,12 +106,12 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); /* Fill SG Array with new values */ - ivtv_udma_fill_sg_array (dma, y_buffer_offset, uv_buffer_offset, y_size); + ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size); /* If we've offset the y plane, ensure top area is blanked */ - if (f->offset_y && itv->yuv_info.blanking_dmaptr) { + if (f->offset_y && yi->blanking_dmaptr) { dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16); - dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr); + dma->SGarray[dma->SG_length].src = cpu_to_le32(yi->blanking_dmaptr); dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]); dma->SG_length++; } @@ -124,11 +126,11 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, /* We rely on a table held in the firmware - Quick check. */ int ivtv_yuv_filter_check(struct ivtv *itv) { - int i, offset_y, offset_uv; + int i, y, uv; - for (i=0, offset_y = 16, offset_uv = 4; i<16; i++, offset_y += 24, offset_uv += 12) { - if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + offset_y) != i << 16) || - (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + offset_uv) != i << 16)) { + for (i = 0, y = 16, uv = 4; i < 16; i++, y += 24, uv += 12) { + if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + y) != i << 16) || + (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + uv) != i << 16)) { IVTV_WARN ("YUV filter table not found in firmware.\n"); return -1; } @@ -138,69 +140,67 @@ int ivtv_yuv_filter_check(struct ivtv *itv) static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2) { - int filter_index, filter_line; + u32 i, line; /* If any filter is -1, then don't update it */ if (h_filter > -1) { - if (h_filter > 4) h_filter = 4; - filter_index = h_filter * 384; - filter_line = 0; - while (filter_line < 16) { - write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02804); - write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0281c); - filter_index += 4; - write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02808); - write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02820); - filter_index += 4; - write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0280c); - write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02824); - filter_index += 4; - write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02810); - write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02828); - filter_index += 4; - write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02814); - write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0282c); - filter_index += 8; + if (h_filter > 4) + h_filter = 4; + i = IVTV_YUV_HORIZONTAL_FILTER_OFFSET + (h_filter * 384); + for (line = 0; line < 16; line++) { + write_reg(read_dec(i), 0x02804); + write_reg(read_dec(i), 0x0281c); + i += 4; + write_reg(read_dec(i), 0x02808); + write_reg(read_dec(i), 0x02820); + i += 4; + write_reg(read_dec(i), 0x0280c); + write_reg(read_dec(i), 0x02824); + i += 4; + write_reg(read_dec(i), 0x02810); + write_reg(read_dec(i), 0x02828); + i += 4; + write_reg(read_dec(i), 0x02814); + write_reg(read_dec(i), 0x0282c); + i += 8; write_reg(0, 0x02818); write_reg(0, 0x02830); - filter_line ++; } - IVTV_DEBUG_YUV("h_filter -> %d\n",h_filter); + IVTV_DEBUG_YUV("h_filter -> %d\n", h_filter); } if (v_filter_1 > -1) { - if (v_filter_1 > 4) v_filter_1 = 4; - filter_index = v_filter_1 * 192; - filter_line = 0; - while (filter_line < 16) { - write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02900); - filter_index += 4; - write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02904); - filter_index += 8; + if (v_filter_1 > 4) + v_filter_1 = 4; + i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_1 * 192); + for (line = 0; line < 16; line++) { + write_reg(read_dec(i), 0x02900); + i += 4; + write_reg(read_dec(i), 0x02904); + i += 8; write_reg(0, 0x02908); - filter_line ++; } - IVTV_DEBUG_YUV("v_filter_1 -> %d\n",v_filter_1); + IVTV_DEBUG_YUV("v_filter_1 -> %d\n", v_filter_1); } if (v_filter_2 > -1) { - if (v_filter_2 > 4) v_filter_2 = 4; - filter_index = v_filter_2 * 192; - filter_line = 0; - while (filter_line < 16) { - write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x0290c); - filter_index += 4; - write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02910); - filter_index += 8; + if (v_filter_2 > 4) + v_filter_2 = 4; + i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_2 * 192); + for (line = 0; line < 16; line++) { + write_reg(read_dec(i), 0x0290c); + i += 4; + write_reg(read_dec(i), 0x02910); + i += 8; write_reg(0, 0x02914); - filter_line ++; } - IVTV_DEBUG_YUV("v_filter_2 -> %d\n",v_filter_2); + IVTV_DEBUG_YUV("v_filter_2 -> %d\n", v_filter_2); } } -static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *window) +static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *f) { + struct yuv_playback_info *yi = &itv->yuv_info; u32 reg_2834, reg_2838, reg_283c; u32 reg_2844, reg_2854, reg_285c; u32 reg_2864, reg_2874, reg_2890; @@ -209,18 +209,19 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info * int h_filter; u32 master_width; - IVTV_DEBUG_WARN( "Need to adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n", - window->tru_w, window->src_w, window->dst_w,window->src_x, window->dst_x); + IVTV_DEBUG_WARN + ("Adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n", + f->tru_w, f->src_w, f->dst_w, f->src_x, f->dst_x); /* How wide is the src image */ - x_cutoff = window->src_w + window->src_x; + x_cutoff = f->src_w + f->src_x; /* Set the display width */ - reg_2834 = window->dst_w; + reg_2834 = f->dst_w; reg_2838 = reg_2834; /* Set the display position */ - reg_2890 = window->dst_x; + reg_2890 = f->dst_x; /* Index into the image horizontally */ reg_2870 = 0; @@ -231,32 +232,31 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info * Gradually adjust the offset to avoid the video 'snapping' left/right if it gets dragged through this region. Only do this if osd is full width. */ - if (window->vis_w == 720) { - if ((window->tru_x - window->pan_x > -1) && (window->tru_x - window->pan_x <= 40) && (window->dst_w >= 680)){ - reg_2870 = 10 - (window->tru_x - window->pan_x) / 4; - } - else if ((window->tru_x - window->pan_x < 0) && (window->tru_x - window->pan_x >= -20) && (window->dst_w >= 660)) { - reg_2870 = (10 + (window->tru_x - window->pan_x) / 2); - } + if (f->vis_w == 720) { + if ((f->tru_x - f->pan_x > -1) && (f->tru_x - f->pan_x <= 40) && (f->dst_w >= 680)) + reg_2870 = 10 - (f->tru_x - f->pan_x) / 4; + else if ((f->tru_x - f->pan_x < 0) && (f->tru_x - f->pan_x >= -20) && (f->dst_w >= 660)) + reg_2870 = (10 + (f->tru_x - f->pan_x) / 2); - if (window->dst_w >= window->src_w) + if (f->dst_w >= f->src_w) reg_2870 = reg_2870 << 16 | reg_2870; else reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1); } - if (window->dst_w < window->src_w) + if (f->dst_w < f->src_w) reg_2870 = 0x000d000e - reg_2870; else reg_2870 = 0x0012000e - reg_2870; /* We're also using 2870 to shift the image left (src_x & negative dst_x) */ - reg_2870_offset = (window->src_x*((window->dst_w << 21)/window->src_w))>>19; + reg_2870_offset = (f->src_x * ((f->dst_w << 21) / f->src_w)) >> 19; - if (window->dst_w >= window->src_w) { + if (f->dst_w >= f->src_w) { x_cutoff &= ~1; - master_width = (window->src_w * 0x00200000) / (window->dst_w); - if (master_width * window->dst_w != window->src_w * 0x00200000) master_width ++; + master_width = (f->src_w * 0x00200000) / (f->dst_w); + if (master_width * f->dst_w != f->src_w * 0x00200000) + master_width++; reg_2834 = (reg_2834 << 16) | x_cutoff; reg_2838 = (reg_2838 << 16) | x_cutoff; reg_283c = master_width >> 2; @@ -267,17 +267,17 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info * /* We also need to factor in the scaling (src_w - dst_w) / (src_w / 4) */ - if (window->dst_w > window->src_w) - reg_2870_base = ((window->dst_w - window->src_w)<<16) / (window->src_w <<14); + if (f->dst_w > f->src_w) + reg_2870_base = ((f->dst_w - f->src_w)<<16) / (f->src_w <<14); else reg_2870_base = 0; reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base); reg_2874 = 0; - } - else if (window->dst_w < window->src_w / 2) { - master_width = (window->src_w * 0x00080000) / window->dst_w; - if (master_width * window->dst_w != window->src_w * 0x00080000) master_width ++; + } else if (f->dst_w < f->src_w / 2) { + master_width = (f->src_w * 0x00080000) / f->dst_w; + if (master_width * f->dst_w != f->src_w * 0x00080000) + master_width++; reg_2834 = (reg_2834 << 16) | x_cutoff; reg_2838 = (reg_2838 << 16) | x_cutoff; reg_283c = master_width >> 2; @@ -285,13 +285,13 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info * reg_2854 = master_width; reg_285c = master_width >> 1; reg_2864 = master_width >> 1; - reg_2870 += (((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset); - reg_2870 += (5 - (((window->src_w + window->src_w / 2) - 1) / window->dst_w)) << 16; + reg_2870 += ((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset; + reg_2870 += (5 - (((f->src_w + f->src_w / 2) - 1) / f->dst_w)) << 16; reg_2874 = 0x00000012; - } - else { - master_width = (window->src_w * 0x00100000) / window->dst_w; - if (master_width * window->dst_w != window->src_w * 0x00100000) master_width ++; + } else { + master_width = (f->src_w * 0x00100000) / f->dst_w; + if (master_width * f->dst_w != f->src_w * 0x00100000) + master_width++; reg_2834 = (reg_2834 << 16) | x_cutoff; reg_2838 = (reg_2838 << 16) | x_cutoff; reg_283c = master_width >> 2; @@ -299,62 +299,70 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info * reg_2854 = master_width; reg_285c = master_width >> 1; reg_2864 = master_width >> 1; - reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1); - reg_2870 += (5 - (((window->src_w * 3) - 1) / window->dst_w)) << 16; + reg_2870 += ((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1; + reg_2870 += (5 - (((f->src_w * 3) - 1) / f->dst_w)) << 16; reg_2874 = 0x00000001; } /* Select the horizontal filter */ - if (window->src_w == window->dst_w) { + if (f->src_w == f->dst_w) { /* An exact size match uses filter 0 */ h_filter = 0; - } - else { + } else { /* Figure out which filter to use */ - h_filter = ((window->src_w << 16) / window->dst_w) >> 15; + h_filter = ((f->src_w << 16) / f->dst_w) >> 15; h_filter = (h_filter >> 1) + (h_filter & 1); /* Only an exact size match can use filter 0 */ - if (h_filter == 0) h_filter = 1; + h_filter += !h_filter; } write_reg(reg_2834, 0x02834); write_reg(reg_2838, 0x02838); - IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",itv->yuv_info.reg_2834, reg_2834, itv->yuv_info.reg_2838, reg_2838); + IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n", + yi->reg_2834, reg_2834, yi->reg_2838, reg_2838); write_reg(reg_283c, 0x0283c); write_reg(reg_2844, 0x02844); - IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",itv->yuv_info.reg_283c, reg_283c, itv->yuv_info.reg_2844, reg_2844); + IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n", + yi->reg_283c, reg_283c, yi->reg_2844, reg_2844); write_reg(0x00080514, 0x02840); write_reg(0x00100514, 0x02848); - IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",itv->yuv_info.reg_2840, 0x00080514, itv->yuv_info.reg_2848, 0x00100514); + IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n", + yi->reg_2840, 0x00080514, yi->reg_2848, 0x00100514); write_reg(reg_2854, 0x02854); - IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",itv->yuv_info.reg_2854, reg_2854); + IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n", + yi->reg_2854, reg_2854); write_reg(reg_285c, 0x0285c); write_reg(reg_2864, 0x02864); - IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",itv->yuv_info.reg_285c, reg_285c, itv->yuv_info.reg_2864, reg_2864); + IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n", + yi->reg_285c, reg_285c, yi->reg_2864, reg_2864); write_reg(reg_2874, 0x02874); - IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",itv->yuv_info.reg_2874, reg_2874); + IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n", + yi->reg_2874, reg_2874); write_reg(reg_2870, 0x02870); - IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",itv->yuv_info.reg_2870, reg_2870); + IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n", + yi->reg_2870, reg_2870); - write_reg( reg_2890,0x02890); - IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",itv->yuv_info.reg_2890, reg_2890); + write_reg(reg_2890, 0x02890); + IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n", + yi->reg_2890, reg_2890); /* Only update the filter if we really need to */ - if (h_filter != itv->yuv_info.h_filter) { - ivtv_yuv_filter (itv,h_filter,-1,-1); - itv->yuv_info.h_filter = h_filter; + if (h_filter != yi->h_filter) { + ivtv_yuv_filter(itv, h_filter, -1, -1); + yi->h_filter = h_filter; } } -static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *window) +static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *f) { + struct yuv_playback_info *yi = &itv->yuv_info; u32 master_height; u32 reg_2918, reg_291c, reg_2920, reg_2928; u32 reg_2930, reg_2934, reg_293c; @@ -362,69 +370,59 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi u32 reg_2950, reg_2954, reg_2958, reg_295c; u32 reg_2960, reg_2964, reg_2968, reg_296c; u32 reg_289c; - u32 src_y_major_y, src_y_minor_y; - u32 src_y_major_uv, src_y_minor_uv; + u32 src_major_y, src_minor_y; + u32 src_major_uv, src_minor_uv; u32 reg_2964_base, reg_2968_base; int v_filter_1, v_filter_2; - IVTV_DEBUG_WARN("Need to adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n", - window->tru_h, window->src_h, window->dst_h,window->src_y, window->dst_y); + IVTV_DEBUG_WARN + ("Adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n", + f->tru_h, f->src_h, f->dst_h, f->src_y, f->dst_y); /* What scaling mode is being used... */ - if (window->interlaced_y) { - IVTV_DEBUG_YUV("Scaling mode Y: Interlaced\n"); - } - else { - IVTV_DEBUG_YUV("Scaling mode Y: Progressive\n"); - } + IVTV_DEBUG_YUV("Scaling mode Y: %s\n", + f->interlaced_y ? "Interlaced" : "Progressive"); - if (window->interlaced_uv) { - IVTV_DEBUG_YUV("Scaling mode UV: Interlaced\n"); - } - else { - IVTV_DEBUG_YUV("Scaling mode UV: Progressive\n"); - } + IVTV_DEBUG_YUV("Scaling mode UV: %s\n", + f->interlaced_uv ? "Interlaced" : "Progressive"); /* What is the source video being treated as... */ - if (window->interlaced) { - IVTV_DEBUG_WARN("Source video: Interlaced\n"); - } - else { - IVTV_DEBUG_WARN("Source video: Non-interlaced\n"); - } + IVTV_DEBUG_WARN("Source video: %s\n", + f->interlaced ? "Interlaced" : "Progressive"); /* We offset into the image using two different index methods, so split the y source coord into two parts. */ - if (window->src_y < 8) { - src_y_minor_uv = window->src_y; - src_y_major_uv = 0; - } - else { - src_y_minor_uv = 8; - src_y_major_uv = window->src_y - 8; + if (f->src_y < 8) { + src_minor_uv = f->src_y; + src_major_uv = 0; + } else { + src_minor_uv = 8; + src_major_uv = f->src_y - 8; } - src_y_minor_y = src_y_minor_uv; - src_y_major_y = src_y_major_uv; + src_minor_y = src_minor_uv; + src_major_y = src_major_uv; - if (window->offset_y) src_y_minor_y += 16; + if (f->offset_y) + src_minor_y += 16; - if (window->interlaced_y) - reg_2918 = (window->dst_h << 16) | (window->src_h + src_y_minor_y); + if (f->interlaced_y) + reg_2918 = (f->dst_h << 16) | (f->src_h + src_minor_y); else - reg_2918 = (window->dst_h << 16) | ((window->src_h + src_y_minor_y) << 1); + reg_2918 = (f->dst_h << 16) | ((f->src_h + src_minor_y) << 1); - if (window->interlaced_uv) - reg_291c = (window->dst_h << 16) | ((window->src_h + src_y_minor_uv) >> 1); + if (f->interlaced_uv) + reg_291c = (f->dst_h << 16) | ((f->src_h + src_minor_uv) >> 1); else - reg_291c = (window->dst_h << 16) | (window->src_h + src_y_minor_uv); + reg_291c = (f->dst_h << 16) | (f->src_h + src_minor_uv); - reg_2964_base = (src_y_minor_y * ((window->dst_h << 16)/window->src_h)) >> 14; - reg_2968_base = (src_y_minor_uv * ((window->dst_h << 16)/window->src_h)) >> 14; + reg_2964_base = (src_minor_y * ((f->dst_h << 16) / f->src_h)) >> 14; + reg_2968_base = (src_minor_uv * ((f->dst_h << 16) / f->src_h)) >> 14; - if (window->dst_h / 2 >= window->src_h && !window->interlaced_y) { - master_height = (window->src_h * 0x00400000) / window->dst_h; - if ((window->src_h * 0x00400000) - (master_height * window->dst_h) >= window->dst_h / 2) master_height ++; + if (f->dst_h / 2 >= f->src_h && !f->interlaced_y) { + master_height = (f->src_h * 0x00400000) / f->dst_h; + if ((f->src_h * 0x00400000) - (master_height * f->dst_h) >= f->dst_h / 2) + master_height++; reg_2920 = master_height >> 2; reg_2928 = master_height >> 3; reg_2930 = master_height; @@ -432,45 +430,42 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi reg_2964_base >>= 3; reg_2968_base >>= 3; reg_296c = 0x00000000; - } - else if (window->dst_h >= window->src_h) { - master_height = (window->src_h * 0x00400000) / window->dst_h; + } else if (f->dst_h >= f->src_h) { + master_height = (f->src_h * 0x00400000) / f->dst_h; master_height = (master_height >> 1) + (master_height & 1); reg_2920 = master_height >> 2; reg_2928 = master_height >> 2; reg_2930 = master_height; reg_2940 = master_height >> 1; reg_296c = 0x00000000; - if (window->interlaced_y) { + if (f->interlaced_y) { reg_2964_base >>= 3; - } - else { - reg_296c ++; + } else { + reg_296c++; reg_2964_base >>= 2; } - if (window->interlaced_uv) reg_2928 >>= 1; + if (f->interlaced_uv) + reg_2928 >>= 1; reg_2968_base >>= 3; - } - else if (window->dst_h >= window->src_h / 2) { - master_height = (window->src_h * 0x00200000) / window->dst_h; + } else if (f->dst_h >= f->src_h / 2) { + master_height = (f->src_h * 0x00200000) / f->dst_h; master_height = (master_height >> 1) + (master_height & 1); reg_2920 = master_height >> 2; reg_2928 = master_height >> 2; reg_2930 = master_height; reg_2940 = master_height; reg_296c = 0x00000101; - if (window->interlaced_y) { + if (f->interlaced_y) { reg_2964_base >>= 2; - } - else { - reg_296c ++; + } else { + reg_296c++; reg_2964_base >>= 1; } - if (window->interlaced_uv) reg_2928 >>= 1; + if (f->interlaced_uv) + reg_2928 >>= 1; reg_2968_base >>= 2; - } - else { - master_height = (window->src_h * 0x00100000) / window->dst_h; + } else { + master_height = (f->src_h * 0x00100000) / f->dst_h; master_height = (master_height >> 1) + (master_height & 1); reg_2920 = master_height >> 2; reg_2928 = master_height >> 2; @@ -483,13 +478,12 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi /* FIXME These registers change depending on scaled / unscaled output We really need to work out what they should be */ - if (window->src_h == window->dst_h){ + if (f->src_h == f->dst_h) { reg_2934 = 0x00020000; reg_293c = 0x00100000; reg_2944 = 0x00040000; reg_294c = 0x000b0000; - } - else { + } else { reg_2934 = 0x00000FF0; reg_293c = 0x00000FF0; reg_2944 = 0x00000FF0; @@ -497,34 +491,36 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi } /* The first line to be displayed */ - reg_2950 = 0x00010000 + src_y_major_y; - if (window->interlaced_y) reg_2950 += 0x00010000; + reg_2950 = 0x00010000 + src_major_y; + if (f->interlaced_y) + reg_2950 += 0x00010000; reg_2954 = reg_2950 + 1; - reg_2958 = 0x00010000 + (src_y_major_y >> 1); - if (window->interlaced_uv) reg_2958 += 0x00010000; + reg_2958 = 0x00010000 + (src_major_y >> 1); + if (f->interlaced_uv) + reg_2958 += 0x00010000; reg_295c = reg_2958 + 1; - if (itv->yuv_info.decode_height == 480) + if (yi->decode_height == 480) reg_289c = 0x011e0017; else reg_289c = 0x01500017; - if (window->dst_y < 0) - reg_289c = (reg_289c - ((window->dst_y & ~1)<<15))-(window->dst_y >>1); + if (f->dst_y < 0) + reg_289c = (reg_289c - ((f->dst_y & ~1)<<15))-(f->dst_y >>1); else - reg_289c = (reg_289c + ((window->dst_y & ~1)<<15))+(window->dst_y >>1); + reg_289c = (reg_289c + ((f->dst_y & ~1)<<15))+(f->dst_y >>1); /* How much of the source to decode. Take into account the source offset */ - reg_2960 = ((src_y_minor_y + window->src_h + src_y_major_y) - 1 ) | - ((((src_y_minor_uv + window->src_h + src_y_major_uv) - 1) & ~1) << 15); + reg_2960 = ((src_minor_y + f->src_h + src_major_y) - 1) | + (((src_minor_uv + f->src_h + src_major_uv - 1) & ~1) << 15); /* Calculate correct value for register 2964 */ - if (window->src_h == window->dst_h) + if (f->src_h == f->dst_h) { reg_2964 = 1; - else { - reg_2964 = 2 + ((window->dst_h << 1) / window->src_h); + } else { + reg_2964 = 2 + ((f->dst_h << 1) / f->src_h); reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1); } reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1); @@ -539,94 +535,107 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi /* Deviate further from what it should be. I find the flicker headache inducing so try to reduce it slightly. Leave 2968 as-is otherwise colours foul. */ - if ((reg_2964 != 0x00010001) && (window->dst_h / 2 <= window->src_h)) - reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF)/2); + if ((reg_2964 != 0x00010001) && (f->dst_h / 2 <= f->src_h)) + reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF) / 2); - if (!window->interlaced_y) reg_2964 -= 0x00010001; - if (!window->interlaced_uv) reg_2968 -= 0x00010001; + if (!f->interlaced_y) + reg_2964 -= 0x00010001; + if (!f->interlaced_uv) + reg_2968 -= 0x00010001; reg_2964 += ((reg_2964_base << 16) | reg_2964_base); reg_2968 += ((reg_2968_base << 16) | reg_2968_base); /* Select the vertical filter */ - if (window->src_h == window->dst_h) { + if (f->src_h == f->dst_h) { /* An exact size match uses filter 0/1 */ v_filter_1 = 0; v_filter_2 = 1; - } - else { + } else { /* Figure out which filter to use */ - v_filter_1 = ((window->src_h << 16) / window->dst_h) >> 15; + v_filter_1 = ((f->src_h << 16) / f->dst_h) >> 15; v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); /* Only an exact size match can use filter 0 */ - if (v_filter_1 == 0) v_filter_1 = 1; + v_filter_1 += !v_filter_1; v_filter_2 = v_filter_1; } write_reg(reg_2934, 0x02934); write_reg(reg_293c, 0x0293c); - IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",itv->yuv_info.reg_2934, reg_2934, itv->yuv_info.reg_293c, reg_293c); + IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n", + yi->reg_2934, reg_2934, yi->reg_293c, reg_293c); write_reg(reg_2944, 0x02944); write_reg(reg_294c, 0x0294c); - IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",itv->yuv_info.reg_2944, reg_2944, itv->yuv_info.reg_294c, reg_294c); + IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n", + yi->reg_2944, reg_2944, yi->reg_294c, reg_294c); /* Ensure 2970 is 0 (does it ever change ?) */ /* write_reg(0,0x02970); */ -/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n",itv->yuv_info.reg_2970, 0); */ +/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n", yi->reg_2970, 0); */ write_reg(reg_2930, 0x02938); write_reg(reg_2930, 0x02930); - IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",itv->yuv_info.reg_2930, reg_2930, itv->yuv_info.reg_2938, reg_2930); + IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n", + yi->reg_2930, reg_2930, yi->reg_2938, reg_2930); write_reg(reg_2928, 0x02928); - write_reg(reg_2928+0x514, 0x0292C); - IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",itv->yuv_info.reg_2928, reg_2928, itv->yuv_info.reg_292c, reg_2928+0x514); + write_reg(reg_2928 + 0x514, 0x0292C); + IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n", + yi->reg_2928, reg_2928, yi->reg_292c, reg_2928 + 0x514); write_reg(reg_2920, 0x02920); - write_reg(reg_2920+0x514, 0x02924); - IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",itv->yuv_info.reg_2920, reg_2920, itv->yuv_info.reg_2924, 0x514+reg_2920); + write_reg(reg_2920 + 0x514, 0x02924); + IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n", + yi->reg_2920, reg_2920, yi->reg_2924, reg_2920 + 0x514); - write_reg (reg_2918,0x02918); - write_reg (reg_291c,0x0291C); - IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",itv->yuv_info.reg_2918,reg_2918,itv->yuv_info.reg_291c,reg_291c); + write_reg(reg_2918, 0x02918); + write_reg(reg_291c, 0x0291C); + IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n", + yi->reg_2918, reg_2918, yi->reg_291c, reg_291c); write_reg(reg_296c, 0x0296c); - IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",itv->yuv_info.reg_296c, reg_296c); + IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n", + yi->reg_296c, reg_296c); write_reg(reg_2940, 0x02948); write_reg(reg_2940, 0x02940); - IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",itv->yuv_info.reg_2940, reg_2940, itv->yuv_info.reg_2948, reg_2940); + IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n", + yi->reg_2940, reg_2940, yi->reg_2948, reg_2940); write_reg(reg_2950, 0x02950); write_reg(reg_2954, 0x02954); - IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",itv->yuv_info.reg_2950, reg_2950, itv->yuv_info.reg_2954, reg_2954); + IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n", + yi->reg_2950, reg_2950, yi->reg_2954, reg_2954); write_reg(reg_2958, 0x02958); write_reg(reg_295c, 0x0295C); - IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",itv->yuv_info.reg_2958, reg_2958, itv->yuv_info.reg_295c, reg_295c); + IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n", + yi->reg_2958, reg_2958, yi->reg_295c, reg_295c); write_reg(reg_2960, 0x02960); - IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",itv->yuv_info.reg_2960, reg_2960); + IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n", + yi->reg_2960, reg_2960); write_reg(reg_2964, 0x02964); write_reg(reg_2968, 0x02968); - IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",itv->yuv_info.reg_2964, reg_2964, itv->yuv_info.reg_2968, reg_2968); + IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n", + yi->reg_2964, reg_2964, yi->reg_2968, reg_2968); - write_reg( reg_289c,0x0289c); - IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",itv->yuv_info.reg_289c, reg_289c); + write_reg(reg_289c, 0x0289c); + IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n", + yi->reg_289c, reg_289c); /* Only update filter 1 if we really need to */ - if (v_filter_1 != itv->yuv_info.v_filter_1) { - ivtv_yuv_filter (itv,-1,v_filter_1,-1); - itv->yuv_info.v_filter_1 = v_filter_1; + if (v_filter_1 != yi->v_filter_1) { + ivtv_yuv_filter(itv, -1, v_filter_1, -1); + yi->v_filter_1 = v_filter_1; } /* Only update filter 2 if we really need to */ - if (v_filter_2 != itv->yuv_info.v_filter_2) { - ivtv_yuv_filter (itv,-1,-1,v_filter_2); - itv->yuv_info.v_filter_2 = v_filter_2; + if (v_filter_2 != yi->v_filter_2) { + ivtv_yuv_filter(itv, -1, -1, v_filter_2); + yi->v_filter_2 = v_filter_2; } - } /* Modify the supplied coordinate information to fit the visible osd area */ @@ -668,7 +677,7 @@ static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f) /* If there's nothing to safe to display, we may as well stop now */ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 || - (int)f->src_w <= 2 || (int)f->src_h <= 2) { + (int)f->src_w <= 2 || (int)f->src_h <= 2) { return IVTV_YUV_UPDATE_INVALID; } @@ -749,23 +758,23 @@ static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f) /* Check again. If there's nothing to safe to display, stop now */ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 || - (int)f->src_w <= 2 || (int)f->src_h <= 2) { + (int)f->src_w <= 2 || (int)f->src_h <= 2) { return IVTV_YUV_UPDATE_INVALID; } /* Both x offset & width are linked, so they have to be done together */ if ((of->dst_w != f->dst_w) || (of->src_w != f->src_w) || - (of->dst_x != f->dst_x) || (of->src_x != f->src_x) || - (of->pan_x != f->pan_x) || (of->vis_w != f->vis_w)) { + (of->dst_x != f->dst_x) || (of->src_x != f->src_x) || + (of->pan_x != f->pan_x) || (of->vis_w != f->vis_w)) { yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL; } if ((of->src_h != f->src_h) || (of->dst_h != f->dst_h) || - (of->dst_y != f->dst_y) || (of->src_y != f->src_y) || - (of->pan_y != f->pan_y) || (of->vis_h != f->vis_h) || - (of->lace_mode != f->lace_mode) || - (of->interlaced_y != f->interlaced_y) || - (of->interlaced_uv != f->interlaced_uv)) { + (of->dst_y != f->dst_y) || (of->src_y != f->src_y) || + (of->pan_y != f->pan_y) || (of->vis_h != f->vis_h) || + (of->lace_mode != f->lace_mode) || + (of->interlaced_y != f->interlaced_y) || + (of->interlaced_uv != f->interlaced_uv)) { yuv_update |= IVTV_YUV_UPDATE_VERTICAL; } @@ -773,24 +782,24 @@ static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f) } /* Update the scaling register to the requested value */ -void ivtv_yuv_work_handler (struct ivtv *itv) +void ivtv_yuv_work_handler(struct ivtv *itv) { struct yuv_playback_info *yi = &itv->yuv_info; struct yuv_frame_info f; int frame = yi->update_frame; u32 yuv_update; -/* IVTV_DEBUG_YUV("Update yuv registers for frame %d\n",frame); */ + IVTV_DEBUG_YUV("Update yuv registers for frame %d\n", frame); f = yi->new_frame_info[frame]; /* Update the osd pan info */ - f.pan_x = itv->yuv_info.osd_x_pan; - f.pan_y = itv->yuv_info.osd_y_pan; - f.vis_w = itv->yuv_info.osd_vis_w; - f.vis_h = itv->yuv_info.osd_vis_h; + f.pan_x = yi->osd_x_pan; + f.pan_y = yi->osd_y_pan; + f.vis_w = yi->osd_vis_w; + f.vis_h = yi->osd_vis_h; /* Calculate the display window coordinates. Exit if nothing left */ - if (!(yuv_update = ivtv_yuv_window_setup (itv, &f))) + if (!(yuv_update = ivtv_yuv_window_setup(itv, &f))) return; if (yuv_update & IVTV_YUV_UPDATE_INVALID) { @@ -807,7 +816,7 @@ void ivtv_yuv_work_handler (struct ivtv *itv) yi->old_frame_info = f; } -static void ivtv_yuv_init (struct ivtv *itv) +static void ivtv_yuv_init(struct ivtv *itv) { struct yuv_playback_info *yi = &itv->yuv_info; @@ -876,25 +885,23 @@ static void ivtv_yuv_init (struct ivtv *itv) if (!yi->osd_vis_w) yi->osd_vis_w = 720 - yi->osd_x_offset; - if (!yi->osd_vis_h) + if (!yi->osd_vis_h) { yi->osd_vis_h = yi->decode_height - yi->osd_y_offset; - else { + } else if (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) { /* If output video standard has changed, requested height may - not be legal */ - if (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) { - IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n", - yi->osd_vis_h + yi->osd_y_offset, - yi->decode_height); - yi->osd_vis_h = yi->decode_height - yi->osd_y_offset; - } + not be legal */ + IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n", + yi->osd_vis_h + yi->osd_y_offset, + yi->decode_height); + yi->osd_vis_h = yi->decode_height - yi->osd_y_offset; } } /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */ - yi->blanking_ptr = kzalloc(720*16, GFP_KERNEL); - if (yi->blanking_ptr) + yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL); + if (yi->blanking_ptr) { yi->blanking_dmaptr = pci_map_single(itv->dev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE); - else { + } else { yi->blanking_dmaptr = 0; IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n"); } @@ -993,8 +1000,8 @@ void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args) if (nf->tru_h <= lace_threshold || nf->tru_h > 576 || nf->tru_w > 720) { nf->interlaced = 0; if ((nf->tru_h < 512) || - (nf->tru_h > 576 && nf->tru_h < 1021) || - (nf->tru_w > 720 && nf->tru_h < 1021)) + (nf->tru_h > 576 && nf->tru_h < 1021) || + (nf->tru_w > 720 && nf->tru_h < 1021)) nf->interlaced_y = 0; else nf->interlaced_y = 1; @@ -1020,7 +1027,7 @@ void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args) if (memcmp(&yi->old_frame_info_args, nf, sizeof(*nf))) { yi->old_frame_info_args = *nf; nf->update = 1; -/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */ + IVTV_DEBUG_YUV("Requesting reg update for frame %d\n", frame); } nf->update |= update; @@ -1051,10 +1058,10 @@ int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args) ivtv_udma_prepare(itv); prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); /* if no UDMA is pending and no UDMA is in progress, then the DMA - is finished */ + is finished */ while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) { /* don't interrupt if the DMA is in progress but break off - a still pending DMA. */ + a still pending DMA. */ got_sig = signal_pending(current); if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) break; @@ -1121,7 +1128,7 @@ int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void *src) /* IVTV_IOC_DMA_FRAME ioctl handler */ int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) { - IVTV_DEBUG_INFO("yuv_prep_frame\n"); +/* IVTV_DEBUG_INFO("yuv_prep_frame\n"); */ ivtv_yuv_next_free(itv); ivtv_yuv_setup_frame(itv, args); @@ -1130,91 +1137,90 @@ int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) void ivtv_yuv_close(struct ivtv *itv) { + struct yuv_playback_info *yi = &itv->yuv_info; int h_filter, v_filter_1, v_filter_2; IVTV_DEBUG_YUV("ivtv_yuv_close\n"); ivtv_waitq(&itv->vsync_waitq); - atomic_set(&itv->yuv_info.next_dma_frame, -1); - atomic_set(&itv->yuv_info.next_fill_frame, 0); + atomic_set(&yi->next_dma_frame, -1); + atomic_set(&yi->next_fill_frame, 0); /* Reset registers we have changed so mpeg playback works */ /* If we fully restore this register, the display may remain active. Restore, but set one bit to blank the video. Firmware will always clear this bit when needed, so not a problem. */ - write_reg(itv->yuv_info.reg_2898 | 0x01000000, 0x2898); - - write_reg(itv->yuv_info.reg_2834, 0x02834); - write_reg(itv->yuv_info.reg_2838, 0x02838); - write_reg(itv->yuv_info.reg_283c, 0x0283c); - write_reg(itv->yuv_info.reg_2840, 0x02840); - write_reg(itv->yuv_info.reg_2844, 0x02844); - write_reg(itv->yuv_info.reg_2848, 0x02848); - write_reg(itv->yuv_info.reg_2854, 0x02854); - write_reg(itv->yuv_info.reg_285c, 0x0285c); - write_reg(itv->yuv_info.reg_2864, 0x02864); - write_reg(itv->yuv_info.reg_2870, 0x02870); - write_reg(itv->yuv_info.reg_2874, 0x02874); - write_reg(itv->yuv_info.reg_2890, 0x02890); - write_reg(itv->yuv_info.reg_289c, 0x0289c); - - write_reg(itv->yuv_info.reg_2918, 0x02918); - write_reg(itv->yuv_info.reg_291c, 0x0291c); - write_reg(itv->yuv_info.reg_2920, 0x02920); - write_reg(itv->yuv_info.reg_2924, 0x02924); - write_reg(itv->yuv_info.reg_2928, 0x02928); - write_reg(itv->yuv_info.reg_292c, 0x0292c); - write_reg(itv->yuv_info.reg_2930, 0x02930); - write_reg(itv->yuv_info.reg_2934, 0x02934); - write_reg(itv->yuv_info.reg_2938, 0x02938); - write_reg(itv->yuv_info.reg_293c, 0x0293c); - write_reg(itv->yuv_info.reg_2940, 0x02940); - write_reg(itv->yuv_info.reg_2944, 0x02944); - write_reg(itv->yuv_info.reg_2948, 0x02948); - write_reg(itv->yuv_info.reg_294c, 0x0294c); - write_reg(itv->yuv_info.reg_2950, 0x02950); - write_reg(itv->yuv_info.reg_2954, 0x02954); - write_reg(itv->yuv_info.reg_2958, 0x02958); - write_reg(itv->yuv_info.reg_295c, 0x0295c); - write_reg(itv->yuv_info.reg_2960, 0x02960); - write_reg(itv->yuv_info.reg_2964, 0x02964); - write_reg(itv->yuv_info.reg_2968, 0x02968); - write_reg(itv->yuv_info.reg_296c, 0x0296c); - write_reg(itv->yuv_info.reg_2970, 0x02970); + write_reg(yi->reg_2898 | 0x01000000, 0x2898); + + write_reg(yi->reg_2834, 0x02834); + write_reg(yi->reg_2838, 0x02838); + write_reg(yi->reg_283c, 0x0283c); + write_reg(yi->reg_2840, 0x02840); + write_reg(yi->reg_2844, 0x02844); + write_reg(yi->reg_2848, 0x02848); + write_reg(yi->reg_2854, 0x02854); + write_reg(yi->reg_285c, 0x0285c); + write_reg(yi->reg_2864, 0x02864); + write_reg(yi->reg_2870, 0x02870); + write_reg(yi->reg_2874, 0x02874); + write_reg(yi->reg_2890, 0x02890); + write_reg(yi->reg_289c, 0x0289c); + + write_reg(yi->reg_2918, 0x02918); + write_reg(yi->reg_291c, 0x0291c); + write_reg(yi->reg_2920, 0x02920); + write_reg(yi->reg_2924, 0x02924); + write_reg(yi->reg_2928, 0x02928); + write_reg(yi->reg_292c, 0x0292c); + write_reg(yi->reg_2930, 0x02930); + write_reg(yi->reg_2934, 0x02934); + write_reg(yi->reg_2938, 0x02938); + write_reg(yi->reg_293c, 0x0293c); + write_reg(yi->reg_2940, 0x02940); + write_reg(yi->reg_2944, 0x02944); + write_reg(yi->reg_2948, 0x02948); + write_reg(yi->reg_294c, 0x0294c); + write_reg(yi->reg_2950, 0x02950); + write_reg(yi->reg_2954, 0x02954); + write_reg(yi->reg_2958, 0x02958); + write_reg(yi->reg_295c, 0x0295c); + write_reg(yi->reg_2960, 0x02960); + write_reg(yi->reg_2964, 0x02964); + write_reg(yi->reg_2968, 0x02968); + write_reg(yi->reg_296c, 0x0296c); + write_reg(yi->reg_2970, 0x02970); /* Prepare to restore filters */ /* First the horizontal filter */ - if ((itv->yuv_info.reg_2834 & 0x0000FFFF) == (itv->yuv_info.reg_2834 >> 16)) { + if ((yi->reg_2834 & 0x0000FFFF) == (yi->reg_2834 >> 16)) { /* An exact size match uses filter 0 */ h_filter = 0; - } - else { + } else { /* Figure out which filter to use */ - h_filter = ((itv->yuv_info.reg_2834 << 16) / (itv->yuv_info.reg_2834 >> 16)) >> 15; + h_filter = ((yi->reg_2834 << 16) / (yi->reg_2834 >> 16)) >> 15; h_filter = (h_filter >> 1) + (h_filter & 1); /* Only an exact size match can use filter 0. */ - if (h_filter < 1) h_filter = 1; + h_filter += !h_filter; } /* Now the vertical filter */ - if ((itv->yuv_info.reg_2918 & 0x0000FFFF) == (itv->yuv_info.reg_2918 >> 16)) { + if ((yi->reg_2918 & 0x0000FFFF) == (yi->reg_2918 >> 16)) { /* An exact size match uses filter 0/1 */ v_filter_1 = 0; v_filter_2 = 1; - } - else { + } else { /* Figure out which filter to use */ - v_filter_1 = ((itv->yuv_info.reg_2918 << 16) / (itv->yuv_info.reg_2918 >> 16)) >> 15; + v_filter_1 = ((yi->reg_2918 << 16) / (yi->reg_2918 >> 16)) >> 15; v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); /* Only an exact size match can use filter 0 */ - if (v_filter_1 == 0) v_filter_1 = 1; + v_filter_1 += !v_filter_1; v_filter_2 = v_filter_1; } /* Now restore the filters */ - ivtv_yuv_filter (itv,h_filter,v_filter_1,v_filter_2); + ivtv_yuv_filter(itv, h_filter, v_filter_1, v_filter_2); /* and clear a few registers */ write_reg(0, 0x02814); @@ -1223,19 +1229,18 @@ void ivtv_yuv_close(struct ivtv *itv) write_reg(0, 0x02910); /* Release the blanking buffer */ - if (itv->yuv_info.blanking_ptr) { - kfree (itv->yuv_info.blanking_ptr); - itv->yuv_info.blanking_ptr = NULL; - pci_unmap_single(itv->dev, itv->yuv_info.blanking_dmaptr, 720*16, PCI_DMA_TODEVICE); + if (yi->blanking_ptr) { + kfree(yi->blanking_ptr); + yi->blanking_ptr = NULL; + pci_unmap_single(itv->dev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE); } /* Invalidate the old dimension information */ - itv->yuv_info.old_frame_info.src_w = 0; - itv->yuv_info.old_frame_info.src_h = 0; - itv->yuv_info.old_frame_info_args.src_w = 0; - itv->yuv_info.old_frame_info_args.src_h = 0; + yi->old_frame_info.src_w = 0; + yi->old_frame_info.src_h = 0; + yi->old_frame_info_args.src_w = 0; + yi->old_frame_info_args.src_h = 0; /* All done. */ clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags); } - -- cgit v1.2.3 From 1cd04c75ca99803aa0a1bd9b4952f2d61e72c4f4 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 22:48:52 -0600 Subject: pvrusb2: Rework pipeline state control From: Mike Isely This is a new implementation for video pipeline control within the pvrusb2 driver. Actual start/stop of the pipeline is moved to the driver's kernel thread. Pipeline stages are controlled autonomously based on surrounding pipeline or application control state. Kernel thread management is also cleaned up and moved into the internal control structure of the driver, solving a set up / tear down race along the way. Better failure recovery is implemented with this new control strategy. Also with this change comes better control of the cx23416 encoder, building on additional information learned about the peculiarities of controlling this part (this information was the original trigger for this rework). With this change, overall encoder stability should be considerably improved. Yes, this is a large change for this driver, but due to the nature of the feature being worked on, the changes are fairly pervasive and would be difficult to break into smaller pieces with any semblence of step-wise stability. Signed-off-by: Mike Isely --- .../drivers/media/video/pvrusb2/pvrusb2-context.c | 72 +- .../drivers/media/video/pvrusb2/pvrusb2-context.h | 5 +- .../media/video/pvrusb2/pvrusb2-cx2584x-v4l.c | 4 +- linux/drivers/media/video/pvrusb2/pvrusb2-debug.h | 39 +- .../drivers/media/video/pvrusb2/pvrusb2-debugifc.c | 177 +-- .../drivers/media/video/pvrusb2/pvrusb2-encoder.c | 54 +- .../drivers/media/video/pvrusb2/pvrusb2-encoder.h | 1 + .../media/video/pvrusb2/pvrusb2-hdw-internal.h | 64 +- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 1167 +++++++++++++------- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h | 138 +-- .../drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 6 +- .../media/video/pvrusb2/pvrusb2-video-v4l.c | 4 +- 13 files changed, 948 insertions(+), 785 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c index 80e2e5ea1..e59833f8f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -32,64 +32,32 @@ static void pvr2_context_destroy(struct pvr2_context *mp) { - if (mp->hdw) pvr2_hdw_destroy(mp->hdw); pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp); - if (mp->workqueue) { - flush_workqueue(mp->workqueue); - destroy_workqueue(mp->workqueue); - } + if (mp->hdw) pvr2_hdw_destroy(mp->hdw); kfree(mp); } -static void pvr2_context_trigger_poll(struct pvr2_context *mp) -{ - queue_work(mp->workqueue,&mp->workpoll); -} - - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -static void pvr2_context_poll(struct pvr2_context *mp) -#else -static void pvr2_context_poll(struct work_struct *work) -#endif -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) - struct pvr2_context *mp = - container_of(work, struct pvr2_context, workpoll); -#endif - pvr2_context_enter(mp); do { - pvr2_hdw_poll(mp->hdw); - } while (0); pvr2_context_exit(mp); -} - - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -static void pvr2_context_setup(struct pvr2_context *mp) -#else -static void pvr2_context_setup(struct work_struct *work) -#endif +static void pvr2_context_state_check(struct pvr2_context *mp) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) - struct pvr2_context *mp = - container_of(work, struct pvr2_context, workinit); + if (mp->init_flag) return; + + switch (pvr2_hdw_get_state(mp->hdw)) { + case PVR2_STATE_WARM: break; + case PVR2_STATE_ERROR: break; + case PVR2_STATE_READY: break; + case PVR2_STATE_RUN: break; + default: return; + } -#endif pvr2_context_enter(mp); do { - if (!pvr2_hdw_dev_ok(mp->hdw)) break; - pvr2_hdw_setup(mp->hdw); - pvr2_hdw_setup_poll_trigger( - mp->hdw, - (void (*)(void *))pvr2_context_trigger_poll, - mp); - if (!pvr2_hdw_dev_ok(mp->hdw)) break; - if (!pvr2_hdw_init_ok(mp->hdw)) break; + mp->init_flag = !0; mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw); if (mp->setup_func) { mp->setup_func(mp); } } while (0); pvr2_context_exit(mp); -} + } struct pvr2_context *pvr2_context_create( @@ -109,16 +77,10 @@ struct pvr2_context *pvr2_context_create( mp = NULL; goto done; } - - mp->workqueue = create_singlethread_workqueue("pvrusb2"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - INIT_WORK(&mp->workinit,(void (*)(void*))pvr2_context_setup,mp); - INIT_WORK(&mp->workpoll,(void (*)(void*))pvr2_context_poll,mp); -#else - INIT_WORK(&mp->workinit, pvr2_context_setup); - INIT_WORK(&mp->workpoll, pvr2_context_poll); -#endif - queue_work(mp->workqueue,&mp->workinit); + pvr2_hdw_set_state_callback(mp->hdw, + (void (*)(void *))pvr2_context_state_check, + mp); + pvr2_context_state_check(mp); done: return mp; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h index 0a05ea692..237ece0b9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h @@ -53,14 +53,11 @@ struct pvr2_context { struct semaphore mutex; #endif int disconnect_flag; + int init_flag; /* Called after pvr2_context initialization is complete */ void (*setup_func)(struct pvr2_context *); - /* Work queue overhead for out-of-line processing */ - struct workqueue_struct *workqueue; - struct work_struct workinit; - struct work_struct workpoll; }; struct pvr2_channel { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index c7041921a..f712b38d9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -141,7 +141,7 @@ static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = { static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt) { ctxt->client->handler = NULL; - ctxt->hdw->decoder_ctrl = NULL; + pvr2_hdw_set_decoder(ctxt->hdw,NULL); kfree(ctxt); } @@ -242,7 +242,7 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, ctxt->client = cp; ctxt->hdw = hdw; ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; - hdw->decoder_ctrl = &ctxt->ctrl; + pvr2_hdw_set_decoder(hdw,&ctxt->ctrl); cp->handler = &ctxt->handler; { /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h b/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h index da6441b88..fca49d8a9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h @@ -34,25 +34,26 @@ extern int pvrusb2_debug; #define PVR2_TRACE_INIT (1 << 5) /* misc initialization steps */ #define PVR2_TRACE_START_STOP (1 << 6) /* Streaming start / stop */ #define PVR2_TRACE_CTL (1 << 7) /* commit of control changes */ -#define PVR2_TRACE_DEBUG (1 << 8) /* Temporary debug code */ -#define PVR2_TRACE_EEPROM (1 << 9) /* eeprom parsing / report */ -#define PVR2_TRACE_STRUCT (1 << 10) /* internal struct creation */ -#define PVR2_TRACE_OPEN_CLOSE (1 << 11) /* application open / close */ -#define PVR2_TRACE_CREG (1 << 12) /* Main critical region entry / exit */ -#define PVR2_TRACE_SYSFS (1 << 13) /* Sysfs driven I/O */ -#define PVR2_TRACE_FIRMWARE (1 << 14) /* firmware upload actions */ -#define PVR2_TRACE_CHIPS (1 << 15) /* chip broadcast operation */ -#define PVR2_TRACE_I2C (1 << 16) /* I2C related stuff */ -#define PVR2_TRACE_I2C_CMD (1 << 17) /* Software commands to I2C modules */ -#define PVR2_TRACE_I2C_CORE (1 << 18) /* I2C core debugging */ -#define PVR2_TRACE_I2C_TRAF (1 << 19) /* I2C traffic through the adapter */ -#define PVR2_TRACE_V4LIOCTL (1 << 20) /* v4l ioctl details */ -#define PVR2_TRACE_ENCODER (1 << 21) /* mpeg2 encoder operation */ -#define PVR2_TRACE_BUF_POOL (1 << 22) /* Track buffer pool management */ -#define PVR2_TRACE_BUF_FLOW (1 << 23) /* Track buffer flow in system */ -#define PVR2_TRACE_DATA_FLOW (1 << 24) /* Track data flow */ -#define PVR2_TRACE_DEBUGIFC (1 << 25) /* Debug interface actions */ -#define PVR2_TRACE_GPIO (1 << 26) /* GPIO state bit changes */ +#define PVR2_TRACE_STATE (1 << 8) /* Device state changes */ +#define PVR2_TRACE_STBITS (1 << 9) /* Individual bit state changes */ +#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */ +#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */ +#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */ +#define PVR2_TRACE_CREG (1 << 13) /* Main critical region entry / exit */ +#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */ +#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */ +#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */ +#define PVR2_TRACE_I2C (1 << 17) /* I2C related stuff */ +#define PVR2_TRACE_I2C_CMD (1 << 18) /* Software commands to I2C modules */ +#define PVR2_TRACE_I2C_CORE (1 << 19) /* I2C core debugging */ +#define PVR2_TRACE_I2C_TRAF (1 << 20) /* I2C traffic through the adapter */ +#define PVR2_TRACE_V4LIOCTL (1 << 21) /* v4l ioctl details */ +#define PVR2_TRACE_ENCODER (1 << 22) /* mpeg2 encoder operation */ +#define PVR2_TRACE_BUF_POOL (1 << 23) /* Track buffer pool management */ +#define PVR2_TRACE_BUF_FLOW (1 << 24) /* Track buffer flow in system */ +#define PVR2_TRACE_DATA_FLOW (1 << 25) /* Track data flow */ +#define PVR2_TRACE_DEBUGIFC (1 << 26) /* Debug interface actions */ +#define PVR2_TRACE_GPIO (1 << 27) /* GPIO state bit changes */ #endif /* __PVRUSB2_HDW_INTERNAL_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index 6f135f4a2..b0687430f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -31,14 +31,6 @@ struct debugifc_mask_item { unsigned long msk; }; -static struct debugifc_mask_item mask_items[] = { - {"ENC_FIRMWARE",(1<name)) { - return mip->msk; - } - } - return 0; -} - - -static int debugifc_print_mask(char *buf,unsigned int sz, - unsigned long msk,unsigned long val) -{ - struct debugifc_mask_item *mip; - unsigned int idx; - int bcnt = 0; - int ccnt; - for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) { - mip = mask_items + idx; - if (!(mip->msk & msk)) continue; - ccnt = scnprintf(buf,sz,"%s%c%s", - (bcnt ? " " : ""), - ((mip->msk & val) ? '+' : '-'), - mip->name); - sz -= ccnt; - buf += ccnt; - bcnt += ccnt; - } - return bcnt; -} - -static unsigned int debugifc_parse_subsys_mask(const char *buf, - unsigned int count, - unsigned long *mskPtr, - unsigned long *valPtr) -{ - const char *wptr; - unsigned int consume_cnt = 0; - unsigned int scnt; - unsigned int wlen; - int mode; - unsigned long m1,msk,val; - - msk = 0; - val = 0; - - while (count) { - scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); - if (!scnt) break; - consume_cnt += scnt; count -= scnt; buf += scnt; - if (!wptr) break; - - mode = 0; - if (wlen) switch (wptr[0]) { - case '+': - wptr++; - wlen--; - break; - case '-': - mode = 1; - wptr++; - wlen--; - break; - } - if (!wlen) continue; - m1 = debugifc_find_mask(wptr,wlen); - if (!m1) break; - msk |= m1; - if (!mode) val |= m1; - } - *mskPtr = msk; - *valPtr = val; - return consume_cnt; -} - - int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) { int bcnt = 0; int ccnt; - struct pvr2_hdw_debug_info dbg; - - pvr2_hdw_get_debug_info(hdw,&dbg); - - ccnt = scnprintf(buf,acnt,"big lock %s; ctl lock %s", - (dbg.big_lock_held ? "held" : "free"), - (dbg.ctl_lock_held ? "held" : "free")); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - if (dbg.ctl_lock_held) { - ccnt = scnprintf(buf,acnt,"; cmd_state=%d cmd_code=%d" - " cmd_wlen=%d cmd_rlen=%d" - " wpend=%d rpend=%d tmout=%d rstatus=%d" - " wstatus=%d", - dbg.cmd_debug_state,dbg.cmd_code, - dbg.cmd_debug_write_len, - dbg.cmd_debug_read_len, - dbg.cmd_debug_write_pend, - dbg.cmd_debug_read_pend, - dbg.cmd_debug_timeout, - dbg.cmd_debug_rstatus, - dbg.cmd_debug_wstatus); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - } - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf( - buf,acnt,"driver flags: %s %s %s\n", - (dbg.flag_init_ok ? "initialized" : "uninitialized"), - (dbg.flag_ok ? "ok" : "fail"), - (dbg.flag_disconnected ? "disconnected" : "connected")); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: "); + ccnt = scnprintf(buf,acnt,"Driver state info:\n"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = debugifc_print_mask(buf,acnt,dbg.subsys_flags,dbg.subsys_flags); + ccnt = pvr2_hdw_state_report(hdw,buf,acnt); bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: "); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = debugifc_print_mask(buf,acnt,~dbg.subsys_flags,dbg.subsys_flags); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; ccnt = pvr2_i2c_report(hdw,buf,acnt); @@ -290,7 +162,6 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, { int bcnt = 0; int ccnt; - unsigned long msk; int ret; u32 gpio_dir,gpio_in,gpio_out; @@ -311,28 +182,6 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, pvr2_hdw_get_streaming(hdw) ? "on" : "off"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; - msk = pvr2_hdw_subsys_get(hdw); - ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: "); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = debugifc_print_mask(buf,acnt,msk,msk); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: "); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = debugifc_print_mask(buf,acnt,~msk,msk); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - - msk = pvr2_hdw_subsys_stream_get(hdw); - ccnt = scnprintf(buf,acnt,"Subsystems stopped on stream shutdown: "); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = debugifc_print_mask(buf,acnt,msk,msk); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - return bcnt; } @@ -369,28 +218,10 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, return pvr2_upload_firmware2(hdw); } else if (debugifc_match_keyword(wptr,wlen,"decoder")) { return pvr2_hdw_cmd_decoder_reset(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"worker")) { + return pvr2_hdw_untrip(hdw); } return -EINVAL; - } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) { - unsigned long msk = 0; - unsigned long val = 0; - if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) { - pvr2_trace(PVR2_TRACE_DEBUGIFC, - "debugifc parse error on subsys mask"); - return -EINVAL; - } - pvr2_hdw_subsys_bit_chg(hdw,msk,val); - return 0; - } else if (debugifc_match_keyword(wptr,wlen,"stream_flags")) { - unsigned long msk = 0; - unsigned long val = 0; - if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) { - pvr2_trace(PVR2_TRACE_DEBUGIFC, - "debugifc parse error on stream mask"); - return -EINVAL; - } - pvr2_hdw_subsys_stream_bit_chg(hdw,msk,val); - return 0; } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); if (!scnt) return -EINVAL; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 7a27dece2..f4394d610 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -231,7 +231,7 @@ static int pvr2_encoder_cmd(void *ctxt, LOCK_TAKE(hdw->ctl_lock); do { - if (!hdw->flag_encoder_ok) { + if (!hdw->state_encoder_ok) { ret = -EIO; break; } @@ -300,7 +300,11 @@ static int pvr2_encoder_cmd(void *ctxt, ret = -EBUSY; } if (ret) { - hdw->flag_encoder_ok = 0; + hdw->state_encoder_ok = 0; + pvr2_trace(PVR2_TRACE_STBITS, + "State bit %s <-- %s", + "state_encoder_ok", + (hdw->state_encoder_ok ? "true" : "false")); pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Giving up on command." @@ -427,6 +431,24 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) return ret; } +int pvr2_encoder_adjust(struct pvr2_hdw *hdw) +{ + int ret; + ret = cx2341x_update(hdw,pvr2_encoder_cmd, + (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), + &hdw->enc_ctl_state); + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Error from cx2341x module code=%d",ret); + } else { + memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state, + sizeof(struct cx2341x_mpeg_params)); + hdw->enc_cur_valid = !0; + } + return ret; +} + + int pvr2_encoder_configure(struct pvr2_hdw *hdw) { int ret; @@ -469,18 +491,10 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) return ret; } - ret = cx2341x_update(hdw,pvr2_encoder_cmd, - (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), - &hdw->enc_ctl_state); - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Error from cx2341x module code=%d",ret); - return ret; - } - - ret = 0; + ret = pvr2_encoder_adjust(hdw); + if (ret) return ret; - if (!ret) ret = pvr2_encoder_vcmd( + ret = pvr2_encoder_vcmd( hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); if (ret) { @@ -489,10 +503,6 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) return ret; } - hdw->subsys_enabled_mask |= (1<enc_cur_state,&hdw->enc_ctl_state, - sizeof(struct cx2341x_mpeg_params)); - hdw->enc_cur_valid = !0; return 0; } @@ -511,7 +521,7 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw) pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); - switch (hdw->config) { + switch (hdw->active_stream_type) { case pvr2_config_vbi: status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 0x01,0x14); @@ -525,9 +535,6 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw) 0,0x13); break; } - if (!status) { - hdw->subsys_enabled_mask |= (1<config) { + switch (hdw->active_stream_type) { case pvr2_config_vbi: status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 0x01,0x01,0x14); @@ -559,9 +566,6 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw) pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401); pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); - if (!status) { - hdw->subsys_enabled_mask &= ~(1< #include +#include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) #include #else @@ -183,6 +184,12 @@ struct pvr2_hdw { /* Device type, one of PVR2_HDW_TYPE_xxxxx */ unsigned int hdw_type; + /* Kernel worker thread handling */ + struct workqueue_struct *workqueue; + struct work_struct workpoll; /* Update driver state */ + struct work_struct worki2csync; /* Update i2c clients */ + struct work_struct workinit; /* Driver initialization sequence */ + /* Video spigot */ struct pvr2_stream *vid_stream; @@ -194,9 +201,6 @@ struct pvr2_hdw { #endif int big_lock_held; /* For debugging */ - void (*poll_trigger_func)(void *); - void *poll_trigger_data; - char name[32]; /* I2C stuff */ @@ -241,14 +245,48 @@ struct pvr2_hdw { unsigned int cmd_debug_write_len; // unsigned int cmd_debug_read_len; // + /* Bits of state that describe what is going on with various parts + of the driver. */ + volatile int state_encoder_ok; /* Encoder is operational */ + volatile int state_encoder_run; /* Encoder is running */ + volatile int state_encoder_config; /* Encoder is configured */ + volatile int state_encoder_waitok; /* Encoder pre-wait done */ + volatile int state_decoder_run; /* Decoder is running */ + volatile int state_usbstream_run; /* FX2 is streaming */ + volatile int state_decoder_quiescent; /* Decoder idle for > 50msec */ + volatile int state_pipeline_config; /* Pipeline is configured */ + int state_pipeline_req; /* Somebody wants to stream */ + int state_pipeline_pause; /* Pipeline must be paused */ + int state_pipeline_idle; /* Pipeline not running */ + + /* This is the master state of the driver. It is the combined + result of other bits of state. Examining this will indicate the + overall state of the driver. Values here are one of + PVR2_STATE_xxxx */ + unsigned int master_state; + + /* True if states must be re-evaluated */ + int state_stale; + + void (*state_func)(void *); + void *state_data; + + /* Timer for measuring decoder settling time */ + struct timer_list quiescent_timer; + + /* Timer for measuring encoder pre-wait time */ + struct timer_list encoder_wait_timer; + + /* Place to block while waiting for state changes */ + wait_queue_head_t state_wait_data; + + int flag_ok; /* device in known good state */ int flag_disconnected; /* flag_ok == 0 due to disconnect */ int flag_init_ok; /* true if structure is fully initialized */ - int flag_streaming_enabled; /* true if streaming should be on */ int fw1_state; /* current situation with fw1 */ - int flag_encoder_ok; /* True if encoder is healthy */ - - int flag_decoder_is_tuned; + int flag_decoder_missed;/* We've noticed missing decoder */ + int flag_tripped; /* Indicates overall failure to start */ struct pvr2_decoder_ctrl *decoder_ctrl; @@ -257,12 +295,6 @@ struct pvr2_hdw { unsigned int fw_size; int fw_cpu_flag; /* True if we are dealing with the CPU */ - // Which subsystem pieces have been enabled / configured - unsigned long subsys_enabled_mask; - - // Which subsystems are manipulated to enable streaming - unsigned long subsys_stream_mask; - // True if there is a request to trigger logging of state in each // module. int log_requested; @@ -312,13 +344,16 @@ struct pvr2_hdw { /* Location of eeprom or a negative number if none */ int eeprom_addr; - enum pvr2_config config; + enum pvr2_config active_stream_type; + enum pvr2_config desired_stream_type; /* Control state needed for cx2341x module */ struct cx2341x_mpeg_params enc_cur_state; struct cx2341x_mpeg_params enc_ctl_state; /* True if an encoder attribute has changed */ int enc_stale; + /* True if an unsafe encoder attribute has changed */ + int enc_unsafe_stale; /* True if enc_cur_state is valid */ int enc_cur_valid; @@ -348,6 +383,7 @@ struct pvr2_hdw { /* This function gets the current frequency */ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); +void pvr2_hdw_set_decoder(struct pvr2_hdw *,struct pvr2_decoder_ctrl *); #endif /* __PVRUSB2_HDW_INTERNAL_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 328bd1ba6..2119aae33 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -247,32 +247,46 @@ static const char *control_values_hsm[] = { }; -static const char *control_values_subsystem[] = { - [PVR2_SUBSYS_B_ENC_FIRMWARE] = "enc_firmware", - [PVR2_SUBSYS_B_ENC_CFG] = "enc_config", - [PVR2_SUBSYS_B_DIGITIZER_RUN] = "digitizer_run", - [PVR2_SUBSYS_B_USBSTREAM_RUN] = "usbstream_run", - [PVR2_SUBSYS_B_ENC_RUN] = "enc_run", +static const char *pvr2_state_names[] = { + [PVR2_STATE_NONE] = "none", + [PVR2_STATE_DEAD] = "dead", + [PVR2_STATE_COLD] = "cold", + [PVR2_STATE_WARM] = "warm", + [PVR2_STATE_ERROR] = "error", + [PVR2_STATE_READY] = "ready", + [PVR2_STATE_RUN] = "run", }; + +static void pvr2_hdw_state_sched(struct pvr2_hdw *); +static int pvr2_hdw_state_eval(struct pvr2_hdw *); static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); +static void pvr2_hdw_worker_i2c(struct work_struct *work); +static void pvr2_hdw_worker_poll(struct work_struct *work); +static void pvr2_hdw_worker_init(struct work_struct *work); +static int pvr2_hdw_wait(struct pvr2_hdw *,int state); +static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *); +static void pvr2_hdw_state_log_state(struct pvr2_hdw *); static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); -static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); +static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw); static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); -static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw); -static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, - unsigned long msk, - unsigned long val); -static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw, - unsigned long msk, - unsigned long val); +static void pvr2_hdw_quiescent_timeout(unsigned long); +static void pvr2_hdw_encoder_wait_timeout(unsigned long); static int pvr2_send_request_ex(struct pvr2_hdw *hdw, unsigned int timeout,int probe_fl, void *write_data,unsigned int write_len, void *read_data,unsigned int read_len); + +static void trace_stbit(const char *name,int val) +{ + pvr2_trace(PVR2_TRACE_STBITS, + "State bit %s <-- %s", + name,(val ? "true" : "false")); +} + static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp) { struct pvr2_hdw *hdw = cptr->hdw; @@ -481,6 +495,7 @@ static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr) static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr) { cptr->hdw->enc_stale = 0; + cptr->hdw->enc_unsafe_stale = 0; } static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp) @@ -503,6 +518,7 @@ static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp) static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v) { int ret; + struct pvr2_hdw *hdw = cptr->hdw; struct v4l2_ext_controls cs; struct v4l2_ext_control c1; memset(&cs,0,sizeof(cs)); @@ -511,10 +527,22 @@ static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v) cs.count = 1; c1.id = cptr->info->v4l_id; c1.value = v; - ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs, + ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state, + hdw->state_encoder_run, &cs, VIDIOC_S_EXT_CTRLS); + if (ret == -EBUSY) { + /* Oops. cx2341x is telling us it's not safe to change + this control while we're capturing. Make a note of this + fact so that the pipeline will be stopped the next time + controls are committed. Then go on ahead and store this + change anyway. */ + ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state, + 0, &cs, + VIDIOC_S_EXT_CTRLS); + if (!ret) hdw->enc_unsafe_stale = !0; + } if (ret) return ret; - cptr->hdw->enc_stale = !0; + hdw->enc_stale = !0; return 0; } @@ -545,7 +573,13 @@ static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr) static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = cptr->hdw->flag_streaming_enabled; + *vp = cptr->hdw->state_pipeline_req; + return 0; +} + +static int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->master_state; return 0; } @@ -658,29 +692,6 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) return 0; } -static int ctrl_subsys_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->subsys_enabled_mask; - return 0; -} - -static int ctrl_subsys_set(struct pvr2_ctrl *cptr,int m,int v) -{ - pvr2_hdw_subsys_bit_chg_no_lock(cptr->hdw,m,v); - return 0; -} - -static int ctrl_subsys_stream_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->subsys_stream_mask; - return 0; -} - -static int ctrl_subsys_stream_set(struct pvr2_ctrl *cptr,int m,int v) -{ - pvr2_hdw_subsys_stream_bit_chg_no_lock(cptr->hdw,m,v); - return 0; -} static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v) { @@ -915,6 +926,11 @@ static const struct pvr2_ctl_info control_defs[] = { .name = "usb_speed", .get_value = ctrl_hsm_get, DEFENUM(control_values_hsm), + },{ + .desc = "Master State", + .name = "master_state", + .get_value = ctrl_masterstate_get, + DEFENUM(pvr2_state_names), },{ .desc = "Signal Present", .name = "signal_present", @@ -955,20 +971,6 @@ static const struct pvr2_ctl_info control_defs[] = { .val_to_sym = ctrl_std_val_to_sym, .sym_to_val = ctrl_std_sym_to_val, .type = pvr2_ctl_bitmask, - },{ - .desc = "Subsystem enabled mask", - .name = "debug_subsys_mask", - .skip_init = !0, - .get_value = ctrl_subsys_get, - .set_value = ctrl_subsys_set, - DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem), - },{ - .desc = "Subsystem stream mask", - .name = "debug_subsys_stream_mask", - .skip_init = !0, - .get_value = ctrl_subsys_stream_get, - .set_value = ctrl_subsys_stream_set, - DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem), },{ .desc = "Video Standard Name", .name = "video_standard", @@ -1258,8 +1260,6 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) time we configure the encoder, then we'll fully configure it. */ hdw->enc_cur_valid = 0; - hdw->flag_encoder_ok = 0; - /* First prepare firmware loading */ ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ @@ -1357,310 +1357,129 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload post-proc failure"); - } else { - hdw->flag_encoder_ok = !0; - hdw->subsys_enabled_mask |= (1<flag_ok) return; - - msk &= PVR2_SUBSYS_ALL; - nmsk = (hdw->subsys_enabled_mask & ~msk) | (val & msk); - nmsk &= PVR2_SUBSYS_ALL; - - for (;;) { - tryCount++; - if (!((nmsk ^ hdw->subsys_enabled_mask) & - PVR2_SUBSYS_ALL)) break; - if (tryCount > 4) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Too many retries when configuring device;" - " giving up"); - pvr2_hdw_render_useless(hdw); - break; - } - if (tryCount > 1) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Retrying device reconfiguration"); - } - pvr2_trace(PVR2_TRACE_INIT, - "subsys mask changing 0x%lx:0x%lx" - " from 0x%lx to 0x%lx", - msk,val,hdw->subsys_enabled_mask,nmsk); - - vmsk = (nmsk ^ hdw->subsys_enabled_mask) & - hdw->subsys_enabled_mask; - if (vmsk) { - if (vmsk & (1<subsys_enabled_mask &= - ~FIRMWARE_RECOVERY_BITS; - continue; - } - } - if (vmsk & (1<decoder_ctrl) { - hdw->decoder_ctrl->enable( - hdw->decoder_ctrl->ctxt,0); - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING:" - " No decoder present"); - } - hdw->subsys_enabled_mask &= - ~(1<subsys_enabled_mask &= - ~(vmsk & PVR2_SUBSYS_CFG_ALL); - } - } - vmsk = (nmsk ^ hdw->subsys_enabled_mask) & nmsk; - if (vmsk) { - if (vmsk & (1<subsys_enabled_mask &= - ~FIRMWARE_RECOVERY_BITS; - continue; - } - } - if (vmsk & (1<decoder_ctrl) { - hdw->decoder_ctrl->enable( - hdw->decoder_ctrl->ctxt,!0); - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING:" - " No decoder present"); - } - hdw->subsys_enabled_mask |= - (1<subsys_enabled_mask &= - ~FIRMWARE_RECOVERY_BITS; - continue; - } - } - } - } -} - - -void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw, - unsigned long msk,unsigned long val) +static const char *pvr2_get_state_name(unsigned int st) { - LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_subsys_bit_chg_no_lock(hdw,msk,val); - } while (0); LOCK_GIVE(hdw->big_lock); -} - -#if 0 -/* Shortcut for pvr2_hdw_subsys_bit_chg(hdw,msk,msk) */ -void pvr2_hdw_subsys_bit_set(struct pvr2_hdw *hdw,unsigned long msk) -{ - pvr2_hdw_subsys_bit_chg(hdw,msk,msk); + if (st < ARRAY_SIZE(pvr2_state_names)) { + return pvr2_state_names[st]; + } + return "???"; } - -#endif /* 0 */ -#if 0 -/* Shortcut for pvr2_hdw_subsys_bit_chg(hdw,msk,0) */ -void pvr2_hdw_subsys_bit_clr(struct pvr2_hdw *hdw,unsigned long msk) +static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl) { - pvr2_hdw_subsys_bit_chg(hdw,msk,0); + if (!hdw->decoder_ctrl) { + if (!hdw->flag_decoder_missed) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: No decoder present"); + hdw->flag_decoder_missed = !0; + trace_stbit("flag_decoder_missed", + hdw->flag_decoder_missed); + } + return -EIO; + } + hdw->decoder_ctrl->enable(hdw->decoder_ctrl->ctxt,enablefl); + return 0; } -#endif /* 0 */ -unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *hdw) +void pvr2_hdw_set_decoder(struct pvr2_hdw *hdw,struct pvr2_decoder_ctrl *ptr) { - return hdw->subsys_enabled_mask; + if (hdw->decoder_ctrl == ptr) return; + hdw->decoder_ctrl = ptr; + if (hdw->decoder_ctrl && hdw->flag_decoder_missed) { + hdw->flag_decoder_missed = 0; + trace_stbit("flag_decoder_missed", + hdw->flag_decoder_missed); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Decoder has appeared"); + pvr2_hdw_state_sched(hdw); + } } -unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *hdw) +int pvr2_hdw_get_state(struct pvr2_hdw *hdw) { - return hdw->subsys_stream_mask; + return hdw->master_state; } -static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw, - unsigned long msk, - unsigned long val) +static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw) { - unsigned long val2; - msk &= PVR2_SUBSYS_ALL; - val2 = ((hdw->subsys_stream_mask & ~msk) | (val & msk)); - pvr2_trace(PVR2_TRACE_INIT, - "stream mask changing 0x%lx:0x%lx from 0x%lx to 0x%lx", - msk,val,hdw->subsys_stream_mask,val2); - hdw->subsys_stream_mask = val2; + if (!hdw->flag_tripped) return 0; + hdw->flag_tripped = 0; + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Clearing driver error statuss"); + return !0; } -void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw, - unsigned long msk, - unsigned long val) +int pvr2_hdw_untrip(struct pvr2_hdw *hdw) { + int fl; LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,msk,val); + fl = pvr2_hdw_untrip_unlocked(hdw); } while (0); LOCK_GIVE(hdw->big_lock); + if (fl) pvr2_hdw_state_sched(hdw); + return 0; } -static int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl) +const char *pvr2_hdw_get_state_name(unsigned int id) { - if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0; - if (enableFl) { - pvr2_trace(PVR2_TRACE_START_STOP, - "/*--TRACE_STREAM--*/ enable"); - pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,~0); - } else { - pvr2_trace(PVR2_TRACE_START_STOP, - "/*--TRACE_STREAM--*/ disable"); - pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0); - } - if (!hdw->flag_ok) return -EIO; - hdw->flag_streaming_enabled = enableFl != 0; - return 0; + if (id >= ARRAY_SIZE(pvr2_state_names)) return NULL; + return pvr2_state_names[id]; } int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw) { - return hdw->flag_streaming_enabled != 0; + return hdw->state_pipeline_req != 0; } int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag) { - int ret; + int ret,st; LOCK_TAKE(hdw->big_lock); do { - ret = pvr2_hdw_set_streaming_no_lock(hdw,enable_flag); + pvr2_hdw_untrip_unlocked(hdw); + if ((!enable_flag) != !(hdw->state_pipeline_req)) { + hdw->state_pipeline_req = enable_flag != 0; + pvr2_trace(PVR2_TRACE_START_STOP, + "/*--TRACE_STREAM--*/ %s", + enable_flag ? "enable" : "disable"); + } + pvr2_hdw_state_sched(hdw); } while (0); LOCK_GIVE(hdw->big_lock); - return ret; -} - - -static int pvr2_hdw_set_stream_type_no_lock(struct pvr2_hdw *hdw, - enum pvr2_config config) -{ - unsigned long sm = hdw->subsys_enabled_mask; - if (!hdw->flag_ok) return -EIO; - pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0); - hdw->config = config; - pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,sm); + if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret; + if (enable_flag) { + while ((st = hdw->master_state) != PVR2_STATE_RUN) { + if (st != PVR2_STATE_READY) return -EIO; + if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret; + } + } return 0; } int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config) { - int ret; - if (!hdw->flag_ok) return -EIO; + int fl; LOCK_TAKE(hdw->big_lock); - ret = pvr2_hdw_set_stream_type_no_lock(hdw,config); + if ((fl = (hdw->desired_stream_type != config)) != 0) { + hdw->desired_stream_type = config; + hdw->state_pipeline_config = 0; + trace_stbit("state_pipeline_config", + hdw->state_pipeline_config); + pvr2_hdw_state_sched(hdw); + } LOCK_GIVE(hdw->big_lock); - return ret; + if (fl) return 0; + return pvr2_hdw_wait(hdw,0); } @@ -1893,12 +1712,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) { pvr2_hdw_cmd_powerup(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; - - if (pvr2_upload_firmware2(hdw)){ - pvr2_trace(PVR2_TRACE_ERROR_LEGS,"device unstable!!"); - pvr2_hdw_render_useless(hdw); - return; - } } // This step MUST happen after the earlier powerup step. @@ -1951,8 +1764,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_hdw_commit_ctl_internal(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; + pvr2_hdw_commit_setup(hdw); hdw->vid_stream = pvr2_stream_create(); if (!pvr2_hdw_dev_ok(hdw)) return; @@ -1972,25 +1784,25 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; - /* Make sure everything is up to date */ - pvr2_i2c_core_sync(hdw); - - if (!pvr2_hdw_dev_ok(hdw)) return; - hdw->flag_init_ok = !0; + + pvr2_hdw_state_sched(hdw); } -int pvr2_hdw_setup(struct pvr2_hdw *hdw) +/* Set up the structure and attempt to put the device into a usable state. + This can be a time-consuming operation, which is why it is not done + internally as part of the create() step. */ +static void pvr2_hdw_setup(struct pvr2_hdw *hdw) { pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw); - LOCK_TAKE(hdw->big_lock); do { + do { pvr2_hdw_setup_low(hdw); pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d", - hdw,hdw->flag_ok,hdw->flag_init_ok); + hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok); if (pvr2_hdw_dev_ok(hdw)) { - if (pvr2_hdw_init_ok(hdw)) { + if (hdw->flag_init_ok) { pvr2_trace( PVR2_TRACE_INFO, "Device initialization" @@ -2040,9 +1852,8 @@ int pvr2_hdw_setup(struct pvr2_hdw *hdw) " the pvrusb2 device" " in order to recover."); } - } while (0); LOCK_GIVE(hdw->big_lock); + } while (0); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw); - return hdw->flag_init_ok; } @@ -2071,6 +1882,19 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", hdw,pvr2_device_names[hdw_type]); if (!hdw) goto fail; + + init_timer(&hdw->quiescent_timer); + hdw->quiescent_timer.data = (unsigned long)hdw; + hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout; + + init_timer(&hdw->encoder_wait_timer); + hdw->encoder_wait_timer.data = (unsigned long)hdw; + hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout; + + hdw->master_state = PVR2_STATE_DEAD; + + init_waitqueue_head(&hdw->state_wait_data); + hdw->tuner_signal_stale = !0; cx2341x_fill_defaults(&hdw->enc_ctl_state); @@ -2211,18 +2035,25 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1; hdw->name[cnt1] = 0; + hdw->workqueue = create_singlethread_workqueue(hdw->name); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + INIT_WORK(&hdw->workpoll,(void (*)(void*))pvr2_hdw_worker_poll, + &hdw->workpoll); + INIT_WORK(&hdw->worki2csync,(void (*)(void*))pvr2_hdw_worker_i2c, + &hdw->worki2csync); + INIT_WORK(&hdw->workinit,(void (*)(void*))pvr2_hdw_worker_init, + &hdw->workinit); +#else + INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll); + INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c); + INIT_WORK(&hdw->workinit,pvr2_hdw_worker_init); +#endif + pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", hdw->unit_number,hdw->name); hdw->tuner_type = -1; hdw->flag_ok = !0; - /* Initialize the mask of subsystems that we will shut down when we - stop streaming. */ - hdw->subsys_stream_mask = PVR2_SUBSYS_RUN_ALL; - hdw->subsys_stream_mask |= (1<subsys_stream_mask); hdw->usb_intf = intf; hdw->usb_dev = interface_to_usbdev(intf); @@ -2238,15 +2069,25 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, mutex_init(&hdw->ctl_lock_mutex); mutex_init(&hdw->big_lock_mutex); + queue_work(hdw->workqueue,&hdw->workinit); return hdw; fail: if (hdw) { + del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_wait_timer); + if (hdw->workqueue) { + flush_workqueue(hdw->workqueue); + destroy_workqueue(hdw->workqueue); + hdw->workqueue = NULL; + } usb_free_urb(hdw->ctl_read_urb); usb_free_urb(hdw->ctl_write_urb); kfree(hdw->ctl_read_buffer); kfree(hdw->ctl_write_buffer); kfree(hdw->controls); kfree(hdw->mpeg_ctrl_info); + kfree(hdw->std_defs); + kfree(hdw->std_enum_names); kfree(hdw); } return NULL; @@ -2277,10 +2118,10 @@ static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw) kfree(hdw->ctl_write_buffer); hdw->ctl_write_buffer = NULL; } - pvr2_hdw_render_useless_unlocked(hdw); hdw->flag_disconnected = !0; hdw->usb_dev = NULL; hdw->usb_intf = NULL; + pvr2_hdw_render_useless(hdw); } @@ -2289,6 +2130,13 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) { if (!hdw) return; pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw); + del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_wait_timer); + if (hdw->workqueue) { + flush_workqueue(hdw->workqueue); + destroy_workqueue(hdw->workqueue); + hdw->workqueue = NULL; + } if (hdw->fw_buffer) { kfree(hdw->fw_buffer); hdw->fw_buffer = NULL; @@ -2317,12 +2165,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) } -int pvr2_hdw_init_ok(struct pvr2_hdw *hdw) -{ - return hdw->flag_init_ok; -} - - int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw) { return (hdw && hdw->flag_ok); @@ -2500,17 +2342,11 @@ static const char *get_ctrl_typename(enum pvr2_ctl_type tp) } -/* Commit all control changes made up to this point. Subsystems can be - indirectly affected by these changes. For a given set of things being - committed, we'll clear the affected subsystem bits and then once we're - done committing everything we'll make a request to restore the subsystem - state(s) back to their previous value before this function was called. - Thus we can automatically reconfigure affected pieces of the driver as - controls are changed. */ -static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) +/* Figure out if we need to commit control changes. If so, mark internal + state flags to indicate this fact and return true. Otherwise do nothing + else and return false. */ +static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw) { - unsigned long saved_subsys_mask = hdw->subsys_enabled_mask; - unsigned long stale_subsys_mask = 0; unsigned int idx; struct pvr2_ctrl *cptr; int value; @@ -2545,6 +2381,25 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) return 0; } + hdw->state_pipeline_config = 0; + trace_stbit("state_pipeline_config",hdw->state_pipeline_config); + pvr2_hdw_state_sched(hdw); + + return !0; +} + + +/* Perform all operations needed to commit all control changes. This must + be performed in synchronization with the pipeline state and is thus + expected to be called as part of the driver's worker thread. Return + true if commit successful, otherwise return false to indicate that + commit isn't possible at this time. */ +static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) +{ + unsigned int idx; + struct pvr2_ctrl *cptr; + int disruptive_change; + /* When video standard changes, reset the hres and vres values - but if the user has pending changes there, then let the changes take priority. */ @@ -2563,27 +2418,26 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) } } - if (hdw->std_dirty || - hdw->enc_stale || - hdw->srate_dirty || - hdw->res_ver_dirty || - hdw->res_hor_dirty || - 0) { - /* If any of this changes, then the encoder needs to be - reconfigured, and we need to reset the stream. */ - stale_subsys_mask |= (1<subsys_stream_mask; -#endif - } - - if (hdw->input_dirty) { - /* pk: If input changes to or from radio, then the encoder - needs to be restarted (for ENC_MUTE_VIDEO to work) */ - stale_subsys_mask |= (1< encoder connection be made quiescent before we + can proceed. */ + disruptive_change = + (hdw->std_dirty || + hdw->enc_unsafe_stale || + hdw->srate_dirty || + hdw->res_ver_dirty || + hdw->res_hor_dirty || + hdw->input_dirty || + (hdw->active_stream_type != hdw->desired_stream_type)); + if (disruptive_change && !hdw->state_pipeline_idle) { + /* Pipeline is not idle; we can't proceed. Arrange to + cause pipeline to stop so that we can try this again + later.... */ + hdw->state_pipeline_pause = !0; + return 0; } - if (hdw->srate_dirty) { /* Write new sample rate into control structure since * the master copy is stale. We must track srate @@ -2612,60 +2466,88 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) cptr->info->clear_dirty(cptr); } + if (hdw->active_stream_type != hdw->desired_stream_type) { + /* Handle any side effects of stream config here */ + hdw->active_stream_type = hdw->desired_stream_type; + } + /* Now execute i2c core update */ pvr2_i2c_core_sync(hdw); - pvr2_hdw_subsys_bit_chg_no_lock(hdw,stale_subsys_mask,0); - pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,saved_subsys_mask); + if (hdw->state_encoder_run) { + /* If encoder isn't running, then this will get worked out + later when we start the encoder. */ + if (pvr2_encoder_adjust(hdw) < 0) return !0; + } - return 0; + hdw->state_pipeline_config = !0; + trace_stbit("state_pipeline_config",hdw->state_pipeline_config); + return !0; } int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw) { + int fl; + LOCK_TAKE(hdw->big_lock); + fl = pvr2_hdw_commit_setup(hdw); + LOCK_GIVE(hdw->big_lock); + if (!fl) return 0; + return pvr2_hdw_wait(hdw,0); +} + + +static void pvr2_hdw_worker_i2c(struct work_struct *work) +{ + struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,worki2csync); LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_commit_ctl_internal(hdw); + pvr2_i2c_core_sync(hdw); } while (0); LOCK_GIVE(hdw->big_lock); - return 0; } -void pvr2_hdw_poll(struct pvr2_hdw *hdw) +static void pvr2_hdw_worker_poll(struct work_struct *work) { + int fl = 0; + struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll); LOCK_TAKE(hdw->big_lock); do { - pvr2_i2c_core_sync(hdw); + fl = pvr2_hdw_state_eval(hdw); } while (0); LOCK_GIVE(hdw->big_lock); + if (fl && hdw->state_func) { + hdw->state_func(hdw->state_data); + } } -void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *hdw, - void (*func)(void *), - void *data) +static void pvr2_hdw_worker_init(struct work_struct *work) { + struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workinit); LOCK_TAKE(hdw->big_lock); do { - hdw->poll_trigger_func = func; - hdw->poll_trigger_data = data; + pvr2_hdw_setup(hdw); } while (0); LOCK_GIVE(hdw->big_lock); } -void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *hdw) +static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state) { - if (hdw->poll_trigger_func) { - hdw->poll_trigger_func(hdw->poll_trigger_data); - } + return wait_event_interruptible( + hdw->state_wait_data, + (hdw->state_stale == 0) && + (!state || (hdw->master_state != state))); } -#if 0 -void pvr2_hdw_poll_trigger(struct pvr2_hdw *hdw) + +void pvr2_hdw_set_state_callback(struct pvr2_hdw *hdw, + void (*callback_func)(void *), + void *callback_data) { LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_poll_trigger_unlocked(hdw); + hdw->state_data = callback_data; + hdw->state_func = callback_func; } while (0); LOCK_GIVE(hdw->big_lock); } -#endif /* 0 */ + /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) { @@ -2728,6 +2610,7 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw) pvr2_i2c_core_sync(hdw); pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:"); cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2"); + pvr2_hdw_state_log_state(hdw); printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr); } while (0); LOCK_GIVE(hdw->big_lock); } @@ -3026,7 +2909,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, " without lock!!"); return -EDEADLK; } - if ((!hdw->flag_ok) && !probe_fl) { + if (!hdw->flag_ok && !probe_fl) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Attempted to execute control transfer" " when device not ok"); @@ -3280,7 +3163,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, hdw->cmd_debug_state = 0; if ((status < 0) && (!probe_fl)) { - pvr2_hdw_render_useless_unlocked(hdw); + pvr2_hdw_render_useless(hdw); } return status; } @@ -3340,24 +3223,17 @@ static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data) } -static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw) +void pvr2_hdw_render_useless(struct pvr2_hdw *hdw) { if (!hdw->flag_ok) return; - pvr2_trace(PVR2_TRACE_INIT,"render_useless"); - hdw->flag_ok = 0; + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Device being rendered inoperable"); if (hdw->vid_stream) { pvr2_stream_setup(hdw->vid_stream,NULL,0,0); } - hdw->flag_streaming_enabled = 0; - hdw->subsys_enabled_mask = 0; -} - - -void pvr2_hdw_render_useless(struct pvr2_hdw *hdw) -{ - LOCK_TAKE(hdw->ctl_lock); - pvr2_hdw_render_useless_unlocked(hdw); - LOCK_GIVE(hdw->ctl_lock); + hdw->flag_ok = 0; + trace_stbit("flag_ok",hdw->flag_ok); + pvr2_hdw_state_sched(hdw); } @@ -3412,7 +3288,6 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) int status; LOCK_TAKE(hdw->ctl_lock); do { pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset"); - hdw->flag_ok = !0; hdw->cmd_buffer[0] = FX2CMD_DEEP_RESET; status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); @@ -3462,26 +3337,473 @@ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) (runFl ? FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF); status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); - if (!status) { - hdw->subsys_enabled_mask = - ((hdw->subsys_enabled_mask & - ~(1<state_encoder_ok) return 0; + if (hdw->flag_tripped) return 0; + if (hdw->state_encoder_run) return 0; + if (hdw->state_encoder_config) return 0; + if (hdw->state_decoder_run) return 0; + if (hdw->state_usbstream_run) return 0; + if (pvr2_upload_firmware2(hdw) < 0) { + hdw->flag_tripped = !0; + trace_stbit("flag_tripped",hdw->flag_tripped); + return !0; + } + hdw->state_encoder_ok = !0; + trace_stbit("state_encoder_ok",hdw->state_encoder_ok); + return !0; +} + + +/* Evaluate whether or not state_encoder_config can change */ +static int state_eval_encoder_config(struct pvr2_hdw *hdw) +{ + if (hdw->state_encoder_config) { + if (hdw->state_encoder_ok) { + if (hdw->state_pipeline_req && + !hdw->state_pipeline_pause) return 0; + } + hdw->state_encoder_config = 0; + hdw->state_encoder_waitok = 0; + trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok); + /* paranoia - solve race if timer just completed */ + del_timer_sync(&hdw->encoder_wait_timer); + } else { + if (!hdw->state_encoder_ok || + !hdw->state_pipeline_idle || + hdw->state_pipeline_pause || + !hdw->state_pipeline_req || + !hdw->state_pipeline_config) { + /* We must reset the enforced wait interval if + anything has happened that might have disturbed + the encoder. This should be a rare case. */ + if (timer_pending(&hdw->encoder_wait_timer)) { + del_timer_sync(&hdw->encoder_wait_timer); + } + if (hdw->state_encoder_waitok) { + /* Must clear the state - therefore we did + something to a state bit and must also + return true. */ + hdw->state_encoder_waitok = 0; + trace_stbit("state_encoder_waitok", + hdw->state_encoder_waitok); + return !0; + } + return 0; + } + if (!hdw->state_encoder_waitok) { + if (!timer_pending(&hdw->encoder_wait_timer)) { + /* waitok flag wasn't set and timer isn't + running. Check flag once more to avoid + a race then start the timer. This is + the point when we measure out a minimal + quiet interval before doing something to + the encoder. */ + if (!hdw->state_encoder_waitok) { + hdw->encoder_wait_timer.expires = + jiffies + (HZ*50/1000); + add_timer(&hdw->encoder_wait_timer); + } + } + /* We can't continue until we know we have been + quiet for the interval measured by this + timer. */ + return 0; + } + pvr2_encoder_configure(hdw); + if (hdw->state_encoder_ok) hdw->state_encoder_config = !0; + } + trace_stbit("state_encoder_config",hdw->state_encoder_config); + return !0; +} + + +/* Evaluate whether or not state_encoder_run can change */ +static int state_eval_encoder_run(struct pvr2_hdw *hdw) +{ + if (hdw->state_encoder_run) { + if (hdw->state_encoder_ok) { + if (hdw->state_decoder_run) return 0; + if (pvr2_encoder_stop(hdw) < 0) return !0; + } + hdw->state_encoder_run = 0; + } else { + if (!hdw->state_encoder_ok) return 0; + if (!hdw->state_decoder_run) return 0; + if (pvr2_encoder_start(hdw) < 0) return !0; + hdw->state_encoder_run = !0; + } + trace_stbit("state_encoder_run",hdw->state_encoder_run); + return !0; +} + + +/* Timeout function for quiescent timer. */ +static void pvr2_hdw_quiescent_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + hdw->state_decoder_quiescent = !0; + trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent); + hdw->state_stale = !0; + queue_work(hdw->workqueue,&hdw->workpoll); +} + + +/* Timeout function for encoder wait timer. */ +static void pvr2_hdw_encoder_wait_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + hdw->state_encoder_waitok = !0; + trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok); + hdw->state_stale = !0; + queue_work(hdw->workqueue,&hdw->workpoll); +} + + +/* Evaluate whether or not state_decoder_run can change */ +static int state_eval_decoder_run(struct pvr2_hdw *hdw) +{ + if (hdw->state_decoder_run) { + if (hdw->state_encoder_ok) { + if (hdw->state_pipeline_req && + !hdw->state_pipeline_pause) return 0; + } + if (!hdw->flag_decoder_missed) { + pvr2_decoder_enable(hdw,0); + } + hdw->state_decoder_quiescent = 0; + hdw->state_decoder_run = 0; + /* paranoia - solve race if timer just completed */ + del_timer_sync(&hdw->quiescent_timer); + } else { + if (!hdw->state_decoder_quiescent) { + if (!timer_pending(&hdw->quiescent_timer)) { + /* We don't do something about the + quiescent timer until right here because + we also want to catch cases where the + decoder was already not running (like + after initialization) as opposed to + knowing that we had just stopped it. + The second flag check is here to cover a + race - the timer could have run and set + this flag just after the previous check + but before we did the pending check. */ + if (!hdw->state_decoder_quiescent) { + hdw->quiescent_timer.expires = + jiffies + (HZ*50/1000); + add_timer(&hdw->quiescent_timer); + } + } + /* Don't allow decoder to start again until it has + been quiesced first. This little detail should + hopefully further stabilize the encoder. */ + return 0; + } + if (!hdw->state_pipeline_req || + hdw->state_pipeline_pause || + !hdw->state_pipeline_config || + !hdw->state_encoder_config || + !hdw->state_encoder_ok) return 0; + del_timer_sync(&hdw->quiescent_timer); + if (hdw->flag_decoder_missed) return 0; + if (pvr2_decoder_enable(hdw,!0) < 0) return 0; + hdw->state_decoder_quiescent = 0; + hdw->state_decoder_run = !0; + } + trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent); + trace_stbit("state_decoder_run",hdw->state_decoder_run); + return !0; +} + + +/* Evaluate whether or not state_usbstream_run can change */ +static int state_eval_usbstream_run(struct pvr2_hdw *hdw) +{ + if (hdw->state_usbstream_run) { + if (hdw->state_encoder_ok) { + if (hdw->state_encoder_run) return 0; + } + pvr2_hdw_cmd_usbstream(hdw,0); + hdw->state_usbstream_run = 0; + } else { + if (!hdw->state_encoder_ok || + !hdw->state_encoder_run || + !hdw->state_pipeline_req || + hdw->state_pipeline_pause) return 0; + if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; + hdw->state_usbstream_run = !0; + } + trace_stbit("state_usbstream_run",hdw->state_usbstream_run); + return !0; +} + + +/* Attempt to configure pipeline, if needed */ +static int state_eval_pipeline_config(struct pvr2_hdw *hdw) +{ + if (hdw->state_pipeline_config || + hdw->state_pipeline_pause) return 0; + pvr2_hdw_commit_execute(hdw); + return !0; +} + + +/* Update pipeline idle and pipeline pause tracking states based on other + inputs. This must be called whenever the other relevant inputs have + changed. */ +static int state_update_pipeline_state(struct pvr2_hdw *hdw) +{ + unsigned int st; + int updatedFl = 0; + /* Update pipeline state */ + st = !(hdw->state_encoder_run || + hdw->state_decoder_run || + hdw->state_usbstream_run || + (!hdw->state_decoder_quiescent)); + if (!st != !hdw->state_pipeline_idle) { + hdw->state_pipeline_idle = st; + updatedFl = !0; + } + if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) { + hdw->state_pipeline_pause = 0; + updatedFl = !0; + } + return updatedFl; +} + + +typedef int (*state_eval_func)(struct pvr2_hdw *); + +/* Set of functions to be run to evaluate various states in the driver. */ +const static state_eval_func eval_funcs[] = { + state_eval_pipeline_config, + state_eval_encoder_ok, + state_eval_encoder_config, + state_eval_decoder_run, + state_eval_encoder_run, + state_eval_usbstream_run, +}; + + +/* Process various states and return true if we did anything interesting. */ +static int pvr2_hdw_state_update(struct pvr2_hdw *hdw) +{ + unsigned int i; + int state_updated = 0; + int check_flag; + + if (!hdw->state_stale) return 0; + if ((hdw->fw1_state != FW1_STATE_OK) || + !hdw->flag_ok) { + hdw->state_stale = 0; + return !0; + } + /* This loop is the heart of the entire driver. It keeps trying to + evaluate various bits of driver state until nothing changes for + one full iteration. Each "bit of state" tracks some global + aspect of the driver, e.g. whether decoder should run, if + pipeline is configured, usb streaming is on, etc. We separately + evaluate each of those questions based on other driver state to + arrive at the correct running configuration. */ + do { + check_flag = 0; + state_update_pipeline_state(hdw); + /* Iterate over each bit of state */ + for (i = 0; (iflag_ok; i++) { + if ((*eval_funcs[i])(hdw)) { + check_flag = !0; + state_updated = !0; + state_update_pipeline_state(hdw); + } + } + } while (check_flag && hdw->flag_ok); + hdw->state_stale = 0; + trace_stbit("state_stale",hdw->state_stale); + return state_updated; +} + + +static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, + char *buf,unsigned int acnt) +{ + switch (which) { + case 0: + return scnprintf( + buf,acnt, + "driver:%s%s%s%s%s", + (hdw->flag_ok ? " " : " "), + (hdw->flag_init_ok ? " " : " "), + (hdw->flag_disconnected ? " " : + " "), + (hdw->flag_tripped ? " " : ""), + (hdw->flag_decoder_missed ? " " : "")); + case 1: + return scnprintf( + buf,acnt, + "pipeline:%s%s%s%s", + (hdw->state_pipeline_idle ? " " : ""), + (hdw->state_pipeline_config ? + " " : " "), + (hdw->state_pipeline_req ? " " : ""), + (hdw->state_pipeline_pause ? " " : "")); + case 2: + return scnprintf( + buf,acnt, + "worker:%s%s%s%s%s%s", + (hdw->state_decoder_run ? + " " : + (hdw->state_decoder_quiescent ? + "" : " ")), + (hdw->state_decoder_quiescent ? + " " : ""), + (hdw->state_encoder_ok ? + "" : " "), + (hdw->state_encoder_run ? + " " : " "), + (hdw->state_encoder_config ? + " " : + (hdw->state_encoder_waitok ? + "" : " ")), + (hdw->state_usbstream_run ? + " " : " ")); + break; + case 3: + return scnprintf( + buf,acnt, + "state: %s", + pvr2_get_state_name(hdw->master_state)); + break; + default: break; + } + return 0; +} + + +unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, + char *buf,unsigned int acnt) +{ + unsigned int bcnt,ccnt,idx; + bcnt = 0; + LOCK_TAKE(hdw->big_lock); + for (idx = 0; ; idx++) { + ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt); + if (!ccnt) break; + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + if (!acnt) break; + buf[0] = '\n'; ccnt = 1; + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + } + LOCK_GIVE(hdw->big_lock); + return bcnt; +} + + +static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw) +{ + char buf[128]; + unsigned int idx,ccnt; + + for (idx = 0; ; idx++) { + ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf)); + if (!ccnt) break; + printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf); + } +} + + +/* Evaluate and update the driver's current state, taking various actions + as appropriate for the update. */ +static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) +{ + unsigned int st; + int state_updated = 0; + int callback_flag = 0; + + pvr2_trace(PVR2_TRACE_STBITS, + "Drive state check START"); + if (pvrusb2_debug & PVR2_TRACE_STBITS) { + pvr2_hdw_state_log_state(hdw); + } + + /* Process all state and get back over disposition */ + state_updated = pvr2_hdw_state_update(hdw); + + /* Update master state based upon all other states. */ + if (!hdw->flag_ok) { + st = PVR2_STATE_DEAD; + } else if (hdw->fw1_state != FW1_STATE_OK) { + st = PVR2_STATE_COLD; + } else if (!hdw->state_encoder_ok) { + st = PVR2_STATE_WARM; + } else if (hdw->flag_tripped || hdw->flag_decoder_missed) { + st = PVR2_STATE_ERROR; + } else if (hdw->state_encoder_run && + hdw->state_decoder_run && + hdw->state_usbstream_run) { + st = PVR2_STATE_RUN; + } else { + st = PVR2_STATE_READY; + } + if (hdw->master_state != st) { + pvr2_trace(PVR2_TRACE_STATE, + "Device state change from %s to %s", + pvr2_get_state_name(hdw->master_state), + pvr2_get_state_name(st)); + hdw->master_state = st; + state_updated = !0; + callback_flag = !0; + } + if (state_updated) { + /* Trigger anyone waiting on any state changes here. */ + wake_up(&hdw->state_wait_data); + } + + if (pvrusb2_debug & PVR2_TRACE_STBITS) { + pvr2_hdw_state_log_state(hdw); + } + pvr2_trace(PVR2_TRACE_STBITS, + "Drive state check DONE callback=%d",callback_flag); + + return callback_flag; +} + + +/* Cause kernel thread to check / update driver state */ +static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw) +{ + if (hdw->state_stale) return; + hdw->state_stale = !0; + trace_stbit("state_stale",hdw->state_stale); + queue_work(hdw->workqueue,&hdw->workpoll); +} + + +void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw, + struct pvr2_hdw_debug_info *ptr) { ptr->big_lock_held = hdw->big_lock_held; ptr->ctl_lock_held = hdw->ctl_lock_held; - ptr->flag_ok = hdw->flag_ok; ptr->flag_disconnected = hdw->flag_disconnected; ptr->flag_init_ok = hdw->flag_init_ok; - ptr->flag_streaming_enabled = hdw->flag_streaming_enabled; - ptr->subsys_flags = hdw->subsys_enabled_mask; + ptr->flag_ok = hdw->flag_ok; + ptr->fw1_state = hdw->fw1_state; + ptr->flag_decoder_missed = hdw->flag_decoder_missed; + ptr->flag_tripped = hdw->flag_tripped; + ptr->state_encoder_ok = hdw->state_encoder_ok; + ptr->state_encoder_run = hdw->state_encoder_run; + ptr->state_decoder_run = hdw->state_decoder_run; + ptr->state_usbstream_run = hdw->state_usbstream_run; + ptr->state_decoder_quiescent = hdw->state_decoder_quiescent; + ptr->state_pipeline_config = hdw->state_pipeline_config; + ptr->state_pipeline_req = hdw->state_pipeline_req; + ptr->state_pipeline_pause = hdw->state_pipeline_pause; + ptr->state_pipeline_idle = hdw->state_pipeline_idle; ptr->cmd_debug_state = hdw->cmd_debug_state; ptr->cmd_code = hdw->cmd_debug_code; ptr->cmd_debug_write_len = hdw->cmd_debug_write_len; @@ -3494,6 +3816,15 @@ void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw, } +void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw, + struct pvr2_hdw_debug_info *ptr) +{ + LOCK_TAKE(hdw->ctl_lock); do { + pvr2_hdw_get_debug_info_unlocked(hdw,ptr); + } while(0); LOCK_GIVE(hdw->ctl_lock); +} + + int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp) { return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 6bc05192a..99c4966b1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -45,27 +45,6 @@ #define PVR2_CVAL_INPUT_COMPOSITE 2 #define PVR2_CVAL_INPUT_RADIO 3 -/* Subsystem definitions - these are various pieces that can be - independently stopped / started. Usually you don't want to mess with - this directly (let the driver handle things itself), but it is useful - for debugging. */ -#define PVR2_SUBSYS_B_ENC_FIRMWARE 0 -#define PVR2_SUBSYS_B_ENC_CFG 1 -#define PVR2_SUBSYS_B_DIGITIZER_RUN 2 -#define PVR2_SUBSYS_B_USBSTREAM_RUN 3 -#define PVR2_SUBSYS_B_ENC_RUN 4 - -#define PVR2_SUBSYS_CFG_ALL ( \ - (1 << PVR2_SUBSYS_B_ENC_FIRMWARE) | \ - (1 << PVR2_SUBSYS_B_ENC_CFG) ) -#define PVR2_SUBSYS_RUN_ALL ( \ - (1 << PVR2_SUBSYS_B_DIGITIZER_RUN) | \ - (1 << PVR2_SUBSYS_B_USBSTREAM_RUN) | \ - (1 << PVR2_SUBSYS_B_ENC_RUN) ) -#define PVR2_SUBSYS_ALL ( \ - PVR2_SUBSYS_CFG_ALL | \ - PVR2_SUBSYS_RUN_ALL ) - enum pvr2_config { pvr2_config_empty, /* No configuration */ pvr2_config_mpeg, /* Encoded / compressed video */ @@ -80,8 +59,41 @@ enum pvr2_v4l_type { pvr2_v4l_type_radio, }; +/* Major states that we can be in: + * + * DEAD - Device is in an unusable state and cannot be recovered. This + * can happen if we completely lose the ability to communicate with it + * (but it might still on the bus). In this state there's nothing we can + * do; it must be replugged in order to recover. + * + * COLD - Device is in an unusuable state, needs microcontroller firmware. + * + * WARM - We can communicate with the device and the proper + * microcontroller firmware is running, but other device initialization is + * still needed (e.g. encoder firmware). + * + * ERROR - A problem prevents capture operation (e.g. encoder firmware + * missing). + * + * READY - Device is operational, but not streaming. + * + * RUN - Device is streaming. + * + */ +#define PVR2_STATE_NONE 0 +#define PVR2_STATE_DEAD 1 +#define PVR2_STATE_COLD 2 +#define PVR2_STATE_WARM 3 +#define PVR2_STATE_ERROR 4 +#define PVR2_STATE_READY 5 +#define PVR2_STATE_RUN 6 + +/* Translate configuration enum to a string label */ const char *pvr2_config_get_name(enum pvr2_config); +/* Translate a master state enum to a string label */ +const char *pvr2_hdw_get_state_name(unsigned int); + struct pvr2_hdw; /* Create and return a structure for interacting with the underlying @@ -89,20 +101,6 @@ struct pvr2_hdw; struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, const struct usb_device_id *devid); -/* Poll for background activity (if any) */ -void pvr2_hdw_poll(struct pvr2_hdw *); - -/* Trigger a poll to take place later at a convenient time */ -#if 0 -void pvr2_hdw_poll_trigger(struct pvr2_hdw *); -#endif /* 0 */ -void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *); - -/* Register a callback used to trigger a future poll */ -void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *, - void (*func)(void *), - void *data); - #if 0 /* Get pointer to structure given unit number */ struct pvr2_hdw *pvr2_hdw_find(int unit_number); @@ -111,14 +109,10 @@ struct pvr2_hdw *pvr2_hdw_find(int unit_number); /* Destroy hardware interaction structure */ void pvr2_hdw_destroy(struct pvr2_hdw *); -/* Set up the structure and attempt to put the device into a usable state. - This can be a time-consuming operation, which is why it is not done - internally as part of the create() step. Return value is exactly the - same as pvr2_hdw_init_ok(). */ -int pvr2_hdw_setup(struct pvr2_hdw *); - -/* Initialization succeeded */ -int pvr2_hdw_init_ok(struct pvr2_hdw *); +/* Register a function to be called whenever the master state changes. */ +void pvr2_hdw_set_state_callback(struct pvr2_hdw *, + void (*callback_func)(void *), + void *callback_data); /* Return true if in the ready (normal) state */ int pvr2_hdw_dev_ok(struct pvr2_hdw *); @@ -176,6 +170,9 @@ int pvr2_hdw_set_streaming(struct pvr2_hdw *,int); /* Find out if streaming is on */ int pvr2_hdw_get_streaming(struct pvr2_hdw *); +/* Retrieve driver overall state */ +int pvr2_hdw_get_state(struct pvr2_hdw *); + /* Configure the type of stream to generate */ int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); @@ -186,26 +183,6 @@ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, unsigned int idx); -/* Enable / disable various pieces of hardware. Items to change are - identified by bit positions within msk, and new state for each item is - identified by corresponding bit positions within val. */ -void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw, - unsigned long msk,unsigned long val); - -/* Retrieve mask indicating which pieces of hardware are currently enabled - / configured. */ -unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *); - -/* Adjust mask of what get shut down when streaming is stopped. This is a - debugging aid. */ -void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw, - unsigned long msk,unsigned long val); - -/* Retrieve mask indicating which pieces of hardware are disabled when - streaming is turned off. */ -unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *); - - /* Enable / disable retrieval of CPU firmware or prom contents. This must be enabled before pvr2_hdw_cpufw_get() will function. Note that doing this may prevent the device from running (and leaving this mode may @@ -262,6 +239,9 @@ void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int); /* Execute a USB-commanded device reset */ void pvr2_hdw_device_reset(struct pvr2_hdw *); +/* Reset worker's error trapping circuit breaker */ +int pvr2_hdw_untrip(struct pvr2_hdw *); + /* Execute hard reset command (after this point it's likely that the encoder will have to be reconfigured). This also clears the "useless" state. */ @@ -284,11 +264,21 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val); struct pvr2_hdw_debug_info { int big_lock_held; int ctl_lock_held; - int flag_ok; int flag_disconnected; int flag_init_ok; - int flag_streaming_enabled; - unsigned long subsys_flags; + int flag_ok; + int fw1_state; + int flag_decoder_missed; + int flag_tripped; + int state_encoder_ok; + int state_encoder_run; + int state_decoder_run; + int state_usbstream_run; + int state_decoder_quiescent; + int state_pipeline_config; + int state_pipeline_req; + int state_pipeline_pause; + int state_pipeline_idle; int cmd_debug_state; int cmd_debug_write_len; int cmd_debug_read_len; @@ -304,8 +294,20 @@ struct pvr2_hdw_debug_info { diagnosing lockups. Note that this operation is completed without any kind of locking and so it is not atomic and may yield inconsistent results. This is *purely* a debugging aid. */ -void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw, - struct pvr2_hdw_debug_info *); +void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw, + struct pvr2_hdw_debug_info *); + +/* Intrusively retrieve internal state info - this is useful for + diagnosing overall driver state. This operation synchronizes against + the overall driver mutex - so if there are locking problems this will + likely hang! This is *purely* a debugging aid. */ +void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw, + struct pvr2_hdw_debug_info *); + +/* Report out several lines of text that describes driver internal state. + Results are written into the passed-in buffer. */ +unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, + char *buf_ptr,unsigned int buf_size); /* Cause modules to log their state once */ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 97f94e598..bf8c9ee12 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -978,7 +978,7 @@ static int pvr2_i2c_attach_inform(struct i2c_client *client) list_add_tail(&cp->list,&hdw->i2c_clients); hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT; } while (0); mutex_unlock(&hdw->i2c_list_lock); - if (fl) pvr2_hdw_poll_trigger_unlocked(hdw); + if (fl) queue_work(hdw->workqueue,&hdw->worki2csync); return 0; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index b192db67e..1770a847a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -1016,10 +1016,8 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) sp = fh->dev_info->stream->stream; pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh); pvr2_hdw_set_stream_type(hdw,fh->dev_info->config); - pvr2_hdw_set_streaming(hdw,!0); - ret = pvr2_ioread_set_enabled(fh->rhp,!0); - - return ret; + if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret; + return pvr2_ioread_set_enabled(fh->rhp,!0); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index b063110de..8a742221e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -130,7 +130,7 @@ static const struct pvr2_v4l_decoder_ops decoder_ops[] = { static void decoder_detach(struct pvr2_v4l_decoder *ctxt) { ctxt->client->handler = NULL; - ctxt->hdw->decoder_ctrl = NULL; + pvr2_hdw_set_decoder(ctxt->hdw,NULL); kfree(ctxt); } @@ -218,7 +218,7 @@ int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw, ctxt->client = cp; ctxt->hdw = hdw; ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; - hdw->decoder_ctrl = &ctxt->ctrl; + pvr2_hdw_set_decoder(hdw,&ctxt->ctrl); cp->handler = &ctxt->handler; pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up", cp->client->addr); -- cgit v1.2.3 From 2446fc6debfd93e7b8b9cad4b22083994f686e59 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 22:53:12 -0600 Subject: pvrusb2: Centralize device specific attributes into a single place. From: Mike Isely The pvrusb2 driver currently supports two variants of the Hauppauge PVR USB2. However there are other hardware types potentially supportable, but the driver at the moment is not structured to make it easy to describe these minor variations. This changeset is the first set of changes to make such additional device support possible. Device attributes are held in several tables all contained within pvrusb2-devattr.c; all other device-specific driver behavior now derives from these tables. Signed-off-by: Mike Isely --- .../drivers/media/video/pvrusb2/pvrusb2-devattr.c | 101 +++++++++++++++++++++ .../drivers/media/video/pvrusb2/pvrusb2-devattr.h | 87 ++++++++++++++++++ .../drivers/media/video/pvrusb2/pvrusb2-encoder.c | 16 ++-- .../media/video/pvrusb2/pvrusb2-hdw-internal.h | 6 +- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 91 +++---------------- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h | 3 - .../drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 8 +- linux/drivers/media/video/pvrusb2/pvrusb2-main.c | 1 + 8 files changed, 219 insertions(+), 94 deletions(-) create mode 100644 linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c create mode 100644 linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c new file mode 100644 index 000000000..aebcb846d --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -0,0 +1,101 @@ +/* + * + * $Id$ + * + * Copyright (C) 2007 Mike Isely + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + +This source file should encompass ALL per-device type information for the +driver. To define a new device, add elements to the pvr2_device_table and +pvr2_device_desc structures. + +*/ + +#include "pvrusb2-devattr.h" +#include + +/* Known major hardware variants, keyed from device ID */ +#define PVR2_HDW_TYPE_29XXX 0 +#define PVR2_HDW_TYPE_24XXX 1 + +struct usb_device_id pvr2_device_table[] = { + [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) }, + [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) }, + { } +}; + +/* Names of other client modules to request for 24xxx model hardware */ +static const char *pvr2_client_24xxx[] = { + "cx25840", + "tuner", + "wm8775", +}; + +/* Names of other client modules to request for 29xxx model hardware */ +static const char *pvr2_client_29xxx[] = { + "msp3400", + "saa7115", + "tuner", +}; + +/* Firmware file name(s) for 29xxx devices */ +static const char *pvr2_fw1_names_29xxx[] = { + "v4l-pvrusb2-29xxx-01.fw", +}; + +/* Firmware file name(s) for 29xxx devices */ +static const char *pvr2_fw1_names_24xxx[] = { + "v4l-pvrusb2-24xxx-01.fw", +}; + +const struct pvr2_device_desc pvr2_device_descriptions[] = { + [PVR2_HDW_TYPE_29XXX] = { + .description = "WinTV PVR USB2 Model Category 29xxxx", + .shortname = "29xxx", + .client_modules.lst = pvr2_client_29xxx, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx), + .fx2_firmware.lst = pvr2_fw1_names_29xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), + }, + [PVR2_HDW_TYPE_24XXX] = { + .description = "WinTV PVR USB2 Model Category 24xxxx", + .shortname = "24xxx", + .client_modules.lst = pvr2_client_24xxx, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx), + .fx2_firmware.lst = pvr2_fw1_names_24xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx), + .flag_has_cx25840 = !0, + .flag_has_wm8775 = !0, + }, +}; + +const unsigned int pvr2_device_count = ARRAY_SIZE(pvr2_device_descriptions); + +MODULE_DEVICE_TABLE(usb, pvr2_device_table); + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h new file mode 100644 index 000000000..6576aefd2 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -0,0 +1,87 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PVRUSB2_DEVATTR_H +#define __PVRUSB2_DEVATTR_H + +#include + +/* + + This header defines structures used to describe attributes of a device. + +*/ + + +struct pvr2_string_table { + const char **lst; + unsigned int cnt; +}; + + +/* This describes a particular hardware type (except for the USB device ID + which must live in a separate structure due to environmental + constraints). See the top of pvrusb2-hdw.c for where this is + instantiated. */ +struct pvr2_device_desc { + /* Single line text description of hardware */ + const char *description; + + /* Single token identifier for hardware */ + const char *shortname; + + /* List of additional client modules we need to load */ + struct pvr2_string_table client_modules; + + /* List of FX2 firmware file names we should search; if empty then + FX2 firmware check / load is skipped and we assume the device + was initialized from internal ROM. */ + struct pvr2_string_table fx2_firmware; + + /* If set, we don't bother trying to load cx23416 firmware. */ + char flag_skip_cx23416_firmware; + + /* Device does not require a powerup command to be issued. */ + char flag_no_powerup; + + /* Device has a cx25840 - this enables special additional logic to + handle it. */ + char flag_has_cx25840; + + /* Device has a wm8775 - this enables special additional logic to + ensure that it is found. */ + char flag_has_wm8775; +}; + +extern const struct pvr2_device_desc pvr2_device_descriptions[]; +extern struct usb_device_id pvr2_device_table[]; +extern const unsigned int pvr2_device_count; + +#endif /* __PVRUSB2_HDW_INTERNAL_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index f4394d610..4c52855e8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -403,13 +403,13 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) /* This ENC_MISC(3,encMisc3Arg) command is critical - without it there will eventually be video corruption. Also, the - 29xxx case is strange - the Windows driver is passing 1 - regardless of device type but if we have 1 for 29xxx device - the video turns sluggish. */ - switch (hdw->hdw_type) { - case PVR2_HDW_TYPE_24XXX: encMisc3Arg = 1; break; - case PVR2_HDW_TYPE_29XXX: encMisc3Arg = 0; break; - default: break; + saa7115 case is strange - the Windows driver is passing 1 + regardless of device type but if we have 1 for saa7115 + devices the video turns sluggish. */ + if (hdw->hdw_desc->flag_has_cx25840) { + encMisc3Arg = 1; + } else { + encMisc3Arg = 0; } ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3, encMisc3Arg,0,0); @@ -467,7 +467,7 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) /* saa7115: 0xf0 */ val = 0xf0; - if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { + if (hdw->hdw_desc->flag_has_cx25840) { /* ivtv cx25840: 0x140 */ val = 0x140; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 70ed3eef2..cd06005ae 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -44,6 +44,7 @@ #include "pvrusb2-hdw.h" #include "pvrusb2-io.h" #include +#include "pvrusb2-devattr.h" /* Legal values for PVR2_CID_HSM */ #define PVR2_CVAL_HSM_FAIL 0 @@ -166,10 +167,6 @@ struct pvr2_decoder_ctrl { #define FW1_STATE_RELOAD 3 #define FW1_STATE_OK 4 -/* Known major hardware variants, keyed from device ID */ -#define PVR2_HDW_TYPE_29XXX 0 -#define PVR2_HDW_TYPE_24XXX 1 - typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); #define PVR2_I2C_FUNC_CNT 128 @@ -183,6 +180,7 @@ struct pvr2_hdw { /* Device type, one of PVR2_HDW_TYPE_xxxxx */ unsigned int hdw_type; + const struct pvr2_device_desc *hdw_desc; /* Kernel worker thread handling */ struct workqueue_struct *workqueue; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 2119aae33..91922569a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -42,47 +42,6 @@ #define TV_MIN_FREQ 55250000L #define TV_MAX_FREQ 850000000L -struct usb_device_id pvr2_device_table[] = { - [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) }, - [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) }, - { } -}; - -MODULE_DEVICE_TABLE(usb, pvr2_device_table); - -static const char *pvr2_device_names[] = { - [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx", - [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx", -}; - -struct pvr2_string_table { - const char **lst; - unsigned int cnt; -}; - -// Names of other client modules to request for 24xxx model hardware -static const char *pvr2_client_24xxx[] = { - "cx25840", - "tuner", - "wm8775", -}; - -// Names of other client modules to request for 29xxx model hardware -static const char *pvr2_client_29xxx[] = { - "msp3400", - "saa7115", - "tuner", -}; - -static struct pvr2_string_table pvr2_client_lists[] = { - [PVR2_HDW_TYPE_29XXX] = { - pvr2_client_29xxx, ARRAY_SIZE(pvr2_client_29xxx) - }, - [PVR2_HDW_TYPE_24XXX] = { - pvr2_client_24xxx, ARRAY_SIZE(pvr2_client_24xxx) - }, -}; - static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL}; static DEFINE_MUTEX(pvr2_unit_mtx); @@ -395,8 +354,8 @@ static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp) static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp) { - /* Actual minimum depends on device type. */ - if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { + /* Actual minimum depends on device digitizer type. */ + if (cptr->hdw->hdw_desc->flag_has_cx25840) { *vp = 75; } else { *vp = 17; @@ -1141,23 +1100,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) unsigned int pipe; int ret; u16 address; - static const char *fw_files_29xxx[] = { - "v4l-pvrusb2-29xxx-01.fw", - }; - static const char *fw_files_24xxx[] = { - "v4l-pvrusb2-24xxx-01.fw", - }; - static const struct pvr2_string_table fw_file_defs[] = { - [PVR2_HDW_TYPE_29XXX] = { - fw_files_29xxx, ARRAY_SIZE(fw_files_29xxx) - }, - [PVR2_HDW_TYPE_24XXX] = { - fw_files_24xxx, ARRAY_SIZE(fw_files_24xxx) - }, - }; - if ((hdw->hdw_type >= ARRAY_SIZE(fw_file_defs)) || - (!fw_file_defs[hdw->hdw_type].lst)) { + if (!hdw->hdw_desc->fx2_firmware.cnt) { hdw->fw1_state = FW1_STATE_OK; return 0; } @@ -1167,8 +1111,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) trace_firmware("pvr2_upload_firmware1"); ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller", - fw_file_defs[hdw->hdw_type].cnt, - fw_file_defs[hdw->hdw_type].lst); + hdw->hdw_desc->fx2_firmware.cnt, + hdw->hdw_desc->fx2_firmware.lst); if (ret < 0) { if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING; return ret; @@ -1243,8 +1187,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) CX2341X_FIRM_ENC_FILENAME, }; - if ((hdw->hdw_type != PVR2_HDW_TYPE_29XXX) && - (hdw->hdw_type != PVR2_HDW_TYPE_24XXX)) { + if (hdw->hdw_desc->flag_skip_cx23416_firmware) { return 0; } @@ -1662,8 +1605,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) unsigned int idx; struct pvr2_ctrl *cptr; int reloadFl = 0; - if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) || - (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) { + if (hdw->hdw_desc->fx2_firmware.cnt) { if (!reloadFl) { reloadFl = (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints @@ -1699,17 +1641,11 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) } if (!pvr2_hdw_dev_ok(hdw)) return; - if (hdw->hdw_type < ARRAY_SIZE(pvr2_client_lists)) { - for (idx = 0; - idx < pvr2_client_lists[hdw->hdw_type].cnt; - idx++) { - request_module( - pvr2_client_lists[hdw->hdw_type].lst[idx]); - } + for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) { + request_module(hdw->hdw_desc->client_modules.lst[idx]); } - if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) || - (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) { + if (!hdw->hdw_desc->flag_no_powerup) { pvr2_hdw_cmd_powerup(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; } @@ -1867,20 +1803,22 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, unsigned int hdw_type; int valid_std_mask; struct pvr2_ctrl *cptr; + const struct pvr2_device_desc *hdw_desc; __u8 ifnum; struct v4l2_queryctrl qctrl; struct pvr2_ctl_info *ciptr; hdw_type = devid - pvr2_device_table; - if (hdw_type >= ARRAY_SIZE(pvr2_device_names)) { + if (hdw_type >= pvr2_device_count) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Bogus device type of %u reported",hdw_type); return NULL; } + hdw_desc = pvr2_device_descriptions + hdw_type; hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", - hdw,pvr2_device_names[hdw_type]); + hdw,hdw_desc->description); if (!hdw) goto fail; init_timer(&hdw->quiescent_timer); @@ -1904,6 +1842,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, GFP_KERNEL); if (!hdw->controls) goto fail; hdw->hdw_type = hdw_type; + hdw->hdw_desc = hdw_desc; for (idx = 0; idx < hdw->control_cnt; idx++) { cptr = hdw->controls + idx; cptr->hdw = hdw; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 99c4966b1..8ad15d3f3 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -317,9 +317,6 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw); a debugging aid. */ int pvr2_upload_firmware2(struct pvr2_hdw *hdw); -/* List of device types that we can match */ -extern struct usb_device_id pvr2_device_table[]; - #endif /* __PVRUSB2_HDW_H */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index bf8c9ee12..713c7011c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -1066,14 +1066,16 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) printk(KERN_INFO "%s: IR disabled\n",hdw->name); hdw->i2c_func[0x18] = i2c_black_hole; } else if (ir_mode[hdw->unit_number] == 1) { - if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { + if (hdw->hdw_desc->flag_has_cx25840) { hdw->i2c_func[0x18] = i2c_24xxx_ir; } } - if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { - hdw->i2c_func[0x1b] = i2c_hack_wm8775; + if (hdw->hdw_desc->flag_has_cx25840) { hdw->i2c_func[0x44] = i2c_hack_cx25840; } + if (hdw->hdw_desc->flag_has_wm8775) { + hdw->i2c_func[0x1b] = i2c_hack_wm8775; + } // Configure the adapter and set up everything else related to it. memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c index 687096d1f..ea69ce6a8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -28,6 +28,7 @@ #include #include "pvrusb2-hdw.h" +#include "pvrusb2-devattr.h" #include "pvrusb2-context.h" #include "pvrusb2-debug.h" #include "pvrusb2-v4l2.h" -- cgit v1.2.3 From dd9cc2999bfc382787413a184733709ef86d0897 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 23:30:20 -0600 Subject: pvrusb2: Add pvrusb2-devattr.o to driver build From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile index 69b3e43cd..47284e558 100644 --- a/linux/drivers/media/video/pvrusb2/Makefile +++ b/linux/drivers/media/video/pvrusb2/Makefile @@ -6,7 +6,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-encoder.o pvrusb2-video-v4l.o \ pvrusb2-eeprom.o pvrusb2-tuner.o \ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ - pvrusb2-ctrl.o pvrusb2-std.o \ + pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) -- cgit v1.2.3 From aad9eaebf68a27da1d3d889b885379cd7e27cba0 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 22:55:07 -0600 Subject: pvrusb2: Remove obsolete global hardware type enumeration From: Mike Isely Device-specific driver behavior is now defined by generic device characteristics rather than by specific device model information. With this change, the hardware type field can go away, thus this change. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 4 ++-- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index cd06005ae..a8e829d37 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -178,8 +178,8 @@ struct pvr2_hdw { struct usb_device *usb_dev; struct usb_interface *usb_intf; - /* Device type, one of PVR2_HDW_TYPE_xxxxx */ - unsigned int hdw_type; + /* Device description, anything that must adjust behavior based on + device specific info will use information held here. */ const struct pvr2_device_desc *hdw_desc; /* Kernel worker thread handling */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 91922569a..da9bbf4fa 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1841,7 +1841,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, GFP_KERNEL); if (!hdw->controls) goto fail; - hdw->hdw_type = hdw_type; hdw->hdw_desc = hdw_desc; for (idx = 0; idx < hdw->control_cnt; idx++) { cptr = hdw->controls + idx; -- cgit v1.2.3 From a8ebb7bc1537421088db546cc9dd2accc4099bae Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 22:58:20 -0600 Subject: pvrusb2: Implement functions to pass descriptive hardware info From: Mike Isely Implement additional pvrusb2 device info table entries for a device identifier and a device description. Export this information via the driver's internal API. Make this information available via the sysfs driver interface. Also propagate this information into the v4l2 capability structure. An app can now retrieve and report a descriptive string about the particular type of hardware device it is operating. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 12 +++++ linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h | 6 +++ linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 60 +++++++++++++++++++++++ linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 1 + 4 files changed, 79 insertions(+) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index da9bbf4fa..10365be4a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2493,6 +2493,18 @@ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) } +const char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw) +{ + return hdw->hdw_desc->description; +} + + +const char *pvr2_hdw_get_type(struct pvr2_hdw *hdw) +{ + return hdw->hdw_desc->shortname; +} + + int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) { int result; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 8ad15d3f3..b5fb55952 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -164,6 +164,12 @@ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *); /* Query device and see if it thinks it is on a high-speed USB link */ int pvr2_hdw_is_hsm(struct pvr2_hdw *); +/* Return a string token representative of the hardware type */ +const char *pvr2_hdw_get_type(struct pvr2_hdw *); + +/* Return a single line description of the hardware type */ +const char *pvr2_hdw_get_desc(struct pvr2_hdw *); + /* Turn streaming on/off */ int pvr2_hdw_set_streaming(struct pvr2_hdw *,int); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 7bcf0996f..7825d5b6f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -44,10 +44,14 @@ struct pvr2_sysfs { struct device_attribute attr_v4l_radio_minor_number; struct device_attribute attr_unit_number; struct device_attribute attr_bus_info; + struct device_attribute attr_hdw_name; + struct device_attribute attr_hdw_desc; int v4l_minor_number_created_ok; int v4l_radio_minor_number_created_ok; int unit_number_created_ok; int bus_info_created_ok; + int hdw_name_created_ok; + int hdw_desc_created_ok; }; #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC @@ -721,6 +725,14 @@ static void class_dev_destroy(struct pvr2_sysfs *sfp) pvr2_sysfs_tear_down_debugifc(sfp); #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ pvr2_sysfs_tear_down_controls(sfp); + if (sfp->hdw_desc_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->attr_hdw_desc); + } + if (sfp->hdw_name_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->attr_hdw_name); + } if (sfp->bus_info_created_ok) { device_remove_file(sfp->class_dev, &sfp->attr_bus_info); @@ -767,6 +779,28 @@ static ssize_t bus_info_show(struct device *class_dev, } +static ssize_t hdw_name_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = (struct pvr2_sysfs *)class_dev->driver_data; + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%s\n", + pvr2_hdw_get_type(sfp->channel.hdw)); +} + + +static ssize_t hdw_desc_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = (struct pvr2_sysfs *)class_dev->driver_data; + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%s\n", + pvr2_hdw_get_desc(sfp->channel.hdw)); +} + + static ssize_t v4l_radio_minor_number_show(struct device *class_dev, struct device_attribute *attr, char *buf) @@ -880,6 +914,32 @@ static void class_dev_create(struct pvr2_sysfs *sfp, sfp->bus_info_created_ok = !0; } + sfp->attr_hdw_name.attr.name = "device_hardware_type"; + sfp->attr_hdw_name.attr.mode = S_IRUGO; + sfp->attr_hdw_name.show = hdw_name_show; + sfp->attr_hdw_name.store = NULL; + ret = device_create_file(sfp->class_dev, + &sfp->attr_hdw_name); + if (ret < 0) { + printk(KERN_WARNING "%s: device_create_file error: %d\n", + __FUNCTION__, ret); + } else { + sfp->hdw_name_created_ok = !0; + } + + sfp->attr_hdw_desc.attr.name = "device_hardware_description"; + sfp->attr_hdw_desc.attr.mode = S_IRUGO; + sfp->attr_hdw_desc.show = hdw_desc_show; + sfp->attr_hdw_desc.store = NULL; + ret = device_create_file(sfp->class_dev, + &sfp->attr_hdw_desc); + if (ret < 0) { + printk(KERN_WARNING "%s: device_create_file error: %d\n", + __FUNCTION__, ret); + } else { + sfp->hdw_desc_created_ok = !0; + } + pvr2_sysfs_add_controls(sfp); #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC pvr2_sysfs_add_debugifc(sfp); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 1770a847a..69f2a7d32 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -206,6 +206,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw), sizeof(cap->bus_info)); + strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card)); ret = 0; break; -- cgit v1.2.3 From 4cb090552f06470e17ce82a5f4290ad5ac41f62c Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 23:00:51 -0600 Subject: pvrusb2: Miscellaneous tweaks for controlling tuner type and video standard From: Mike Isely Correctly mark when a tuner type is set. Report more faithfully information about known supported device video standards. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c | 1 + linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index bd5feef0e..b831559bd 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -145,6 +145,7 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) trace_eeprom("serial_number=%d",tvdata.serial_number); trace_eeprom("rev_str=%s",tvdata.rev_str); hdw->tuner_type = tvdata.tuner_type; + hdw->tuner_updated = !0; hdw->serial_number = tvdata.serial_number; hdw->std_mask_eeprom = tvdata.tuner_formats; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 10365be4a..76611f2cf 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1103,7 +1103,10 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) if (!hdw->hdw_desc->fx2_firmware.cnt) { hdw->fw1_state = FW1_STATE_OK; - return 0; + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Connected device type defines" + " no firmware to upload; ignoring firmware"); + return -ENOTTY; } hdw->fw1_state = FW1_STATE_FAILED; // default result @@ -1534,7 +1537,8 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); pvr2_trace(PVR2_TRACE_STD, - "Supported video standard(s) reported by eeprom: %.*s", + "Supported video standard(s) reported available" + " in hardware: %.*s", bcnt,buf); hdw->std_mask_avail = hdw->std_mask_eeprom; @@ -1692,9 +1696,9 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_setup: Tuner type overridden to %d", hdw->tuner_type); + hdw->tuner_updated = !0; } - hdw->tuner_updated = !0; pvr2_i2c_core_check_stale(hdw); hdw->tuner_updated = 0; -- cgit v1.2.3 From e859ac824f9a284fa50783c8536c5b95c6ff2e79 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 23:04:11 -0600 Subject: pvrusb2: Existence of Hauppauge ROM is a device-specific attribute From: Mike Isely Arrange so that the pvrusb2 driver can optionally work without a Hauppauge ROM being present - which is fairly important for devices that happen to not come from Hauppauge. The expected existence of a Hauppauge ROM is now a device attribute. The tuner type is now also a device attribute, which is consulted if there is no ROM. Signed-off-by: Mike Isely --- .../drivers/media/video/pvrusb2/pvrusb2-devattr.c | 2 ++ .../drivers/media/video/pvrusb2/pvrusb2-devattr.h | 7 ++++++ linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 25 ++++++++++++++-------- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index aebcb846d..54a401e8b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -72,6 +72,7 @@ const struct pvr2_device_desc pvr2_device_descriptions[] = { .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx), .fx2_firmware.lst = pvr2_fw1_names_29xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), + .flag_has_hauppauge_rom = !0, }, [PVR2_HDW_TYPE_24XXX] = { .description = "WinTV PVR USB2 Model Category 24xxxx", @@ -82,6 +83,7 @@ const struct pvr2_device_desc pvr2_device_descriptions[] = { .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx), .flag_has_cx25840 = !0, .flag_has_wm8775 = !0, + .flag_has_hauppauge_rom = !0, }, }; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h index 6576aefd2..f63c3ddb8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -55,9 +55,16 @@ struct pvr2_device_desc { was initialized from internal ROM. */ struct pvr2_string_table fx2_firmware; + /* V4L tuner type ID to use with this device (only used if the + driver could not discover the type any other way). */ + int default_tuner_type; + /* If set, we don't bother trying to load cx23416 firmware. */ char flag_skip_cx23416_firmware; + /* Device has a hauppauge eeprom which we can interrogate. */ + char flag_has_hauppauge_rom; + /* Device does not require a powerup command to be issued. */ char flag_no_powerup; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 76611f2cf..477955f7a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1438,6 +1438,7 @@ static int get_default_tuner_type(struct pvr2_hdw *hdw) } if (tp < 0) return -EINVAL; hdw->tuner_type = tp; + hdw->tuner_updated = !0; return 0; } @@ -1679,15 +1680,22 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) // thread-safe against the normal pvr2_send_request() mechanism. // (We should make it thread safe). - ret = pvr2_hdw_get_eeprom_addr(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Unable to determine location of eeprom, skipping"); - } else { - hdw->eeprom_addr = ret; - pvr2_eeprom_analyze(hdw); + if (hdw->hdw_desc->flag_has_hauppauge_rom) { + ret = pvr2_hdw_get_eeprom_addr(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Unable to determine location of eeprom," + " skipping"); + } else { + hdw->eeprom_addr = ret; + pvr2_eeprom_analyze(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + } + } else { + hdw->tuner_type = hdw->hdw_desc->default_tuner_type; + hdw->tuner_updated = !0; + hdw->std_mask_eeprom = V4L2_STD_ALL; } pvr2_hdw_setup_std(hdw); @@ -1696,7 +1704,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_setup: Tuner type overridden to %d", hdw->tuner_type); - hdw->tuner_updated = !0; } pvr2_i2c_core_check_stale(hdw); -- cgit v1.2.3 From b47742c14ba54360223cf7cbc4929deceaf1bc58 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 23:07:26 -0600 Subject: pvrusb2: Implement signal routing schemes From: Mike Isely The exact routing of video and audio signals within a device is a device-specific attribute. Hauppauge devices do it one way; other types of device may route things differently. Unfortunately it is rather impractical to define chip-specific routing at the device attribute level, so instead what happens here is that "schemes" are defined. Each chip level interface implements its part of a given scheme and the scheme as a whole is made into a device specific attribute controlled via a table entry in pvrusb2-devattr.c. The only scheme defined here is for Hauppauge devices, but clearly this opens the door for other possibilities to follow. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-audio.c | 62 +++++++++++++------ .../media/video/pvrusb2/pvrusb2-cx2584x-v4l.c | 69 ++++++++++++++++------ .../drivers/media/video/pvrusb2/pvrusb2-devattr.c | 2 + .../drivers/media/video/pvrusb2/pvrusb2-devattr.h | 9 +++ .../media/video/pvrusb2/pvrusb2-video-v4l.c | 51 +++++++++++----- 5 files changed, 140 insertions(+), 53 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 3ed700041..3ecf38d40 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -36,34 +36,58 @@ struct pvr2_msp3400_handler { }; + +struct routing_scheme { + const int *def; + unsigned int cnt; +}; + +static const int routing_scheme0[] = { + [PVR2_CVAL_INPUT_TV] = MSP_INPUT_DEFAULT, + [PVR2_CVAL_INPUT_RADIO] = MSP_INPUT(MSP_IN_SCART2, + MSP_IN_TUNER1, + MSP_DSP_IN_SCART, + MSP_DSP_IN_SCART), + [PVR2_CVAL_INPUT_COMPOSITE] = MSP_INPUT(MSP_IN_SCART1, + MSP_IN_TUNER1, + MSP_DSP_IN_SCART, + MSP_DSP_IN_SCART), + [PVR2_CVAL_INPUT_SVIDEO] = MSP_INPUT(MSP_IN_SCART1, + MSP_IN_TUNER1, + MSP_DSP_IN_SCART, + MSP_DSP_IN_SCART), +}; + +static const struct routing_scheme routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), + }, +}; + /* This function selects the correct audio input source */ static void set_stereo(struct pvr2_msp3400_handler *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; struct v4l2_routing route; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo"); - route.input = MSP_INPUT_DEFAULT; - route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); - switch (hdw->input_val) { - case PVR2_CVAL_INPUT_TV: - break; - case PVR2_CVAL_INPUT_RADIO: - /* Assume that msp34xx also handle FM decoding, in which case - we're still using the tuner. */ - /* HV: actually it is more likely to be the SCART2 input if - the ivtv experience is any indication. */ - route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, - MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); - break; - case PVR2_CVAL_INPUT_SVIDEO: - case PVR2_CVAL_INPUT_COMPOSITE: - /* SCART 1 input */ - route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, - MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); - break; + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != 0) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + route.input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** i2c msp3400 v4l2 set_stereo:" + " Invalid routing scheme (%u) and/or input (%d)", + sid,hdw->input_val); + return; } + route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index f712b38d9..6b0442eec 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -50,34 +50,65 @@ struct pvr2_v4l_cx2584x { }; +struct routing_scheme_item { + int vid; + int aud; +}; + +struct routing_scheme { + const struct routing_scheme_item *def; + unsigned int cnt; +}; + +static const struct routing_scheme_item routing_scheme0[] = { + [PVR2_CVAL_INPUT_TV] = { + .vid = CX25840_COMPOSITE7, + .aud = CX25840_AUDIO8, + }, + [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */ + .vid = CX25840_COMPOSITE3, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_COMPOSITE] = { + .vid = CX25840_COMPOSITE3, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_SVIDEO] = { + .vid = CX25840_SVIDEO1, + .aud = CX25840_AUDIO_SERIAL, + }, +}; + +static const struct routing_scheme routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), + }, +}; + static void set_input(struct pvr2_v4l_cx2584x *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; struct v4l2_routing route; enum cx25840_video_input vid_input; enum cx25840_audio_input aud_input; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; memset(&route,0,sizeof(route)); - switch(hdw->input_val) { - case PVR2_CVAL_INPUT_TV: - vid_input = CX25840_COMPOSITE7; - aud_input = CX25840_AUDIO8; - break; - case PVR2_CVAL_INPUT_RADIO: // Treat same as composite - case PVR2_CVAL_INPUT_COMPOSITE: - vid_input = CX25840_COMPOSITE3; - aud_input = CX25840_AUDIO_SERIAL; - break; - case PVR2_CVAL_INPUT_SVIDEO: - vid_input = CX25840_SVIDEO1; - aud_input = CX25840_AUDIO_SERIAL; - break; - default: - // Just set it to be composite input for now... - vid_input = CX25840_COMPOSITE3; - aud_input = CX25840_AUDIO_SERIAL; - break; + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != 0) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + vid_input = sp->def[hdw->input_val].vid; + aud_input = sp->def[hdw->input_val].aud; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** i2c cx2584x set_input:" + " Invalid routing scheme (%u) and/or input (%d)", + sid,hdw->input_val); + return; } pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x", diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 54a401e8b..464a13a06 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -73,6 +73,7 @@ const struct pvr2_device_desc pvr2_device_descriptions[] = { .fx2_firmware.lst = pvr2_fw1_names_29xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), .flag_has_hauppauge_rom = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, }, [PVR2_HDW_TYPE_24XXX] = { .description = "WinTV PVR USB2 Model Category 24xxxx", @@ -84,6 +85,7 @@ const struct pvr2_device_desc pvr2_device_descriptions[] = { .flag_has_cx25840 = !0, .flag_has_wm8775 = !0, .flag_has_hauppauge_rom = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, }, }; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h index f63c3ddb8..05eb2c669 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -35,6 +35,7 @@ struct pvr2_string_table { unsigned int cnt; }; +#define PVR2_ROUTING_SCHEME_HAUPPAUGE 0 /* This describes a particular hardware type (except for the USB device ID which must live in a separate structure due to environmental @@ -55,6 +56,14 @@ struct pvr2_device_desc { was initialized from internal ROM. */ struct pvr2_string_table fx2_firmware; + /* Signal routing scheme used by device, contains one of + PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we + encounter them. This is an arbitrary integer scheme id; its + meaning is contained entirely within the driver and is + interpreted by logic which must send commands to the chip-level + drivers (search for things which touch this field). */ + unsigned int signal_routing_scheme; + /* V4L tuner type ID to use with this device (only used if the driver could not discover the type any other way). */ int default_tuner_type; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index 8a742221e..36ce6dc66 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -50,29 +50,50 @@ struct pvr2_v4l_decoder { }; +struct routing_scheme { + const int *def; + unsigned int cnt; +}; + + +static const int routing_scheme0[] = { + [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, + /* In radio mode, we mute the video, but point at one + spot just to stay consistent */ + [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, + [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE5, + [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, +}; + +static const struct routing_scheme routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), + }, +}; + static void set_input(struct pvr2_v4l_decoder *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; struct v4l2_routing route; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val); - switch(hdw->input_val) { - case PVR2_CVAL_INPUT_TV: - route.input = SAA7115_COMPOSITE4; - break; - case PVR2_CVAL_INPUT_COMPOSITE: - route.input = SAA7115_COMPOSITE5; - break; - case PVR2_CVAL_INPUT_SVIDEO: - route.input = SAA7115_SVIDEO2; - break; - case PVR2_CVAL_INPUT_RADIO: - // In radio mode, we mute the video, but point at one - // spot just to stay consistent - route.input = SAA7115_COMPOSITE5; - default: + + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != 0) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + route.input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** i2c v4l2 set_input:" + " Invalid routing scheme (%u) and/or input (%d)", + sid,hdw->input_val); return; } + route.output = 0; pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route); } -- cgit v1.2.3 From fa5023bc58b70398ee8da40d7496bc0a24476f52 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 23:09:42 -0600 Subject: pvrusb2: Use of virtual IR chip is a device-specific attribute From: Mike Isely For Hauppauge 24xxx devices, the IR receiver is a custom piece of logic that is very specific to the device. The pvrusb2 driver can virtualize this to make it look like a more normal IR receiver found in other Hauppauge devices. The decision of whether or not to enable this virtualization however is a device-specific attribute, thus this changeset. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c | 1 + linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h | 4 ++++ linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 464a13a06..6ba0b79e3 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -85,6 +85,7 @@ const struct pvr2_device_desc pvr2_device_descriptions[] = { .flag_has_cx25840 = !0, .flag_has_wm8775 = !0, .flag_has_hauppauge_rom = !0, + .flag_has_hauppauge_custom_ir = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, }, }; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h index 05eb2c669..2aff5a3d5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -84,6 +84,10 @@ struct pvr2_device_desc { /* Device has a wm8775 - this enables special additional logic to ensure that it is found. */ char flag_has_wm8775; + + /* Device has IR hardware that can be faked into looking like a + normal Hauppauge i2c IR receiver. */ + char flag_has_hauppauge_custom_ir; }; extern const struct pvr2_device_desc pvr2_device_descriptions[]; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 713c7011c..98d918420 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -1066,7 +1066,7 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) printk(KERN_INFO "%s: IR disabled\n",hdw->name); hdw->i2c_func[0x18] = i2c_black_hole; } else if (ir_mode[hdw->unit_number] == 1) { - if (hdw->hdw_desc->flag_has_cx25840) { + if (hdw->hdw_desc->flag_has_hauppauge_custom_ir) { hdw->i2c_func[0x18] = i2c_24xxx_ir; } } -- cgit v1.2.3 From 6946442e01cb5656be50ea2e30628b871552ba15 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 23:11:38 -0600 Subject: pvrusb2: Soften the crashed encoder warning message From: Mike Isely The pvrusb2 driver has been successfully recovering from a crashed encoder now for over 2 years. I think it's time to reduce the perceived severity of the warning message. While I'd still very much like to stop these crashes, the recovery logic is solid enough that the problem is effectively benign. No point in panicing the users over it. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 4c52855e8..d71bb3dff 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -308,8 +308,7 @@ static int pvr2_encoder_cmd(void *ctxt, pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Giving up on command." - " It is likely that" - " this is a bad idea..."); + " This is normally recovered by the driver."); break; } wrData[0] = 0x7; -- cgit v1.2.3 From 78b61f4bfe07619481350c1abf4fba98d13a52ca Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 25 Nov 2007 23:14:23 -0600 Subject: pvrusb2: Enable support for "GOTVIEW USB2.0 DVD2" hardware From: Mike Isely This changeset allows the pvrusb2 driver to operate a new device type ("GOTVIEW USB2.0 DVD2"). Changes amount to defining a new routing scheme for the device and adding appropriate table entries into pvrusb2-devattr.c. Signed-off-by: Mike Isely --- .../media/video/pvrusb2/pvrusb2-cx2584x-v4l.c | 24 ++++++++++++++++++++++ .../drivers/media/video/pvrusb2/pvrusb2-devattr.c | 20 ++++++++++++++++++ .../drivers/media/video/pvrusb2/pvrusb2-devattr.h | 1 + 3 files changed, 45 insertions(+) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index 6b0442eec..c64bd6078 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -79,11 +79,35 @@ static const struct routing_scheme_item routing_scheme0[] = { }, }; +/* Specific to gotview device */ +static const struct routing_scheme_item routing_schemegv[] = { + [PVR2_CVAL_INPUT_TV] = { + .vid = CX25840_COMPOSITE2, + .aud = CX25840_AUDIO5, + }, + [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */ + .vid = CX25840_COMPOSITE1, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_COMPOSITE] = { + .vid = CX25840_COMPOSITE1, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_SVIDEO] = { + .vid = (CX25840_SVIDEO_LUMA3|CX25840_SVIDEO_CHROMA4), + .aud = CX25840_AUDIO_SERIAL, + }, +}; + static const struct routing_scheme routing_schemes[] = { [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { .def = routing_scheme0, .cnt = ARRAY_SIZE(routing_scheme0), }, + [PVR2_ROUTING_SCHEME_GOTVIEW] = { + .def = routing_schemegv, + .cnt = ARRAY_SIZE(routing_schemegv), + }, }; static void set_input(struct pvr2_v4l_cx2584x *ctxt) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 6ba0b79e3..e0a5bb9a9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -29,14 +29,20 @@ pvr2_device_desc structures. #include "pvrusb2-devattr.h" #include +/* All this is needed in order to pull in tuner type ids... */ +#include +#include +#include /* Known major hardware variants, keyed from device ID */ #define PVR2_HDW_TYPE_29XXX 0 #define PVR2_HDW_TYPE_24XXX 1 +#define PVR2_HDW_TYPE_GOTVIEW_2 2 struct usb_device_id pvr2_device_table[] = { [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) }, [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) }, + [PVR2_HDW_TYPE_GOTVIEW_2] = { USB_DEVICE(0x1164, 0x0622) }, { } }; @@ -54,6 +60,12 @@ static const char *pvr2_client_29xxx[] = { "tuner", }; +// Names of other client modules to request for Gotview 2 model hardware +static const char *pvr2_client_gotview_2[] = { + "cx25840", + "tuner", +}; + /* Firmware file name(s) for 29xxx devices */ static const char *pvr2_fw1_names_29xxx[] = { "v4l-pvrusb2-29xxx-01.fw", @@ -88,6 +100,14 @@ const struct pvr2_device_desc pvr2_device_descriptions[] = { .flag_has_hauppauge_custom_ir = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, }, + [PVR2_HDW_TYPE_GOTVIEW_2] = { + .description = "Gotview USB 2.0 DVD 2", + .shortname = "gv2", + .client_modules.lst = pvr2_client_gotview_2, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2), + .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, + }, }; const unsigned int pvr2_device_count = ARRAY_SIZE(pvr2_device_descriptions); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h index 2aff5a3d5..b9517e1ca 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -36,6 +36,7 @@ struct pvr2_string_table { }; #define PVR2_ROUTING_SCHEME_HAUPPAUGE 0 +#define PVR2_ROUTING_SCHEME_GOTVIEW 1 /* This describes a particular hardware type (except for the USB device ID which must live in a separate structure due to environmental -- cgit v1.2.3 From c6262dc71d89fe506f76f60098af43b7c7b26b5d Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 27 Nov 2007 14:58:33 -0500 Subject: pvrusb2: fix typo in comments From: Michael Krufky Firmware file name(s) for 24xxx devices Signed-off-by: Michael Krufky --- linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index e0a5bb9a9..e4a311a81 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -71,7 +71,7 @@ static const char *pvr2_fw1_names_29xxx[] = { "v4l-pvrusb2-29xxx-01.fw", }; -/* Firmware file name(s) for 29xxx devices */ +/* Firmware file name(s) for 24xxx devices */ static const char *pvr2_fw1_names_24xxx[] = { "v4l-pvrusb2-24xxx-01.fw", }; -- cgit v1.2.3 From dec053bf7e536421dc5c18e56236951a4f3b94e1 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 1 Dec 2007 15:40:16 -0500 Subject: tda18271: only force init once during attach From: Michael Krufky Once the image rejection calibration procedure has been successful, we should not initialize the tuner registers again. Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/tda18271-fe.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/dvb/frontends/tda18271-fe.c b/linux/drivers/media/dvb/frontends/tda18271-fe.c index 08990ddaa..17bfeb875 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-fe.c +++ b/linux/drivers/media/dvb/frontends/tda18271-fe.c @@ -338,6 +338,20 @@ static int tda18271_init_regs(struct dvb_frontend *fe) return 0; } +static int tda18271_init(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + tda18271_read_regs(fe); + + /* test IR_CAL_OK to see if we need init */ + if ((regs[R_EP1] & 0x08) == 0) + tda18271_init_regs(fe); + + return 0; +} + static int tda18271_tune(struct dvb_frontend *fe, u32 ifc, u32 freq, u32 bw, u8 std) { @@ -748,7 +762,7 @@ static struct dvb_tuner_ops tda18271_tuner_ops = { .frequency_max = 864000000, .frequency_step = 62500 }, - .init = tda18271_init_regs, + .init = tda18271_init, .set_params = tda18271_set_params, .set_analog_params = tda18271_set_analog_params, .release = tda18271_release, @@ -774,6 +788,8 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, fe->tuner_priv = priv; + tda18271_init_regs(fe); + return fe; } EXPORT_SYMBOL_GPL(tda18271_attach); -- cgit v1.2.3 From 770ad653c5820c33e580feeeea29733f0806db9d Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 2 Dec 2007 00:32:49 -0500 Subject: tda18271: remove duplicated code From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/tda18271-fe.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/linux/drivers/media/dvb/frontends/tda18271-fe.c b/linux/drivers/media/dvb/frontends/tda18271-fe.c index 17bfeb875..93755373f 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-fe.c +++ b/linux/drivers/media/dvb/frontends/tda18271-fe.c @@ -360,12 +360,7 @@ static int tda18271_tune(struct dvb_frontend *fe, u32 div, N = 0; int i; - tda18271_read_regs(fe); - - /* test IR_CAL_OK to see if we need init */ - if ((regs[R_EP1] & 0x08) == 0) - tda18271_init_regs(fe); - + tda18271_init(fe); #if 0 /* FIXME: FM Radio support */ if (t->mode == V4L2_TUNER_RADIO) -- cgit v1.2.3 From a5fdd04aa2921408778fc074e2af92ff3a9a409b Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 2 Dec 2007 00:45:04 -0500 Subject: tda18271: improve debug flexibility From: Michael Krufky converted debug module option to an or-able setting. 1 = info 2 = table map values 4 = register dumps Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/tda18271-fe.c | 70 ++++++++++++++----------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/linux/drivers/media/dvb/frontends/tda18271-fe.c b/linux/drivers/media/dvb/frontends/tda18271-fe.c index 93755373f..981596371 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-fe.c +++ b/linux/drivers/media/dvb/frontends/tda18271-fe.c @@ -28,12 +28,20 @@ static int tda18271_debug; module_param_named(debug, tda18271_debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +MODULE_PARM_DESC(debug, "set debug level (info=1, map=2, reg=4 (or-able))"); #define dprintk(level, fmt, arg...) do {\ - if (tda18271_debug >= level) \ + if (tda18271_debug & level) \ printk(KERN_DEBUG "%s: " fmt, __FUNCTION__, ##arg); } while (0) +#define DBG_INFO 1 +#define DBG_MAP 2 +#define DBG_REG 4 + +#define dbg_info(fmt, arg...) dprintk(DBG_INFO, fmt, ##arg) +#define dbg_map(fmt, arg...) dprintk(DBG_MAP, fmt, ##arg) +#define dbg_reg(fmt, arg...) dprintk(DBG_REG, fmt, ##arg) + /*---------------------------------------------------------------------*/ #define TDA18271_ANALOG 0 @@ -76,23 +84,23 @@ static void tda18271_dump_regs(struct dvb_frontend *fe) struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; - dprintk(1, "=== TDA18271 REG DUMP ===\n"); - dprintk(1, "ID_BYTE = 0x%x\n", 0xff & regs[R_ID]); - dprintk(1, "THERMO_BYTE = 0x%x\n", 0xff & regs[R_TM]); - dprintk(1, "POWER_LEVEL_BYTE = 0x%x\n", 0xff & regs[R_PL]); - dprintk(1, "EASY_PROG_BYTE_1 = 0x%x\n", 0xff & regs[R_EP1]); - dprintk(1, "EASY_PROG_BYTE_2 = 0x%x\n", 0xff & regs[R_EP2]); - dprintk(1, "EASY_PROG_BYTE_3 = 0x%x\n", 0xff & regs[R_EP3]); - dprintk(1, "EASY_PROG_BYTE_4 = 0x%x\n", 0xff & regs[R_EP4]); - dprintk(1, "EASY_PROG_BYTE_5 = 0x%x\n", 0xff & regs[R_EP5]); - dprintk(1, "CAL_POST_DIV_BYTE = 0x%x\n", 0xff & regs[R_CPD]); - dprintk(1, "CAL_DIV_BYTE_1 = 0x%x\n", 0xff & regs[R_CD1]); - dprintk(1, "CAL_DIV_BYTE_2 = 0x%x\n", 0xff & regs[R_CD2]); - dprintk(1, "CAL_DIV_BYTE_3 = 0x%x\n", 0xff & regs[R_CD3]); - dprintk(1, "MAIN_POST_DIV_BYTE = 0x%x\n", 0xff & regs[R_MPD]); - dprintk(1, "MAIN_DIV_BYTE_1 = 0x%x\n", 0xff & regs[R_MD1]); - dprintk(1, "MAIN_DIV_BYTE_2 = 0x%x\n", 0xff & regs[R_MD2]); - dprintk(1, "MAIN_DIV_BYTE_3 = 0x%x\n", 0xff & regs[R_MD3]); + dbg_reg("=== TDA18271 REG DUMP ===\n"); + dbg_reg("ID_BYTE = 0x%x\n", 0xff & regs[R_ID]); + dbg_reg("THERMO_BYTE = 0x%x\n", 0xff & regs[R_TM]); + dbg_reg("POWER_LEVEL_BYTE = 0x%x\n", 0xff & regs[R_PL]); + dbg_reg("EASY_PROG_BYTE_1 = 0x%x\n", 0xff & regs[R_EP1]); + dbg_reg("EASY_PROG_BYTE_2 = 0x%x\n", 0xff & regs[R_EP2]); + dbg_reg("EASY_PROG_BYTE_3 = 0x%x\n", 0xff & regs[R_EP3]); + dbg_reg("EASY_PROG_BYTE_4 = 0x%x\n", 0xff & regs[R_EP4]); + dbg_reg("EASY_PROG_BYTE_5 = 0x%x\n", 0xff & regs[R_EP5]); + dbg_reg("CAL_POST_DIV_BYTE = 0x%x\n", 0xff & regs[R_CPD]); + dbg_reg("CAL_DIV_BYTE_1 = 0x%x\n", 0xff & regs[R_CD1]); + dbg_reg("CAL_DIV_BYTE_2 = 0x%x\n", 0xff & regs[R_CD2]); + dbg_reg("CAL_DIV_BYTE_3 = 0x%x\n", 0xff & regs[R_CD3]); + dbg_reg("MAIN_POST_DIV_BYTE = 0x%x\n", 0xff & regs[R_MPD]); + dbg_reg("MAIN_DIV_BYTE_1 = 0x%x\n", 0xff & regs[R_MD1]); + dbg_reg("MAIN_DIV_BYTE_2 = 0x%x\n", 0xff & regs[R_MD2]); + dbg_reg("MAIN_DIV_BYTE_3 = 0x%x\n", 0xff & regs[R_MD3]); } static void tda18271_read_regs(struct dvb_frontend *fe) @@ -119,7 +127,7 @@ static void tda18271_read_regs(struct dvb_frontend *fe) printk("ERROR: %s: i2c_transfer returned: %d\n", __FUNCTION__, ret); - if (tda18271_debug > 2) + if (tda18271_debug & DBG_REG) tda18271_dump_regs(fe); } @@ -367,7 +375,7 @@ static int tda18271_tune(struct dvb_frontend *fe, freq = freq / 1000; #endif - dprintk(1, "freq = %d, ifc = %d\n", freq, ifc); + dbg_info("freq = %d, ifc = %d\n", freq, ifc); /* RF tracking filter calibration */ @@ -378,7 +386,7 @@ static int tda18271_tune(struct dvb_frontend *fe, break; i++; } - dprintk(2, "bp filter = 0x%x, i = %d\n", tda18271_bp_filter[i].val, i); + dbg_map("bp filter = 0x%x, i = %d\n", tda18271_bp_filter[i].val, i); regs[R_EP1] &= ~0x07; /* clear bp filter bits */ regs[R_EP1] |= tda18271_bp_filter[i].val; @@ -417,7 +425,7 @@ static int tda18271_tune(struct dvb_frontend *fe, break; i++; } - dprintk(2, "cal pll, pd = 0x%x, d = 0x%x, i = %d\n", + dbg_map("cal pll, pd = 0x%x, d = 0x%x, i = %d\n", tda18271_cal_pll[i].pd, tda18271_cal_pll[i].d, i); regs[R_CPD] = tda18271_cal_pll[i].pd; @@ -444,7 +452,7 @@ static int tda18271_tune(struct dvb_frontend *fe, break; i++; } - dprintk(2, "main pll, pd = 0x%x, d = 0x%x, i = %d\n", + dbg_map("main pll, pd = 0x%x, d = 0x%x, i = %d\n", tda18271_main_pll[i].pd, tda18271_main_pll[i].d, i); regs[R_MPD] = (0x7f & tda18271_main_pll[i].pd); @@ -473,7 +481,7 @@ static int tda18271_tune(struct dvb_frontend *fe, break; i++; } - dprintk(2, "km = 0x%x, i = %d\n", tda18271_km[i].val, i); + dbg_map("km = 0x%x, i = %d\n", tda18271_km[i].val, i); regs[R_EB13] &= 0x83; regs[R_EB13] |= tda18271_km[i].val; @@ -486,7 +494,7 @@ static int tda18271_tune(struct dvb_frontend *fe, break; i++; } - dprintk(2, "rf band = 0x%x, i = %d\n", tda18271_rf_band[i].val, i); + dbg_map("rf band = 0x%x, i = %d\n", tda18271_rf_band[i].val, i); regs[R_EP2] &= ~0xe0; /* clear rf band bits */ regs[R_EP2] |= (tda18271_rf_band[i].val << 5); @@ -498,7 +506,7 @@ static int tda18271_tune(struct dvb_frontend *fe, break; i++; } - dprintk(2, "gain taper = 0x%x, i = %d\n", + dbg_map("gain taper = 0x%x, i = %d\n", tda18271_gain_taper[i].val, i); regs[R_EP2] &= ~0x1f; /* clear gain taper bits */ @@ -533,7 +541,7 @@ static int tda18271_tune(struct dvb_frontend *fe, break; i++; } - dprintk(2, "rf cal = 0x%x, i = %d\n", tda18271_rf_cal[i].val, i); + dbg_map("rf cal = 0x%x, i = %d\n", tda18271_rf_cal[i].val, i); /* VHF_Low band only */ if (tda18271_rf_cal[i].rfmax != 0) { @@ -587,7 +595,7 @@ static int tda18271_tune(struct dvb_frontend *fe, break; i++; } - dprintk(2, "main pll, pd = 0x%x, d = 0x%x, i = %d\n", + dbg_map("main pll, pd = 0x%x, d = 0x%x, i = %d\n", tda18271_main_pll[i].pd, tda18271_main_pll[i].d, i); regs[R_MPD] = (0x7f & tda18271_main_pll[i].pd); @@ -723,7 +731,7 @@ static int tda18271_set_analog_params(struct dvb_frontend *fe, if (params->mode == V4L2_TUNER_RADIO) sgIF = 88; /* if frequency is 5.5 MHz */ - dprintk(1, "setting tda18271 to system %s\n", mode); + dbg_info("setting tda18271 to system %s\n", mode); return tda18271_tune(fe, sgIF * 62500, params->frequency * 62500, 0, std); @@ -770,7 +778,7 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, { struct tda18271_priv *priv = NULL; - dprintk(1, "@ %d-%04x\n", i2c_adapter_id(i2c), addr); + dbg_info("@ %d-%04x\n", i2c_adapter_id(i2c), addr); priv = kzalloc(sizeof(struct tda18271_priv), GFP_KERNEL); if (priv == NULL) return NULL; -- cgit v1.2.3 From f4fea806a1d15163ee19b12a8755446d076c25fb Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 2 Dec 2007 09:03:57 -0500 Subject: tda18271: set image rejection validity From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/tda18271-fe.c | 11 ++++++++++- linux/drivers/media/dvb/frontends/tda18271-priv.h | 1 + linux/drivers/media/dvb/frontends/tda18271-tables.c | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/dvb/frontends/tda18271-fe.c b/linux/drivers/media/dvb/frontends/tda18271-fe.c index 981596371..b2baff34c 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-fe.c +++ b/linux/drivers/media/dvb/frontends/tda18271-fe.c @@ -584,7 +584,16 @@ static int tda18271_tune(struct dvb_frontend *fe, regs[R_EP4] &= ~0x80; /* turn this bit on only for fm */ - /* FIXME: image rejection validity EP5[2:0] */ + /* image rejection validity EP5[2:0] */ + i = 0; + while ((tda18271_ir_measure[i].rfmax * 1000) < freq) { + if (tda18271_ir_measure[i].rfmax == 0) + break; + i++; + } + dbg_map("ir measure, i = %d\n", i); + regs[R_EP5] &= ~0x07; + regs[R_EP5] |= tda18271_ir_measure[i].val; /* calculate MAIN PLL */ N = freq + ifc; diff --git a/linux/drivers/media/dvb/frontends/tda18271-priv.h b/linux/drivers/media/dvb/frontends/tda18271-priv.h index 71b4d7963..a3158eb25 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-priv.h +++ b/linux/drivers/media/dvb/frontends/tda18271-priv.h @@ -84,6 +84,7 @@ extern struct tda18271_map tda18271_km[]; extern struct tda18271_map tda18271_rf_band[]; extern struct tda18271_map tda18271_gain_taper[]; extern struct tda18271_map tda18271_rf_cal[]; +extern struct tda18271_map tda18271_ir_measure[]; #endif /* __TDA18271_PRIV_H__ */ diff --git a/linux/drivers/media/dvb/frontends/tda18271-tables.c b/linux/drivers/media/dvb/frontends/tda18271-tables.c index a018b5140..d65d3411e 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-tables.c +++ b/linux/drivers/media/dvb/frontends/tda18271-tables.c @@ -244,6 +244,14 @@ struct tda18271_map tda18271_rf_cal[] = { { .rfmax = 0, .val = 0x00 }, /* end */ }; +struct tda18271_map tda18271_ir_measure[] = { + { .rfmax = 30000, .val = 4}, + { .rfmax = 200000, .val = 5}, + { .rfmax = 600000, .val = 6}, + { .rfmax = 865000, .val = 7}, + { .rfmax = 0, .val = 0}, /* end */ +}; + /* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- -- cgit v1.2.3 From ebb0ad8c7f65b123c7dbc628e6daaba6db39a75b Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 2 Dec 2007 14:36:05 -0500 Subject: tda18271: convert table lookup loops to functions From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/tda18271-fe.c | 119 +++++---------------- linux/drivers/media/dvb/frontends/tda18271-priv.h | 46 ++++---- .../drivers/media/dvb/frontends/tda18271-tables.c | 106 ++++++++++++++++-- 3 files changed, 149 insertions(+), 122 deletions(-) diff --git a/linux/drivers/media/dvb/frontends/tda18271-fe.c b/linux/drivers/media/dvb/frontends/tda18271-fe.c index b2baff34c..0aec38488 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-fe.c +++ b/linux/drivers/media/dvb/frontends/tda18271-fe.c @@ -26,22 +26,10 @@ #include "tda18271.h" #include "tda18271-priv.h" -static int tda18271_debug; +int tda18271_debug; module_param_named(debug, tda18271_debug, int, 0644); MODULE_PARM_DESC(debug, "set debug level (info=1, map=2, reg=4 (or-able))"); -#define dprintk(level, fmt, arg...) do {\ - if (tda18271_debug & level) \ - printk(KERN_DEBUG "%s: " fmt, __FUNCTION__, ##arg); } while (0) - -#define DBG_INFO 1 -#define DBG_MAP 2 -#define DBG_REG 4 - -#define dbg_info(fmt, arg...) dprintk(DBG_INFO, fmt, ##arg) -#define dbg_map(fmt, arg...) dprintk(DBG_MAP, fmt, ##arg) -#define dbg_reg(fmt, arg...) dprintk(DBG_REG, fmt, ##arg) - /*---------------------------------------------------------------------*/ #define TDA18271_ANALOG 0 @@ -366,7 +354,7 @@ static int tda18271_tune(struct dvb_frontend *fe, struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; u32 div, N = 0; - int i; + u8 d, pd, val; tda18271_init(fe); #if 0 @@ -380,16 +368,10 @@ static int tda18271_tune(struct dvb_frontend *fe, /* RF tracking filter calibration */ /* calculate BP_Filter */ - i = 0; - while ((tda18271_bp_filter[i].rfmax * 1000) < freq) { - if (tda18271_bp_filter[i + 1].rfmax == 0) - break; - i++; - } - dbg_map("bp filter = 0x%x, i = %d\n", tda18271_bp_filter[i].val, i); + tda18271_calc_bp_filter(&freq, &val); regs[R_EP1] &= ~0x07; /* clear bp filter bits */ - regs[R_EP1] |= tda18271_bp_filter[i].val; + regs[R_EP1] |= val; tda18271_write_regs(fe, R_EP1, 1); regs[R_EB4] &= 0x07; @@ -419,18 +401,11 @@ static int tda18271_tune(struct dvb_frontend *fe, break; } - i = 0; - while ((tda18271_cal_pll[i].lomax * 1000) < N) { - if (tda18271_cal_pll[i + 1].lomax == 0) - break; - i++; - } - dbg_map("cal pll, pd = 0x%x, d = 0x%x, i = %d\n", - tda18271_cal_pll[i].pd, tda18271_cal_pll[i].d, i); + tda18271_calc_cal_pll(&N, &pd, &d); - regs[R_CPD] = tda18271_cal_pll[i].pd; + regs[R_CPD] = pd; - div = ((tda18271_cal_pll[i].d * (N / 1000)) << 7) / 125; + div = ((d * (N / 1000)) << 7) / 125; regs[R_CD1] = 0xff & (div >> 16); regs[R_CD2] = 0xff & (div >> 8); regs[R_CD3] = 0xff & div; @@ -446,16 +421,9 @@ static int tda18271_tune(struct dvb_frontend *fe, break; } - i = 0; - while ((tda18271_main_pll[i].lomax * 1000) < N) { - if (tda18271_main_pll[i + 1].lomax == 0) - break; - i++; - } - dbg_map("main pll, pd = 0x%x, d = 0x%x, i = %d\n", - tda18271_main_pll[i].pd, tda18271_main_pll[i].d, i); + tda18271_calc_main_pll(&N, &pd, &d); - regs[R_MPD] = (0x7f & tda18271_main_pll[i].pd); + regs[R_MPD] = (0x7f & pd); switch (priv->mode) { case TDA18271_ANALOG: @@ -466,7 +434,7 @@ static int tda18271_tune(struct dvb_frontend *fe, break; } - div = ((tda18271_main_pll[i].d * (N / 1000)) << 7) / 125; + div = ((d * (N / 1000)) << 7) / 125; regs[R_MD1] = 0xff & (div >> 16); regs[R_MD2] = 0xff & (div >> 8); regs[R_MD3] = 0xff & div; @@ -475,42 +443,23 @@ static int tda18271_tune(struct dvb_frontend *fe, msleep(5); /* RF tracking filter calibration initialization */ /* search for K,M,CO for RF Calibration */ - i = 0; - while ((tda18271_km[i].rfmax * 1000) < freq) { - if (tda18271_km[i + 1].rfmax == 0) - break; - i++; - } - dbg_map("km = 0x%x, i = %d\n", tda18271_km[i].val, i); + tda18271_calc_km(&freq, &val); regs[R_EB13] &= 0x83; - regs[R_EB13] |= tda18271_km[i].val; + regs[R_EB13] |= val; tda18271_write_regs(fe, R_EB13, 1); /* search for RF_BAND */ - i = 0; - while ((tda18271_rf_band[i].rfmax * 1000) < freq) { - if (tda18271_rf_band[i + 1].rfmax == 0) - break; - i++; - } - dbg_map("rf band = 0x%x, i = %d\n", tda18271_rf_band[i].val, i); + tda18271_calc_rf_band(&freq, &val); regs[R_EP2] &= ~0xe0; /* clear rf band bits */ - regs[R_EP2] |= (tda18271_rf_band[i].val << 5); + regs[R_EP2] |= (val << 5); /* search for Gain_Taper */ - i = 0; - while ((tda18271_gain_taper[i].rfmax * 1000) < freq) { - if (tda18271_gain_taper[i + 1].rfmax == 0) - break; - i++; - } - dbg_map("gain taper = 0x%x, i = %d\n", - tda18271_gain_taper[i].val, i); + tda18271_calc_gain_taper(&freq, &val); regs[R_EP2] &= ~0x1f; /* clear gain taper bits */ - regs[R_EP2] |= tda18271_gain_taper[i].val; + regs[R_EP2] |= val; tda18271_write_regs(fe, R_EP2, 1); tda18271_write_regs(fe, R_EP1, 1); @@ -535,17 +484,11 @@ static int tda18271_tune(struct dvb_frontend *fe, tda18271_write_regs(fe, R_EP1, 1); /* RF tracking filer correction for VHF_Low band */ - i = 0; - while ((tda18271_rf_cal[i].rfmax * 1000) < freq) { - if (tda18271_rf_cal[i].rfmax == 0) - break; - i++; - } - dbg_map("rf cal = 0x%x, i = %d\n", tda18271_rf_cal[i].val, i); + tda18271_calc_rf_cal(&freq, &val); /* VHF_Low band only */ - if (tda18271_rf_cal[i].rfmax != 0) { - regs[R_EB14] = tda18271_rf_cal[i].val; + if (val != 0) { + regs[R_EB14] = val; tda18271_write_regs(fe, R_EB14, 1); } @@ -585,29 +528,17 @@ static int tda18271_tune(struct dvb_frontend *fe, regs[R_EP4] &= ~0x80; /* turn this bit on only for fm */ /* image rejection validity EP5[2:0] */ - i = 0; - while ((tda18271_ir_measure[i].rfmax * 1000) < freq) { - if (tda18271_ir_measure[i].rfmax == 0) - break; - i++; - } - dbg_map("ir measure, i = %d\n", i); + tda18271_calc_ir_measure(&freq, &val); + regs[R_EP5] &= ~0x07; - regs[R_EP5] |= tda18271_ir_measure[i].val; + regs[R_EP5] |= val; /* calculate MAIN PLL */ N = freq + ifc; - i = 0; - while ((tda18271_main_pll[i].lomax * 1000) < N) { - if (tda18271_main_pll[i + 1].lomax == 0) - break; - i++; - } - dbg_map("main pll, pd = 0x%x, d = 0x%x, i = %d\n", - tda18271_main_pll[i].pd, tda18271_main_pll[i].d, i); + tda18271_calc_main_pll(&N, &pd, &d); - regs[R_MPD] = (0x7f & tda18271_main_pll[i].pd); + regs[R_MPD] = (0x7f & pd); switch (priv->mode) { case TDA18271_ANALOG: regs[R_MPD] &= ~0x08; @@ -617,7 +548,7 @@ static int tda18271_tune(struct dvb_frontend *fe, break; } - div = ((tda18271_main_pll[i].d * (N / 1000)) << 7) / 125; + div = ((d * (N / 1000)) << 7) / 125; regs[R_MD1] = 0xff & (div >> 16); regs[R_MD2] = 0xff & (div >> 8); regs[R_MD3] = 0xff & div; diff --git a/linux/drivers/media/dvb/frontends/tda18271-priv.h b/linux/drivers/media/dvb/frontends/tda18271-priv.h index a3158eb25..d56c2fe3e 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-priv.h +++ b/linux/drivers/media/dvb/frontends/tda18271-priv.h @@ -21,6 +21,7 @@ #ifndef __TDA18271_PRIV_H__ #define __TDA18271_PRIV_H__ +#include #include #define R_ID 0x00 /* ID byte */ @@ -65,26 +66,31 @@ #define TDA18271_NUM_REGS 39 -struct tda18271_pll_map { - u32 lomax; - u8 pd; /* post div */ - u8 d; /* div */ -}; - -extern struct tda18271_pll_map tda18271_main_pll[]; -extern struct tda18271_pll_map tda18271_cal_pll[]; - -struct tda18271_map { - u32 rfmax; - u8 val; -}; - -extern struct tda18271_map tda18271_bp_filter[]; -extern struct tda18271_map tda18271_km[]; -extern struct tda18271_map tda18271_rf_band[]; -extern struct tda18271_map tda18271_gain_taper[]; -extern struct tda18271_map tda18271_rf_cal[]; -extern struct tda18271_map tda18271_ir_measure[]; +extern int tda18271_debug; + +#define dprintk(level, fmt, arg...) do {\ + if (tda18271_debug & level) \ + printk(KERN_DEBUG "%s: " fmt, __FUNCTION__, ##arg); } while (0) + +#define DBG_INFO 1 +#define DBG_MAP 2 +#define DBG_REG 4 + +#define dbg_info(fmt, arg...) dprintk(DBG_INFO, fmt, ##arg) +#define dbg_map(fmt, arg...) dprintk(DBG_MAP, fmt, ##arg) +#define dbg_reg(fmt, arg...) dprintk(DBG_REG, fmt, ##arg) + +/*---------------------------------------------------------------------*/ + +extern void tda18271_calc_cal_pll(u32 *freq, u8 *post_div, u8 *div); +extern void tda18271_calc_main_pll(u32 *freq, u8 *post_div, u8 *div); + +extern void tda18271_calc_bp_filter(u32 *freq, u8 *val); +extern void tda18271_calc_km(u32 *freq, u8 *val); +extern void tda18271_calc_rf_band(u32 *freq, u8 *val); +extern void tda18271_calc_gain_taper(u32 *freq, u8 *val); +extern void tda18271_calc_rf_cal(u32 *freq, u8 *val); +extern void tda18271_calc_ir_measure(u32 *freq, u8 *val); #endif /* __TDA18271_PRIV_H__ */ diff --git a/linux/drivers/media/dvb/frontends/tda18271-tables.c b/linux/drivers/media/dvb/frontends/tda18271-tables.c index d65d3411e..65387bb05 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-tables.c +++ b/linux/drivers/media/dvb/frontends/tda18271-tables.c @@ -20,7 +20,20 @@ #include "tda18271-priv.h" -struct tda18271_pll_map tda18271_main_pll[] = { +struct tda18271_pll_map { + u32 lomax; + u8 pd; /* post div */ + u8 d; /* div */ +}; + +struct tda18271_map { + u32 rfmax; + u8 val; +}; + +/*---------------------------------------------------------------------*/ + +static struct tda18271_pll_map tda18271_main_pll[] = { { .lomax = 32000, .pd = 0x5f, .d = 0xf0 }, { .lomax = 35000, .pd = 0x5e, .d = 0xe0 }, { .lomax = 37000, .pd = 0x5d, .d = 0xd0 }, @@ -64,7 +77,7 @@ struct tda18271_pll_map tda18271_main_pll[] = { { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ }; -struct tda18271_pll_map tda18271_cal_pll[] = { +static struct tda18271_pll_map tda18271_cal_pll[] = { { .lomax = 33000, .pd = 0xdd, .d = 0xd0 }, { .lomax = 36000, .pd = 0xdc, .d = 0xc0 }, { .lomax = 40000, .pd = 0xdb, .d = 0xb0 }, @@ -103,7 +116,7 @@ struct tda18271_pll_map tda18271_cal_pll[] = { { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ }; -struct tda18271_map tda18271_bp_filter[] = { +static struct tda18271_map tda18271_bp_filter[] = { { .rfmax = 62000, .val = 0x00 }, { .rfmax = 84000, .val = 0x01 }, { .rfmax = 100000, .val = 0x02 }, @@ -114,7 +127,7 @@ struct tda18271_map tda18271_bp_filter[] = { { .rfmax = 0, .val = 0x00 }, /* end */ }; -struct tda18271_map tda18271_km[] = { +static struct tda18271_map tda18271_km[] = { { .rfmax = 61100, .val = 0x74 }, { .rfmax = 350000, .val = 0x40 }, { .rfmax = 720000, .val = 0x30 }, @@ -122,7 +135,7 @@ struct tda18271_map tda18271_km[] = { { .rfmax = 0, .val = 0x00 }, /* end */ }; -struct tda18271_map tda18271_rf_band[] = { +static struct tda18271_map tda18271_rf_band[] = { { .rfmax = 47900, .val = 0x00 }, { .rfmax = 61100, .val = 0x01 }, /* { .rfmax = 152600, .val = 0x02 }, */ @@ -134,7 +147,7 @@ struct tda18271_map tda18271_rf_band[] = { { .rfmax = 0, .val = 0x00 }, /* end */ }; -struct tda18271_map tda18271_gain_taper[] = { +static struct tda18271_map tda18271_gain_taper[] = { { .rfmax = 45400, .val = 0x1f }, { .rfmax = 45800, .val = 0x1e }, { .rfmax = 46200, .val = 0x1d }, @@ -223,7 +236,7 @@ struct tda18271_map tda18271_gain_taper[] = { { .rfmax = 0, .val = 0x00 }, /* end */ }; -struct tda18271_map tda18271_rf_cal[] = { +static struct tda18271_map tda18271_rf_cal[] = { { .rfmax = 41000, .val = 0x1e }, { .rfmax = 43000, .val = 0x30 }, { .rfmax = 45000, .val = 0x43 }, @@ -244,7 +257,7 @@ struct tda18271_map tda18271_rf_cal[] = { { .rfmax = 0, .val = 0x00 }, /* end */ }; -struct tda18271_map tda18271_ir_measure[] = { +static struct tda18271_map tda18271_ir_measure[] = { { .rfmax = 30000, .val = 4}, { .rfmax = 200000, .val = 5}, { .rfmax = 600000, .val = 6}, @@ -252,6 +265,83 @@ struct tda18271_map tda18271_ir_measure[] = { { .rfmax = 0, .val = 0}, /* end */ }; +/*---------------------------------------------------------------------*/ + +static void tda18271_lookup_map(struct tda18271_map *map, + u32 *freq, u8 *val) +{ + int i = 0; + while ((map[i].rfmax * 1000) < *freq) { + if (map[i + 1].rfmax == 0) + break; + i++; + } + *val = map[i].val; +} + +static void tda18271_lookup_pll_map(struct tda18271_pll_map *map, + u32 *freq, u8 *post_div, u8 *div) +{ + int i = 0; + while ((map[i].lomax * 1000) < *freq) { + if (map[i + 1].lomax == 0) + break; + i++; + } + *post_div = map[i].pd; + *div = map[i].d; +} + +/*---------------------------------------------------------------------*/ + +void tda18271_calc_cal_pll(u32 *freq, u8 *post_div, u8 *div) +{ + tda18271_lookup_pll_map(tda18271_cal_pll, freq, post_div, div); + dbg_map("post div = 0x%02x, div = 0x%02x\n", *post_div, *div); +} + +void tda18271_calc_main_pll(u32 *freq, u8 *post_div, u8 *div) +{ + tda18271_lookup_pll_map(tda18271_main_pll, freq, post_div, div); + dbg_map("post div = 0x%02x, div = 0x%02x\n", *post_div, *div); +} + +void tda18271_calc_bp_filter(u32 *freq, u8 *val) +{ + tda18271_lookup_map(tda18271_bp_filter, freq, val); + dbg_map("0x%02x\n", *val); +} + +void tda18271_calc_km(u32 *freq, u8 *val) +{ + tda18271_lookup_map(tda18271_km, freq, val); + dbg_map("0x%02x\n", *val); +} + +void tda18271_calc_rf_band(u32 *freq, u8 *val) +{ + tda18271_lookup_map(tda18271_rf_band, freq, val); + dbg_map("0x%02x\n", *val); +} + +void tda18271_calc_gain_taper(u32 *freq, u8 *val) +{ + tda18271_lookup_map(tda18271_gain_taper, freq, val); + dbg_map("0x%02x\n", *val); +} + +void tda18271_calc_rf_cal(u32 *freq, u8 *val) +{ + tda18271_lookup_map(tda18271_rf_cal, freq, val); + dbg_map("0x%02x\n", *val); +} + +void tda18271_calc_ir_measure(u32 *freq, u8 *val) +{ + tda18271_lookup_map(tda18271_ir_measure, freq, val); + dbg_map("0x%02x\n", *val); +} + /* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- -- cgit v1.2.3 From 71505e4c9a56607cc30704dc33fb3c53bb3c742e Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 2 Dec 2007 15:37:38 -0500 Subject: tda18271: fix register dump format From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/tda18271-fe.c | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/linux/drivers/media/dvb/frontends/tda18271-fe.c b/linux/drivers/media/dvb/frontends/tda18271-fe.c index 0aec38488..579229f3d 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-fe.c +++ b/linux/drivers/media/dvb/frontends/tda18271-fe.c @@ -73,22 +73,22 @@ static void tda18271_dump_regs(struct dvb_frontend *fe) unsigned char *regs = priv->tda18271_regs; dbg_reg("=== TDA18271 REG DUMP ===\n"); - dbg_reg("ID_BYTE = 0x%x\n", 0xff & regs[R_ID]); - dbg_reg("THERMO_BYTE = 0x%x\n", 0xff & regs[R_TM]); - dbg_reg("POWER_LEVEL_BYTE = 0x%x\n", 0xff & regs[R_PL]); - dbg_reg("EASY_PROG_BYTE_1 = 0x%x\n", 0xff & regs[R_EP1]); - dbg_reg("EASY_PROG_BYTE_2 = 0x%x\n", 0xff & regs[R_EP2]); - dbg_reg("EASY_PROG_BYTE_3 = 0x%x\n", 0xff & regs[R_EP3]); - dbg_reg("EASY_PROG_BYTE_4 = 0x%x\n", 0xff & regs[R_EP4]); - dbg_reg("EASY_PROG_BYTE_5 = 0x%x\n", 0xff & regs[R_EP5]); - dbg_reg("CAL_POST_DIV_BYTE = 0x%x\n", 0xff & regs[R_CPD]); - dbg_reg("CAL_DIV_BYTE_1 = 0x%x\n", 0xff & regs[R_CD1]); - dbg_reg("CAL_DIV_BYTE_2 = 0x%x\n", 0xff & regs[R_CD2]); - dbg_reg("CAL_DIV_BYTE_3 = 0x%x\n", 0xff & regs[R_CD3]); - dbg_reg("MAIN_POST_DIV_BYTE = 0x%x\n", 0xff & regs[R_MPD]); - dbg_reg("MAIN_DIV_BYTE_1 = 0x%x\n", 0xff & regs[R_MD1]); - dbg_reg("MAIN_DIV_BYTE_2 = 0x%x\n", 0xff & regs[R_MD2]); - dbg_reg("MAIN_DIV_BYTE_3 = 0x%x\n", 0xff & regs[R_MD3]); + dbg_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]); + dbg_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]); + dbg_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]); + dbg_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]); + dbg_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]); + dbg_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]); + dbg_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]); + dbg_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]); + dbg_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]); + dbg_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]); + dbg_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]); + dbg_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]); + dbg_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]); + dbg_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]); + dbg_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]); + dbg_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]); } static void tda18271_read_regs(struct dvb_frontend *fe) -- cgit v1.2.3 From 97b7ad6194868f627050701ecfdb59b8723b7d9c Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 2 Dec 2007 20:04:57 -0600 Subject: pvrusb2: Change division to bit-or for tveeprom standards From: Roel Kluin <12o3l@tiscali.nl> Signed-off-by: Roel Kluin <12o3l@tiscali.nl> Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 477955f7a..b0e32a20c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1524,7 +1524,7 @@ const static struct pvr2_std_hack std_eeprom_maps[] = { }, { /* PAL(D/D1/K) */ .pat = V4L2_STD_DK, - .std = V4L2_STD_PAL_D/V4L2_STD_PAL_D1|V4L2_STD_PAL_K, + .std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K, }, }; -- cgit v1.2.3 From dedddec7bfa58699731c228f8e7984461217b94c Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 2 Dec 2007 20:44:40 -0600 Subject: pvrusb2: Fix compilation for kernels older than 2.6.18 From: Mike Isely Move VIDEO_PVRUSB2_SYSFS such that it is turned off for kernels older than 2.6.18. This should allow the driver to still compile using older kernel (minus the sysfs interface). This change is due to the driver's sysfs implementation moving forward to the newer class interface mechanism; the differences are too great to cleanly separate with ifdefs (and I expect the divergence to grow as other changes are made to take advantage of the new mechanism), so for now we simply give up trying to deal with the old interface. Signed-off-by: Mike Isely --- v4l/versions.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/v4l/versions.txt b/v4l/versions.txt index affc221bb..af560b999 100644 --- a/v4l/versions.txt +++ b/v4l/versions.txt @@ -14,6 +14,8 @@ VIDEO_CAFE_CCIC # Uses remap_vmalloc_range() [2.6.18] VIDEOBUF_VMALLOC +# Uses new class interface, which did not start to appear until 2.6.18. +VIDEO_PVRUSB2_SYSFS # DVB_CORE_ATTACH relies on symbol_put_addr which hangs pre-2.6.17 [2.6.17] @@ -59,7 +61,6 @@ USB_PWC_DEBUG USB_STV680 VIDEO_PVRUSB2 VIDEO_PVRUSB2_24XXX -VIDEO_PVRUSB2_SYSFS VIDEO_PVRUSB2_DEBUGIFC VIDEO_USBVISION -- cgit v1.2.3 From e197760836fc4b0c7ac3411e6edd31dac68e5d82 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 2 Dec 2007 20:51:34 -0600 Subject: pvrusb2: Implement default standard selection based on device type From: Mike Isely This adds a default video standard setting to the pvr2_device_desc structure for describing device types. With this change it is possible to set a reasonable default standard based on device type. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c | 3 +-- linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h | 7 +++++++ linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 6 +++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index e4a311a81..eed64571c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -29,9 +29,8 @@ pvr2_device_desc structures. #include "pvrusb2-devattr.h" #include -/* All this is needed in order to pull in tuner type ids... */ +/* This is needed in order to pull in tuner type ids... */ #include -#include #include /* Known major hardware variants, keyed from device ID */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h index b9517e1ca..830f27961 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -22,6 +22,7 @@ #define __PVRUSB2_DEVATTR_H #include +#include /* @@ -69,6 +70,12 @@ struct pvr2_device_desc { driver could not discover the type any other way). */ int default_tuner_type; + /* Initial standard bits to use for this device, if not zero. + Anything set here is also implied as an available standard. + Note: This is ignored if overridden on the module load line via + the video_std module option. */ + v4l2_std_id default_std_mask; + /* If set, we don't bother trying to load cx23416 firmware. */ char flag_skip_cx23416_firmware; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index b0e32a20c..8a27129b1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1449,8 +1449,12 @@ static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw) int tp = 0; if ((unit_number >= 0) && (unit_number < PVR_NUM)) { tp = video_std[unit_number]; + if (tp) return tp; } - return tp; + if (hdw->hdw_desc->default_std_mask) { + return hdw->hdw_desc->default_std_mask; + } + return 0; } -- cgit v1.2.3 From 44549c9cceb63ac4b50f950c361ecb2bea79bce2 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 2 Dec 2007 22:43:23 -0600 Subject: pvrusb2: Remove use of volatile in command sequencer From: Mike Isely pvrusb2: Remove use of volatile for command sequencer; these variables are set by interrupt-context code and we check their state in such a manner that there should be no race conditions. This had originally been done out of paranoia, and I have since been convinced that the paranoia is not required. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index a8e829d37..ff99ed5fa 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -233,9 +233,9 @@ struct pvr2_hdw { struct urb *ctl_read_urb; unsigned char *ctl_write_buffer; unsigned char *ctl_read_buffer; - volatile int ctl_write_pend_flag; - volatile int ctl_read_pend_flag; - volatile int ctl_timeout_flag; + int ctl_write_pend_flag; + int ctl_read_pend_flag; + int ctl_timeout_flag; struct completion ctl_done; unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE]; int cmd_debug_state; // Low level command debugging info -- cgit v1.2.3 From e39a15b8e87d62fd7bc1974bd27012d80048ddef Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 2 Dec 2007 22:44:43 -0600 Subject: pvrusb2: Remove use of volatile in pipeline control state machine From: Mike Isely pvrusb2: Eliminate use of volatile in pipeline control state variables. These were all cases of paranoia; upon further review the overall mechanism employed here should not require use of volatile. This had originally been done out of paranoia, and I have since been convinced that the paranoia is not required. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index ff99ed5fa..dfaa347b1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -245,14 +245,14 @@ struct pvr2_hdw { /* Bits of state that describe what is going on with various parts of the driver. */ - volatile int state_encoder_ok; /* Encoder is operational */ - volatile int state_encoder_run; /* Encoder is running */ - volatile int state_encoder_config; /* Encoder is configured */ - volatile int state_encoder_waitok; /* Encoder pre-wait done */ - volatile int state_decoder_run; /* Decoder is running */ - volatile int state_usbstream_run; /* FX2 is streaming */ - volatile int state_decoder_quiescent; /* Decoder idle for > 50msec */ - volatile int state_pipeline_config; /* Pipeline is configured */ + int state_encoder_ok; /* Encoder is operational */ + int state_encoder_run; /* Encoder is running */ + int state_encoder_config; /* Encoder is configured */ + int state_encoder_waitok; /* Encoder pre-wait done */ + int state_decoder_run; /* Decoder is running */ + int state_usbstream_run; /* FX2 is streaming */ + int state_decoder_quiescent; /* Decoder idle for > 50msec */ + int state_pipeline_config; /* Pipeline is configured */ int state_pipeline_req; /* Somebody wants to stream */ int state_pipeline_pause; /* Pipeline must be paused */ int state_pipeline_idle; /* Pipeline not running */ -- cgit v1.2.3 From ca31a764dce00de5f60c73c819d4d4a3be4e32f7 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 2 Dec 2007 22:45:26 -0600 Subject: pvrusb2: Expand comment in device attributes description. From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h index 830f27961..a9c3d99b6 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -94,7 +94,13 @@ struct pvr2_device_desc { char flag_has_wm8775; /* Device has IR hardware that can be faked into looking like a - normal Hauppauge i2c IR receiver. */ + normal Hauppauge i2c IR receiver. This is currently very + specific to the 24xxx device, where Hauppauge had replaced their + 'standard' I2C IR receiver with a bunch of FPGA logic controlled + directly via the FX2. Turning this on tells the pvrusb2 driver + to virtualize the presence of the non-existant IR receiver chip and + implement the virtual receiver in terms of appropriate FX2 + commands. */ char flag_has_hauppauge_custom_ir; }; -- cgit v1.2.3 From 74ad60100684b29485d113272840d70af61d8026 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 2 Dec 2007 22:47:12 -0600 Subject: pvrusb2: minor rework for default video standard handling From: Mike Isely pvrusb2: When a per-device-type default video standard is declared, handle it in such a way that it can be correctly and unambiguously reported in the system log. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 8a27129b1..22d9c8f5c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1451,9 +1451,6 @@ static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw) tp = video_std[unit_number]; if (tp) return tp; } - if (hdw->hdw_desc->default_std_mask) { - return hdw->hdw_desc->default_std_mask; - } return 0; } @@ -1536,9 +1533,10 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) { char buf[40]; unsigned int bcnt; - v4l2_std_id std1,std2; + v4l2_std_id std1,std2,std3; std1 = get_default_standard(hdw); + std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask; bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); pvr2_trace(PVR2_TRACE_STD, @@ -1548,7 +1546,7 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) hdw->std_mask_avail = hdw->std_mask_eeprom; - std2 = std1 & ~hdw->std_mask_avail; + std2 = (std1|std3) & ~hdw->std_mask_avail; if (std2) { bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2); pvr2_trace(PVR2_TRACE_STD, @@ -1570,6 +1568,16 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) pvr2_hdw_internal_find_stdenum(hdw); return; } + if (std3) { + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3); + pvr2_trace(PVR2_TRACE_STD, + "Initial video standard" + " (determined by device type): %.*s",bcnt,buf); + hdw->std_mask_cur = std3; + hdw->std_dirty = !0; + pvr2_hdw_internal_find_stdenum(hdw); + return; + } { unsigned int idx; -- cgit v1.2.3 From 81c351c02f6ad4d967f24dc5f132fc6f5a755570 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 2 Dec 2007 23:10:04 -0600 Subject: pvrusb2: Recognize ATSC video standard bit values From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-std.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c index dd49a60ec..b37854b06 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -51,6 +51,10 @@ struct std_name { V4L2_STD_NTSC_M_KR| \ V4L2_STD_NTSC_443) +#define CSTD_ATSC \ + (V4L2_STD_ATSC_8_VSB| \ + V4L2_STD_ATSC_16_VSB) + #define CSTD_SECAM \ (V4L2_STD_SECAM_B| \ V4L2_STD_SECAM_D| \ @@ -83,6 +87,7 @@ static const struct std_name std_groups[] = { {"PAL",CSTD_PAL}, {"NTSC",CSTD_NTSC}, {"SECAM",CSTD_SECAM}, + {"ATSC",CSTD_ATSC}, }; /* Mapping of standard bits to modulation system */ @@ -105,6 +110,8 @@ static const struct std_name std_items[] = { {"N",TSTD_N}, {"Nc",TSTD_Nc}, {"60",TSTD_60}, + {"8VSB",V4L2_STD_ATSC_8_VSB}, + {"16VSB",V4L2_STD_ATSC_16_VSB}, }; -- cgit v1.2.3 From f038f28e4892d23a5f89fe6173e668e94c59281b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 3 Dec 2007 16:18:12 -0200 Subject: Copy kernel's checkpatch.pl and improve prep_commit_msg.pl From: Mauro Carvalho Chehab Kernel's checkpatch.pl appeared only on kernel version 2.6.22. However, several improvements happened on recent kernels. This patch copies the latest version (0.11) of checkpatch.pl to the v4l-dvb development tree. Also, now, prep_commit_msg.pl checks if the script is available at Kernel and checks for its version. If the version is older than the in-tree one, it uses the in-tree. Otherwise, kernel version is used. It is always a good idea to use kernel's checkpatch.pl, since some additional tests can be done when using the kernel's, like checking for the usage of depreciated API's. Signed-off-by: Mauro Carvalho Chehab --- v4l/scripts/checkpatch.pl | 1419 ++++++++++++++++++++++++++++++++++++++++ v4l/scripts/prep_commit_msg.pl | 36 +- 2 files changed, 1453 insertions(+), 2 deletions(-) create mode 100755 v4l/scripts/checkpatch.pl diff --git a/v4l/scripts/checkpatch.pl b/v4l/scripts/checkpatch.pl new file mode 100755 index 000000000..7a3098b43 --- /dev/null +++ b/v4l/scripts/checkpatch.pl @@ -0,0 +1,1419 @@ +#!/usr/bin/perl -w +# (c) 2001, Dave Jones. (the file handling bit) +# (c) 2005, Joel Schopp (the ugly bit) +# (c) 2007, Andy Whitcroft (new conditions, test suite, etc) +# Licensed under the terms of the GNU GPL License version 2 + +use strict; + +my $P = $0; +$P =~ s@.*/@@g; + +my $V = '0.11'; + +use Getopt::Long qw(:config no_auto_abbrev); + +my $quiet = 0; +my $tree = 1; +my $chk_signoff = 1; +my $chk_patch = 1; +my $tst_type = 0; +my $emacs = 0; +my $file = 0; +my $check = 0; +my $root; +GetOptions( + 'q|quiet+' => \$quiet, + 'tree!' => \$tree, + 'signoff!' => \$chk_signoff, + 'patch!' => \$chk_patch, + 'test-type!' => \$tst_type, + 'emacs!' => \$emacs, + 'file!' => \$file, + 'subjective!' => \$check, + 'strict!' => \$check, + 'root=s' => \$root, +) or exit; + +my $exit = 0; + +if ($#ARGV < 0) { + print "usage: $P [options] patchfile\n"; + print "version: $V\n"; + print "options: -q => quiet\n"; + print " --no-tree => run without a kernel tree\n"; + print " --emacs => emacs compile window format\n"; + print " --file => check a source file\n"; + print " --strict => enable more subjective tests\n"; + print " --root => path to the kernel tree root\n"; + exit(1); +} + +if ($tree) { + if (defined $root) { + if (!top_of_kernel_tree($root)) { + die "$P: $root: --root does not point at a valid tree\n"; + } + } else { + if (top_of_kernel_tree('.')) { + $root = '.'; + } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && + top_of_kernel_tree($1)) { + $root = $1; + } + } + + if (!defined $root) { + print "Must be run from the top-level dir. of a kernel tree\n"; + exit(2); + } +} + +my $emitted_corrupt = 0; + +our $Ident = qr{[A-Za-z_][A-Za-z\d_]*}; +our $Storage = qr{extern|static|asmlinkage}; +our $Sparse = qr{ + __user| + __kernel| + __force| + __iomem| + __must_check| + __init_refok| + __kprobes| + fastcall + }x; +our $Attribute = qr{ + const| + __read_mostly| + __kprobes| + __(?:mem|cpu|dev|)(?:initdata|init) + }x; +our $Inline = qr{inline|__always_inline|noinline}; +our $NonptrType = qr{ + \b + (?:const\s+)? + (?:unsigned\s+)? + (?: + void| + char| + short| + int| + long| + unsigned| + float| + double| + bool| + long\s+int| + long\s+long| + long\s+long\s+int| + (?:__)?(?:u|s|be|le)(?:8|16|32|64)| + struct\s+$Ident| + union\s+$Ident| + enum\s+$Ident| + ${Ident}_t| + ${Ident}_handler| + ${Ident}_handler_fn + ) + (?:\s+$Sparse)* + \b + }x; + +our $Type = qr{ + \b$NonptrType\b + (?:\s*\*+\s*const|\s*\*+|(?:\s*\[\s*\])+)? + (?:\s+$Sparse|\s+$Attribute)* + }x; +our $Declare = qr{(?:$Storage\s+)?$Type}; +our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; +our $Lval = qr{$Ident(?:$Member)*}; + +our $Constant = qr{(?:[0-9]+|0x[0-9a-fA-F]+)[UL]*}; +our $Assignment = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)}; +our $Operators = qr{ + <=|>=|==|!=| + =>|->|<<|>>|<|>|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/ + }x; + +our $Bare = ''; + +$chk_signoff = 0 if ($file); + +my @dep_includes = (); +my @dep_functions = (); +my $removal = "Documentation/feature-removal-schedule.txt"; +if ($tree && -f "$root/$removal") { + open(REMOVE, "<$root/$removal") || + die "$P: $removal: open failed - $!\n"; + while () { + if (/^Check:\s+(.*\S)/) { + for my $entry (split(/[, ]+/, $1)) { + if ($entry =~ m@include/(.*)@) { + push(@dep_includes, $1); + + } elsif ($entry !~ m@/@) { + push(@dep_functions, $entry); + } + } + } + } +} + +my @rawlines = (); +for my $filename (@ARGV) { + if ($file) { + open(FILE, "diff -u /dev/null $filename|") || + die "$P: $filename: diff failed - $!\n"; + } else { + open(FILE, "<$filename") || + die "$P: $filename: open failed - $!\n"; + } + while () { + chomp; + push(@rawlines, $_); + } + close(FILE); + if (!process($filename, @rawlines)) { + $exit = 1; + } + @rawlines = (); +} + +exit($exit); + +sub top_of_kernel_tree { + my ($root) = @_; + + my @tree_check = ( + "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", + "README", "Documentation", "arch", "include", "drivers", + "fs", "init", "ipc", "kernel", "lib", "scripts", + ); + + foreach my $check (@tree_check) { + if (! -e $root . '/' . $check) { + return 0; + } + } + return 1; +} + +sub expand_tabs { + my ($str) = @_; + + my $res = ''; + my $n = 0; + for my $c (split(//, $str)) { + if ($c eq "\t") { + $res .= ' '; + $n++; + for (; ($n % 8) != 0; $n++) { + $res .= ' '; + } + next; + } + $res .= $c; + $n++; + } + + return $res; +} +sub copy_spacing { + my ($str) = @_; + + my $res = ''; + for my $c (split(//, $str)) { + if ($c eq "\t") { + $res .= $c; + } else { + $res .= ' '; + } + } + + return $res; +} + +sub line_stats { + my ($line) = @_; + + # Drop the diff line leader and expand tabs + $line =~ s/^.//; + $line = expand_tabs($line); + + # Pick the indent from the front of the line. + my ($white) = ($line =~ /^(\s*)/); + + return (length($line), length($white)); +} + +sub sanitise_line { + my ($line) = @_; + + my $res = ''; + my $l = ''; + + my $quote = ''; + + foreach my $c (split(//, $line)) { + if ($l ne "\\" && ($c eq "'" || $c eq '"')) { + if ($quote eq '') { + $quote = $c; + $res .= $c; + $l = $c; + next; + } elsif ($quote eq $c) { + $quote = ''; + } + } + if ($quote && $c ne "\t") { + $res .= "X"; + } else { + $res .= $c; + } + + $l = $c; + } + + return $res; +} + +sub ctx_block_get { + my ($linenr, $remain, $outer, $open, $close, $off) = @_; + my $line; + my $start = $linenr - 1; + my $blk = ''; + my @o; + my @c; + my @res = (); + + my $level = 0; + for ($line = $start; $remain > 0; $line++) { + next if ($rawlines[$line] =~ /^-/); + $remain--; + + $blk .= $rawlines[$line]; + foreach my $c (split(//, $rawlines[$line])) { + ##print "C<$c>L<$level><$open$close>O<$off>\n"; + if ($off > 0) { + $off--; + next; + } + + if ($c eq $close && $level > 0) { + $level--; + last if ($level == 0); + } elsif ($c eq $open) { + $level++; + } + } + + if (!$outer || $level <= 1) { + push(@res, $rawlines[$line]); + } + + last if ($level == 0); + } + + return ($level, @res); +} +sub ctx_block_outer { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); + return @r; +} +sub ctx_block { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); + return @r; +} +sub ctx_statement { + my ($linenr, $remain, $off) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); + return @r; +} +sub ctx_block_level { + my ($linenr, $remain) = @_; + + return ctx_block_get($linenr, $remain, 0, '{', '}', 0); +} +sub ctx_statement_level { + my ($linenr, $remain, $off) = @_; + + return ctx_block_get($linenr, $remain, 0, '(', ')', $off); +} + +sub ctx_locate_comment { + my ($first_line, $end_line) = @_; + + # Catch a comment on the end of the line itself. + my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*$@); + return $current_comment if (defined $current_comment); + + # Look through the context and try and figure out if there is a + # comment. + my $in_comment = 0; + $current_comment = ''; + for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { + my $line = $rawlines[$linenr - 1]; + #warn " $line\n"; + if ($linenr == $first_line and $line =~ m@^.\s*\*@) { + $in_comment = 1; + } + if ($line =~ m@/\*@) { + $in_comment = 1; + } + if (!$in_comment && $current_comment ne '') { + $current_comment = ''; + } + $current_comment .= $line . "\n" if ($in_comment); + if ($line =~ m@\*/@) { + $in_comment = 0; + } + } + + chomp($current_comment); + return($current_comment); +} +sub ctx_has_comment { + my ($first_line, $end_line) = @_; + my $cmt = ctx_locate_comment($first_line, $end_line); + + ##print "LINE: $rawlines[$end_line - 1 ]\n"; + ##print "CMMT: $cmt\n"; + + return ($cmt ne ''); +} + +sub cat_vet { + my ($vet) = @_; + my ($res, $coded); + + $res = ''; + while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { + $res .= $1; + if ($2 ne '') { + $coded = sprintf("^%c", unpack('C', $2) + 64); + $res .= $coded; + } + } + $res =~ s/$/\$/; + + return $res; +} + +sub annotate_values { + my ($stream, $type) = @_; + + my $res; + my $cur = $stream; + + my $debug = 0; + + print "$stream\n" if ($debug); + + ##my $type = 'N'; + my $pos = 0; + my $preprocessor = 0; + my $paren = 0; + my @paren_type; + + # Include any user defined types we may have found as we went. + my $type_match = "(?:$Type$Bare)"; + + while (length($cur)) { + print " <$type> " if ($debug); + if ($cur =~ /^(\s+)/o) { + print "WS($1)\n" if ($debug); + if ($1 =~ /\n/ && $preprocessor) { + $preprocessor = 0; + $type = 'N'; + } + + } elsif ($cur =~ /^($type_match)/) { + print "DECLARE($1)\n" if ($debug); + $type = 'T'; + + } elsif ($cur =~ /^(#\s*define\s*$Ident)(\(?)/o) { + print "DEFINE($1)\n" if ($debug); + $preprocessor = 1; + $paren_type[$paren] = 'N'; + + } elsif ($cur =~ /^(#\s*(?:ifdef|ifndef|if|else|endif))/o) { + print "PRE($1)\n" if ($debug); + $preprocessor = 1; + $type = 'N'; + + } elsif ($cur =~ /^(\\\n)/o) { + print "PRECONT($1)\n" if ($debug); + + } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { + print "SIZEOF($1)\n" if ($debug); + if (defined $2) { + $paren_type[$paren] = 'V'; + } + $type = 'N'; + + } elsif ($cur =~ /^(if|while|typeof)\b/o) { + print "COND($1)\n" if ($debug); + $paren_type[$paren] = 'N'; + $type = 'N'; + + } elsif ($cur =~/^(return|case|else)/o) { + print "KEYWORD($1)\n" if ($debug); + $type = 'N'; + + } elsif ($cur =~ /^(\()/o) { + print "PAREN('$1')\n" if ($debug); + $paren++; + $type = 'N'; + + } elsif ($cur =~ /^(\))/o) { + $paren-- if ($paren > 0); + if (defined $paren_type[$paren]) { + $type = $paren_type[$paren]; + undef $paren_type[$paren]; + print "PAREN('$1') -> $type\n" if ($debug); + } else { + print "PAREN('$1')\n" if ($debug); + } + + } elsif ($cur =~ /^($Ident)\(/o) { + print "FUNC($1)\n" if ($debug); + $paren_type[$paren] = 'V'; + + } elsif ($cur =~ /^($Ident|$Constant)/o) { + print "IDENT($1)\n" if ($debug); + $type = 'V'; + + } elsif ($cur =~ /^($Assignment)/o) { + print "ASSIGN($1)\n" if ($debug); + $type = 'N'; + + } elsif ($cur =~ /^(;|{|}|\?|:|\[)/o) { + print "END($1)\n" if ($debug); + $type = 'N'; + + } elsif ($cur =~ /^($Operators)/o) { + print "OP($1)\n" if ($debug); + if ($1 ne '++' && $1 ne '--') { + $type = 'N'; + } + + } elsif ($cur =~ /(^.)/o) { + print "C($1)\n" if ($debug); + } + if (defined $1) { + $cur = substr($cur, length($1)); + $res .= $type x length($1); + } + } + + return $res; +} + +my $prefix = ''; + +my @report = (); +sub report { + push(@report, $prefix . $_[0]); +} +sub report_dump { + @report; +} +sub ERROR { + report("ERROR: $_[0]\n"); + our $clean = 0; + our $cnt_error++; +} +sub WARN { + report("WARNING: $_[0]\n"); + our $clean = 0; + our $cnt_warn++; +} +sub CHK { + if ($check) { + report("CHECK: $_[0]\n"); + our $clean = 0; + our $cnt_chk++; + } +} + +sub process { + my $filename = shift; + my @lines = @_; + + my $linenr=0; + my $prevline=""; + my $stashline=""; + + my $length; + my $indent; + my $previndent=0; + my $stashindent=0; + + our $clean = 1; + my $signoff = 0; + my $is_patch = 0; + + our $cnt_lines = 0; + our $cnt_error = 0; + our $cnt_warn = 0; + our $cnt_chk = 0; + + # Trace the real file/line as we go. + my $realfile = ''; + my $realline = 0; + my $realcnt = 0; + my $here = ''; + my $in_comment = 0; + my $first_line = 0; + + my $prev_values = 'N'; + + # Possible bare types. + my @bare = (); + + # Pre-scan the patch looking for any __setup documentation. + my @setup_docs = (); + my $setup_docs = 0; + foreach my $line (@lines) { + if ($line=~/^\+\+\+\s+(\S+)/) { + $setup_docs = 0; + if ($1 =~ m@Documentation/kernel-parameters.txt$@) { + $setup_docs = 1; + } + next; + } + + if ($setup_docs && $line =~ /^\+/) { + push(@setup_docs, $line); + } + } + + $prefix = ''; + + foreach my $line (@lines) { + $linenr++; + + my $rawline = $line; + + +#extract the filename as it passes + if ($line=~/^\+\+\+\s+(\S+)/) { + $realfile=$1; + $realfile =~ s@^[^/]*/@@; + $in_comment = 0; + next; + } +#extract the line range in the file after the patch is applied + if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $is_patch = 1; + $first_line = $linenr + 1; + $in_comment = 0; + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + $prev_values = 'N'; + next; + } + +# track the line number as we move through the hunk, note that +# new versions of GNU diff omit the leading space on completely +# blank context lines so we need to count that too. + if ($line =~ /^( |\+|$)/) { + $realline++; + $realcnt-- if ($realcnt != 0); + + # track any sort of multi-line comment. Obviously if + # the added text or context do not include the whole + # comment we will not see it. Such is life. + # + # Guestimate if this is a continuing comment. If this + # is the start of a diff block and this line starts + # ' *' then it is very likely a comment. + if ($linenr == $first_line and $line =~ m@^.\s*\*@) { + $in_comment = 1; + } + if ($line =~ m@/\*@) { + $in_comment = 1; + } + if ($line =~ m@\*/@) { + $in_comment = 0; + } + + # Measure the line length and indent. + ($length, $indent) = line_stats($line); + + # Track the previous line. + ($prevline, $stashline) = ($stashline, $line); + ($previndent, $stashindent) = ($stashindent, $indent); + + } elsif ($realcnt == 1) { + $realcnt--; + } + +#make up the handle for any error we report on this line + $here = "#$linenr: " if (!$file); + $here = "#$realline: " if ($file); + $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); + + my $hereline = "$here\n$line\n"; + my $herecurr = "$here\n$line\n"; + my $hereprev = "$here\n$prevline\n$line\n"; + + $prefix = "$filename:$realline: " if ($emacs && $file); + $prefix = "$filename:$linenr: " if ($emacs && !$file); + $cnt_lines++ if ($realcnt != 0); + +#check the patch for a signoff: + if ($line =~ /^\s*signed-off-by:/i) { + # This is a signoff, if ugly, so do not double report. + $signoff++; + if (!($line =~ /^\s*Signed-off-by:/)) { + WARN("Signed-off-by: is the preferred form\n" . + $herecurr); + } + if ($line =~ /^\s*signed-off-by:\S/i) { + WARN("need space after Signed-off-by:\n" . + $herecurr); + } + } + +# Check for wrappage within a valid hunk of the file + if ($realcnt != 0 && $line !~ m{^(?:\+|-| |$)}) { + ERROR("patch seems to be corrupt (line wrapped?)\n" . + $herecurr) if (!$emitted_corrupt++); + } + +# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php + if (($realfile =~ /^$/ || $line =~ /^\+/) && + !($line =~ m/^( + [\x09\x0A\x0D\x20-\x7E] # ASCII + | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 + )*$/x )) { + ERROR("Invalid UTF-8\n" . $herecurr); + } + +#ignore lines being removed + if ($line=~/^-/) {next;} + +# check we are in a valid source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); + +#trailing whitespace + if ($line =~ /^\+.*\015/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("DOS line endings\n" . $herevet); + + } elsif ($line =~ /^\+.*\S\s+$/ || $line =~ /^\+\s+$/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("trailing whitespace\n" . $herevet); + } +#80 column limit + if ($line =~ /^\+/ && !($prevline=~/\/\*\*/) && $length > 80) { + WARN("line over 80 characters\n" . $herecurr); + } + +# check we are in a valid source file *.[hc] if not then ignore this hunk + next if ($realfile !~ /\.[hc]$/); + +# at the beginning of a line any tabs must come first and anything +# more than 8 must use tabs. + if ($line=~/^\+\s* \t\s*\S/ or $line=~/^\+\s* \s*/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use tabs not spaces\n" . $herevet); + } + +# Remove comments from the line before processing. + my $comment_edge = ($line =~ s@/\*.*\*/@@g) + + ($line =~ s@/\*.*@@) + + ($line =~ s@^(.).*\*/@$1@); + +# The rest of our checks refer specifically to C style +# only apply those _outside_ comments. Only skip +# lines in the middle of comments. + next if (!$comment_edge && $in_comment); + +# Standardise the strings and chars within the input to simplify matching. + $line = sanitise_line($line); + +# Check for potential 'bare' types + if ($realcnt && + $line !~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?$Type\b/ && + $line !~ /$Ident:\s*$/ && + $line !~ /^.\s*$Ident\s*\(/ && + # definitions in global scope can only start with types + ($line =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?($Ident)\b/ || + # declarations always start with types + $line =~ /^.\s*(?:$Storage\s+)?($Ident)\b\s*\**\s*$Ident\s*(?:;|=)/) || + # any (foo ... *) is a pointer cast, and foo is a type + $line =~ /\(($Ident)(?:\s+$Sparse)*\s*\*+\s*\)/) { + my $possible = $1; + if ($possible !~ /^(?:$Storage|$Type|DEFINE_\S+)$/ && + $possible ne 'goto' && $possible ne 'return' && + $possible ne 'struct' && $possible ne 'enum' && + $possible ne 'case' && $possible ne 'else' && + $possible ne 'typedef') { + #print "POSSIBLE<$possible>\n"; + push(@bare, $possible); + my $bare = join("|", @bare); + $Bare = '|' . qr{ + \b(?:$bare)\b + (?:\s*\*+\s*const|\s*\*+|(?:\s*\[\s*\])+)? + (?:\s+$Sparse)* + }x; + } + } + +# +# Checks which may be anchored in the context. +# + +# Check for switch () and associated case and default +# statements should be at the same indent. + if ($line=~/\bswitch\s*\(.*\)/) { + my $err = ''; + my $sep = ''; + my @ctx = ctx_block_outer($linenr, $realcnt); + shift(@ctx); + for my $ctx (@ctx) { + my ($clen, $cindent) = line_stats($ctx); + if ($ctx =~ /^\+\s*(case\s+|default:)/ && + $indent != $cindent) { + $err .= "$sep$ctx\n"; + $sep = ''; + } else { + $sep = "[...]\n"; + } + } + if ($err ne '') { + ERROR("switch and case should be at the same indent\n$hereline$err"); + } + } + +# if/while/etc brace do not go on next line, unless defining a do while loop, +# or if that brace on the next line is for something else + if ($line =~ /\b(?:(if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.#/) { + my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); + my $ctx_ln = $linenr + $#ctx + 1; + my $ctx_cnt = $realcnt - $#ctx - 1; + my $ctx = join("\n", @ctx); + + # Skip over any removed lines in the context following statement. + while ($ctx_cnt > 0 && $lines[$ctx_ln - 1] =~ /^-/) { + $ctx_ln++; + $ctx_cnt--; + } + ##warn "line<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>"; + + if ($ctx !~ /{\s*/ && $ctx_cnt > 0 && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { + ERROR("That open brace { should be on the previous line\n" . + "$here\n$ctx\n$lines[$ctx_ln - 1]"); + } + if ($level == 0 && $ctx =~ /\)\s*\;\s*$/ && defined $lines[$ctx_ln - 1]) { + my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); + if ($nindent > $indent) { + WARN("Trailing semicolon indicates no statements, indent implies otherwise\n" . + "$here\n$ctx\n$lines[$ctx_ln - 1]"); + } + } + } + + # Track the 'values' across context and added lines. + my $opline = $line; $opline =~ s/^./ /; + my $curr_values = annotate_values($opline . "\n", $prev_values); + $curr_values = $prev_values . $curr_values; + #warn "--> $opline\n"; + #warn "--> $curr_values ($prev_values)\n"; + $prev_values = substr($curr_values, -1); + +#ignore lines not being added + if ($line=~/^[^\+]/) {next;} + +# TEST: allow direct testing of the type matcher. + if ($tst_type && $line =~ /^.$Declare$/) { + ERROR("TEST: is type $Declare\n" . $herecurr); + next; + } + +# check for initialisation to aggregates open brace on the next line + if ($prevline =~ /$Declare\s*$Ident\s*=\s*$/ && + $line =~ /^.\s*{/) { + ERROR("That open brace { should be on the previous line\n" . $hereprev); + } + +# +# Checks which are anchored on the added line. +# + +# check for malformed paths in #include statements (uses RAW line) + if ($rawline =~ m{^.#\s*include\s+[<"](.*)[">]}) { + my $path = $1; + if ($path =~ m{//}) { + ERROR("malformed #include filename\n" . + $herecurr); + } + # Sanitise this special form of string. + $path = 'X' x length($path); + $line =~ s{\<.*\>}{<$path>}; + } + +# no C99 // comments + if ($line =~ m{//}) { + ERROR("do not use C99 // comments\n" . $herecurr); + } + # Remove C99 comments. + $line =~ s@//.*@@; + $opline =~ s@//.*@@; + +#EXPORT_SYMBOL should immediately follow its function closing }. + if (($line =~ /EXPORT_SYMBOL.*\((.*)\)/) || + ($line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { + my $name = $1; + if (($prevline !~ /^}/) && + ($prevline !~ /^\+}/) && + ($prevline !~ /^ }/) && + ($prevline !~ /\b\Q$name\E(?:\s+$Attribute)?\s*(?:;|=)/)) { + WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); + } + } + +# check for external initialisers. + if ($line =~ /^.$Type\s*$Ident\s*=\s*(0|NULL);/) { + ERROR("do not initialise externals to 0 or NULL\n" . + $herecurr); + } +# check for static initialisers. + if ($line =~ /\s*static\s.*=\s*(0|NULL);/) { + ERROR("do not initialise statics to 0 or NULL\n" . + $herecurr); + } + +# check for new typedefs, only function parameters and sparse annotations +# make sense. + if ($line =~ /\btypedef\s/ && + $line !~ /\btypedef\s+$Type\s+\(\s*\*?$Ident\s*\)\s*\(/ && + $line !~ /\b__bitwise(?:__|)\b/) { + WARN("do not add new typedefs\n" . $herecurr); + } + +# * goes on variable not on type + if ($line =~ m{\($NonptrType(\*+)(?:\s+const)?\)}) { + ERROR("\"(foo$1)\" should be \"(foo $1)\"\n" . + $herecurr); + + } elsif ($line =~ m{\($NonptrType\s+(\*+)(?!\s+const)\s+\)}) { + ERROR("\"(foo $1 )\" should be \"(foo $1)\"\n" . + $herecurr); + + } elsif ($line =~ m{$NonptrType(\*+)(?:\s+(?:$Attribute|$Sparse))?\s+[A-Za-z\d_]+}) { + ERROR("\"foo$1 bar\" should be \"foo $1bar\"\n" . + $herecurr); + + } elsif ($line =~ m{$NonptrType\s+(\*+)(?!\s+(?:$Attribute|$Sparse))\s+[A-Za-z\d_]+}) { + ERROR("\"foo $1 bar\" should be \"foo $1bar\"\n" . + $herecurr); + } + +# # no BUG() or BUG_ON() +# if ($line =~ /\b(BUG|BUG_ON)\b/) { +# print "Try to use WARN_ON & Recovery code rather than BUG() or BUG_ON()\n"; +# print "$herecurr"; +# $clean = 0; +# } + +# printk should use KERN_* levels. Note that follow on printk's on the +# same line do not need a level, so we use the current block context +# to try and find and validate the current printk. In summary the current +# printk includes all preceeding printk's which have no newline on the end. +# we assume the first bad printk is the one to report. + if ($line =~ /\bprintk\((?!KERN_)\s*"/) { + my $ok = 0; + for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { + #print "CHECK<$lines[$ln - 1]\n"; + # we have a preceeding printk if it ends + # with "\n" ignore it, else it is to blame + if ($lines[$ln - 1] =~ m{\bprintk\(}) { + if ($rawlines[$ln - 1] !~ m{\\n"}) { + $ok = 1; + } + last; + } + } + if ($ok == 0) { + WARN("printk() should include KERN_ facility level\n" . $herecurr); + } + } + +# function brace can't be on same line, except for #defines of do while, +# or if closed on same line + if (($line=~/$Type\s*[A-Za-z\d_]+\(.*\).* {/) and + !($line=~/\#define.*do\s{/) and !($line=~/}/)) { + ERROR("open brace '{' following function declarations go on the next line\n" . $herecurr); + } + +# check for spaces between functions and their parentheses. + while ($line =~ /($Ident)\s+\(/g) { + if ($1 !~ /^(?:if|for|while|switch|return|volatile|__volatile__|__attribute__|format|__extension__|Copyright|case)$/ && + $line !~ /$Type\s+\(/ && $line !~ /^.\#\s*define\b/) { + WARN("no space between function name and open parenthesis '('\n" . $herecurr); + } + } +# Check operator spacing. + if (!($line=~/\#\s*include/)) { + my $ops = qr{ + <<=|>>=|<=|>=|==|!=| + \+=|-=|\*=|\/=|%=|\^=|\|=|&=| + =>|->|<<|>>|<|>|=|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/ + }x; + my @elements = split(/($ops|;)/, $opline); + my $off = 0; + + my $blank = copy_spacing($opline); + + for (my $n = 0; $n < $#elements; $n += 2) { + $off += length($elements[$n]); + + my $a = ''; + $a = 'V' if ($elements[$n] ne ''); + $a = 'W' if ($elements[$n] =~ /\s$/); + $a = 'B' if ($elements[$n] =~ /(\[|\()$/); + $a = 'O' if ($elements[$n] eq ''); + $a = 'E' if ($elements[$n] eq '' && $n == 0); + + my $op = $elements[$n + 1]; + + my $c = ''; + if (defined $elements[$n + 2]) { + $c = 'V' if ($elements[$n + 2] ne ''); + $c = 'W' if ($elements[$n + 2] =~ /^\s/); + $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); + $c = 'O' if ($elements[$n + 2] eq ''); + $c = 'E' if ($elements[$n + 2] =~ /\s*\\$/); + } else { + $c = 'E'; + } + + # Pick up the preceeding and succeeding characters. + my $ca = substr($opline, 0, $off); + my $cc = ''; + if (length($opline) >= ($off + length($elements[$n + 1]))) { + $cc = substr($opline, $off + length($elements[$n + 1])); + } + my $cb = "$ca$;$cc"; + + my $ctx = "${a}x${c}"; + + my $at = "(ctx:$ctx)"; + + my $ptr = substr($blank, 0, $off) . "^"; + my $hereptr = "$hereline$ptr\n"; + + # Classify operators into binary, unary, or + # definitions (* only) where they have more + # than one mode. + my $op_type = substr($curr_values, $off + 1, 1); + my $op_left = substr($curr_values, $off, 1); + my $is_unary; + if ($op_type eq 'T') { + $is_unary = 2; + } elsif ($op_left eq 'V') { + $is_unary = 0; + } else { + $is_unary = 1; + } + #if ($op eq '-' || $op eq '&' || $op eq '*') { + # print "UNARY: <$op_left$op_type $is_unary $a:$op:$c> <$ca:$op:$cc> <$unary_ctx>\n"; + #} + + # ; should have either the end of line or a space or \ after it + if ($op eq ';') { + if ($ctx !~ /.x[WEB]/ && $cc !~ /^\\/ && + $cc !~ /^;/) { + ERROR("need space after that '$op' $at\n" . $hereptr); + } + + # // is a comment + } elsif ($op eq '//') { + + # -> should have no spaces + } elsif ($op eq '->') { + if ($ctx =~ /Wx.|.xW/) { + ERROR("no spaces around that '$op' $at\n" . $hereptr); + } + + # , must have a space on the right. + } elsif ($op eq ',') { + if ($ctx !~ /.xW|.xE/ && $cc !~ /^}/) { + ERROR("need space after that '$op' $at\n" . $hereptr); + } + + # '*' as part of a type definition -- reported already. + } elsif ($op eq '*' && $is_unary == 2) { + #warn "'*' is part of type\n"; + + # unary operators should have a space before and + # none after. May be left adjacent to another + # unary operator, or a cast + } elsif ($op eq '!' || $op eq '~' || + ($is_unary && ($op eq '*' || $op eq '-' || $op eq '&'))) { + if ($ctx !~ /[WEB]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { + ERROR("need space before that '$op' $at\n" . $hereptr); + } + if ($ctx =~ /.xW/) { + ERROR("no space after that '$op' $at\n" . $hereptr); + } + + # unary ++ and unary -- are allowed no space on one side. + } elsif ($op eq '++' or $op eq '--') { + if ($ctx !~ /[WOB]x[^W]/ && $ctx !~ /[^W]x[WOBE]/) { + ERROR("need space one side of that '$op' $at\n" . $hereptr); + } + if ($ctx =~ /Wx./ && $cc =~ /^;/) { + ERROR("no space before that '$op' $at\n" . $hereptr); + } + + # << and >> may either have or not have spaces both sides + } elsif ($op eq '<<' or $op eq '>>' or + $op eq '&' or $op eq '^' or $op eq '|' or + $op eq '+' or $op eq '-' or + $op eq '*' or $op eq '/') + { + if ($ctx !~ /VxV|WxW|VxE|WxE|VxO/) { + ERROR("need consistent spacing around '$op' $at\n" . + $hereptr); + } + + # All the others need spaces both sides. + } elsif ($ctx !~ /[EW]x[WE]/) { + # Ignore email addresses + if (!($op eq '<' && $cb =~ /$;\S+\@\S+>/) && + !($op eq '>' && $cb =~ /<\S+\@\S+$;/)) { + ERROR("need spaces around that '$op' $at\n" . $hereptr); + } + } + $off += length($elements[$n + 1]); + } + } + +# check for multiple assignments + if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { + CHK("multiple assignments should be avoided\n" . $herecurr); + } + +## # check for multiple declarations, allowing for a function declaration +## # continuation. +## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && +## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { +## +## # Remove any bracketed sections to ensure we do not +## # falsly report the parameters of functions. +## my $ln = $line; +## while ($ln =~ s/\([^\(\)]*\)//g) { +## } +## if ($ln =~ /,/) { +## WARN("declaring multiple variables together should be avoided\n" . $herecurr); +## } +## } + +#need space before brace following if, while, etc + if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) || + $line =~ /do{/) { + ERROR("need a space before the open brace '{'\n" . $herecurr); + } + +# closing brace should have a space following it when it has anything +# on the line + if ($line =~ /}(?!(?:,|;|\)))\S/) { + ERROR("need a space after that close brace '}'\n" . $herecurr); + } + +# check spacing on square brackets + if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { + ERROR("no space after that open square bracket '['\n" . $herecurr); + } + if ($line =~ /\s\]/) { + ERROR("no space before that close square bracket ']'\n" . $herecurr); + } + +# check spacing on paretheses + if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && + $line !~ /for\s*\(\s+;/) { + ERROR("no space after that open parenthesis '('\n" . $herecurr); + } + if ($line =~ /\s\)/ && $line !~ /^.\s*\)/ && + $line !~ /for\s*\(.*;\s+\)/) { + ERROR("no space before that close parenthesis ')'\n" . $herecurr); + } + +#goto labels aren't indented, allow a single space however + if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and + !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { + WARN("labels should not be indented\n" . $herecurr); + } + +# Need a space before open parenthesis after if, while etc + if ($line=~/\b(if|while|for|switch)\(/) { + ERROR("need a space before the open parenthesis '('\n" . $herecurr); + } + +# Check for illegal assignment in if conditional. + if ($line=~/\bif\s*\(.*[^<>!=]=[^=]/) { + #next if ($line=~/\".*\Q$op\E.*\"/ or $line=~/\'\Q$op\E\'/); + ERROR("do not use assignment in if condition\n" . $herecurr); + } + + # Check for }else {, these must be at the same + # indent level to be relevant to each other. + if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ and + $previndent == $indent) { + ERROR("else should follow close brace '}'\n" . $hereprev); + } + +#studly caps, commented out until figure out how to distinguish between use of existing and adding new +# if (($line=~/[\w_][a-z\d]+[A-Z]/) and !($line=~/print/)) { +# print "No studly caps, use _\n"; +# print "$herecurr"; +# $clean = 0; +# } + +#no spaces allowed after \ in define + if ($line=~/\#define.*\\\s$/) { + WARN("Whitepspace after \\ makes next lines useless\n" . $herecurr); + } + +#warn if is #included and is available (uses RAW line) + if ($tree && $rawline =~ m{^.\#\s*include\s*\}) { + my $checkfile = "$root/include/linux/$1.h"; + if (-f $checkfile && $1 ne 'irq.h') { + CHK("Use #include instead of \n" . + $herecurr); + } + } + +# if and else should not have general statements after it + if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/ && + $1 !~ /^\s*(?:\sif|{|\\|$)/) { + ERROR("trailing statements should be on next line\n" . $herecurr); + } + +# multi-statement macros should be enclosed in a do while loop, grab the +# first statement and ensure its the whole macro if its not enclosed +# in a known goot container + if ($prevline =~ /\#define.*\\/ && + $prevline !~/(?:do\s+{|\(\{|\{)/ && + $line !~ /(?:do\s+{|\(\{|\{)/ && + $line !~ /^.\s*$Declare\s/) { + # Grab the first statement, if that is the entire macro + # its ok. This may start either on the #define line + # or the one below. + my $ln = $linenr; + my $cnt = $realcnt; + my $off = 0; + + # If the macro starts on the define line start + # grabbing the statement after the identifier + $prevline =~ m{^(.#\s*define\s*$Ident(?:\([^\)]*\))?\s*)(.*)\\\s*$}; + ##print "1<$1> 2<$2>\n"; + if (defined $2 && $2 ne '') { + $off = length($1); + $ln--; + $cnt++; + } + my @ctx = ctx_statement($ln, $cnt, $off); + my $ctx_ln = $ln + $#ctx + 1; + my $ctx = join("\n", @ctx); + + # Pull in any empty extension lines. + while ($ctx =~ /\\$/ && + $lines[$ctx_ln - 1] =~ /^.\s*(?:\\)?$/) { + $ctx .= $lines[$ctx_ln - 1]; + $ctx_ln++; + } + + if ($ctx =~ /\\$/) { + if ($ctx =~ /;/) { + ERROR("Macros with multiple statements should be enclosed in a do - while loop\n" . "$here\n$ctx\n"); + } else { + ERROR("Macros with complex values should be enclosed in parenthesis\n" . "$here\n$ctx\n"); + } + } + } + +# check for redundant bracing round if etc + if ($line =~ /\b(if|while|for|else)\b/) { + # Locate the end of the opening statement. + my @control = ctx_statement($linenr, $realcnt, 0); + my $nr = $linenr + (scalar(@control) - 1); + my $cnt = $realcnt - (scalar(@control) - 1); + + my $off = $realcnt - $cnt; + #print "$off: line<$line>end<" . $lines[$nr - 1] . ">\n"; + + # If this is is a braced statement group check it + if ($lines[$nr - 1] =~ /{\s*$/) { + my ($lvl, @block) = ctx_block_level($nr, $cnt); + + my $stmt = join(' ', @block); + $stmt =~ s/(^[^{]*){//; + my $before = $1; + $stmt =~ s/}([^}]*$)//; + my $after = $1; + + #print "block<" . join(' ', @block) . "><" . scalar(@block) . ">\n"; + #print "stmt<$stmt>\n\n"; + + # Count the ;'s if there is fewer than two + # then there can only be one statement, + # if there is a brace inside we cannot + # trivially detect if its one statement. + # Also nested if's often require braces to + # disambiguate the else binding so shhh there. + my @semi = ($stmt =~ /;/g); + push(@semi, "/**/") if ($stmt =~ m@/\*@); + ##print "semi<" . scalar(@semi) . ">\n"; + if ($lvl == 0 && scalar(@semi) < 2 && + $stmt !~ /{/ && $stmt !~ /\bif\b/ && + $before !~ /}/ && $after !~ /{/) { + my $herectx = "$here\n" . join("\n", @control, @block[1 .. $#block]) . "\n"; + shift(@block); + WARN("braces {} are not necessary for single statement blocks\n" . $herectx); + } + } + } + +# don't include deprecated include files (uses RAW line) + for my $inc (@dep_includes) { + if ($rawline =~ m@\#\s*include\s*\<$inc>@) { + ERROR("Don't use <$inc>: see Documentation/feature-removal-schedule.txt\n" . $herecurr); + } + } + +# don't use deprecated functions + for my $func (@dep_functions) { + if ($line =~ /\b$func\b/) { + ERROR("Don't use $func(): see Documentation/feature-removal-schedule.txt\n" . $herecurr); + } + } + +# no volatiles please + my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; + if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { + WARN("Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); + } + +# SPIN_LOCK_UNLOCKED & RW_LOCK_UNLOCKED are deprecated + if ($line =~ /\b(SPIN_LOCK_UNLOCKED|RW_LOCK_UNLOCKED)/) { + ERROR("Use of $1 is deprecated: see Documentation/spinlocks.txt\n" . $herecurr); + } + +# warn about #if 0 + if ($line =~ /^.#\s*if\s+0\b/) { + CHK("if this code is redundant consider removing it\n" . + $herecurr); + } + +# check for needless kfree() checks + if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { + my $expr = $1; + if ($line =~ /\bkfree\(\Q$expr\E\);/) { + WARN("kfree(NULL) is safe this check is probabally not required\n" . $hereprev); + } + } + +# warn about #ifdefs in C files +# if ($line =~ /^.#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { +# print "#ifdef in C files should be avoided\n"; +# print "$herecurr"; +# $clean = 0; +# } + +# warn about spacing in #ifdefs + if ($line =~ /^.#\s*(ifdef|ifndef|elif)\s\s+/) { + ERROR("exactly one space required after that #$1\n" . $herecurr); + } + +# check for spinlock_t definitions without a comment. + if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/) { + my $which = $1; + if (!ctx_has_comment($first_line, $linenr)) { + CHK("$1 definition without comment\n" . $herecurr); + } + } +# check for memory barriers without a comment. + if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + CHK("memory barrier without comment\n" . $herecurr); + } + } +# check of hardware specific defines + if ($line =~ m@^.#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { + CHK("architecture specific defines should be avoided\n" . $herecurr); + } + +# check the location of the inline attribute, that it is between +# storage class and type. + if ($line =~ /\b$Type\s+$Inline\b/ || + $line =~ /\b$Inline\s+$Storage\b/) { + ERROR("inline keyword should sit between storage class and type\n" . $herecurr); + } + +# check for new externs in .c files. + if ($line =~ /^.\s*extern\s/ && ($realfile =~ /\.c$/)) { + WARN("externs should be avoided in .c files\n" . $herecurr); + } + +# checks for new __setup's + if ($rawline =~ /\b__setup\("([^"]*)"/) { + my $name = $1; + + if (!grep(/$name/, @setup_docs)) { + CHK("__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); + } + } + +# check for pointless casting of kmalloc return + if ($line =~ /\*\s*\)\s*k[czm]alloc\b/) { + WARN("unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); + } + } + + if ($chk_patch && !$is_patch) { + ERROR("Does not appear to be a unified-diff format patch\n"); + } + if ($is_patch && $chk_signoff && $signoff == 0) { + ERROR("Missing Signed-off-by: line(s)\n"); + } + + if ($clean == 0 && ($chk_patch || $is_patch)) { + print report_dump(); + if ($quiet < 2) { + print "total: $cnt_error errors, $cnt_warn warnings, " . + (($check)? "$cnt_chk checks, " : "") . + "$cnt_lines lines checked\n"; + } + } + if ($clean == 1 && $quiet == 0) { + print "Your patch has no obvious style problems and is ready for submission.\n" + } + if ($clean == 0 && $quiet == 0) { + print "Your patch has style problems, please review. If any of these errors\n"; + print "are false positives report them to the maintainer, see\n"; + print "CHECKPATCH in MAINTAINERS.\n"; + } + return $clean; +} diff --git a/v4l/scripts/prep_commit_msg.pl b/v4l/scripts/prep_commit_msg.pl index 1316a7c44..ffd8aeb84 100755 --- a/v4l/scripts/prep_commit_msg.pl +++ b/v4l/scripts/prep_commit_msg.pl @@ -55,14 +55,46 @@ if (!$checkpatch) { $checkpatch="/lib/modules/`uname -r`/build/scripts/checkpatch.pl"; } +my $cp_version; +open IN,"$checkpatch|"; +while () { + tr/A-Z/a-z/; + if (m/version\s*:\s*([\d\.]+)/) { + $cp_version = $1; + } +} +close IN; + +my $intree_checkpatch = "scripts/checkpatch.pl --no-tree"; +if (!open IN,"$intree_checkpatch|") { + $intree_checkpatch = "v4l/".$intree_checkpatch; + open IN,"$intree_checkpatch|"; +} +while () { + tr/A-Z/a-z/; + if (m/version\s*:\s*([\d\.]+)/) { + if ($1 > $cp_version) { + print "# WARNING: $checkpatch version $cp_version is\n" + ."# older than $intree_checkpatch version" + ." $1.\n# Using in-tree one.\n#\n"; + $cp_version = $1; + $checkpatch = $intree_checkpatch; + } + } +} +close IN; + print "# Added/removed/changed files:\n"; system "hg $diff | diffstat -p1 -c"; -open IN,"hg $diff | $checkpatch -q --nosignoff --notree -|"; +open IN,"hg $diff | $checkpatch -q --nosignoff -|"; my $err=0; while () { if (!$err) { - print "#\n# WARN: checkpatch.pl returned some errors. Please fix.\n"; + print "#\n# WARNING: $checkpatch version $cp_version returned ". + "some errors.\n# Please fix.\n#\n"; + + $err=1; } print "# $_"; -- cgit v1.2.3 From 922aa12ba686eec51828927b546f9fab253a3f85 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 3 Dec 2007 16:26:46 -0200 Subject: Added checkpatch.pl requirement at README.patches From: Mauro Carvalho Chehab The usage of scripts/checkpatch.pl were already documented at SubmittingPatches[1], since June, 1st. This patch explicitly adds this as a requirement, since people don't generally check the new requirements added there. [1] referenced at README.patches. Signed-off-by: Mauro Carvalho Chehab --- README.patches | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/README.patches b/README.patches index 2f39f5d52..863415cc8 100644 --- a/README.patches +++ b/README.patches @@ -1,5 +1,5 @@ Mauro Carvalho Chehab - Updated on 2007 September, 6 + Updated on 2007 December, 1st This file describes the general procedures used by the LinuxTV team (*) and by the v4l-dvb community. @@ -144,7 +144,20 @@ b) All commits at mercurial trees should have a consistent message, From: line shouldn't be omitted, since it will be used for the patch author when converting to -git. -c) For "make commit" to work properly, the HGUSER shell environment var +c) Since June, 1st, 2007, all patches are requested to have their coding style + validated by using script/checkpatch.pl. This check runs automatically by + using "make commit". By default, it will try to use the newest version of + the script, between the kernel one and a copy at v4l-dvb development tree. + + It is always a good idea to use in-kernel version, since additional tests + are performed (like checking for the usage of depreciated API's that are + about to be removed). + + It is possible to override the in-kernel checkpatch.pl location, by using + the CHECKPATCH shell environment var to something like: + CHECKPATCH="/usr/src/linux/scripts/checkpatch.pl" + +d) For "make commit" to work properly, the HGUSER shell environment var should be defined (replacing the names at the left): HGUSER="My Name " @@ -171,7 +184,7 @@ c) For "make commit" to work properly, the HGUSER shell environment var It is strongly recommended to have those lines in .bashrc or .profile. -d) All patches shall have a Developers Certificate of Origin +e) All patches shall have a Developers Certificate of Origin version 1.1 in the commit log or the email description, signed by the patch authors, as postulated in the Linux Kernel source at: @@ -194,7 +207,7 @@ d) All patches shall have a Developers Certificate of Origin trees and signed. Otherwise, they may comment on the patch, asking for some review. -e) Although not documented at kernel's Documentation/, a common kernel +f) Although not documented at kernel's Documentation/, a common kernel practice is to use Acked-by: tag. An Acked-by: tag usually means that the acked person didn't write the @@ -206,7 +219,7 @@ e) Although not documented at kernel's Documentation/, a common kernel inserted at master repository. In this case, the ack will be added only at -git tree. -f) Another kernel's practice that is agreed to be good is that a +g) Another kernel's practice that is agreed to be good is that a patchset should be reviewed/tested by other developers. So, a new tag should be used by testers/reviewers. So, reviewers are welcome. After reviewing a patchset, please send an e-mail indicating that, if @@ -214,7 +227,7 @@ f) Another kernel's practice that is agreed to be good is that a Reviewed-by: My Name This is particularly important for Kernel to userspace ABI changes. -g) If the patch also affects other parts of kernel (like Alsa +h) If the patch also affects other parts of kernel (like Alsa or i2c), it is required that, when submitting upstream, the patch also goes to the maintainers of that subsystem. To do this, the developer shall copy the interested parties. @@ -237,7 +250,7 @@ g) If the patch also affects other parts of kernel (like Alsa for their acks, then commit to the development tree or send the patch via email with their Acked-by: already included. -h) Sometimes, mainstream changes affect the v4l-dvb tree, and mast be +i) Sometimes, mainstream changes affect the v4l-dvb tree, and mast be backported to the v4l-dvb tree. This kind of commit to the mercurial tree should follow the rules above and should also have the line: @@ -245,7 +258,7 @@ h) Sometimes, mainstream changes affect the v4l-dvb tree, and mast be Patches with this line will not be submitted upstream. -i) Sometimes it is necessary to introduce some testing code inside a +j) Sometimes it is necessary to introduce some testing code inside a module or remove parts that are not yet finished. Also, compatibility tests may be required to provide backporting. @@ -294,7 +307,7 @@ should be included at the See the file v4l/scripts/gentree.pl for a more complete description of what kind of code will be kept and what kind will be removed. -j) To import contributed stuff to a developer's, a script is provided. +k) To import contributed stuff to a developer's, a script is provided. This allows an easy import of mbox-based patch emails. This is done with (called from the root tree directory): @@ -311,7 +324,7 @@ j) To import contributed stuff to a developer's, a script is provided. that can be applied/unapplied for testing. mailimport trusts on it to work, so, this extension should be enabled for mailimport script to work. -k) By submitting a patch to the subsystem maintainer, either via email +l) By submitting a patch to the subsystem maintainer, either via email or via pull request, the patch author(s) are agreeing that the submitted code will be added on Kernel, and that the submitted code are being released as GPLv2. The author may grant additional licenses @@ -319,7 +332,7 @@ k) By submitting a patch to the subsystem maintainer, either via email later. If no specific clause are added, the added code will be assumed as GPLv2 only. -l) "Commit earlier and commit often". This is a common used rule at +m) "Commit earlier and commit often". This is a common used rule at Kernel. This means that a sooner submission of a patch will mean that a review can happen sooner, and help the develop to address the comments. This helps to reduce the life-cycle for having a changeset -- cgit v1.2.3 From d20ee3454faafdf532c7569784918103cce05f44 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 3 Dec 2007 16:36:00 -0200 Subject: Mercurial rejects time fuse in the form of (MET) at timestamp. Remove it. From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- mailimport | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mailimport b/mailimport index 31638f902..b02b6e779 100755 --- a/mailimport +++ b/mailimport @@ -91,7 +91,7 @@ apply_patch () { done committer=`grep "Committer:" $TMP2|sed s/"#Committer: "//` - date=`grep "Date:" $TMP2|sed s/"#Date: "//` + date=`grep "Date:" $TMP2|sed s/"#Date: "//|sed s/\(.*\)//` if [ "$usemq" != "" ]; then name=`cat $next | perl -ne ' -- cgit v1.2.3 From 833edd5e26e3f0c8b453f4ee65512b200f5b8e43 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 2 Dec 2007 18:47:01 +0100 Subject: ivtv: Remove a invalid shadow-variable From: Richard Knutsson Remove the shadowing 'struct v4l2_chip_ident *chip', since it already exists and makes the if-statement useless. Signed-off-by: Richard Knutsson Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/ivtv/ivtv-ioctl.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c index 30cc3b290..39f20dfb8 100644 --- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c @@ -679,11 +679,8 @@ static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg) chip->ident = V4L2_IDENT_NONE; chip->revision = 0; if (reg->match_type == V4L2_CHIP_MATCH_HOST) { - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) { - struct v4l2_chip_ident *chip = arg; - + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; - } return 0; } if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) -- cgit v1.2.3 From 1ecbaa50c6dfbc09017fff9113c88dac126542b8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 3 Dec 2007 10:48:43 +0100 Subject: dsbr100 violates DMA coherency rules From: Oliver Neukum Signed-off-by: Oliver Neukum Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/dsbr100.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/linux/drivers/media/radio/dsbr100.c b/linux/drivers/media/radio/dsbr100.c index 7f9ceb3cf..3107f00bc 100644 --- a/linux/drivers/media/radio/dsbr100.c +++ b/linux/drivers/media/radio/dsbr100.c @@ -33,6 +33,9 @@ History: + Version 0.43: + Oliver Neukum: avoided DMA coherency issue + Version 0.42: Converted dsbr100 to use video_ioctl2 by Douglas Landgraf @@ -136,7 +139,7 @@ module_param(radio_nr, int, 0); struct dsbr100_device { struct usb_device *usbdev; struct video_device *videodev; - unsigned char transfer_buffer[TB_LEN]; + u8 *transfer_buffer; int curfreq; int stereo; int users; @@ -238,10 +241,7 @@ static void dsbr100_getstat(struct dsbr100_device *radio) /* handle unplugging of the device, release data structures if nothing keeps us from doing it. If something is still keeping us busy, the release callback of v4l will take care -of releasing it. stv680.c does not relase its private -data, so I don't do this here either. Checking out the -code I'd expect I better did that, but if there's a memory -leak here it's tiny (~50 bytes per disconnect) */ +of releasing it. */ static void usb_dsbr100_disconnect(struct usb_interface *intf) { struct dsbr100_device *radio = usb_get_intfdata(intf); @@ -251,6 +251,7 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf) video_unregister_device(radio->videodev); radio->videodev = NULL; if (radio->users) { + kfree(radio->transfer_buffer); kfree(radio); } else { radio->removed = 1; @@ -426,6 +427,7 @@ static int usb_dsbr100_close(struct inode *inode, struct file *file) return -ENODEV; radio->users = 0; if (radio->removed) { + kfree(radio->transfer_buffer); kfree(radio); } return 0; @@ -472,7 +474,12 @@ static int usb_dsbr100_probe(struct usb_interface *intf, if (!(radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL))) return -ENOMEM; + if (!(radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL))) { + kfree(radio); + return -ENOMEM; + } if (!(radio->videodev = video_device_alloc())) { + kfree(radio->transfer_buffer); kfree(radio); return -ENOMEM; } @@ -486,6 +493,7 @@ static int usb_dsbr100_probe(struct usb_interface *intf, if (video_register_device(radio->videodev, VFL_TYPE_RADIO,radio_nr)) { warn("Could not register video device"); video_device_release(radio->videodev); + kfree(radio->transfer_buffer); kfree(radio); return -EIO; } -- cgit v1.2.3 From b64d300b2b65a3074db10a584370a1ef7cad3af3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 5 Dec 2007 16:14:19 -0200 Subject: DVB: Compile 3000MC-specific DIB code only for CONFIG_DVB_DIB3000MC From: Jiri Kosina On Tue, 4 Dec 2007, Ingo Molnar wrote: > drivers/built-in.o: In function `dibusb_dib3000mc_tuner_attach': > : undefined reference to `dib3000mc_get_tuner_i2c_master' > drivers/built-in.o: In function `dibusb_dib3000mc_tuner_attach': > : undefined reference to `dib3000mc_set_config' Seems like -common part contains also code that is not completely common to all the modules. Signed-off-by: Jiri Kosina Tested-by: Ingo Molnar Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-usb/dibusb-common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c index 9a184da01..b95b1eb1e 100644 --- a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -223,6 +223,7 @@ static struct dibx000_agc_config dib3000p_panasonic_agc_config = { .agc2_slope2 = 0x1e, }; +#ifdef CONFIG_DVB_DIB3000MC static struct dib3000mc_config mod3000p_dib3000p_config = { &dib3000p_panasonic_agc_config, @@ -305,6 +306,7 @@ int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap) return 0; } EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach); +#endif /* * common remote control stuff -- cgit v1.2.3 From 87515f66b862942a5944c0e541caa229d12a3bad Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Dec 2007 18:09:53 -0200 Subject: Converted saa7134-video to use video_ioctl2 From: Douglas Schilling Landgraf Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/saa7134-video.c | 1212 +++++++++++---------- 1 file changed, 610 insertions(+), 602 deletions(-) diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index b8523aa91..63c625979 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -1119,8 +1119,11 @@ static struct videobuf_queue_ops video_qops = { /* ------------------------------------------------------------------ */ -static int get_control(struct saa7134_dev *dev, struct v4l2_control *c) +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *c) { + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; const struct v4l2_queryctrl* ctrl; ctrl = ctrl_by_id(c->id); @@ -1166,13 +1169,16 @@ static int get_control(struct saa7134_dev *dev, struct v4l2_control *c) return 0; } -static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh, - struct v4l2_control *c) +static int vidioc_s_ctrl(struct file *file, void *f, + struct v4l2_control *c) { const struct v4l2_queryctrl* ctrl; + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; unsigned long flags; int restart_overlay = 0; + mutex_lock(&dev->lock); ctrl = ctrl_by_id(c->id); if (NULL == ctrl) return -EINVAL; @@ -1255,6 +1261,7 @@ static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh, break; } default: + mutex_unlock(&dev->lock); return -EINVAL; } if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) { @@ -1263,6 +1270,7 @@ static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh, start_preview(dev,fh); spin_unlock_irqrestore(&dev->slock,flags); } + mutex_unlock(&dev->lock); return 0; } @@ -1509,9 +1517,12 @@ static void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f) #endif } -static int saa7134_g_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, - struct v4l2_format *f) +static int vidioc_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) { + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: memset(&f->fmt.pix,0,sizeof(f->fmt.pix)); @@ -1539,9 +1550,11 @@ static int saa7134_g_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, } } -static int saa7134_try_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, - struct v4l2_format *f) +static int vidioc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) { + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; int err; switch (f->type) { @@ -1609,15 +1622,17 @@ static int saa7134_try_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, } } -static int saa7134_s_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, - struct v4l2_format *f) +static int vidioc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) { + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; unsigned long flags; int err; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - err = saa7134_try_fmt(dev,fh,f); + err = vidioc_try_fmt_cap(file, priv, f); if (0 != err) return err; @@ -1662,682 +1677,625 @@ static int saa7134_s_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, } } -int saa7134_common_ioctl(struct saa7134_dev *dev, - unsigned int cmd, void *arg) +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - int err; + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; - switch (cmd) { - case VIDIOC_QUERYCTRL: - { - const struct v4l2_queryctrl *ctrl; - struct v4l2_queryctrl *c = arg; + *i = dev->ctl_input; + return 0; +} - if ((c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) && - (c->id < V4L2_CID_PRIVATE_BASE || - c->id >= V4L2_CID_PRIVATE_LASTP1)) - return -EINVAL; - ctrl = ctrl_by_id(c->id); - *c = (NULL != ctrl) ? *ctrl : no_ctrl; - return 0; - } - case VIDIOC_G_CTRL: - return get_control(dev,arg); - case VIDIOC_S_CTRL: - { - mutex_lock(&dev->lock); - err = set_control(dev,NULL,arg); - mutex_unlock(&dev->lock); - return err; - } - /* --- input switching --------------------------------------- */ - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; - unsigned int n; +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; - n = i->index; - if (n >= SAA7134_INPUT_MAX) - return -EINVAL; - if (NULL == card_in(dev,i->index).name) - return -EINVAL; - memset(i,0,sizeof(*i)); - i->index = n; - i->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(i->name,card_in(dev,n).name); - if (card_in(dev,n).tv) - i->type = V4L2_INPUT_TYPE_TUNER; - i->audioset = 1; - if (n == dev->ctl_input) { - int v1 = saa_readb(SAA7134_STATUS_VIDEO1); - int v2 = saa_readb(SAA7134_STATUS_VIDEO2); - - if (0 != (v1 & 0x40)) - i->status |= V4L2_IN_ST_NO_H_LOCK; - if (0 != (v2 & 0x40)) - i->status |= V4L2_IN_ST_NO_SYNC; - if (0 != (v2 & 0x0e)) - i->status |= V4L2_IN_ST_MACROVISION; - } - for (n = 0; n < TVNORMS; n++) - i->std |= tvnorms[n].id; - return 0; - } - case VIDIOC_G_INPUT: - { - int *i = arg; - *i = dev->ctl_input; - return 0; - } - case VIDIOC_S_INPUT: - { - int *i = arg; + if (i < 0 || i >= SAA7134_INPUT_MAX) + return -EINVAL; + if (NULL == card_in(dev, i).name) + return -EINVAL; + mutex_lock(&dev->lock); + video_mux(dev, i); + mutex_unlock(&dev->lock); + return 0; +} - if (*i < 0 || *i >= SAA7134_INPUT_MAX) - return -EINVAL; - if (NULL == card_in(dev,*i).name) - return -EINVAL; - mutex_lock(&dev->lock); - video_mux(dev,*i); - mutex_unlock(&dev->lock); - return 0; - } +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + const struct v4l2_queryctrl *ctrl; - } + if ((c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) && + (c->id < V4L2_CID_PRIVATE_BASE || + c->id >= V4L2_CID_PRIVATE_LASTP1)) + return -EINVAL; + ctrl = ctrl_by_id(c->id); + *c = (NULL != ctrl) ? *ctrl : no_ctrl; return 0; } -EXPORT_SYMBOL(saa7134_common_ioctl); -/* - * This function is _not_ called directly, but from - * video_generic_ioctl (and maybe others). userspace - * copying is done already, arg is a kernel pointer. - */ -static int video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) { - struct saa7134_fh *fh = file->private_data; + struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; - unsigned long flags; - int err; + unsigned int n; - if (video_debug > 1) - v4l_print_ioctl(dev->name,cmd); + n = i->index; + if (n >= SAA7134_INPUT_MAX) + return -EINVAL; + if (NULL == card_in(dev, i->index).name) + return -EINVAL; + memset(i, 0, sizeof(*i)); + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(i->name, card_in(dev, n).name); + if (card_in(dev, n).tv) + i->type = V4L2_INPUT_TYPE_TUNER; + i->audioset = 1; + if (n == dev->ctl_input) { + int v1 = saa_readb(SAA7134_STATUS_VIDEO1); + int v2 = saa_readb(SAA7134_STATUS_VIDEO2); + + if (0 != (v1 & 0x40)) + i->status |= V4L2_IN_ST_NO_H_LOCK; + if (0 != (v2 & 0x40)) + i->status |= V4L2_IN_ST_NO_SYNC; + if (0 != (v2 & 0x0e)) + i->status |= V4L2_IN_ST_MACROVISION; + } + for (n = 0; n < TVNORMS; n++) + i->std |= tvnorms[n].id; + return 0; +} - switch (cmd) { - case VIDIOC_S_CTRL: - case VIDIOC_S_STD: - case VIDIOC_S_INPUT: - case VIDIOC_S_TUNER: - case VIDIOC_S_FREQUENCY: - err = v4l2_prio_check(&dev->prio,&fh->prio); - if (0 != err) - return err; - } +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - unsigned int tuner_type = dev->tuner_type; - - memset(cap,0,sizeof(*cap)); - strcpy(cap->driver, "saa7134"); - strlcpy(cap->card, saa7134_boards[dev->board].name, - sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); - cap->version = SAA7134_VERSION_CODE; - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_VBI_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - V4L2_CAP_TUNER; - if (saa7134_no_overlay <= 0) { - cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; - } + unsigned int tuner_type = dev->tuner_type; + + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, "saa7134"); + strlcpy(cap->card, saa7134_boards[dev->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cap->version = SAA7134_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + V4L2_CAP_TUNER; + if (saa7134_no_overlay <= 0) + cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; + + if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET)) + cap->capabilities &= ~V4L2_CAP_TUNER; + return 0; +} - if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET)) - cap->capabilities &= ~V4L2_CAP_TUNER; +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * id) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + unsigned long flags; + unsigned int i; + v4l2_std_id fixup; - return 0; + for (i = 0; i < TVNORMS; i++) + if (*id == tvnorms[i].id) + break; + if (i == TVNORMS) + for (i = 0; i < TVNORMS; i++) + if (*id & tvnorms[i].id) + break; + if (i == TVNORMS) + return -EINVAL; + if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) { + if (secam[0] == 'L' || secam[0] == 'l') { + if (secam[1] == 'C' || secam[1] == 'c') + fixup = V4L2_STD_SECAM_LC; + else + fixup = V4L2_STD_SECAM_L; + } else { + if (secam[0] == 'D' || secam[0] == 'd') + fixup = V4L2_STD_SECAM_DK; + else + fixup = V4L2_STD_SECAM; + } + for (i = 0; i < TVNORMS; i++) + if (fixup == tvnorms[i].id) + break; } + mutex_lock(&dev->lock); + if (res_check(fh, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock, flags); + stop_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + + set_tvnorm(dev, &tvnorms[i]); + + spin_lock_irqsave(&dev->slock, flags); + start_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + } else + set_tvnorm(dev, &tvnorms[i]); + saa7134_tvaudio_do_scan(dev); + mutex_unlock(&dev->lock); + return 0; +} - /* --- tv standards ------------------------------------------ */ - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *e = arg; - unsigned int i; +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cap) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; - i = e->index; - if (i >= TVNORMS) - return -EINVAL; - err = v4l2_video_std_construct(e, tvnorms[e->index].id, - tvnorms[e->index].name); - e->index = i; - if (err < 0) - return err; - return 0; + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + cap->bounds = dev->crop_bounds; + cap->defrect = dev->crop_defrect; + cap->pixelaspect.numerator = 1; + cap->pixelaspect.denominator = 1; + if (dev->tvnorm->id & V4L2_STD_525_60) { + cap->pixelaspect.numerator = 11; + cap->pixelaspect.denominator = 10; + } + if (dev->tvnorm->id & V4L2_STD_625_50) { + cap->pixelaspect.numerator = 54; + cap->pixelaspect.denominator = 59; } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; + return 0; +} - *id = dev->tvnorm->id; - return 0; - } - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - unsigned int i; - v4l2_std_id fixup; +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int n; - for (i = 0; i < TVNORMS; i++) - if (*id == tvnorms[i].id) - break; - if (i == TVNORMS) - for (i = 0; i < TVNORMS; i++) - if (*id & tvnorms[i].id) - break; - if (i == TVNORMS) - return -EINVAL; - if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) { - if (secam[0] == 'L' || secam[0] == 'l') { - if (secam[1] == 'C' || secam[1] == 'c') - fixup = V4L2_STD_SECAM_LC; - else - fixup = V4L2_STD_SECAM_L; - } else { - if (secam[0] == 'D' || secam[0] == 'd') - fixup = V4L2_STD_SECAM_DK; - else - fixup = V4L2_STD_SECAM; - } - for (i = 0; i < TVNORMS; i++) - if (fixup == tvnorms[i].id) - break; - } - mutex_lock(&dev->lock); - if (res_check(fh, RESOURCE_OVERLAY)) { - spin_lock_irqsave(&dev->slock,flags); - stop_preview(dev,fh); - spin_unlock_irqrestore(&dev->slock, flags); + if (0 != t->index) + return -EINVAL; + memset(t, 0, sizeof(*t)); + for (n = 0; n < SAA7134_INPUT_MAX; n++) + if (card_in(dev, n).tv) + break; + if (NULL != card_in(dev, n).name) { + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM | + V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2; + t->rangehigh = 0xffffffffUL; + t->rxsubchans = saa7134_tvaudio_getstereo(dev); + t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans); + } + if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03)) + t->signal = 0xffff; + return 0; +} - set_tvnorm(dev,&tvnorms[i]); +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int rx, mode; - spin_lock_irqsave(&dev->slock, flags); - start_preview(dev,fh); - spin_unlock_irqrestore(&dev->slock,flags); - } else - set_tvnorm(dev,&tvnorms[i]); - saa7134_tvaudio_do_scan(dev); - mutex_unlock(&dev->lock); - return 0; + mode = dev->thread.mode; + if (UNSET == mode) { + rx = saa7134_tvaudio_getstereo(dev); + mode = saa7134_tvaudio_rx2mode(t->rxsubchans); } + if (mode != t->audmode) + dev->thread.mode = t->audmode; + return 0; +} - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *cap = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; - if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) - return -EINVAL; - cap->bounds = dev->crop_bounds; - cap->defrect = dev->crop_defrect; - cap->pixelaspect.numerator = 1; - cap->pixelaspect.denominator = 1; - if (dev->tvnorm->id & V4L2_STD_525_60) { - cap->pixelaspect.numerator = 11; - cap->pixelaspect.denominator = 10; - } - if (dev->tvnorm->id & V4L2_STD_625_50) { - cap->pixelaspect.numerator = 54; - cap->pixelaspect.denominator = 59; - } - return 0; - } + memset(f, 0, sizeof(*f)); + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->frequency = dev->ctl_freq; + return 0; +} - case VIDIOC_G_CROP: - { - struct v4l2_crop * crop = arg; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) - return -EINVAL; - crop->c = dev->crop_current; - return 0; - } - case VIDIOC_S_CROP: - { - struct v4l2_crop *crop = arg; - struct v4l2_rect *b = &dev->crop_bounds; + if (0 != f->tuner) + return -EINVAL; + if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) + return -EINVAL; + mutex_lock(&dev->lock); + dev->ctl_freq = f->frequency; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) - return -EINVAL; - if (crop->c.height < 0) - return -EINVAL; - if (crop->c.width < 0) - return -EINVAL; + saa7134_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f); - if (res_locked(fh->dev,RESOURCE_OVERLAY)) - return -EBUSY; - if (res_locked(fh->dev,RESOURCE_VIDEO)) - return -EBUSY; + saa7134_tvaudio_do_scan(dev); + mutex_unlock(&dev->lock); + return 0; +} - if (crop->c.top < b->top) - crop->c.top = b->top; - if (crop->c.top > b->top + b->height) - crop->c.top = b->top + b->height; - if (crop->c.height > b->top - crop->c.top + b->height) - crop->c.height = b->top - crop->c.top + b->height; - - if (crop->c.left < b->left) - crop->c.left = b->left; - if (crop->c.left > b->left + b->width) - crop->c.left = b->left + b->width; - if (crop->c.width > b->left - crop->c.left + b->width) - crop->c.width = b->left - crop->c.left + b->width; - - dev->crop_current = crop->c; - return 0; - } +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + memset(a, 0, sizeof(*a)); + strcpy(a->name, "audio"); + return 0; +} - /* --- tuner ioctls ------------------------------------------ */ - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; - int n; +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + return 0; +} - if (0 != t->index) - return -EINVAL; - memset(t,0,sizeof(*t)); - for (n = 0; n < SAA7134_INPUT_MAX; n++) - if (card_in(dev,n).tv) - break; - if (NULL != card_in(dev,n).name) { - strcpy(t->name, "Television"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM | - V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2; - t->rangehigh = 0xffffffffUL; - t->rxsubchans = saa7134_tvaudio_getstereo(dev); - t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans); - } - if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03)) - t->signal = 0xffff; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *t = arg; - int rx,mode; +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int res = saa7134_resource(fh); - mode = dev->thread.mode; - if (UNSET == mode) { - rx = saa7134_tvaudio_getstereo(dev); - mode = saa7134_tvaudio_rx2mode(t->rxsubchans); - } - if (mode != t->audmode) { - dev->thread.mode = t->audmode; - } - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + if (!res_get(dev, fh, res)) + return -EBUSY; - memset(f,0,sizeof(*f)); - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - f->frequency = dev->ctl_freq; - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + return videobuf_streamon(saa7134_queue(fh)); +} - if (0 != f->tuner) - return -EINVAL; - if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) - return -EINVAL; - if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) - return -EINVAL; - mutex_lock(&dev->lock); - dev->ctl_freq = f->frequency; +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int err; + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int res = saa7134_resource(fh); - saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,f); + err = videobuf_streamoff(saa7134_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} - saa7134_tvaudio_do_scan(dev); - mutex_unlock(&dev->lock); - return 0; - } +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct saa7134_fh *fh = priv; + return videobuf_reqbufs(saa7134_queue(fh), p); +} - /* --- control ioctls ---------------------------------------- */ - case VIDIOC_ENUMINPUT: - case VIDIOC_G_INPUT: - case VIDIOC_S_INPUT: - case VIDIOC_QUERYCTRL: - case VIDIOC_G_CTRL: - case VIDIOC_S_CTRL: - return saa7134_common_ioctl(dev, cmd, arg); +static int vidioc_s_crop(struct file *file, void *f, struct v4l2_crop *crop) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + struct v4l2_rect *b = &dev->crop_bounds; - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + if (crop->c.height < 0) + return -EINVAL; + if (crop->c.width < 0) + return -EINVAL; - memset(a,0,sizeof(*a)); - strcpy(a->name,"audio"); - return 0; - } - case VIDIOC_S_AUDIO: - return 0; - case VIDIOC_G_PARM: - { - struct v4l2_captureparm *parm = arg; - memset(parm,0,sizeof(*parm)); - return 0; - } + if (res_locked(fh->dev, RESOURCE_OVERLAY)) + return -EBUSY; + if (res_locked(fh->dev, RESOURCE_VIDEO)) + return -EBUSY; + + if (crop->c.top < b->top) + crop->c.top = b->top; + if (crop->c.top > b->top + b->height) + crop->c.top = b->top + b->height; + if (crop->c.height > b->top - crop->c.top + b->height) + crop->c.height = b->top - crop->c.top + b->height; + + if (crop->c.left < b->left) + crop->c.left = b->left; + if (crop->c.left > b->left + b->width) + crop->c.left = b->left + b->width; + if (crop->c.width > b->left - crop->c.left + b->width) + crop->c.width = b->left - crop->c.left + b->width; + + dev->crop_current = crop->c; + return 0; +} - case VIDIOC_G_PRIORITY: - { - enum v4l2_priority *p = arg; +static int vidioc_g_crop(struct file *file, void *f, struct v4l2_crop *crop) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; - *p = v4l2_prio_max(&dev->prio); - return 0; - } - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *prio = arg; + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + crop->c = dev->crop_current; + return 0; +} - return v4l2_prio_change(&dev->prio, &fh->prio, *prio); - } +static int vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + memset(parm, 0, sizeof(*parm)); + return 0; +} - /* --- preview ioctls ---------------------------------------- */ - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *f = arg; - enum v4l2_buf_type type; - unsigned int index; - - index = f->index; - type = f->type; - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (saa7134_no_overlay > 0) { - printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); - return -EINVAL; - } - if (index >= FORMATS) - return -EINVAL; - if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY && - formats[index].planar) - return -EINVAL; - memset(f,0,sizeof(*f)); - f->index = index; - f->type = type; - strlcpy(f->description,formats[index].name,sizeof(f->description)); - f->pixelformat = formats[index].fourcc; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (0 != index) - return -EINVAL; - memset(f,0,sizeof(*f)); - f->index = index; - f->type = type; - f->pixelformat = V4L2_PIX_FMT_GREY; - strcpy(f->description,"vbi data"); - break; - default: - return -EINVAL; - } - return 0; - } - case VIDIOC_G_FBUF: - { - struct v4l2_framebuffer *fb = arg; +static int vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; - *fb = dev->ovbuf; - fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; - return 0; - } - case VIDIOC_S_FBUF: - { - struct v4l2_framebuffer *fb = arg; - struct saa7134_format *fmt; + *p = v4l2_prio_max(&dev->prio); + return 0; +} - if(!capable(CAP_SYS_ADMIN) && - !capable(CAP_SYS_RAWIO)) - return -EPERM; +static int vidioc_s_priority(struct file *file, void *f, + enum v4l2_priority prio) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; - /* check args */ - fmt = format_by_fourcc(fb->fmt.pixelformat); - if (NULL == fmt) - return -EINVAL; + return v4l2_prio_change(&dev->prio, &fh->prio, prio); +} - /* ok, accept it */ - dev->ovbuf = *fb; - dev->ovfmt = fmt; - if (0 == dev->ovbuf.fmt.bytesperline) - dev->ovbuf.fmt.bytesperline = - dev->ovbuf.fmt.width*fmt->depth/8; - return 0; - } - case VIDIOC_OVERLAY: - { - int *on = arg; +static int vidioc_g_fbuf(struct file *file, void *f, + struct v4l2_framebuffer *fb) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; - if (*on) { - if (saa7134_no_overlay > 0) { - printk ("no_overlay\n"); - return -EINVAL; - } + *fb = dev->ovbuf; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; - if (!res_get(dev,fh,RESOURCE_OVERLAY)) - return -EBUSY; - spin_lock_irqsave(&dev->slock,flags); - start_preview(dev,fh); - spin_unlock_irqrestore(&dev->slock,flags); - } - if (!*on) { - if (!res_check(fh, RESOURCE_OVERLAY)) - return -EINVAL; - spin_lock_irqsave(&dev->slock,flags); - stop_preview(dev,fh); - spin_unlock_irqrestore(&dev->slock,flags); - res_free(dev,fh,RESOURCE_OVERLAY); - } - return 0; - } + return 0; +} - /* --- capture ioctls ---------------------------------------- */ - case VIDIOC_G_FMT: - { - struct v4l2_format *f = arg; - return saa7134_g_fmt(dev,fh,f); - } - case VIDIOC_S_FMT: - { - struct v4l2_format *f = arg; - return saa7134_s_fmt(dev,fh,f); - } - case VIDIOC_TRY_FMT: - { - struct v4l2_format *f = arg; - return saa7134_try_fmt(dev,fh,f); - } -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGMBUF: - return videobuf_cgmbuf(saa7134_queue(fh), arg, gbuffers); -#endif - case VIDIOC_REQBUFS: - return videobuf_reqbufs(saa7134_queue(fh),arg); +static int vidioc_s_fbuf(struct file *file, void *f, + struct v4l2_framebuffer *fb) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + struct saa7134_format *fmt; + + if (!capable(CAP_SYS_ADMIN) && + !capable(CAP_SYS_RAWIO)) + return -EPERM; - case VIDIOC_QUERYBUF: - return videobuf_querybuf(saa7134_queue(fh),arg); + /* check args */ + fmt = format_by_fourcc(fb->fmt.pixelformat); + if (NULL == fmt) + return -EINVAL; - case VIDIOC_QBUF: - return videobuf_qbuf(saa7134_queue(fh),arg); + /* ok, accept it */ + dev->ovbuf = *fb; + dev->ovfmt = fmt; + if (0 == dev->ovbuf.fmt.bytesperline) + dev->ovbuf.fmt.bytesperline = + dev->ovbuf.fmt.width*fmt->depth/8; + return 0; +} - case VIDIOC_DQBUF: - return videobuf_dqbuf(saa7134_queue(fh),arg, - file->f_flags & O_NONBLOCK); +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct saa7134_fh *fh = priv; + return videobuf_querybuf(saa7134_queue(fh), b); +} - case VIDIOC_STREAMON: - { - int res = saa7134_resource(fh); +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct saa7134_fh *fh = priv; + return videobuf_qbuf(saa7134_queue(fh), b); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct saa7134_fh *fh = priv; + return videobuf_dqbuf(saa7134_queue(fh), b, + file->f_flags & O_NONBLOCK); +} + +static int vidioc_overlay(struct file *file, void *f, unsigned int on) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + unsigned long flags; - if (!res_get(dev,fh,res)) + if (on) { + if (saa7134_no_overlay > 0) { + dprintk("no_overlay\n"); + return -EINVAL; + } + + if (!res_get(dev, fh, RESOURCE_OVERLAY)) return -EBUSY; - return videobuf_streamon(saa7134_queue(fh)); + spin_lock_irqsave(&dev->slock, flags); + start_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); } - case VIDIOC_STREAMOFF: - { - int res = saa7134_resource(fh); - - err = videobuf_streamoff(saa7134_queue(fh)); - if (err < 0) - return err; - res_free(dev,fh,res); - return 0; + if (!on) { + if (!res_check(fh, RESOURCE_OVERLAY)) + return -EINVAL; + spin_lock_irqsave(&dev->slock, flags); + stop_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + res_free(dev, fh, RESOURCE_OVERLAY); } + return 0; +} + +static int vidioc_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + enum v4l2_buf_type type; + unsigned int index; + index = f->index; + type = f->type; + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (saa7134_no_overlay > 0) + return -EINVAL; + + if (index >= FORMATS) + return -EINVAL; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY && + formats[index].planar) + return -EINVAL; + memset(f, 0, sizeof(*f)); + f->index = index; + f->type = type; + strlcpy(f->description, formats[index].name, + sizeof(f->description)); + f->pixelformat = formats[index].fourcc; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (0 != index) + return -EINVAL; + memset(f, 0, sizeof(*f)); + f->index = index; + f->type = type; + f->pixelformat = V4L2_PIX_FMT_GREY; + strcpy(f->description, "vbi data"); + break; default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - video_do_ioctl); + return -EINVAL; } return 0; } -static int video_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) { - return video_usercopy(inode, file, cmd, arg, video_do_ioctl); + struct saa7134_fh *fh = file->private_data; + return videobuf_cgmbuf(saa7134_queue(fh), mbuf, 8); } +#endif -static int radio_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) { struct saa7134_fh *fh = file->private_data; struct saa7134_dev *dev = fh->dev; - if (video_debug > 1) - v4l_print_ioctl(dev->name,cmd); - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap,0,sizeof(*cap)); - strcpy(cap->driver, "saa7134"); - strlcpy(cap->card, saa7134_boards[dev->board].name, - sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); - cap->version = SAA7134_VERSION_CODE; - cap->capabilities = V4L2_CAP_TUNER; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, "saa7134"); + strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card)); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cap->version = SAA7134_VERSION_CODE; + cap->capabilities = V4L2_CAP_TUNER; + return 0; +} - if (0 != t->index) - return -EINVAL; +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; - memset(t,0,sizeof(*t)); - strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; + if (0 != t->index) + return -EINVAL; - saa7134_i2c_call_clients(dev, VIDIOC_G_TUNER, t); - if (dev->input->amux == TV) { - t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11); - t->rxsubchans = (saa_readb(0x529) & 0x08) ? - V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; - } - return 0; + memset(t, 0, sizeof(*t)); + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + saa7134_i2c_call_clients(dev, VIDIOC_G_TUNER, t); + if (dev->input->amux == TV) { + t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11); + t->rxsubchans = (saa_readb(0x529) & 0x08) ? + V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *t = arg; + return 0; +} +static int radio_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; - if (0 != t->index) - return -EINVAL; + if (0 != t->index) + return -EINVAL; + + saa7134_i2c_call_clients(dev, VIDIOC_S_TUNER, t); + return 0; +} - saa7134_i2c_call_clients(dev,VIDIOC_S_TUNER,t); +static int radio_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; - return 0; - } - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; - if (i->index != 0) - return -EINVAL; - strcpy(i->name,"Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - return 0; - } - case VIDIOC_G_INPUT: - { - int *i = arg; - *i = 0; - return 0; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; + return 0; +} - memset(a,0,sizeof(*a)); - strcpy(a->name,"Radio"); - return 0; - } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; - *id = 0; - return 0; - } - case VIDIOC_S_AUDIO: - case VIDIOC_S_INPUT: - case VIDIOC_S_STD: - return 0; +static int radio_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} - case VIDIOC_QUERYCTRL: - { - const struct v4l2_queryctrl *ctrl; - struct v4l2_queryctrl *c = arg; +static int radio_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + memset(a, 0, sizeof(*a)); + strcpy(a->name, "Radio"); + return 0; +} - if (c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) - return -EINVAL; - if (c->id == V4L2_CID_AUDIO_MUTE) { - ctrl = ctrl_by_id(c->id); - *c = *ctrl; - } else - *c = no_ctrl; - return 0; - } +static int radio_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + return 0; +} - case VIDIOC_G_CTRL: - case VIDIOC_S_CTRL: - case VIDIOC_G_FREQUENCY: - case VIDIOC_S_FREQUENCY: - return video_do_ioctl(inode,file,cmd,arg); +static int radio_s_input(struct file *filp, void *priv, unsigned int i) +{ + return 0; +} - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - radio_do_ioctl); - } +static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) +{ return 0; } -static int radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) { - return video_usercopy(inode, file, cmd, arg, radio_do_ioctl); + const struct v4l2_queryctrl *ctrl; + + if (c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + ctrl = ctrl_by_id(c->id); + *c = *ctrl; + } else + *c = no_ctrl; + return 0; } static const struct file_operations video_fops = @@ -2348,7 +2306,7 @@ static const struct file_operations video_fops = .read = video_read, .poll = video_poll, .mmap = video_mmap, - .ioctl = video_ioctl, + .ioctl = video_ioctl2, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) .compat_ioctl = v4l_compat_ioctl32, #endif @@ -2360,7 +2318,7 @@ static const struct file_operations radio_fops = .owner = THIS_MODULE, .open = video_open, .release = video_release, - .ioctl = radio_ioctl, + .ioctl = video_ioctl2, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) .compat_ioctl = v4l_compat_ioctl32, #endif @@ -2372,11 +2330,47 @@ static const struct file_operations radio_fops = struct video_device saa7134_video_template = { - .name = "saa7134-video", - .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER| - VID_TYPE_CLIPPING|VID_TYPE_SCALES, - .fops = &video_fops, - .minor = -1, + .name = "saa7134-video", + .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER | + VID_TYPE_CLIPPING|VID_TYPE_SCALES, + .fops = &video_fops, + .minor = -1, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, + .vidioc_g_fmt_cap = vidioc_g_fmt_cap, + .vidioc_try_fmt_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif + .vidioc_g_crop = vidioc_g_crop, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_fbuf = vidioc_s_fbuf, + .vidioc_overlay = vidioc_overlay, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, }; struct video_device saa7134_vbi_template = @@ -2393,6 +2387,20 @@ struct video_device saa7134_radio_template = .type = VID_TYPE_TUNER, .fops = &radio_fops, .minor = -1, + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_s_std = radio_s_std, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_input = radio_g_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, }; int saa7134_video_init1(struct saa7134_dev *dev) -- cgit v1.2.3 From b100a7e2f4629f94749200d97d230de781765ee8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Dec 2007 18:23:38 -0200 Subject: Reorder functions to make easier to compare with the previous code From: Mauro Carvalho Chehab After this patch, the order of the functions will be the same as before the patch converting the driver to user video_ioctl2. This makes easier to diff between the previous version and the newer one. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/saa7134-video.c | 348 +++++++++++----------- 1 file changed, 174 insertions(+), 174 deletions(-) diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index 63c625979..098ac1879 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -1677,30 +1677,6 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, } } -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; - - *i = dev->ctl_input; - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; - - if (i < 0 || i >= SAA7134_INPUT_MAX) - return -EINVAL; - if (NULL == card_in(dev, i).name) - return -EINVAL; - mutex_lock(&dev->lock); - video_mux(dev, i); - mutex_unlock(&dev->lock); - return 0; -} - static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) { @@ -1751,6 +1727,30 @@ static int vidioc_enum_input(struct file *file, void *priv, return 0; } +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + *i = dev->ctl_input; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + if (i < 0 || i >= SAA7134_INPUT_MAX) + return -EINVAL; + if (NULL == card_in(dev, i).name) + return -EINVAL; + mutex_lock(&dev->lock); + video_mux(dev, i); + mutex_unlock(&dev->lock); + return 0; +} + static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1854,6 +1854,55 @@ static int vidioc_cropcap(struct file *file, void *priv, return 0; } +static int vidioc_g_crop(struct file *file, void *f, struct v4l2_crop *crop) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + crop->c = dev->crop_current; + return 0; +} + +static int vidioc_s_crop(struct file *file, void *f, struct v4l2_crop *crop) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + struct v4l2_rect *b = &dev->crop_bounds; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + if (crop->c.height < 0) + return -EINVAL; + if (crop->c.width < 0) + return -EINVAL; + + if (res_locked(fh->dev, RESOURCE_OVERLAY)) + return -EBUSY; + if (res_locked(fh->dev, RESOURCE_VIDEO)) + return -EBUSY; + + if (crop->c.top < b->top) + crop->c.top = b->top; + if (crop->c.top > b->top + b->height) + crop->c.top = b->top + b->height; + if (crop->c.height > b->top - crop->c.top + b->height) + crop->c.height = b->top - crop->c.top + b->height; + + if (crop->c.left < b->left) + crop->c.left = b->left; + if (crop->c.left > b->left + b->width) + crop->c.left = b->left + b->width; + if (crop->c.width > b->left - crop->c.left + b->width) + crop->c.width = b->left - crop->c.left + b->width; + + dev->crop_current = crop->c; + return 0; +} + static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1946,113 +1995,64 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; - int res = saa7134_resource(fh); - - if (!res_get(dev, fh, res)) - return -EBUSY; - - return videobuf_streamon(saa7134_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - int err; - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; - int res = saa7134_resource(fh); - - err = videobuf_streamoff(saa7134_queue(fh)); - if (err < 0) - return err; - res_free(dev, fh, res); - return 0; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct saa7134_fh *fh = priv; - return videobuf_reqbufs(saa7134_queue(fh), p); -} - -static int vidioc_s_crop(struct file *file, void *f, struct v4l2_crop *crop) +static int vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) { struct saa7134_fh *fh = f; struct saa7134_dev *dev = fh->dev; - struct v4l2_rect *b = &dev->crop_bounds; - - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) - return -EINVAL; - if (crop->c.height < 0) - return -EINVAL; - if (crop->c.width < 0) - return -EINVAL; - - if (res_locked(fh->dev, RESOURCE_OVERLAY)) - return -EBUSY; - if (res_locked(fh->dev, RESOURCE_VIDEO)) - return -EBUSY; - if (crop->c.top < b->top) - crop->c.top = b->top; - if (crop->c.top > b->top + b->height) - crop->c.top = b->top + b->height; - if (crop->c.height > b->top - crop->c.top + b->height) - crop->c.height = b->top - crop->c.top + b->height; - - if (crop->c.left < b->left) - crop->c.left = b->left; - if (crop->c.left > b->left + b->width) - crop->c.left = b->left + b->width; - if (crop->c.width > b->left - crop->c.left + b->width) - crop->c.width = b->left - crop->c.left + b->width; - - dev->crop_current = crop->c; + *p = v4l2_prio_max(&dev->prio); return 0; } -static int vidioc_g_crop(struct file *file, void *f, struct v4l2_crop *crop) +static int vidioc_s_priority(struct file *file, void *f, + enum v4l2_priority prio) { struct saa7134_fh *fh = f; struct saa7134_dev *dev = fh->dev; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) - return -EINVAL; - crop->c = dev->crop_current; - return 0; -} - -static int vidioc_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *parm) -{ - memset(parm, 0, sizeof(*parm)); - return 0; + return v4l2_prio_change(&dev->prio, &fh->prio, prio); } -static int vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) +static int vidioc_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { - struct saa7134_fh *fh = f; - struct saa7134_dev *dev = fh->dev; + enum v4l2_buf_type type; + unsigned int index; - *p = v4l2_prio_max(&dev->prio); - return 0; -} + index = f->index; + type = f->type; + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (saa7134_no_overlay > 0) + return -EINVAL; -static int vidioc_s_priority(struct file *file, void *f, - enum v4l2_priority prio) -{ - struct saa7134_fh *fh = f; - struct saa7134_dev *dev = fh->dev; + if (index >= FORMATS) + return -EINVAL; - return v4l2_prio_change(&dev->prio, &fh->prio, prio); + if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY && + formats[index].planar) + return -EINVAL; + memset(f, 0, sizeof(*f)); + f->index = index; + f->type = type; + strlcpy(f->description, formats[index].name, + sizeof(f->description)); + f->pixelformat = formats[index].fourcc; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (0 != index) + return -EINVAL; + memset(f, 0, sizeof(*f)); + f->index = index; + f->type = type; + f->pixelformat = V4L2_PIX_FMT_GREY; + strcpy(f->description, "vbi data"); + break; + default: + return -EINVAL; + } + return 0; } static int vidioc_g_fbuf(struct file *file, void *f, @@ -2092,26 +2092,6 @@ static int vidioc_s_fbuf(struct file *file, void *f, return 0; } -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct saa7134_fh *fh = priv; - return videobuf_querybuf(saa7134_queue(fh), b); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct saa7134_fh *fh = priv; - return videobuf_qbuf(saa7134_queue(fh), b); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct saa7134_fh *fh = priv; - return videobuf_dqbuf(saa7134_queue(fh), b, - file->f_flags & O_NONBLOCK); -} - static int vidioc_overlay(struct file *file, void *f, unsigned int on) { struct saa7134_fh *fh = f; @@ -2141,55 +2121,75 @@ static int vidioc_overlay(struct file *file, void *f, unsigned int on) return 0; } -static int vidioc_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) { - enum v4l2_buf_type type; - unsigned int index; + struct saa7134_fh *fh = file->private_data; + return videobuf_cgmbuf(saa7134_queue(fh), mbuf, 8); +} +#endif - index = f->index; - type = f->type; - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (saa7134_no_overlay > 0) - return -EINVAL; +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct saa7134_fh *fh = priv; + return videobuf_reqbufs(saa7134_queue(fh), p); +} - if (index >= FORMATS) - return -EINVAL; +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct saa7134_fh *fh = priv; + return videobuf_querybuf(saa7134_queue(fh), b); +} - if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY && - formats[index].planar) - return -EINVAL; - memset(f, 0, sizeof(*f)); - f->index = index; - f->type = type; - strlcpy(f->description, formats[index].name, - sizeof(f->description)); - f->pixelformat = formats[index].fourcc; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (0 != index) - return -EINVAL; - memset(f, 0, sizeof(*f)); - f->index = index; - f->type = type; - f->pixelformat = V4L2_PIX_FMT_GREY; - strcpy(f->description, "vbi data"); - break; - default: - return -EINVAL; - } +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct saa7134_fh *fh = priv; + return videobuf_qbuf(saa7134_queue(fh), b); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct saa7134_fh *fh = priv; + return videobuf_dqbuf(saa7134_queue(fh), b, + file->f_flags & O_NONBLOCK); +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int res = saa7134_resource(fh); + + if (!res_get(dev, fh, res)) + return -EBUSY; + + return videobuf_streamon(saa7134_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int err; + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int res = saa7134_resource(fh); + + err = videobuf_streamoff(saa7134_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); return 0; } -#ifdef CONFIG_VIDEO_V4L1_COMPAT -static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) +static int vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) { - struct saa7134_fh *fh = file->private_data; - return videobuf_cgmbuf(saa7134_queue(fh), mbuf, 8); + memset(parm, 0, sizeof(*parm)); + return 0; } -#endif static int radio_querycap(struct file *file, void *priv, struct v4l2_capability *cap) -- cgit v1.2.3 From 47afcf131805c1773242a182777117da38956ffc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Dec 2007 18:34:48 -0200 Subject: Fix some errors at the video_ioctl2 conversion From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/saa7134-core.c | 1 + linux/drivers/media/video/saa7134/saa7134-video.c | 388 ++++++++++++---------- linux/drivers/media/video/saa7134/saa7134.h | 1 + 3 files changed, 218 insertions(+), 172 deletions(-) diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index 639f5851b..74b881864 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -930,6 +930,7 @@ static struct video_device *vdev_init(struct saa7134_dev *dev, vfd->dev = &dev->pci->dev; vfd->release = video_device_release; #endif + vfd->debug = video_debug; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, saa7134_boards[dev->board].name); return vfd; diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index 098ac1879..7d04a24e0 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -38,7 +38,7 @@ /* ------------------------------------------------------------------ */ -static unsigned int video_debug = 0; +unsigned int video_debug; static unsigned int gbuffers = 8; static unsigned int noninterlaced = 0; static unsigned int gbufsize = 720*576*4; @@ -54,7 +54,7 @@ module_param_string(secam, secam, sizeof(secam), 0644); MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc"); -#define dprintk(fmt, arg...) if (video_debug) \ +#define dprintk(fmt, arg...) if (video_debug&0x04) \ printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg) /* ------------------------------------------------------------------ */ @@ -217,6 +217,12 @@ static struct saa7134_format formats[] = { .vbi_v_start_1 = 273, \ .src_timing = 7 +#define SAA7134_NORMS \ + V4L2_STD_PAL | V4L2_STD_PAL_N | \ + V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \ + V4L2_STD_NTSC | V4L2_STD_PAL_M | \ + V4L2_STD_PAL_60 + static struct saa7134_tvnorm tvnorms[] = { { .name = "PAL", /* autodetect */ @@ -542,7 +548,6 @@ void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits) static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) { - dprintk("set tv norm = %s\n",norm->name); dev->tvnorm = norm; @@ -561,7 +566,6 @@ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) dev->crop_current = dev->crop_defrect; saa7134_set_tvnorm_hw(dev); - } static void video_mux(struct saa7134_dev *dev, int input) @@ -1177,11 +1181,18 @@ static int vidioc_s_ctrl(struct file *file, void *f, struct saa7134_dev *dev = fh->dev; unsigned long flags; int restart_overlay = 0; + int err = -EINVAL; + + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; mutex_lock(&dev->lock); + ctrl = ctrl_by_id(c->id); if (NULL == ctrl) - return -EINVAL; + goto error; + dprintk("set_control name=%s val=%d\n",ctrl->name,c->value); switch (ctrl->type) { case V4L2_CTRL_TYPE_BOOLEAN: @@ -1261,8 +1272,7 @@ static int vidioc_s_ctrl(struct file *file, void *f, break; } default: - mutex_unlock(&dev->lock); - return -EINVAL; + goto error; } if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock,flags); @@ -1270,8 +1280,11 @@ static int vidioc_s_ctrl(struct file *file, void *f, start_preview(dev,fh); spin_unlock_irqrestore(&dev->slock,flags); } + err = 0; + +error: mutex_unlock(&dev->lock); - return 0; + return err; } /* ------------------------------------------------------------------ */ @@ -1494,8 +1507,11 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma) /* ------------------------------------------------------------------ */ -static void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f) +static int vidioc_try_get_set_fmt_vbi(struct file *file, void *priv, + struct v4l2_format *f) { + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; struct saa7134_tvnorm *norm = dev->tvnorm; f->fmt.vbi.sampling_rate = 6750000 * 4; @@ -1515,39 +1531,37 @@ static void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f) f->fmt.vbi.start[1] += 3*2; } #endif + return 0; } static int vidioc_g_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - memset(&f->fmt.pix,0,sizeof(f->fmt.pix)); - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; - f->fmt.pix.field = fh->cap.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - return 0; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (saa7134_no_overlay > 0) { - printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); - return -EINVAL; - } - f->fmt.win = fh->win; - return 0; - case V4L2_BUF_TYPE_VBI_CAPTURE: - saa7134_vbi_fmt(dev,f); - return 0; - default: + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->cap.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; +} + +static int vidioc_g_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_fh *fh = priv; + + if (saa7134_no_overlay > 0) { + printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); return -EINVAL; } + f->fmt.win = fh->win; + + return 0; } static int vidioc_try_fmt_cap(struct file *file, void *priv, @@ -1555,126 +1569,122 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; - int err; + struct saa7134_format *fmt; + enum v4l2_field field; + unsigned int maxw, maxh; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - { - struct saa7134_format *fmt; - enum v4l2_field field; - unsigned int maxw, maxh; + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (NULL == fmt) - return -EINVAL; + field = f->fmt.pix.field; + maxw = min(dev->crop_current.width*4, dev->crop_bounds.width); + maxh = min(dev->crop_current.height*4, dev->crop_bounds.height); - field = f->fmt.pix.field; - maxw = min(dev->crop_current.width*4, dev->crop_bounds.width); - maxh = min(dev->crop_current.height*4, dev->crop_bounds.height); + if (V4L2_FIELD_ANY == field) { + field = (f->fmt.pix.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } - if (V4L2_FIELD_ANY == field) { - field = (f->fmt.pix.height > maxh/2) - ? V4L2_FIELD_INTERLACED - : V4L2_FIELD_BOTTOM; - } - switch (field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - maxh = maxh / 2; - break; - case V4L2_FIELD_INTERLACED: - break; - default: - return -EINVAL; - } + f->fmt.pix.field = field; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.field = field; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - f->fmt.pix.width &= ~0x03; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; +} - return 0; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (saa7134_no_overlay > 0) { - printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); - return -EINVAL; - } - err = verify_preview(dev,&f->fmt.win); - if (0 != err) - return err; - return 0; - case V4L2_BUF_TYPE_VBI_CAPTURE: - saa7134_vbi_fmt(dev,f); - return 0; - default: +static int vidioc_try_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + if (saa7134_no_overlay > 0) { + printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); return -EINVAL; } + + return verify_preview(dev, &f->fmt.win); } static int vidioc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct saa7134_fh *fh = priv; + int err; + + err = vidioc_try_fmt_cap(file, priv, f); + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->cap.field = f->fmt.pix.field; + return 0; +} + +static int vidioc_s_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; - unsigned long flags; int err; + unsigned int flags; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - err = vidioc_try_fmt_cap(file, priv, f); - if (0 != err) - return err; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->cap.field = f->fmt.pix.field; - return 0; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (saa7134_no_overlay > 0) { - printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); - return -EINVAL; - } - err = verify_preview(dev,&f->fmt.win); - if (0 != err) - return err; - - mutex_lock(&dev->lock); - fh->win = f->fmt.win; - fh->nclips = f->fmt.win.clipcount; - if (fh->nclips > 8) - fh->nclips = 8; - if (copy_from_user(fh->clips,f->fmt.win.clips, - sizeof(struct v4l2_clip)*fh->nclips)) { - mutex_unlock(&dev->lock); - return -EFAULT; - } + if (saa7134_no_overlay > 0) { + printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + return -EINVAL; + } + err = verify_preview(dev, &f->fmt.win); + if (0 != err) + return err; - if (res_check(fh, RESOURCE_OVERLAY)) { - spin_lock_irqsave(&dev->slock,flags); - stop_preview(dev,fh); - start_preview(dev,fh); - spin_unlock_irqrestore(&dev->slock,flags); - } + mutex_lock(&dev->lock); + + fh->win = f->fmt.win; + fh->nclips = f->fmt.win.clipcount; + + if (fh->nclips > 8) + fh->nclips = 8; + + if (copy_from_user(fh->clips, f->fmt.win.clips, + sizeof(struct v4l2_clip)*fh->nclips)) { mutex_unlock(&dev->lock); - return 0; - case V4L2_BUF_TYPE_VBI_CAPTURE: - saa7134_vbi_fmt(dev,f); - return 0; - default: - return -EINVAL; + return -EFAULT; } + + if (res_check(fh, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock, flags); + stop_preview(dev, fh); + start_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + } + + mutex_unlock(&dev->lock); + return 0; } static int vidioc_queryctrl(struct file *file, void *priv, @@ -1722,8 +1732,7 @@ static int vidioc_enum_input(struct file *file, void *priv, if (0 != (v2 & 0x0e)) i->status |= V4L2_IN_ST_MACROVISION; } - for (n = 0; n < TVNORMS; n++) - i->std |= tvnorms[n].id; + i->std = SAA7134_NORMS; return 0; } @@ -1740,6 +1749,11 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; + int err; + + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; if (i < 0 || i >= SAA7134_INPUT_MAX) return -EINVAL; @@ -1759,7 +1773,6 @@ static int vidioc_querycap(struct file *file, void *priv, unsigned int tuner_type = dev->tuner_type; - memset(cap, 0, sizeof(*cap)); strcpy(cap->driver, "saa7134"); strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card)); @@ -1786,16 +1799,23 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * id) unsigned long flags; unsigned int i; v4l2_std_id fixup; + int err; + + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; for (i = 0; i < TVNORMS; i++) if (*id == tvnorms[i].id) break; + if (i == TVNORMS) for (i = 0; i < TVNORMS; i++) if (*id & tvnorms[i].id) break; if (i == TVNORMS) return -EINVAL; + if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) { if (secam[0] == 'L' || secam[0] == 'l') { if (secam[1] == 'C' || secam[1] == 'c') @@ -1812,6 +1832,9 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * id) if (fixup == tvnorms[i].id) break; } + + *id = tvnorms[i].id; + mutex_lock(&dev->lock); if (res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock, flags); @@ -1825,6 +1848,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * id) spin_unlock_irqrestore(&dev->slock, flags); } else set_tvnorm(dev, &tvnorms[i]); + saa7134_tvaudio_do_scan(dev); mutex_unlock(&dev->lock); return 0; @@ -1937,7 +1961,11 @@ static int vidioc_s_tuner(struct file *file, void *priv, { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; - int rx, mode; + int rx, mode, err; + + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; mode = dev->thread.mode; if (UNSET == mode) { @@ -1946,6 +1974,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, } if (mode != t->audmode) dev->thread.mode = t->audmode; + return 0; } @@ -1955,9 +1984,9 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; - memset(f, 0, sizeof(*f)); f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->ctl_freq; + return 0; } @@ -1966,6 +1995,11 @@ static int vidioc_s_frequency(struct file *file, void *priv, { struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; + int err; + + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; if (0 != f->tuner) return -EINVAL; @@ -1985,7 +2019,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { - memset(a, 0, sizeof(*a)); strcpy(a->name, "audio"); return 0; } @@ -2016,42 +2049,45 @@ static int vidioc_s_priority(struct file *file, void *f, static int vidioc_enum_fmt_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - enum v4l2_buf_type type; - unsigned int index; + if (f->index >= FORMATS) + return -EINVAL; - index = f->index; - type = f->type; - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (saa7134_no_overlay > 0) - return -EINVAL; + strlcpy(f->description, formats[f->index].name, + sizeof(f->description)); - if (index >= FORMATS) - return -EINVAL; + f->pixelformat = formats[f->index].fourcc; - if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY && - formats[index].planar) - return -EINVAL; - memset(f, 0, sizeof(*f)); - f->index = index; - f->type = type; - strlcpy(f->description, formats[index].name, - sizeof(f->description)); - f->pixelformat = formats[index].fourcc; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (0 != index) - return -EINVAL; - memset(f, 0, sizeof(*f)); - f->index = index; - f->type = type; - f->pixelformat = V4L2_PIX_FMT_GREY; - strcpy(f->description, "vbi data"); - break; - default: + return 0; +} + +static int vidioc_enum_fmt_overlay(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (saa7134_no_overlay > 0) { + printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); return -EINVAL; } + + if ((f->index >= FORMATS) || formats[f->index].planar) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, + sizeof(f->description)); + + f->pixelformat = formats[f->index].fourcc; + + return 0; +} + +static int vidioc_enum_fmt_vbi(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (0 != f->index) + return -EINVAL; + + f->pixelformat = V4L2_PIX_FMT_GREY; + strcpy(f->description, "vbi data"); + return 0; } @@ -2187,7 +2223,6 @@ static int vidioc_streamoff(struct file *file, void *priv, static int vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *parm) { - memset(parm, 0, sizeof(*parm)); return 0; } @@ -2197,7 +2232,6 @@ static int radio_querycap(struct file *file, void *priv, struct saa7134_fh *fh = file->private_data; struct saa7134_dev *dev = fh->dev; - memset(cap, 0, sizeof(*cap)); strcpy(cap->driver, "saa7134"); strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card)); sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); @@ -2340,6 +2374,14 @@ struct video_device saa7134_video_template = .vidioc_g_fmt_cap = vidioc_g_fmt_cap, .vidioc_try_fmt_cap = vidioc_try_fmt_cap, .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_enum_fmt_overlay = vidioc_enum_fmt_overlay, + .vidioc_g_fmt_overlay = vidioc_g_fmt_overlay, + .vidioc_try_fmt_overlay = vidioc_try_fmt_overlay, + .vidioc_s_fmt_overlay = vidioc_s_fmt_overlay, + .vidioc_enum_fmt_vbi = vidioc_enum_fmt_vbi, + .vidioc_g_fmt_vbi = vidioc_try_get_set_fmt_vbi, + .vidioc_try_fmt_vbi = vidioc_try_get_set_fmt_vbi, + .vidioc_s_fmt_vbi = vidioc_try_get_set_fmt_vbi, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, @@ -2371,6 +2413,8 @@ struct video_device saa7134_video_template = .vidioc_g_parm = vidioc_g_parm, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .tvnorms = SAA7134_NORMS, + .current_norm = V4L2_STD_PAL, }; struct video_device saa7134_vbi_template = diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 0df575366..336fe002e 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -646,6 +646,7 @@ void saa7134_i2c_call_clients(struct saa7134_dev *dev, /* ----------------------------------------------------------- */ /* saa7134-video.c */ +extern unsigned int video_debug; extern struct video_device saa7134_video_template; extern struct video_device saa7134_radio_template; -- cgit v1.2.3