V4L/DVB (3584): Implement V4L2_TUNER_MODE_LANG1_LANG2 audio mode

Add a new audio mode V4L2_TUNER_MODE_LANG1_LANG2 (used by VIDIOC_G/S_TUNER).
This mode allows the user to select both languages of a bilingual transmission,
one language on the left, one on the right audio channel. If there is no
bilingual transmission, or it is not supported, then this mode should act like
V4L2_TUNER_MODE_STEREO.
This mode is introduced for PVR-like drivers where it is useful to be able to
record both languages of a bilingual broadcast.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
index 2f23cea..603a22e 100644
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -369,6 +369,11 @@
 			fm_matrix = 0x3001; // stereo
 			src = 0x0020;
 			break;
+		case V4L2_TUNER_MODE_LANG1_LANG2:
+			dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n");
+			fm_matrix = 0x3000; // bilingual
+			src = 0x0020;
+			break;
 		case V4L2_TUNER_MODE_LANG1:
 			dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n");
 			fm_matrix = 0x3000; // mono
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 80e4a74..74def9c 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -1878,7 +1878,8 @@
 			bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
 			if (t->audmode == V4L2_TUNER_MODE_MONO)
 				va.mode = VIDEO_SOUND_MONO;
-			else if (t->audmode == V4L2_TUNER_MODE_STEREO)
+			else if (t->audmode == V4L2_TUNER_MODE_STEREO ||
+				 t->audmode == V4L2_TUNER_MODE_LANG1_LANG2)
 				va.mode = VIDEO_SOUND_STEREO;
 			else if (t->audmode == V4L2_TUNER_MODE_LANG1)
 				va.mode = VIDEO_SOUND_LANG1;
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 7aee376..a65b3cc 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -810,13 +810,14 @@
 			   bilingual -> lang1 */
 			cx25840_and_or(client, 0x809, ~0xf, 0x00);
 			break;
+		case V4L2_TUNER_MODE_STEREO:
 		case V4L2_TUNER_MODE_LANG1:
 			/* mono      -> mono
 			   stereo    -> stereo
 			   bilingual -> lang1 */
 			cx25840_and_or(client, 0x809, ~0xf, 0x04);
 			break;
-		case V4L2_TUNER_MODE_STEREO:
+		case V4L2_TUNER_MODE_LANG1_LANG2:
 			/* mono      -> mono
 			   stereo    -> stereo
 			   bilingual -> lang1/lang2 */
@@ -824,7 +825,7 @@
 			break;
 		case V4L2_TUNER_MODE_LANG2:
 			/* mono      -> mono
-			   stereo    ->stereo
+			   stereo    -> stereo
 			   bilingual -> lang2 */
 			cx25840_and_or(client, 0x809, ~0xf, 0x01);
 			break;
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c
index da8d97c..641a0c5 100644
--- a/drivers/media/video/cx88/cx88-tvaudio.c
+++ b/drivers/media/video/cx88/cx88-tvaudio.c
@@ -885,6 +885,7 @@
 			set_audio_standard_BTSC(core, 1, EN_BTSC_FORCE_SAP);
 			break;
 		case V4L2_TUNER_MODE_STEREO:
+		case V4L2_TUNER_MODE_LANG1_LANG2:
 			set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_STEREO);
 			break;
 		}
@@ -905,6 +906,7 @@
 							 EN_NICAM_FORCE_MONO2);
 				break;
 			case V4L2_TUNER_MODE_STEREO:
+			case V4L2_TUNER_MODE_LANG1_LANG2:
 				set_audio_standard_NICAM(core,
 							 EN_NICAM_FORCE_STEREO);
 				break;
@@ -926,6 +928,7 @@
 							      EN_A2_FORCE_MONO2);
 					break;
 				case V4L2_TUNER_MODE_STEREO:
