V4L/DVB (9653): em28xx: improve AC97 handling
authorMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 20 Nov 2008 12:06:09 +0000 (09:06 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 29 Dec 2008 19:53:35 +0000 (17:53 -0200)
AC97 devices provide several input and outputs. However, before this
patch, em28xx device weren't properly allowing the usage of ac97
possible combinations. Also, several input volumes were left untouched,
instead of making sure that the volumes were set on mute state.

This patch improves support for ac97 devices by allowing to use any
inputs, and making sure that unused inputs are set on mute state.

Yet, some work is still needed to select the AC97 output.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/em28xx/em28xx-core.c
drivers/media/video/em28xx/em28xx-reg.h
drivers/media/video/em28xx/em28xx.h

index b0a238421f2bb2d571c6a8057d394dcab50cc360..1cf5b443092cc07463f6df95d18d16e91afa6178 100644 (file)
@@ -298,29 +298,44 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val)
        return 0;
 }
 
-static int set_ac97_em202_input(struct em28xx *dev)
+struct em28xx_input_table {
+       enum em28xx_amux amux;
+       u8               reg;
+};
+
+static struct em28xx_input_table inputs[] = {
+       { EM28XX_AMUX_VIDEO,    AC97_VIDEO_VOL   },
+       { EM28XX_AMUX_LINE_IN,  AC97_LINEIN_VOL  },
+       { EM28XX_AMUX_PHONE,    AC97_PHONE_VOL   },
+       { EM28XX_AMUX_MIC,      AC97_MIC_VOL     },
+       { EM28XX_AMUX_CD,       AC97_CD_VOL      },
+       { EM28XX_AMUX_AUX,      AC97_AUX_VOL     },
+       { EM28XX_AMUX_PCM_OUT,  AC97_PCM_OUT_VOL },
+};
+
+static int set_ac97_input(struct em28xx *dev)
 {
-       int ret;
-       u16 enable  = 0x0808;           /* 12 dB attenuation Left/Right */
-       u16 disable = 0x8808;           /* bit 15 - mute volumme */
-       u16 video, line;
-
-       if (dev->ctl_ainput == EM28XX_AMUX_VIDEO) {
-               video = enable;
-               line = disable;
-       } else {
-               video = disable;
-               line  = enable;
-       }
+       int ret, i;
+       enum em28xx_amux amux = dev->ctl_ainput;
 
-       /* Sets em202 AC97 mixer registers */
-       ret = em28xx_write_ac97(dev, AC97_VIDEO_VOL, video);
-       if (ret < 0)
-               return ret;
+       /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that
+          em28xx should point to LINE IN, while AC97 should use VIDEO
+        */
+       if (amux == EM28XX_AMUX_VIDEO2)
+               amux = dev->ctl_ainput;
 
-       ret = em28xx_write_ac97(dev, AC97_LINEIN_VOL, line);
+       /* Mute all entres but the one that were selected */
+       for (i = 0; i < ARRAY_SIZE(inputs); i++) {
+               if (amux == inputs[i].amux)
+                       ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808);
+               else
+                       ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000);
 
-       return ret;
+               if (ret < 0)
+                       em28xx_warn("couldn't setup AC97 register %d\n",
+                                    inputs[i].reg);
+       }
+       return 0;
 }
 
 static int em28xx_set_audio_source(struct em28xx *dev)
@@ -329,10 +344,10 @@ static int em28xx_set_audio_source(struct em28xx *dev)
        u8 input;
 
        if (dev->is_em2800) {
-               if (dev->ctl_ainput)
-                       input = EM2800_AUDIO_SRC_LINE;
-               else
+               if (dev->ctl_ainput == EM28XX_AMUX_VIDEO)
                        input = EM2800_AUDIO_SRC_TUNER;
+               else
+                       input = EM2800_AUDIO_SRC_LINE;
 
                ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1);
                if (ret < 0)
@@ -360,16 +375,11 @@ static int em28xx_set_audio_source(struct em28xx *dev)
        switch (dev->audio_mode.ac97) {
        case EM28XX_NO_AC97:
                break;
-       case EM28XX_AC97_OTHER:
-               /* We don't know how to handle this chip.
-                  Let's hope it is close enough to em202 to work
-                */
-       case EM28XX_AC97_EM202:
-               ret = set_ac97_em202_input(dev);
-               break;
+       default:
+               ret = set_ac97_input(dev);
        }
 
