cx25840: improve audio for cx2388x drivers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Searching for testers, this patch is big one, it was more then week of
work and testing, so i appriciate any comments and recommendations.
Signed-off-by: Miroslav Slugen <thunder.mmm@xxxxxxxxx>
From: Miroslav Slugen <thunder.mmm@xxxxxxxxx>
Date: Mon, 12 Dec 2011 00:19:34 +0100
Subject: [PATCH] This patch should fix many analog audio issues for cx23885 based cards.

1. Rewrite part of set_input to cx25840_audio_set_path, this part should be
 set only when soft reset is asserted and microcontroler is stopped.
 
2. While using AUDIO6 and AUDIO7 inputs we should always stop microcontroler,
 because they are not tuner inputs, this should fix no-audio for composite
 s-video and componnet input on many types of tuners.

3. Add radio_deemphasis support, in Europe we use 50us, with 75us sound is not so cool.

4. Add audio_standard_force support. On many types of cards autodetection of
 audio standard just doesn't work, my research in this shows that it could be
 just in wrong registers settings or in detection algorithm, so if we want to
 have audio on such broken tuners we have to force audio standard, this is done
 in input_change and should not introduce any regression, i tested this behavior
 on card with working autodetection Leadtek DVR3200H_xc4000 and with not working
 DVR3200H_xc3028, first card has audio even with previouse code, but xc3028
 (older model) has no audio, to get audio support it require to set AUD_STANDARD
 register, of course forced mode is available in driver level (pvr150_workaround)
 or as option to module.
 
5. Improve format detection as writen in cx25840 datasheet, this is done in
 cx23885_initialize and in input_change functions.
 In init it is necesary to reset tuner autodetection and tuner.
 In input change i just add others formats from datasheet, previouse code was
 just for NTSC detection, and it is also necesary to reset microcontroler, but
 only if it is running.

