Hi all,
There have been various bug reports lately regarding freezing of the PC
when starting a capture or when changing channels.
I've been stress testing this yesterday and today and I believe I have
found and fixed the problem. And as an additional bonus I believe that
this patch also fixes the bug that sometimes occurs where the top third
of the captured screen is continuously flickering.
I've attached two patches: ivtv-0.10.diff is against ivtv-0.10 and
ivtv.diff is against the http://www.linuxtv.org/hg/~hverkuil/v4l-dvb
repository (for kernels >= 2.6.22).
These patches also make an important change in the behavior of the
driver: if a capture is in progress, then it is no longer possible to
change inputs or open the radio device. EBUSY is returned as an error.
The underlying problem was the use of the CX2341X_ENC_INITIALIZE_INPUT
firmware call. This call should be made whenever the digitizer (saa7115
or cx25840) has changed, either because the TV standard was changed,
another input was chosen or the video format changes (e.g. capture
size, VBI settings). But it turned out that the digitizer has to be
turned off before calling this API and turned on afterwards. This is
definitely the case for the saa7115, the cx25840 seems to be more
forgiving.
If the digitizer is NOT turned off, then this FW call can sometimes
freeze your PC: it looks like the MPEG card crashes and freezes the PCI
bus. Any PCI access simply freezes everything.
The problem was that CX2341X_ENC_INITIALIZE_INPUT was called on every
channel change, even though there was no need to call it at that time.
So this call was removed.
The CX2341X_ENC_INITIALIZE_INPUT was also called on the start of a
capture and ensures that the MPEG encoder has the correct digitizer
settings. If this is not called, then flickering may occur.
Opening the radio device or changing input also requires a call to
CX2341X_ENC_INITIALIZE_INPUT. However, testing showed that this call
doesn't do anything when a capture is in progress, hence the new
requirement that the radio device or changing inputs can only be done
when no capture is running. Otherwise, changing inputs in midstream can
cause flickering.
While running a stress test app I also noticed that when recordings are
stopped and started in quick succession the start of the capture can
sometimes be garbled. It is actually a left-over from the previous
capture. It turns out that stopping the capture needs to wait a bit
(100 ms is enough) to make sure the last pending interrupts are
handled. This actually makes for simpler code and works very well.
Please test this patch, whether or not you have these problems. I'd like
to get some feedback on this!
Thanks,
Hans
The CX2341X_ENC_INITIALIZE_INPUT firmware call is very important
Index: ivtv-ioctl.c
===================================================================
--- ivtv-ioctl.c (revision 3935)
+++ ivtv-ioctl.c (working copy)
@@ -784,6 +784,9 @@
IVTV_DEBUG_INFO("Input unchanged\n");
break;
}
+ if (atomic_read(&itv->capturing) > 0) {
+ return -EBUSY;
+ }
IVTV_DEBUG_INFO("Changing input from %d to %d\n",
itv->active_input, inp);
Index: ivtv-streams.c
===================================================================
--- ivtv-streams.c (revision 3935)
+++ ivtv-streams.c (working copy)
@@ -557,9 +557,9 @@
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
/* Initialize Digitizer for Capture */
+ itv->video_dec_func(itv, VIDIOC_STREAMOFF, 0);
ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
-
- ivtv_sleep_timeout(HZ / 10, 0);
+ itv->video_dec_func(itv, VIDIOC_STREAMON, 0);
}
/* begin_capture */
@@ -723,7 +723,6 @@
int cap_type;
unsigned long then;
int stopmode;
- u32 data[CX2341X_MBOX_MAX_DATA];
if (s->v4l2dev == NULL)
return -EINVAL;
@@ -802,29 +801,9 @@
}
then = jiffies;
- /* Make sure DMA is complete */
- add_wait_queue(&s->waitq, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- do {
- /* check if DMA is pending */
- if ((s->type == IVTV_ENC_STREAM_TYPE_MPG) && /* MPG Only */
- (read_reg(IVTV_REG_DMASTATUS) & 0x02)) {
- /* Check for last DMA */
- ivtv_vapi_result(itv, data, CX2341X_ENC_GET_SEQ_END, 2, 0, 0);
- if (data[0] == 1) {
- IVTV_DEBUG_DMA("%s: Last DMA of size 0x%08x\n", s->name, data[1]);
- break;
- }
- } else if (read_reg(IVTV_REG_DMASTATUS) & 0x02) {
- break;
- }
-
- ivtv_sleep_timeout(HZ / 100, 1);
- } while (then + HZ * 2 > jiffies);
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&s->waitq, &wait);
+ /* Handle any pending interrupts */
+ ivtv_sleep_timeout(HZ / 10, 1);
}
atomic_dec(&itv->capturing);
Index: ivtv-fileops.c
===================================================================
--- ivtv-fileops.c (revision 3935)
+++ ivtv-fileops.c (working copy)
@@ -816,12 +816,20 @@
return -EBUSY;
}
+ if (!test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+ if (atomic_read(&itv->capturing) > 0) {
+ /* switching to radio while capture is
+ in progress is not polite */
+ kfree(item);
+ return -EBUSY;
+ }
+ }
+ /* Mark that the radio is being used. */
+ set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
/* We have the radio */
ivtv_mute(itv);
/* Switch tuner to radio */
ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL);
- /* Mark that the radio is being used. */
- set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
/* Select the correct audio input (i.e. radio tuner) */
ivtv_audio_set_io(itv);
if (itv->hw_flags & IVTV_HW_SAA711X)
@@ -863,13 +871,8 @@
{
struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 0 };
- /* initialize or refresh input */
- if (atomic_read(&itv->capturing) == 0)
- ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
-
- ivtv_sleep_timeout(HZ / 10, 0);
-
if (atomic_read(&itv->capturing)) {
+ ivtv_sleep_timeout(HZ / 10, 0);
ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
}
diff -r c392ac29add0 linux/drivers/media/video/ivtv/ivtv-fileops.c
--- a/linux/drivers/media/video/ivtv/ivtv-fileops.c Wed Jul 25 23:02:03 2007 +0200
+++ b/linux/drivers/media/video/ivtv/ivtv-fileops.c Sat Jul 28 12:06:43 2007 +0200
@@ -888,12 +888,20 @@ int ivtv_v4l2_open(struct inode *inode,
return -EBUSY;
}
+ if (!test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+ if (atomic_read(&itv->capturing) > 0) {
+ /* switching to radio while capture is
+ in progress is not polite */
+ kfree(item);
+ return -EBUSY;
+ }
+ }
+ /* Mark that the radio is being used. */
+ set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
/* We have the radio */
ivtv_mute(itv);
/* Switch tuner to radio */
ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL);
- /* Mark that the radio is being used. */
- set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
/* Select the correct audio input (i.e. radio tuner) */
ivtv_audio_set_io(itv);
if (itv->hw_flags & IVTV_HW_SAA711X)
@@ -935,13 +943,8 @@ void ivtv_unmute(struct ivtv *itv)
{
struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 0 };
- /* initialize or refresh input */
- if (atomic_read(&itv->capturing) == 0)
- ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
-
- ivtv_msleep_timeout(100, 0);
-
if (atomic_read(&itv->capturing)) {
+ ivtv_msleep_timeout(100, 0);
ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
}
diff -r c392ac29add0 linux/drivers/media/video/ivtv/ivtv-ioctl.c
--- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c Wed Jul 25 23:02:03 2007 +0200
+++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c Sat Jul 28 12:06:43 2007 +0200
@@ -903,6 +903,9 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, s
if (inp == itv->active_input) {
IVTV_DEBUG_INFO("Input unchanged\n");
break;
+ }
+ if (atomic_read(&itv->capturing) > 0) {
+ return -EBUSY;
}
IVTV_DEBUG_INFO("Changing input from %d to %d\n",
itv->active_input, inp);
diff -r c392ac29add0 linux/drivers/media/video/ivtv/ivtv-streams.c
--- a/linux/drivers/media/video/ivtv/ivtv-streams.c Wed Jul 25 23:02:03 2007 +0200
+++ b/linux/drivers/media/video/ivtv/ivtv-streams.c Sat Jul 28 12:06:44 2007 +0200
@@ -554,9 +554,9 @@ int ivtv_start_v4l2_encode_stream(struct
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
/* Initialize Digitizer for Capture */
+ itv->video_dec_func(itv, VIDIOC_STREAMOFF, 0);
ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
-
- ivtv_msleep_timeout(100, 0);
+ itv->video_dec_func(itv, VIDIOC_STREAMON, 0);
}
/* begin_capture */
@@ -713,7 +713,6 @@ int ivtv_stop_v4l2_encode_stream(struct
int cap_type;
unsigned long then;
int stopmode;
- u32 data[CX2341X_MBOX_MAX_DATA];
if (s->v4l2dev == NULL)
return -EINVAL;
@@ -793,27 +792,9 @@ int ivtv_stop_v4l2_encode_stream(struct
}
then = jiffies;
- /* Make sure DMA is complete */
- add_wait_queue(&s->waitq, &wait);
- do {
- /* check if DMA is pending */
- if ((s->type == IVTV_ENC_STREAM_TYPE_MPG) && /* MPG Only */
- (read_reg(IVTV_REG_DMASTATUS) & 0x02)) {
- /* Check for last DMA */
- ivtv_vapi_result(itv, data, CX2341X_ENC_GET_SEQ_END, 2, 0, 0);
-
- if (data[0] == 1) {
- IVTV_DEBUG_DMA("%s: Last DMA of size 0x%08x\n", s->name, data[1]);
- break;
- }
- } else if (read_reg(IVTV_REG_DMASTATUS) & 0x02) {
- break;
- }
- } while (!ivtv_msleep_timeout(10, 1) &&
- then + msecs_to_jiffies(2000) > jiffies);
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&s->waitq, &wait);
+
+ /* Handle any pending interrupts */
+ ivtv_msleep_timeout(100, 1);
}
atomic_dec(&itv->capturing);
_______________________________________________
ivtv-devel mailing list
[email protected]
http://ivtvdriver.org/mailman/listinfo/ivtv-devel