From 00b8730f5db19f9ea0985d7f14f869df79a0bf76 Mon Sep 17 00:00:00 2001
From: Mauro Carvalho Chehab <mchehab@infradead.org>
Date: Wed, 6 Feb 2008 18:34:13 -0300
Subject: [PATCH] V4L/DVB (7163): em28xx: makes audio settings more stable

Improves audio configurations on em28xx:
        - mutes audio before changing amux;
        - adds a delay after setting audio src;
        - waits up to 50ms for ac97 busy.

Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
---
 drivers/media/video/em28xx/em28xx-core.c  | 28 ++++++++++++++++-------
 drivers/media/video/em28xx/em28xx-video.c |  3 +--
 2 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 6965bafe54a1..275f1e936304 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -255,21 +255,26 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
  */
 static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val)
 {
-	int ret;
+	int ret, i;
 	u8 addr = reg & 0x7f;
 	if ((ret = em28xx_write_regs(dev, AC97LSB_REG, val, 2)) < 0)
 		return ret;
 	if ((ret = em28xx_write_regs(dev, AC97ADDR_REG, &addr, 1)) < 0)
 		return ret;
-	if ((ret = em28xx_read_reg(dev, AC97BUSY_REG)) < 0)
-		return ret;
-	else if (((u8) ret) & 0x01) {
-		em28xx_warn ("AC97 command still being executed: not handled properly!\n");
+
+	/* Wait up to 50 ms for AC97 command to complete */
+	for (i = 0; i < 10; i++) {
+		if ((ret = em28xx_read_reg(dev, AC97BUSY_REG)) < 0)
+			return ret;
+		if (!((u8) ret) & 0x01)
+			return 0;
+		msleep(5);
 	}
+	em28xx_warn ("AC97 command still being executed: not handled properly!\n");
 	return 0;
 }
 
-int em28xx_set_audio_source(struct em28xx *dev)
+static int em28xx_set_audio_source(struct em28xx *dev)
 {
 	static char *enable  = "\x08\x08";
 	static char *disable = "\x08\x88";
@@ -312,6 +317,7 @@ int em28xx_set_audio_source(struct em28xx *dev)
 	ret = em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0);
 	if (ret < 0)
 		return ret;
+	msleep(5);
 
 	/* Sets AC97 mixer registers
 	   This is seems to be needed, even for non-ac97 configs
@@ -334,9 +340,10 @@ int em28xx_audio_analog_set(struct em28xx *dev)
 	s[0] |= 0x1f - dev->volume;
 	s[1] |= 0x1f - dev->volume;
 
-	if (dev->mute)
-		s[1] |= 0x80;
+	/* Mute */
+	s[1] |= 0x80;
 	ret = em28xx_write_ac97(dev, MASTER_AC97, s);
+
 	if (ret < 0)
 		return ret;
 
@@ -354,6 +361,11 @@ int em28xx_audio_analog_set(struct em28xx *dev)
 	/* Selects the proper audio input */
 	ret = em28xx_set_audio_source(dev);
 
+	/* Unmute device */
+	if (!dev->mute)
+		s[1] &= ~0x80;
+	ret = em28xx_write_ac97(dev, MASTER_AC97, s);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(em28xx_audio_analog_set);
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index f69f591eeaf6..eeda3b2faec8 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -189,7 +189,7 @@ static void video_mux(struct em28xx *dev, int index)
 		em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route);
 	}
 
-	em28xx_set_audio_source(dev);
+	em28xx_audio_analog_set(dev);
 }
 
 /* Usage lock check functions */
@@ -837,7 +837,6 @@ static int em28xx_reg_len(int reg)
 	case AC97LSB_REG:
 	case HSCALELOW_REG:
 	case VSCALELOW_REG:
-
 		return 2;
 	default:
 		return 1;
-- 
2.34.1