V4L/DVB (7163): em28xx: makes audio settings more stable
[deliverable/linux.git] / drivers / media / video / em28xx / em28xx-core.c
index d56484f204677105c9fd435fdeb7bf8aca30ed75..275f1e936304fd151087f9bbc590a3a54f951cc6 100644 (file)
@@ -150,7 +150,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
        if (reg_debug){
                printk(ret < 0 ? " failed!\n" : "%02x values: ", ret);
                for (byte = 0; byte < len; byte++) {
-                       printk(" %02x", buf[byte]);
+                       printk(" %02x", (unsigned char)buf[byte]);
                }
                printk("\n");
        }
@@ -177,7 +177,8 @@ int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg)
                              0x0000, reg, &val, 1, HZ);
 
        if (reg_debug)
-               printk(ret < 0 ? " failed!\n" : "%02x\n", val);
+               printk(ret < 0 ? " failed!\n" :
+                                "%02x\n", (unsigned char) val);
 
        if (ret < 0)
                return ret;
@@ -237,7 +238,7 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
  * sets only some bits (specified by bitmask) of a register, by first reading
  * the actual value
  */
-int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
+static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
                                 u8 bitmask)
 {
        int oldval;
@@ -252,32 +253,122 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
  * em28xx_write_ac97()
  * write a 16 bit value to the specified AC97 address (LSB first!)
  */
-int em28xx_write_ac97(struct em28xx *dev, u8 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;
 }
 
+static int em28xx_set_audio_source(struct em28xx *dev)
+{
+       static char *enable  = "\x08\x08";
+       static char *disable = "\x08\x88";
+       char *video = enable, *line = disable;
+       int ret;
+       u8 input;
+
+       if (dev->is_em2800) {
+               if (dev->ctl_ainput)
+                       input = EM2800_AUDIO_SRC_LINE;
+               else
+                       input = EM2800_AUDIO_SRC_TUNER;
+
+               ret = em28xx_write_regs(dev, EM2800_AUDIOSRC_REG, &input, 1);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (dev->has_msp34xx)
+               input = EM28XX_AUDIO_SRC_TUNER;
+       else {
+               switch (dev->ctl_ainput) {
+               case EM28XX_AMUX_VIDEO:
+                       input = EM28XX_AUDIO_SRC_TUNER;
+                       break;
+               case EM28XX_AMUX_LINE_IN:
+                       input = EM28XX_AUDIO_SRC_LINE;
+                       break;
+               case EM28XX_AMUX_AC97_VIDEO:
+                       input = EM28XX_AUDIO_SRC_LINE;
+                       break;
+               case EM28XX_AMUX_AC97_LINE_IN:
+                       input = EM28XX_AUDIO_SRC_LINE;
+                       video = disable;
+                       line  = enable;
+                       break;
+               }
+       }
+
+       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
+        */
+       ret = em28xx_write_ac97(dev, VIDEO_AC97, video);
+       if (ret < 0)
+               return ret;
+
+       ret = em28xx_write_ac97(dev, LINE_IN_AC97, line);
+
+       return ret;
+}
+
 int em28xx_audio_analog_set(struct em28xx *dev)
 {
+       int ret;
        char s[2] = { 0x00, 0x00 };
+       u8 xclk = 0x07;
+
        s[0] |= 0x1f - dev->volume;
        s[1] |= 0x1f - dev->volume;
-       if (dev->mute)
-               s[1] |= 0x80;
-       return em28xx_write_ac97(dev, MASTER_AC97, s);
-}
 
+       /* Mute */
+       s[1] |= 0x80;
+       ret = em28xx_write_ac97(dev, MASTER_AC97, s);
+
+       if (ret < 0)
+               return ret;
+
+       if (dev->has_12mhz_i2s)
+               xclk |= 0x20;
+
+       if (!dev->mute)
+               xclk |= 0x80;
+
+       ret = em28xx_write_reg_bits(dev, XCLK_REG, xclk, 0xa7);
+       if (ret < 0)
+               return ret;
+       msleep(10);
+
+       /* 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);
 
 int em28xx_colorlevels_set_default(struct em28xx *dev)
 {
This page took 0.026879 seconds and 5 git commands to generate.