+				case V4L2_TUNER_MODE_LANG1_LANG2:
 					set_audio_standard_A2(core,
 							      EN_A2_FORCE_STEREO);
 					break;
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c
index 1c794c3..c3984ea 100644
--- a/drivers/media/video/msp3400-kthreads.c
+++ b/drivers/media/video/msp3400-kthreads.c
@@ -223,9 +223,9 @@
    nor do they support stereo BTSC. */
 static void msp3400c_set_audmode(struct i2c_client *client)
 {
-	static char *strmode[] = { "mono", "stereo", "lang2", "lang1" };
+	static char *strmode[] = { "mono", "stereo", "lang2", "lang1", "lang1+lang2" };
 	struct msp_state *state = i2c_get_clientdata(client);
-	char *modestr = (state->audmode >= 0 && state->audmode < 4) ?
+	char *modestr = (state->audmode >= 0 && state->audmode < 5) ?
 		strmode[state->audmode] : "unknown";
 	int src = 0;	/* channel source: FM/AM, nicam or SCART */
 
@@ -250,6 +250,7 @@
 		case V4L2_TUNER_MODE_MONO:
 		case V4L2_TUNER_MODE_LANG1:
 		case V4L2_TUNER_MODE_LANG2:
+		case V4L2_TUNER_MODE_LANG1_LANG2:
 			msp_write_dsp(client, 0x000e, 0x3000);
 			break;
 		}
@@ -261,6 +262,7 @@
 			msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5));
 			break;
 		case V4L2_TUNER_MODE_STEREO:
+		case V4L2_TUNER_MODE_LANG1_LANG2:
 			msp3400c_set_carrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02));
 			break;
 		case V4L2_TUNER_MODE_LANG1:
@@ -296,6 +298,7 @@
 	/* switch audio */
 	switch (state->audmode) {
 	case V4L2_TUNER_MODE_STEREO:
+	case V4L2_TUNER_MODE_LANG1_LANG2:
 		src |= 0x0020;
 		break;
 	case V4L2_TUNER_MODE_MONO:
@@ -835,6 +838,7 @@
 		matrix = 0x10;
 		break;
 	case V4L2_TUNER_MODE_STEREO:
+	case V4L2_TUNER_MODE_LANG1_LANG2:
 	default:
 		source = 1; /* stereo or A|B */
 		matrix = 0x20;
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index 14ca251..b0aea40 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -790,6 +790,12 @@
 				DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n"));
 				break;
 			}
+			case V4L2_TUNER_MODE_LANG1_LANG2: {
+				mxb->cur_mode = V4L2_TUNER_MODE_LANG1_LANG2;
+				byte = TDA9840_SET_BOTH;
+				DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n"));
+				break;
+			}
 			case V4L2_TUNER_MODE_LANG1: {
 				mxb->cur_mode = V4L2_TUNER_MODE_LANG1;
 				byte = TDA9840_SET_LANG1;
diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c
index 3043233..0db53d1 100644
--- a/drivers/media/video/saa7134/saa7134-tvaudio.c
+++ b/drivers/media/video/saa7134/saa7134-tvaudio.c
@@ -482,12 +482,14 @@
 		[ V4L2_TUNER_MODE_STEREO ] = "stereo",
 		[ V4L2_TUNER_MODE_LANG1  ] = "lang1",
 		[ V4L2_TUNER_MODE_LANG2  ] = "lang2",
+		[ V4L2_TUNER_MODE_LANG1_LANG2  ] = "lang1+lang2",
 	};
 	static u32 fm[] = {
 		[ V4L2_TUNER_MODE_MONO   ] = 0x00,  /* ch1  */
 		[ V4L2_TUNER_MODE_STEREO ] = 0x80,  /* auto */
 		[ V4L2_TUNER_MODE_LANG1  ] = 0x00,  /* ch1  */
 		[ V4L2_TUNER_MODE_LANG2  ] = 0x01,  /* ch2  */
+		[ V4L2_TUNER_MODE_LANG1_LANG2 ] = 0x80,  /* auto */
 	};
 	u32 reg;
 
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index 4e6d030..356bff4 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -1733,6 +1733,7 @@
 			mode = VIDEO_SOUND_MONO;
 			break;
 		case V4L2_TUNER_MODE_STEREO:
+		case V4L2_TUNER_MODE_LANG1_LANG2:
 			mode = VIDEO_SOUND_STEREO;
 			break;
 		case V4L2_TUNER_MODE_LANG1:
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 724cfbf..2275bfe 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -883,6 +883,7 @@
 #define V4L2_TUNER_MODE_LANG2		0x0002
 #define V4L2_TUNER_MODE_SAP		0x0002
 #define V4L2_TUNER_MODE_LANG1		0x0003
+#define V4L2_TUNER_MODE_LANG1_LANG2	0x0004
 
 struct v4l2_frequency
 {