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

Reply via email to