V4L/DVB (7931): cx18: allow for simultaneous digital and analog capture

The HVR-1600 can do both analog and digital capture at the same time.
Due to a driver bug -EBUSY would be returned when attempting to setup an
analog capture while a digital capture was already in progress.

Separate the two internally.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
index 2bdac5e..87cf410 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -159,7 +159,7 @@
 {
 	if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
 		return -EINVAL;
-	if (atomic_read(&cx->capturing) > 0)
+	if (atomic_read(&cx->ana_capturing) > 0)
 		return -EBUSY;
 
 	/* First try to allocate sliced VBI buffers if needed. */
@@ -235,7 +235,7 @@
 		CX18_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
 		if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
 			struct cx2341x_mpeg_params p = cx->params;
-			int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->capturing), arg, cmd);
+			int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), arg, cmd);
 
 			if (err)
 				return err;
@@ -295,7 +295,7 @@
 		CX18_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
 		if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
 			return cx2341x_ext_ctrls(&cx->params,
-					atomic_read(&cx->capturing), arg, cmd);
+					atomic_read(&cx->ana_capturing), arg, cmd);
 		return -EINVAL;
 	}
 
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 0dd4e05..472f88e 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -889,7 +889,7 @@
 
 	/* Stop all captures */
 	CX18_DEBUG_INFO("Stopping all streams\n");
-	if (atomic_read(&cx->capturing) > 0)
+	if (atomic_read(&cx->tot_capturing) > 0)
 		cx18_stop_all_captures(cx);
 
 	/* Interrupts */
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index a2a6c58..9c6a534 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -380,7 +380,8 @@
 	int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
 	struct cx18_stream streams[CX18_MAX_STREAMS]; 	/* Stream data */
 	unsigned long i_flags;  /* global cx18 flags */
-	atomic_t capturing;	/* count number of active capture streams */
+	atomic_t ana_capturing;	/* count number of active analog capture streams */
+	atomic_t tot_capturing;	/* total count number of active capture streams */
 	spinlock_t lock;        /* lock access to this struct */
 	int search_pack_header;
 
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 0b3141d..d0d7888 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -318,7 +318,7 @@
 	size_t tot_written = 0;
 	int single_frame = 0;
 