---
diff -Naurp a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
--- a/drivers/media/video/cx25840/cx25840-audio.c	2012-01-05 00:55:44.000000000 +0100
+++ b/drivers/media/video/cx25840/cx25840-audio.c	2012-01-14 16:05:38.616366747 +0100
@@ -446,14 +446,45 @@ void cx25840_audio_set_path(struct i2c_c
 
 		/* Mute everything to prevent the PFFT! */
 		cx25840_write(client, 0x8d3, 0x1f);
+		cx25840_write(client, 0x8e3, 0x03);
 
-		if (state->aud_input == CX25840_AUDIO_SERIAL) {
+		if (is_cx231xx(state) || is_cx2388x(state)) {
+			/* Path1 require different GPIO0 pin mux */
+			if ((state->aud_input == CX25840_AUDIO6) ||
+			    (state->aud_input == CX25840_AUDIO7)) {
+				cx25840_write(client, 0x124, 0x00);
+			} else {
+				/* Audio channel 1 src : Parallel 1 */
+				cx25840_write(client, 0x124, 0x03);
+			}
+
+			/* Select AFE clock pad output source */
+			if (is_cx2388x(state))
+				cx25840_write(client, 0x144, 0x05);
+
+			/* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
+			cx25840_write(client, 0x914, 0xa0);
+
+			/* I2S_OUT_CTL:
+			 * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
+			 * I2S_OUT_MASTER_MODE = Master
+			 */
+			cx25840_write(client, 0x918, 0xa0);
+			cx25840_write(client, 0x919, 0x01);
+
+			if ((state->aud_input == CX25840_AUDIO6) ||
+			    (state->aud_input == CX25840_AUDIO7)) {
+				cx25840_write4(client, 0x910, 0);
+				cx25840_write4(client, 0x8d0, 0x00063073);
+				/* Reset path1 volume control */
+				cx25840_write4(client, 0x8d4, 0x7fff0024);
+			} else { /* default for all sources, not only AUDIO8 */
+				cx25840_write4(client, 0x910, 0x12b000c9);
+				cx25840_write4(client, 0x8d0, 0x1f063870);
+			}
+		} else if (state->aud_input == CX25840_AUDIO_SERIAL) {
 			/* Set Path1 to Serial Audio Input */
 			cx25840_write4(client, 0x8d0, 0x01011012);
-
-			/* The microcontroller should not be started for the
-			 * non-tuner inputs: autodetection is specific for
-			 * TV audio. */
 		} else {
 			/* Set Path1 to Analog Demod Main Channel */
 			cx25840_write4(client, 0x8d0, 0x1f063870);
@@ -463,7 +494,12 @@ void cx25840_audio_set_path(struct i2c_c
 	set_audclk_freq(client, state->audclk_freq);
 
 	if (!is_cx2583x(state)) {
-		if (state->aud_input != CX25840_AUDIO_SERIAL) {
+		/* The microcontroller should not be started for the
+		 * non-tuner inputs: autodetection is specific for
+		 * TV audio. */
+		if ((state->aud_input != CX25840_AUDIO_SERIAL) &&
+		    (state->aud_input != CX25840_AUDIO6) &&
+		    (state->aud_input != CX25840_AUDIO7)) {
 			/* When the microcontroller detects the
 			 * audio format, it will unmute the lines */
 			cx25840_and_or(client, 0x803, ~0x10, 0x10);
@@ -471,10 +507,6 @@ void cx25840_audio_set_path(struct i2c_c
 
 		/* deassert soft reset */
 		cx25840_and_or(client, 0x810, ~0x1, 0x00);
-
-		/* Ensure the controller is running when we exit */
-		if (is_cx2388x(state) || is_cx231xx(state))
-			cx25840_and_or(client, 0x803, ~0x10, 0x10);
 	}
 }
 
diff -Naurp a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
--- a/drivers/media/video/cx25840/cx25840-core.c	2012-01-14 18:02:41.968366747 +0100
+++ b/drivers/media/video/cx25840/cx25840-core.c	2012-01-14 18:03:33.024366746 +0100
@@ -73,11 +73,18 @@ MODULE_LICENSE("GPL");
 #define CX25840_IR_IRQEN_REG	0x214
 
 static int cx25840_debug;
-
-module_param_named(debug,cx25840_debug, int, 0644);
-
+module_param_named(debug, cx25840_debug, int, 0644);
 MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]");
 
+static unsigned int radio_deemphasis = 0;
+module_param(radio_deemphasis, int, 0644);
+MODULE_PARM_DESC(radio_deemphasis, "Radio deemphasis time constant "
+				   "[0=75us (USA) 1=50us (elsewhere)]");
+
+static unsigned int audio_standard_force = 0;
+module_param(audio_standard_force, int, 0644);
+MODULE_PARM_DESC(audio_standard_force, "Force audio standard for tuners with broken"
+				       "microcontroler autodetection [0=Off (default) 1=TV 2=TV+FM]");
 
 /* ----------------------------------------------------------------------- */
 
@@ -474,7 +481,7 @@ static void cx23885_initialize(struct i2
 	cx25840_and_or(client, 0x102, ~0x01, 0x01);
 	cx25840_and_or(client, 0x102, ~0x01, 0x00);
 
-	/* Stop microcontroller */
+	/* 2. Stop microcontroller */
 	cx25840_and_or(client, 0x803, ~0x10, 0x00);
 
 	/* DIF in reset? */
@@ -536,12 +543,6 @@ static void cx23885_initialize(struct i2
 	cx25840_write4(client, 0x10c, 0x002be2c9);
 	cx25840_write4(client, 0x108, 0x0000040f);
 
-	/* Luma */
-	cx25840_write4(client, 0x414, 0x00107d12);
-
-	/* Chroma */
-	cx25840_write4(client, 0x420, 0x3d008282);
-
 	/*
 	 * Aux PLL
 	 * Initial setup for audio sample clock:
@@ -586,12 +587,6 @@ static void cx23885_initialize(struct i2
 	/* VIN1 & VIN5 */
 	cx25840_write(client, 0x103, 0x11);
 
-	/* Enable format auto detect */
-	cx25840_write(client, 0x400, 0);
-	/* Fast subchroma lock */
-	/* White crush, Chroma AGC & Chroma Killer enabled */
-	cx25840_write(client, 0x401, 0xe8);
-
 	/* Select AFE clock pad output source */
 	cx25840_write(client, 0x144, 0x05);
 
@@ -604,7 +599,14 @@ static void cx23885_initialize(struct i2
 	cx25840_write(client, 0x160, 0x1d);
 	cx25840_write(client, 0x164, 0x00);
 
-	/* Do the firmware load in a work handler to prevent.
+	/* Enable format auto detect */
+	cx25840_write(client, 0x400, 0);
+
+	/* 4. Reset tuner autodetection */
+	cx25840_and_or(client, 0x13c, ~0x01, 0x01);
+	cx25840_and_or(client, 0x13c, ~0x01, 0x00);
+
+	/* 5. Do the firmware load in a work handler to prevent.
 	   Otherwise the kernel is blocked waiting for the
 	   bit-banging i2c interface to finish uploading the
 	   firmware. */
@@ -617,13 +619,32 @@ static void cx23885_initialize(struct i2
 	finish_wait(&state->fw_wait, &wait);
 	destroy_workqueue(q);
 
+	/* 7. Reset and initialize video decoder */
+	cx25840_write4(client, 0x4a4, 0x8000);
+	cx25840_write4(client, 0x4a4, 0);
+	cx25840_write(client, 0x402, 0x00);
+
+	/* 8. White crush, Chroma AGC & Chroma Killer enabled.
+	 * From datasheet and spoted from Leadtek drivers we
+	 * should not set Fast Lock and Auto Chroma Lock Speed.
+	 */
+	cx25840_write(client, 0x401, 0xe0);
+
+	/* Luma */
+	cx25840_write4(client, 0x414, 0x00107d12);
+
+	/* Chroma */
+	cx25840_write4(client, 0x420, 0x3d008282);
+
 	cx25840_std_setup(client);
 
 	/* (re)set input */
 	set_input(client, state->vid_input, state->aud_input);
 
-	/* start microcontroller */
-	cx25840_and_or(client, 0x803, ~0x10, 0x10);
+	/* start microcontroller only for tuner inputs */
+	if ((state->aud_input != CX25840_AUDIO6) &&
+	    (state->aud_input != CX25840_AUDIO7))
+		cx25840_and_or(client, 0x803, ~0x10, 0x10);
 
 	/* Disable and clear video interrupts - we don't use them */
 	cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff);
@@ -678,6 +699,7 @@ static void cx231xx_initialize(struct i2
 
 	/* Enable format auto detect */
 	cx25840_write(client, 0x400, 0);
+
 	/* Fast subchroma lock */
 	/* White crush, Chroma AGC & Chroma Killer enabled */
 	cx25840_write(client, 0x401, 0xe8);
@@ -866,72 +888,112 @@ static void input_change(struct i2c_clie
 {
 	struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 	v4l2_std_id std = state->std;
+	u8 fmt = cx25840_read(client, 0x40D);
+	int hw_fix = (audio_standard_force > state->pvr150_workaround) ?
+			audio_standard_force : state->pvr150_workaround;
 
 	/* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */
 	if (std & V4L2_STD_SECAM) {
 		cx25840_write(client, 0x402, 0);
-	}
-	else {
+	} else {
 		cx25840_write(client, 0x402, 0x04);
 		cx25840_write(client, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
 	}
-	cx25840_and_or(client, 0x401, ~0x60, 0);
-	cx25840_and_or(client, 0x401, ~0x60, 0x60);
+
+	/* Step 9. Improve format detection */
+	if (((fmt & 0x0f) > 0x3) ||
+	    ((fmt & 0x0f) < 0x9)) {
+		/* Switch to NTSC and back */
+		u8 val = cx25840_read(client, 0x400);
+		cx25840_and_or(client, 0x400, ~0x0f, 0x01);
+		/* Disable LCOMB_3LN_EN and LCOMB_2LN_EN */
+		cx25840_and_or(client, 0x47b, ~0x06, 0);
+		cx25840_and_or(client, 0x400, ~0x0f, (val & 0x0f));
+
+		/* Toggle CAGCEN and CKILLEN */
+		cx25840_and_or(client, 0x401, ~0x60, 0);
+		cx25840_and_or(client, 0x401, ~0x60, 0x60);
+	} else if ((fmt & 0x0f) == 0x9) {
+		/* Toggle CKILLEN */
+		cx25840_and_or(client, 0x401, ~0x20, 0);
+		cx25840_and_or(client, 0x401, ~0x20, 0x20);
+		/* Standard autodetection */
+		cx25840_and_or(client, 0x400, ~0x0f, 0x00);
+	} else {
+		/* Toggle CAGCEN and CKILLEN */
+		cx25840_and_or(client, 0x401, ~0x60, 0);
+		cx25840_and_or(client, 0x401, ~0x60, 0x60);
+	}
 
 	/* Don't write into audio registers on cx2583x chips */
 	if (is_cx2583x(state))
 		return;
 
+	/* Toggle microcontroller only when running */
+	if (cx25840_read(client, 0x803) & 0x10) {
+		cx25840_and_or(client, 0x803, ~0x10, 0x00);
+		msleep(1);
+		cx25840_and_or(client, 0x803, ~0x10, 0x10);
+	}
+
 	cx25840_and_or(client, 0x810, ~0x01, 1);
 
+	/* Use default audio format control */
+	cx25840_write(client, 0x80b, 0);
+
 	if (state->radio) {
-		cx25840_write(client, 0x808, 0xf9);
-		cx25840_write(client, 0x80b, 0x00);
-	}
-	else if (std & V4L2_STD_525_60) {
-		/* Certain Hauppauge PVR150 models have a hardware bug
-		   that causes audio to drop out. For these models the
-		   audio standard must be set explicitly.
-		   To be precise: it affects cards with tuner models
-		   85, 99 and 112 (model numbers from tveeprom). */
-		int hw_fix = state->pvr150_workaround;
-
-		if (std == V4L2_STD_NTSC_M_JP) {
-			/* Japan uses EIAJ audio standard */
-			cx25840_write(client, 0x808, hw_fix ? 0x2f : 0xf7);
-		} else if (std == V4L2_STD_NTSC_M_KR) {
-			/* South Korea uses A2 audio standard */
-			cx25840_write(client, 0x808, hw_fix ? 0x3f : 0xf8);
-		} else {
-			/* Others use the BTSC audio standard */
-			cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6);
-		}
-		cx25840_write(client, 0x80b, 0x00);
-	} else if (std & V4L2_STD_PAL) {
-		/* Autodetect audio standard and audio system */
+		/* Disable fm_deviation, set deemphasis, don't mute and prefer stereo */
+		cx25840_write(client, 0x809, radio_deemphasis ? 0x24 : 0x04);
+		/* For FM mode hw_fix is not necessary */
+		cx25840_write(client, 0x808, (hw_fix > 1) ? 0xef : 0xf9);
+	} else if (std == V4L2_STD_NTSC_M_JP) {
+		/* Japan uses EIAJ audio standard */
+		cx25840_write(client, 0x808, hw_fix ? 0x2f : 0xf7);
+	} else if (std == V4L2_STD_NTSC_M_KR) {
+		/* South Korea uses A2 audio standard */
+		cx25840_write(client, 0x808, hw_fix ? 0x3f : 0xf8);
+	} else if (std & (V4L2_STD_525_60 | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
+		/* NTSC, PAL-60, PAL-M and PAL-N uses BTSC audio standard */
+		cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6);
+	} else if ((std & (V4L2_STD_PAL_BG | V4L2_STD_PAL_H)) &&
+		   !(std & V4L2_STD_PAL_DK) &&
+		   !(std & V4L2_STD_PAL_I)) {
+		/* A2-BG */
+		cx25840_write(client, 0x808, hw_fix ? 0x4f : 0xf0);
+	} else if (!(std & (V4L2_STD_PAL_BG | V4L2_STD_PAL_H)) &&
+		    (std & V4L2_STD_PAL_DK) &&
+		   !(std & V4L2_STD_PAL_I)) {
+		/* A2-DK1 */
+		cx25840_write(client, 0x808, hw_fix ? 0x5f : 0xf1);
+	} else if (!(std & (V4L2_STD_PAL_BG | V4L2_STD_PAL_H)) &&
+		   !(std & V4L2_STD_PAL_DK) &&
+		    (std & V4L2_STD_PAL_I)) {
+		/* A1 */
+		cx25840_write(client, 0x808, hw_fix ? 0x8f : 0xf4);
+	} else if ((std & (V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H)) &&
+		   !(std & V4L2_STD_SECAM_DK) &&
+		   !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
+		/* NICAM-BG */
+		cx25840_write(client, 0x808, hw_fix ? 0xaf : 0xf0);
+	} else if (!(std & (V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H)) &&
+		    (std & V4L2_STD_SECAM_DK) &&
+		   !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
+		/* NICAM-DK */
+		cx25840_write(client, 0x808, hw_fix ? 0xbf : 0xf1);
+	} else if (!(std & (V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H)) &&
+		   !(std & V4L2_STD_SECAM_DK) &&
+		    (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
+		/* 6.5 MHz carrier to be interpreted as System L */
+		cx25840_write(client, 0x80b, 0x08);
+		/* NICAM-L */
+		cx25840_write(client, 0x808, hw_fix ? 0xdf : 0xf5);
+        } else if (std & V4L2_STD_SECAM) {
+		/* 6.5 MHz carrier to be autodetected */
+		cx25840_write(client, 0x80b, 0x10);
 		cx25840_write(client, 0x808, 0xff);
-		/* Since system PAL-L is pretty much non-existent and
-		   not used by any public broadcast network, force
-		   6.5 MHz carrier to be interpreted as System DK,
-		   this avoids DK audio detection instability */
-	       cx25840_write(client, 0x80b, 0x00);
-	} else if (std & V4L2_STD_SECAM) {
-		/* Autodetect audio standard and audio system */
+	} else {
+		/* Audio standard autodetection - not working on some cards */
 		cx25840_write(client, 0x808, 0xff);
-		/* If only one of SECAM-DK / SECAM-L is required, then force
-		  6.5MHz carrier, else autodetect it */
-		if ((std & V4L2_STD_SECAM_DK) &&
-		    !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
-			/* 6.5 MHz carrier to be interpreted as System DK */
-			cx25840_write(client, 0x80b, 0x00);
-	       } else if (!(std & V4L2_STD_SECAM_DK) &&
-			  (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
-			/* 6.5 MHz carrier to be interpreted as System L */
-			cx25840_write(client, 0x80b, 0x08);
-	       } else {
-			/* 6.5 MHz carrier to be autodetected */
-			cx25840_write(client, 0x80b, 0x10);
-	       }
 	}
 
 	cx25840_and_or(client, 0x810, ~0x01, 0);
@@ -950,7 +1012,7 @@ static int set_input(struct i2c_client *
 	u8 reg;
 
 	v4l_dbg(1, cx25840_debug, client,
-		"decoder set video input %d, audio input %d\n",
+		"decoder set video input 0x%x, audio input 0x%x\n",
 		vid_input, aud_input);
 
 	if (vid_input >= CX25840_VIN1_CH1) {
@@ -1043,49 +1105,6 @@ static int set_input(struct i2c_client *
 	cx25840_audio_set_path(client);
 	input_change(client);
 
-	if (is_cx2388x(state)) {
-		/* Audio channel 1 src : Parallel 1 */
-		cx25840_write(client, 0x124, 0x03);
-
-		/* Select AFE clock pad output source */
-		cx25840_write(client, 0x144, 0x05);
-
-		/* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
-		cx25840_write(client, 0x914, 0xa0);
-
-		/* I2S_OUT_CTL:
-		 * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
-		 * I2S_OUT_MASTER_MODE = Master
-		 */
-		cx25840_write(client, 0x918, 0xa0);
-		cx25840_write(client, 0x919, 0x01);
-	} else if (is_cx231xx(state)) {
-		/* Audio channel 1 src : Parallel 1 */
-		cx25840_write(client, 0x124, 0x03);
-
-		/* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
-		cx25840_write(client, 0x914, 0xa0);
-
-		/* I2S_OUT_CTL:
-		 * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
-		 * I2S_OUT_MASTER_MODE = Master
-		 */
-		cx25840_write(client, 0x918, 0xa0);
-		cx25840_write(client, 0x919, 0x01);
-	}
-
-	if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) ||
-		(aud_input == CX25840_AUDIO6))) {
-		/* Configure audio from LR1 or LR2 input */
-		cx25840_write4(client, 0x910, 0);
-		cx25840_write4(client, 0x8d0, 0x63073);
-	} else
-	if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) {
-		/* Configure audio from tuner/sif input */
-		cx25840_write4(client, 0x910, 0x12b000c9);
-		cx25840_write4(client, 0x8d0, 0x1f063870);
-	}
-
 	return 0;
 }
 

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux