ALSA: HDA: VIA Add support for codec VT1705CF.
authorLydia Wang <lydiawang@viatech.com.cn>
Mon, 10 Dec 2012 10:28:11 +0000 (18:28 +0800)
committerTakashi Iwai <tiwai@suse.de>
Mon, 10 Dec 2012 10:45:24 +0000 (11:45 +0100)
Add support for new codec VT1705CF.
When power on/off Audio output converter of VT1705CF, the stream tag
will be cleared. But driver caches the value. So when power on Audio
output converter, the update_conv_power_state() will restore the saved
stream tag of it.

Signed-off-by: Lydia Wang <lydiawang@viatech.com.cn>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_via.c

index dd8cd6af5d66829090789f7bb1c5819950a544cf..08ca407fd1678ea7a8f1a03a592cd87bd911c028 100644 (file)
@@ -76,6 +76,7 @@ enum VIA_HDA_CODEC {
        VT2002P,
        VT1812,
        VT1802,
+       VT1705CF,
        CODEC_TYPES,
 };
 
@@ -220,6 +221,7 @@ struct via_spec {
        int vt1708_hp_present;
 
        void (*set_widgets_power_state)(struct hda_codec *codec);
+       unsigned int dac_stream_tag[4];
 
        struct hda_loopback_check loopback;
        int num_loopbacks;
@@ -296,6 +298,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
                codec_type = VT1708S;
        else if ((dev_id & 0xfff) == 0x446)
                codec_type = VT1802;
+       else if (dev_id == 0x4760)
+               codec_type = VT1705CF;
        else
                codec_type = UNKNOWN;
        return codec_type;
@@ -711,6 +715,28 @@ static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
 }
 
+static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
+                              unsigned int parm, unsigned int index)
+{
+       struct via_spec *spec = codec->spec;
+       unsigned int format;
+       if (snd_hda_codec_read(codec, nid, 0,
+                              AC_VERB_GET_POWER_STATE, 0) == parm)
+               return;
+       format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+       if (format && (spec->dac_stream_tag[index] != format))
+               spec->dac_stream_tag[index] = format;
+
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
+       if (parm == AC_PWRST_D0) {
+               format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+               if (!format && (spec->dac_stream_tag[index] != format))
+                       snd_hda_codec_write(codec, nid, 0,
+                                                 AC_VERB_SET_CHANNEL_STREAMID,
+                                                 spec->dac_stream_tag[index]);
+       }
+}
+
 static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
                                unsigned int *affected_parm)
 {
@@ -1085,6 +1111,10 @@ static void __analog_low_current_mode(struct hda_codec *codec, bool force)
                verb = 0xf93;
                parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
                break;
+       case VT1705CF:
+               verb = 0xf82;
+               parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
+               break;
        default:
                return;         /* other codecs are not supported */
        }
@@ -3817,6 +3847,125 @@ static int patch_vt1812(struct hda_codec *codec)
        return 0;
 }
 
+/* patch for vt3476 */
+
+static const struct hda_verb vt3476_init_verbs[] = {
+       /* Enable DMic 8/16/32K */
+       {0x1, 0xF7B, 0x30},
+       /* Enable Boost Volume backdoor */
+       {0x1, 0xFB9, 0x20},
+       /* Enable AOW-MW9 path */
+       {0x1, 0xFB8, 0x10},
+       { }
+};
+
+static void set_widgets_power_state_vt3476(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       int imux_is_smixer;
+       unsigned int parm, parm2;
+       /* MUX10 (1eh) = stereo mixer */
+       imux_is_smixer =
+       snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4;
+       /* inputs */
+       /* PW 5/6/7 (29h/2ah/2bh) */
+       parm = AC_PWRST_D3;
+       set_pin_power_state(codec, 0x29, &parm);
+       set_pin_power_state(codec, 0x2a, &parm);
+       set_pin_power_state(codec, 0x2b, &parm);
+       if (imux_is_smixer)
+               parm = AC_PWRST_D0;
+       /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
+       update_power_state(codec, 0x1e, parm);
+       update_power_state(codec, 0x1f, parm);
+       update_power_state(codec, 0x10, parm);
+       update_power_state(codec, 0x11, parm);
+
+       /* outputs */
+       /* PW3 (27h), MW3(37h), AOW3 (bh) */
+       if (spec->codec_type == VT1705CF) {
+               parm = AC_PWRST_D3;
+               update_power_state(codec, 0x27, parm);
+               update_power_state(codec, 0x37, parm);
+       }       else {
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x27, &parm);
+               update_power_state(codec, 0x37, parm);
+       }
+
+       /* PW2 (26h), MW2(36h), AOW2 (ah) */
+       parm = AC_PWRST_D3;
+       set_pin_power_state(codec, 0x26, &parm);
+       update_power_state(codec, 0x36, parm);
+       if (spec->smart51_enabled) {
+               /* PW7(2bh), MW7(3bh), MUX7(1Bh) */
+               set_pin_power_state(codec, 0x2b, &parm);
+               update_power_state(codec, 0x3b, parm);
+               update_power_state(codec, 0x1b, parm);
+       }
+       update_conv_power_state(codec, 0xa, parm, 2);
+
+       /* PW1 (25h), MW1(35h), AOW1 (9h) */
+       parm = AC_PWRST_D3;
+       set_pin_power_state(codec, 0x25, &parm);
+       update_power_state(codec, 0x35, parm);
+       if (spec->smart51_enabled) {
+               /* PW6(2ah), MW6(3ah), MUX6(1ah) */
+               set_pin_power_state(codec, 0x2a, &parm);
+               update_power_state(codec, 0x3a, parm);
+               update_power_state(codec, 0x1a, parm);
+       }
+       update_conv_power_state(codec, 0x9, parm, 1);
+
+       /* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */
+       parm = AC_PWRST_D3;
+       set_pin_power_state(codec, 0x28, &parm);
+       update_power_state(codec, 0x38, parm);
+       update_power_state(codec, 0x18, parm);
+       if (spec->hp_independent_mode)
+               update_conv_power_state(codec, 0xb, parm, 3);
+       parm2 = parm; /* for pin 0x0b */
+
+       /* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */
+       parm = AC_PWRST_D3;
+       set_pin_power_state(codec, 0x24, &parm);
+       update_power_state(codec, 0x34, parm);
+       if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3)
+               parm = parm2;
+       update_conv_power_state(codec, 0x8, parm, 0);
+       /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
+       update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm);
+}
+
+static int patch_vt3476(struct hda_codec *codec)
+{
+       struct via_spec *spec;
+       int err;
+
+       /* create a codec specific record */
+       spec = via_new_spec(codec);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       spec->aa_mix_nid = 0x3f;
+       add_secret_dac_path(codec);
+
+       /* automatic parse from the BIOS config */
+       err = via_parse_auto_config(codec);
+       if (err < 0) {
+               via_free(codec);
+               return err;
+       }
+
+       spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
+
+       codec->patch_ops = via_patch_ops;
+
+       spec->set_widgets_power_state = set_widgets_power_state_vt3476;
+
+       return 0;
+}
+
 /*
  * patch entries
  */
@@ -3910,6 +4059,8 @@ static const struct hda_codec_preset snd_hda_preset_via[] = {
                .patch = patch_vt2002P},
        { .id = 0x11068446, .name = "VT1802",
                .patch = patch_vt2002P},
+       { .id = 0x11064760, .name = "VT1705CF",
+               .patch = patch_vt3476},
        {} /* terminator */
 };