-	if (atomic_read(&cx->capturing) == 0 && s->id == -1) {
+	if (atomic_read(&cx->ana_capturing) == 0 && s->id == -1) {
 		/* shouldn't happen */
 		CX18_DEBUG_WARN("Stream %s not initialized before read\n",
 				s->name);
@@ -581,7 +581,7 @@
 		cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
 		/* Select correct audio input (i.e. TV tuner or Line in) */
 		cx18_audio_set_io(cx);
-		if (atomic_read(&cx->capturing) > 0) {
+		if (atomic_read(&cx->ana_capturing) > 0) {
 			/* Undo video mute */
 			cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
 				cx->params.video_mute |
@@ -627,7 +627,7 @@
 		}
 
 		if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
-			if (atomic_read(&cx->capturing) > 0) {
+			if (atomic_read(&cx->ana_capturing) > 0) {
 				/* switching to radio while capture is
 				   in progress is not polite */
 				cx18_release_stream(s);
@@ -694,7 +694,7 @@
 
 void cx18_mute(struct cx18 *cx)
 {
-	if (atomic_read(&cx->capturing))
+	if (atomic_read(&cx->ana_capturing))
 		cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
 				cx18_find_handle(cx), 1);
 	CX18_DEBUG_INFO("Mute\n");
@@ -702,7 +702,7 @@
 
 void cx18_unmute(struct cx18 *cx)
 {
-	if (atomic_read(&cx->capturing)) {
+	if (atomic_read(&cx->ana_capturing)) {
 		cx18_msleep_timeout(100, 0);
 		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
 				cx18_find_handle(cx), 12);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index dbdcb86..4151f1e 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -247,7 +247,7 @@
 
 		if (!set_fmt || (cx->params.width == w && cx->params.height == h))
 			return 0;
-		if (atomic_read(&cx->capturing) > 0)
+		if (atomic_read(&cx->ana_capturing) > 0)
 			return -EBUSY;
 
 		cx->params.width = w;
@@ -264,7 +264,7 @@
 	if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
 		if (set_fmt && streamtype == CX18_ENC_STREAM_TYPE_VBI &&
 		    cx->vbi.sliced_in->service_set &&
-		    atomic_read(&cx->capturing) > 0)
+		    atomic_read(&cx->ana_capturing) > 0)
 			return -EBUSY;
 		if (set_fmt) {
 			cx->vbi.sliced_in->service_set = 0;
@@ -293,7 +293,7 @@
 		return 0;
 	if (set == 0)
 		return -EINVAL;
-	if (atomic_read(&cx->capturing) > 0 && cx->vbi.sliced_in->service_set == 0)
+	if (atomic_read(&cx->ana_capturing) > 0 && cx->vbi.sliced_in->service_set == 0)
 		return -EBUSY;
 	cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
 	memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
@@ -581,7 +581,7 @@
 			break;
 
 		if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
-		    atomic_read(&cx->capturing) > 0) {
+		    atomic_read(&cx->ana_capturing) > 0) {
 			/* Switching standard would turn off the radio or mess
 			   with already running streams, prevent that by
 			   returning EBUSY. */
@@ -677,7 +677,7 @@
 			enc->flags = 0;
 			if (try)
 				return 0;
-			if (!atomic_read(&cx->capturing))
+			if (!atomic_read(&cx->ana_capturing))
 				return -EPERM;
 			if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
 				return 0;
@@ -689,7 +689,7 @@
 			enc->flags = 0;
 			if (try)
 				return 0;
-			if (!atomic_read(&cx->capturing))
+			if (!atomic_read(&cx->ana_capturing))
 				return -EPERM;
 			if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
 				return 0;
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index dee6cc9..5a06586 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -444,7 +444,7 @@
 	s->handle = data[0];
 	cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
 
-	if (atomic_read(&cx->capturing) == 0 && !ts) {
+	if (atomic_read(&cx->ana_capturing) == 0 && !ts) {
 		/* Stuff from Windows, we don't know what it is */
 		cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
 		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
@@ -467,7 +467,7 @@
 		cx2341x_update(cx, cx18_api_func, NULL, &cx->params);
 	}
 
-	if (atomic_read(&cx->capturing) == 0) {
+	if (atomic_read(&cx->tot_capturing) == 0) {
 		clear_bit(CX18_F_I_EOS, &cx->i_flags);
 		write_reg(7, CX18_DSP0_INTERRUPT_MASK);
 	}
@@ -493,7 +493,9 @@
 	}
 
 	/* you're live! sit back and await interrupts :) */
-	atomic_inc(&cx->capturing);
+	if (!ts)
+		atomic_inc(&cx->ana_capturing);
+	atomic_inc(&cx->tot_capturing);
 	return 0;
 }
 
@@ -524,7 +526,7 @@
 
 	CX18_DEBUG_INFO("Stop Capture\n");
 
-	if (atomic_read(&cx->capturing) == 0)
+	if (atomic_read(&cx->tot_capturing) == 0)
 		return 0;
 
 	if (s->type == CX18_ENC_STREAM_TYPE_MPG)
@@ -538,7 +540,9 @@
 		CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
 	}
 
-	atomic_dec(&cx->capturing);
+	if (s->type != CX18_ENC_STREAM_TYPE_TS)
+		atomic_dec(&cx->ana_capturing);
+	atomic_dec(&cx->tot_capturing);
 
 	/* Clear capture and no-read bits */
 	clear_bit(CX18_F_S_STREAMING, &s->s_flags);
@@ -546,7 +550,7 @@
 	cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
 	s->handle = 0xffffffff;
 
-	if (atomic_read(&cx->capturing) > 0)
+	if (atomic_read(&cx->tot_capturing) > 0)
 		return 0;
 
 	write_reg(5, CX18_DSP0_INTERRUPT_MASK);