-       return 0;
+       return ret;
 }
 
 int em28xx_audio_analog_set(struct em28xx *dev)
@@ -380,6 +390,9 @@ int em28xx_audio_analog_set(struct em28xx *dev)
        if (!dev->audio_mode.has_audio)
                return 0;
 
+       /* It is assumed that all devices use master volume for output.
+          It would be possible to use also line output.
+        */
        if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
                /* Mute */
                ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, 0x8000);
index 12c9132b099eed88f5ad3c732f9ef5de57b435fc..9727f3828dba60ad0ca7e625c07615aa89d62a6d 100644 (file)
@@ -130,10 +130,13 @@ enum em28xx_chip_id {
 
 /* Standard AC97 registers */
 #define AC97_RESET               0x00
+
+       /* Output volumes */
 #define AC97_MASTER_VOL          0x02
-#define AC97_LINE_LEVEL_VOL      0x04
+#define AC97_LINE_LEVEL_VOL      0x04  /* Some devices use for headphones */
 #define AC97_MASTER_MONO_VOL     0x06
 
+       /* Input volumes */
 #define AC97_PC_BEEP_VOL         0x0a
 #define AC97_PHONE_VOL           0x0c
 #define AC97_MIC_VOL             0x0e
@@ -142,8 +145,12 @@ enum em28xx_chip_id {
 #define AC97_VIDEO_VOL           0x14
 #define AC97_AUX_VOL             0x16
 #define AC97_PCM_OUT_VOL         0x18
+
+       /* capture registers */
 #define AC97_RECORD_SELECT       0x1a
 #define AC97_RECORD_GAIN         0x1c
+
+       /* control registers */
 #define AC97_GENERAL_PURPOSE     0x20
 #define AC97_3D_CTRL             0x22
 #define AC97_AUD_INT_AND_PAG     0x24
@@ -158,10 +165,15 @@ enum em28xx_chip_id {
 #define AC97_PCM_OUT_SURR_SRATE  0x2e
 #define AC97_PCM_OUT_LFE_SRATE   0x30
 #define AC97_PCM_IN_SRATE        0x32
+
+       /* For devices with more than 2 channels, extra output volumes */
 #define AC97_LFE_MASTER_VOL      0x36
 #define AC97_SURR_MASTER_VOL     0x38
+
+       /* Digital SPDIF output control */
 #define AC97_SPDIF_OUT_CTRL      0x3a
 
+       /* Vendor ID identifier */
 #define AC97_VENDOR_ID1          0x7c
 #define AC97_VENDOR_ID2          0x7e
 
index d965caba63e31407c9cc5c659c1767aa09fe8b14..6d04ebf46e7c2befbb702195f6ffc804c3edb24d 100644 (file)
@@ -274,9 +274,25 @@ struct em28xx_audio_mode {
        unsigned int i2s_5rates:1;
 };
 
+/* em28xx has two audio inputs: tuner and line in.
+   However, on most devices, an auxiliary AC97 codec device is used.
+   The AC97 device may have several different inputs and outputs,
+   depending on their model. So, it is possible to use AC97 mixer to
+   address more than two different entries.
+ */
 enum em28xx_amux {
-       EM28XX_AMUX_VIDEO,
-       EM28XX_AMUX_LINE_IN,
+       /* This is the only entry for em28xx tuner input */
+       EM28XX_AMUX_VIDEO,      /* em28xx tuner, AC97 mixer Video */
+
+       EM28XX_AMUX_LINE_IN,    /* AC97 mixer Line In */
+
+       /* Some less-common mixer setups */
+       EM28XX_AMUX_VIDEO2,     /* em28xx Line in, AC97 mixer Video */
+       EM28XX_AMUX_PHONE,
+       EM28XX_AMUX_MIC,
+       EM28XX_AMUX_CD,
+       EM28XX_AMUX_AUX,
+       EM28XX_AMUX_PCM_OUT,
 };
 
 struct em28xx_input {