diff options
-rw-r--r-- | Documentation/sound/alsa/ALSA-Configuration.txt | 7 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 708 |
2 files changed, 709 insertions, 6 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 7d5a1d00259d..4f82145a9390 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt | |||
@@ -844,6 +844,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. | |||
844 | asus-laptop ASUS F2/F3 laptops | 844 | asus-laptop ASUS F2/F3 laptops |
845 | auto auto-config reading BIOS (default) | 845 | auto auto-config reading BIOS (default) |
846 | 846 | ||
847 | ALC861VD/660VD | ||
848 | 3stack 3-jack | ||
849 | 3stack-dig 3-jack with SPDIF OUT | ||
850 | 6stack-dig 6-jack with SPDIF OUT | ||
851 | 3stack-660 3-jack (for ALC660VD) | ||
852 | auto auto-config reading BIOS (default) | ||
853 | |||
847 | CMI9880 | 854 | CMI9880 |
848 | minimal 3-jack in back | 855 | minimal 3-jack in back |
849 | min_fp 3-jack in back, 2-jack in front | 856 | min_fp 3-jack in back, 2-jack in front |
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c022e8157c34..583295deaecd 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c | |||
@@ -108,6 +108,16 @@ enum { | |||
108 | ALC861_MODEL_LAST, | 108 | ALC861_MODEL_LAST, |
109 | }; | 109 | }; |
110 | 110 | ||
111 | /* ALC861-VD models */ | ||
112 | enum { | ||
113 | ALC660VD_3ST, | ||
114 | ALC861VD_3ST, | ||
115 | ALC861VD_3ST_DIG, | ||
116 | ALC861VD_6ST_DIG, | ||
117 | ALC861VD_AUTO, | ||
118 | ALC861VD_MODEL_LAST, | ||
119 | }; | ||
120 | |||
111 | /* ALC882 models */ | 121 | /* ALC882 models */ |
112 | enum { | 122 | enum { |
113 | ALC882_3ST_DIG, | 123 | ALC882_3ST_DIG, |
@@ -7925,20 +7935,706 @@ static int patch_alc861(struct hda_codec *codec) | |||
7925 | } | 7935 | } |
7926 | 7936 | ||
7927 | /* | 7937 | /* |
7938 | * ALC861-VD support | ||
7939 | * | ||
7940 | * Based on ALC882 | ||
7941 | * | ||
7942 | * In addition, an independent DAC | ||
7943 | */ | ||
7944 | #define ALC861VD_DIGOUT_NID 0x06 | ||
7945 | |||
7946 | static hda_nid_t alc861vd_dac_nids[4] = { | ||
7947 | /* front, surr, clfe, side surr */ | ||
7948 | 0x02, 0x03, 0x04, 0x05 | ||
7949 | }; | ||
7950 | |||
7951 | /* dac_nids for ALC660vd are in a different order - according to | ||
7952 | * Realtek's driver. | ||
7953 | * This should probably tesult in a different mixer for 6stack models | ||
7954 | * of ALC660vd codecs, but for now there is only 3stack mixer | ||
7955 | * - and it is the same as in 861vd. | ||
7956 | * adc_nids in ALC660vd are (is) the same as in 861vd | ||
7957 | */ | ||
7958 | static hda_nid_t alc660vd_dac_nids[3] = { | ||
7959 | /* front, rear, clfe, rear_surr */ | ||
7960 | 0x02, 0x04, 0x03 | ||
7961 | }; | ||
7962 | |||
7963 | static hda_nid_t alc861vd_adc_nids[1] = { | ||
7964 | /* ADC0 */ | ||
7965 | 0x09, | ||
7966 | }; | ||
7967 | |||
7968 | /* input MUX */ | ||
7969 | /* FIXME: should be a matrix-type input source selection */ | ||
7970 | static struct hda_input_mux alc861vd_capture_source = { | ||
7971 | .num_items = 4, | ||
7972 | .items = { | ||
7973 | { "Mic", 0x0 }, | ||
7974 | { "Front Mic", 0x1 }, | ||
7975 | { "Line", 0x2 }, | ||
7976 | { "CD", 0x4 }, | ||
7977 | }, | ||
7978 | }; | ||
7979 | |||
7980 | #define alc861vd_mux_enum_info alc_mux_enum_info | ||
7981 | #define alc861vd_mux_enum_get alc_mux_enum_get | ||
7982 | |||
7983 | static int alc861vd_mux_enum_put(struct snd_kcontrol *kcontrol, | ||
7984 | struct snd_ctl_elem_value *ucontrol) | ||
7985 | { | ||
7986 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
7987 | struct alc_spec *spec = codec->spec; | ||
7988 | const struct hda_input_mux *imux = spec->input_mux; | ||
7989 | unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); | ||
7990 | static hda_nid_t capture_mixers[1] = { 0x22 }; | ||
7991 | hda_nid_t nid = capture_mixers[adc_idx]; | ||
7992 | unsigned int *cur_val = &spec->cur_mux[adc_idx]; | ||
7993 | unsigned int i, idx; | ||
7994 | |||
7995 | idx = ucontrol->value.enumerated.item[0]; | ||
7996 | if (idx >= imux->num_items) | ||
7997 | idx = imux->num_items - 1; | ||
7998 | if (*cur_val == idx && ! codec->in_resume) | ||
7999 | return 0; | ||
8000 | for (i = 0; i < imux->num_items; i++) { | ||
8001 | unsigned int v = (i == idx) ? 0x7000 : 0x7080; | ||
8002 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, | ||
8003 | v | (imux->items[i].index << 8)); | ||
8004 | } | ||
8005 | *cur_val = idx; | ||
8006 | return 1; | ||
8007 | } | ||
8008 | |||
8009 | /* | ||
8010 | * 2ch mode | ||
8011 | */ | ||
8012 | static struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = { | ||
8013 | { 2, NULL } | ||
8014 | }; | ||
8015 | |||
8016 | /* | ||
8017 | * 6ch mode | ||
8018 | */ | ||
8019 | static struct hda_verb alc861vd_6stack_ch6_init[] = { | ||
8020 | { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, | ||
8021 | { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
8022 | { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
8023 | { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
8024 | { } /* end */ | ||
8025 | }; | ||
8026 | |||
8027 | /* | ||
8028 | * 8ch mode | ||
8029 | */ | ||
8030 | static struct hda_verb alc861vd_6stack_ch8_init[] = { | ||
8031 | { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
8032 | { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
8033 | { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
8034 | { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
8035 | { } /* end */ | ||
8036 | }; | ||
8037 | |||
8038 | static struct hda_channel_mode alc861vd_6stack_modes[2] = { | ||
8039 | { 6, alc861vd_6stack_ch6_init }, | ||
8040 | { 8, alc861vd_6stack_ch8_init }, | ||
8041 | }; | ||
8042 | |||
8043 | static struct snd_kcontrol_new alc861vd_chmode_mixer[] = { | ||
8044 | { | ||
8045 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
8046 | .name = "Channel Mode", | ||
8047 | .info = alc_ch_mode_info, | ||
8048 | .get = alc_ch_mode_get, | ||
8049 | .put = alc_ch_mode_put, | ||
8050 | }, | ||
8051 | { } /* end */ | ||
8052 | }; | ||
8053 | |||
8054 | static struct snd_kcontrol_new alc861vd_capture_mixer[] = { | ||
8055 | HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), | ||
8056 | HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), | ||
8057 | |||
8058 | { | ||
8059 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
8060 | /* The multiple "Capture Source" controls confuse alsamixer | ||
8061 | * So call somewhat different.. | ||
8062 | *FIXME: the controls appear in the "playback" view! | ||
8063 | */ | ||
8064 | /* .name = "Capture Source", */ | ||
8065 | .name = "Input Source", | ||
8066 | .count = 1, | ||
8067 | .info = alc861vd_mux_enum_info, | ||
8068 | .get = alc861vd_mux_enum_get, | ||
8069 | .put = alc861vd_mux_enum_put, | ||
8070 | }, | ||
8071 | { } /* end */ | ||
8072 | }; | ||
8073 | |||
8074 | /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 | ||
8075 | * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b | ||
8076 | */ | ||
8077 | static struct snd_kcontrol_new alc861vd_6st_mixer[] = { | ||
8078 | HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), | ||
8079 | HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), | ||
8080 | |||
8081 | HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), | ||
8082 | HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), | ||
8083 | |||
8084 | HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, | ||
8085 | HDA_OUTPUT), | ||
8086 | HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, | ||
8087 | HDA_OUTPUT), | ||
8088 | HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), | ||
8089 | HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), | ||
8090 | |||
8091 | HDA_CODEC_VOLUME("Side Playback Volume", 0x05, 0x0, HDA_OUTPUT), | ||
8092 | HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), | ||
8093 | |||
8094 | HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), | ||
8095 | |||
8096 | HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), | ||
8097 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), | ||
8098 | HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), | ||
8099 | |||
8100 | HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), | ||
8101 | HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), | ||
8102 | HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), | ||
8103 | |||
8104 | HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), | ||
8105 | HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), | ||
8106 | |||
8107 | HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), | ||
8108 | HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), | ||
8109 | |||
8110 | HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), | ||
8111 | HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), | ||
8112 | |||
8113 | { } /* end */ | ||
8114 | }; | ||
8115 | |||
8116 | static struct snd_kcontrol_new alc861vd_3st_mixer[] = { | ||
8117 | HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), | ||
8118 | HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), | ||
8119 | |||
8120 | HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), | ||
8121 | |||
8122 | HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), | ||
8123 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), | ||
8124 | HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), | ||
8125 | |||
8126 | HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), | ||
8127 | HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), | ||
8128 | HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), | ||
8129 | |||
8130 | HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), | ||
8131 | HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), | ||
8132 | |||
8133 | HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), | ||
8134 | HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), | ||
8135 | |||
8136 | HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), | ||
8137 | HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), | ||
8138 | |||
8139 | { } /* end */ | ||
8140 | }; | ||
8141 | |||
8142 | /* | ||
8143 | * generic initialization of ADC, input mixers and output mixers | ||
8144 | */ | ||
8145 | static struct hda_verb alc861vd_volume_init_verbs[] = { | ||
8146 | /* | ||
8147 | * Unmute ADC0 and set the default input to mic-in | ||
8148 | */ | ||
8149 | {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
8150 | {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
8151 | |||
8152 | /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of | ||
8153 | * the analog-loopback mixer widget | ||
8154 | */ | ||
8155 | /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ | ||
8156 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
8157 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
8158 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, | ||
8159 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, | ||
8160 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, | ||
8161 | |||
8162 | /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */ | ||
8163 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, | ||
8164 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, | ||
8165 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)}, | ||
8166 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(8)}, | ||
8167 | |||
8168 | /* | ||
8169 | * Set up output mixers (0x02 - 0x05) | ||
8170 | */ | ||
8171 | /* set vol=0 to output mixers */ | ||
8172 | {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, | ||
8173 | {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, | ||
8174 | {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, | ||
8175 | {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, | ||
8176 | |||
8177 | /* set up input amps for analog loopback */ | ||
8178 | /* Amp Indices: DAC = 0, mixer = 1 */ | ||
8179 | {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, | ||
8180 | {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||
8181 | {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, | ||
8182 | {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||
8183 | {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, | ||
8184 | {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||
8185 | {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, | ||
8186 | {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||
8187 | |||
8188 | { } | ||
8189 | }; | ||
8190 | |||
8191 | /* | ||
8192 | * 3-stack pin configuration: | ||
8193 | * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b | ||
8194 | */ | ||
8195 | static struct hda_verb alc861vd_3stack_init_verbs[] = { | ||
8196 | /* | ||
8197 | * Set pin mode and muting | ||
8198 | */ | ||
8199 | /* set front pin widgets 0x14 for output */ | ||
8200 | {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
8201 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
8202 | {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
8203 | |||
8204 | /* Mic (rear) pin: input vref at 80% */ | ||
8205 | {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
8206 | {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||
8207 | /* Front Mic pin: input vref at 80% */ | ||
8208 | {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
8209 | {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||
8210 | /* Line In pin: input */ | ||
8211 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, | ||
8212 | {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||
8213 | /* Line-2 In: Headphone output (output 0 - 0x0c) */ | ||
8214 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
8215 | {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
8216 | {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
8217 | /* CD pin widget for input */ | ||
8218 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, | ||
8219 | |||
8220 | { } | ||
8221 | }; | ||
8222 | |||
8223 | /* | ||
8224 | * 6-stack pin configuration: | ||
8225 | */ | ||
8226 | static struct hda_verb alc861vd_6stack_init_verbs[] = { | ||
8227 | /* | ||
8228 | * Set pin mode and muting | ||
8229 | */ | ||
8230 | /* set front pin widgets 0x14 for output */ | ||
8231 | {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
8232 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
8233 | {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
8234 | |||
8235 | /* Rear Pin: output 1 (0x0d) */ | ||
8236 | {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
8237 | {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
8238 | {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, | ||
8239 | /* CLFE Pin: output 2 (0x0e) */ | ||
8240 | {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
8241 | {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
8242 | {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, | ||
8243 | /* Side Pin: output 3 (0x0f) */ | ||
8244 | {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
8245 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
8246 | {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, | ||
8247 | |||
8248 | /* Mic (rear) pin: input vref at 80% */ | ||
8249 | {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
8250 | {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||
8251 | /* Front Mic pin: input vref at 80% */ | ||
8252 | {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
8253 | {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||
8254 | /* Line In pin: input */ | ||
8255 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, | ||
8256 | {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||
8257 | /* Line-2 In: Headphone output (output 0 - 0x0c) */ | ||
8258 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
8259 | {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
8260 | {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
8261 | /* CD pin widget for input */ | ||
8262 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, | ||
8263 | |||
8264 | { } | ||
8265 | }; | ||
8266 | |||
8267 | /* pcm configuration: identiacal with ALC880 */ | ||
8268 | #define alc861vd_pcm_analog_playback alc880_pcm_analog_playback | ||
8269 | #define alc861vd_pcm_analog_capture alc880_pcm_analog_capture | ||
8270 | #define alc861vd_pcm_digital_playback alc880_pcm_digital_playback | ||
8271 | #define alc861vd_pcm_digital_capture alc880_pcm_digital_capture | ||
8272 | |||
8273 | /* | ||
8274 | * configuration and preset | ||
8275 | */ | ||
8276 | static const char *alc861vd_models[ALC861VD_MODEL_LAST] = { | ||
8277 | [ALC660VD_3ST] = "3stack-660", | ||
8278 | [ALC861VD_3ST] = "3stack", | ||
8279 | [ALC861VD_3ST_DIG] = "3stack-digout", | ||
8280 | [ALC861VD_6ST_DIG] = "6stack-digout", | ||
8281 | [ALC861VD_AUTO] = "auto", | ||
8282 | }; | ||
8283 | |||
8284 | static struct snd_pci_quirk alc861vd_cfg_tbl[] = { | ||
8285 | SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST), | ||
8286 | SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), | ||
8287 | SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), | ||
8288 | |||
8289 | SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_3ST), | ||
8290 | {} | ||
8291 | }; | ||
8292 | |||
8293 | static struct alc_config_preset alc861vd_presets[] = { | ||
8294 | [ALC660VD_3ST] = { | ||
8295 | .mixers = { alc861vd_3st_mixer }, | ||
8296 | .init_verbs = { alc861vd_volume_init_verbs, | ||
8297 | alc861vd_3stack_init_verbs }, | ||
8298 | .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), | ||
8299 | .dac_nids = alc660vd_dac_nids, | ||
8300 | .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), | ||
8301 | .adc_nids = alc861vd_adc_nids, | ||
8302 | .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), | ||
8303 | .channel_mode = alc861vd_3stack_2ch_modes, | ||
8304 | .input_mux = &alc861vd_capture_source, | ||
8305 | }, | ||
8306 | [ALC861VD_3ST] = { | ||
8307 | .mixers = { alc861vd_3st_mixer }, | ||
8308 | .init_verbs = { alc861vd_volume_init_verbs, | ||
8309 | alc861vd_3stack_init_verbs }, | ||
8310 | .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), | ||
8311 | .dac_nids = alc861vd_dac_nids, | ||
8312 | .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), | ||
8313 | .channel_mode = alc861vd_3stack_2ch_modes, | ||
8314 | .input_mux = &alc861vd_capture_source, | ||
8315 | }, | ||
8316 | [ALC861VD_3ST_DIG] = { | ||
8317 | .mixers = { alc861vd_3st_mixer }, | ||
8318 | .init_verbs = { alc861vd_volume_init_verbs, | ||
8319 | alc861vd_3stack_init_verbs }, | ||
8320 | .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), | ||
8321 | .dac_nids = alc861vd_dac_nids, | ||
8322 | .dig_out_nid = ALC861VD_DIGOUT_NID, | ||
8323 | .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), | ||
8324 | .channel_mode = alc861vd_3stack_2ch_modes, | ||
8325 | .input_mux = &alc861vd_capture_source, | ||
8326 | }, | ||
8327 | [ALC861VD_6ST_DIG] = { | ||
8328 | .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer }, | ||
8329 | .init_verbs = { alc861vd_volume_init_verbs, | ||
8330 | alc861vd_6stack_init_verbs }, | ||
8331 | .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), | ||
8332 | .dac_nids = alc861vd_dac_nids, | ||
8333 | .dig_out_nid = ALC861VD_DIGOUT_NID, | ||
8334 | .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes), | ||
8335 | .channel_mode = alc861vd_6stack_modes, | ||
8336 | .input_mux = &alc861vd_capture_source, | ||
8337 | }, | ||
8338 | }; | ||
8339 | |||
8340 | /* | ||
8341 | * BIOS auto configuration | ||
8342 | */ | ||
8343 | static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec, | ||
8344 | hda_nid_t nid, int pin_type, int dac_idx) | ||
8345 | { | ||
8346 | /* set as output */ | ||
8347 | snd_hda_codec_write(codec, nid, 0, | ||
8348 | AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); | ||
8349 | snd_hda_codec_write(codec, nid, 0, | ||
8350 | AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); | ||
8351 | } | ||
8352 | |||
8353 | static void alc861vd_auto_init_multi_out(struct hda_codec *codec) | ||
8354 | { | ||
8355 | struct alc_spec *spec = codec->spec; | ||
8356 | int i; | ||
8357 | |||
8358 | for (i = 0; i <= HDA_SIDE; i++) { | ||
8359 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; | ||
8360 | if (nid) | ||
8361 | alc861vd_auto_set_output_and_unmute(codec, nid, | ||
8362 | PIN_OUT, i); | ||
8363 | } | ||
8364 | } | ||
8365 | |||
8366 | |||
8367 | static void alc861vd_auto_init_hp_out(struct hda_codec *codec) | ||
8368 | { | ||
8369 | struct alc_spec *spec = codec->spec; | ||
8370 | hda_nid_t pin; | ||
8371 | |||
8372 | pin = spec->autocfg.hp_pins[0]; | ||
8373 | if (pin) /* connect to front and use dac 0 */ | ||
8374 | alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); | ||
8375 | } | ||
8376 | |||
8377 | #define alc861vd_is_input_pin(nid) alc880_is_input_pin(nid) | ||
8378 | #define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID | ||
8379 | |||
8380 | static void alc861vd_auto_init_analog_input(struct hda_codec *codec) | ||
8381 | { | ||
8382 | struct alc_spec *spec = codec->spec; | ||
8383 | int i; | ||
8384 | |||
8385 | for (i = 0; i < AUTO_PIN_LAST; i++) { | ||
8386 | hda_nid_t nid = spec->autocfg.input_pins[i]; | ||
8387 | if (alc861vd_is_input_pin(nid)) { | ||
8388 | snd_hda_codec_write(codec, nid, 0, | ||
8389 | AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
8390 | i <= AUTO_PIN_FRONT_MIC ? | ||
8391 | PIN_VREF80 : PIN_IN); | ||
8392 | if (nid != ALC861VD_PIN_CD_NID) | ||
8393 | snd_hda_codec_write(codec, nid, 0, | ||
8394 | AC_VERB_SET_AMP_GAIN_MUTE, | ||
8395 | AMP_OUT_MUTE); | ||
8396 | } | ||
8397 | } | ||
8398 | } | ||
8399 | |||
8400 | #define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02) | ||
8401 | #define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c) | ||
8402 | |||
8403 | /* add playback controls from the parsed DAC table */ | ||
8404 | /* Based on ALC880 version. But ALC861VD has separate, | ||
8405 | * different NIDs for mute/unmute switch and volume control */ | ||
8406 | static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, | ||
8407 | const struct auto_pin_cfg *cfg) | ||
8408 | { | ||
8409 | char name[32]; | ||
8410 | static const char *chname[4] = {"Front", "Surround", "CLFE", "Side"}; | ||
8411 | hda_nid_t nid_v, nid_s; | ||
8412 | int i, err; | ||
8413 | |||
8414 | for (i = 0; i < cfg->line_outs; i++) { | ||
8415 | if (! spec->multiout.dac_nids[i]) | ||
8416 | continue; | ||
8417 | nid_v = alc861vd_idx_to_mixer_vol( | ||
8418 | alc880_dac_to_idx( | ||
8419 | spec->multiout.dac_nids[i])); | ||
8420 | nid_s = alc861vd_idx_to_mixer_switch( | ||
8421 | alc880_dac_to_idx( | ||
8422 | spec->multiout.dac_nids[i])); | ||
8423 | |||
8424 | if (i == 2) { | ||
8425 | /* Center/LFE */ | ||
8426 | if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, | ||
8427 | "Center Playback Volume", | ||
8428 | HDA_COMPOSE_AMP_VAL(nid_v, 1, | ||
8429 | 0, HDA_OUTPUT))) < 0) | ||
8430 | return err; | ||
8431 | if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, | ||
8432 | "LFE Playback Volume", | ||
8433 | HDA_COMPOSE_AMP_VAL(nid_v, 2, | ||
8434 | 0, HDA_OUTPUT))) < 0) | ||
8435 | return err; | ||
8436 | if ((err = add_control(spec, ALC_CTL_BIND_MUTE, | ||
8437 | "Center Playback Switch", | ||
8438 | HDA_COMPOSE_AMP_VAL(nid_s, 1, | ||
8439 | 2, HDA_INPUT))) < 0) | ||
8440 | return err; | ||
8441 | if ((err = add_control(spec, ALC_CTL_BIND_MUTE, | ||
8442 | "LFE Playback Switch", | ||
8443 | HDA_COMPOSE_AMP_VAL(nid_s, 2, | ||
8444 | 2, HDA_INPUT))) < 0) | ||
8445 | return err; | ||
8446 | } else { | ||
8447 | sprintf(name, "%s Playback Volume", chname[i]); | ||
8448 | if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name, | ||
8449 | HDA_COMPOSE_AMP_VAL(nid_v, 3, | ||
8450 | 0, HDA_OUTPUT))) < 0) | ||
8451 | return err; | ||
8452 | sprintf(name, "%s Playback Switch", chname[i]); | ||
8453 | if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name, | ||
8454 | HDA_COMPOSE_AMP_VAL(nid_v, 3, | ||
8455 | 2, HDA_INPUT))) < 0) | ||
8456 | return err; | ||
8457 | } | ||
8458 | } | ||
8459 | return 0; | ||
8460 | } | ||
8461 | |||
8462 | /* add playback controls for speaker and HP outputs */ | ||
8463 | /* Based on ALC880 version. But ALC861VD has separate, | ||
8464 | * different NIDs for mute/unmute switch and volume control */ | ||
8465 | static int alc861vd_auto_create_extra_out(struct alc_spec *spec, | ||
8466 | hda_nid_t pin, const char *pfx) | ||
8467 | { | ||
8468 | hda_nid_t nid_v, nid_s; | ||
8469 | int err; | ||
8470 | char name[32]; | ||
8471 | |||
8472 | if (! pin) | ||
8473 | return 0; | ||
8474 | |||
8475 | if (alc880_is_fixed_pin(pin)) { | ||
8476 | nid_v = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); | ||
8477 | /* specify the DAC as the extra output */ | ||
8478 | if (! spec->multiout.hp_nid) | ||
8479 | spec->multiout.hp_nid = nid_v; | ||
8480 | else | ||
8481 | spec->multiout.extra_out_nid[0] = nid_v; | ||
8482 | /* control HP volume/switch on the output mixer amp */ | ||
8483 | nid_v = alc861vd_idx_to_mixer_vol( | ||
8484 | alc880_fixed_pin_idx(pin)); | ||
8485 | nid_s = alc861vd_idx_to_mixer_switch( | ||
8486 | alc880_fixed_pin_idx(pin)); | ||
8487 | |||
8488 | sprintf(name, "%s Playback Volume", pfx); | ||
8489 | if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name, | ||
8490 | HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, | ||
8491 | HDA_OUTPUT))) < 0) | ||
8492 | return err; | ||
8493 | sprintf(name, "%s Playback Switch", pfx); | ||
8494 | if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name, | ||
8495 | HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, | ||
8496 | HDA_INPUT))) < 0) | ||
8497 | return err; | ||
8498 | } else if (alc880_is_multi_pin(pin)) { | ||
8499 | /* set manual connection */ | ||
8500 | /* we have only a switch on HP-out PIN */ | ||
8501 | sprintf(name, "%s Playback Switch", pfx); | ||
8502 | if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, | ||
8503 | HDA_COMPOSE_AMP_VAL(pin, 3, 0, | ||
8504 | HDA_OUTPUT))) < 0) | ||
8505 | return err; | ||
8506 | } | ||
8507 | return 0; | ||
8508 | } | ||
8509 | |||
8510 | /* parse the BIOS configuration and set up the alc_spec | ||
8511 | * return 1 if successful, 0 if the proper config is not found, | ||
8512 | * or a negative error code | ||
8513 | * Based on ALC880 version - had to change it to override | ||
8514 | * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */ | ||
8515 | static int alc861vd_parse_auto_config(struct hda_codec *codec) | ||
8516 | { | ||
8517 | struct alc_spec *spec = codec->spec; | ||
8518 | int err; | ||
8519 | static hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; | ||
8520 | |||
8521 | if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, | ||
8522 | alc861vd_ignore)) < 0) | ||
8523 | return err; | ||
8524 | if (! spec->autocfg.line_outs) | ||
8525 | return 0; /* can't find valid BIOS pin config */ | ||
8526 | |||
8527 | if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 || | ||
8528 | (err = alc861vd_auto_create_multi_out_ctls(spec, | ||
8529 | &spec->autocfg)) < 0 || | ||
8530 | (err = alc861vd_auto_create_extra_out(spec, | ||
8531 | spec->autocfg.speaker_pins[0], "Speaker")) < 0 || | ||
8532 | (err = alc861vd_auto_create_extra_out(spec, | ||
8533 | spec->autocfg.hp_pins[0], "Headphone")) < 0 || | ||
8534 | (err = alc880_auto_create_analog_input_ctls(spec, | ||
8535 | &spec->autocfg)) < 0) | ||
8536 | return err; | ||
8537 | |||
8538 | spec->multiout.max_channels = spec->multiout.num_dacs * 2; | ||
8539 | |||
8540 | if (spec->autocfg.dig_out_pin) | ||
8541 | spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID; | ||
8542 | |||
8543 | if (spec->kctl_alloc) | ||
8544 | spec->mixers[spec->num_mixers++] = spec->kctl_alloc; | ||
8545 | |||
8546 | spec->init_verbs[spec->num_init_verbs++] | ||
8547 | = alc861vd_volume_init_verbs; | ||
8548 | |||
8549 | spec->num_mux_defs = 1; | ||
8550 | spec->input_mux = &spec->private_imux; | ||
8551 | |||
8552 | return 1; | ||
8553 | } | ||
8554 | |||
8555 | /* additional initialization for auto-configuration model */ | ||
8556 | static void alc861vd_auto_init(struct hda_codec *codec) | ||
8557 | { | ||
8558 | alc861vd_auto_init_multi_out(codec); | ||
8559 | alc861vd_auto_init_hp_out(codec); | ||
8560 | alc861vd_auto_init_analog_input(codec); | ||
8561 | } | ||
8562 | |||
8563 | static int patch_alc861vd(struct hda_codec *codec) | ||
8564 | { | ||
8565 | struct alc_spec *spec; | ||
8566 | int err, board_config; | ||
8567 | |||
8568 | spec = kzalloc(sizeof(*spec), GFP_KERNEL); | ||
8569 | if (spec == NULL) | ||
8570 | return -ENOMEM; | ||
8571 | |||
8572 | codec->spec = spec; | ||
8573 | |||
8574 | board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST, | ||
8575 | alc861vd_models, | ||
8576 | alc861vd_cfg_tbl); | ||
8577 | |||
8578 | if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) { | ||
8579 | printk(KERN_INFO "hda_codec: Unknown model for ALC660VD/" | ||
8580 | "ALC861VD, trying auto-probe from BIOS...\n"); | ||
8581 | board_config = ALC861VD_AUTO; | ||
8582 | } | ||
8583 | |||
8584 | if (board_config == ALC861VD_AUTO) { | ||
8585 | /* automatic parse from the BIOS config */ | ||
8586 | err = alc861vd_parse_auto_config(codec); | ||
8587 | if (err < 0) { | ||
8588 | alc_free(codec); | ||
8589 | return err; | ||
8590 | } else if (! err) { | ||
8591 | printk(KERN_INFO | ||
8592 | "hda_codec: Cannot set up configuration " | ||
8593 | "from BIOS. Using base mode...\n"); | ||
8594 | board_config = ALC861VD_3ST; | ||
8595 | } | ||
8596 | } | ||
8597 | |||
8598 | if (board_config != ALC861VD_AUTO) | ||
8599 | setup_preset(spec, &alc861vd_presets[board_config]); | ||
8600 | |||
8601 | spec->stream_name_analog = "ALC861VD Analog"; | ||
8602 | spec->stream_analog_playback = &alc861vd_pcm_analog_playback; | ||
8603 | spec->stream_analog_capture = &alc861vd_pcm_analog_capture; | ||
8604 | |||
8605 | spec->stream_name_digital = "ALC861VD Digital"; | ||
8606 | spec->stream_digital_playback = &alc861vd_pcm_digital_playback; | ||
8607 | spec->stream_digital_capture = &alc861vd_pcm_digital_capture; | ||
8608 | |||
8609 | spec->adc_nids = alc861vd_adc_nids; | ||
8610 | spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids); | ||
8611 | |||
8612 | spec->mixers[spec->num_mixers] = alc861vd_capture_mixer; | ||
8613 | spec->num_mixers++; | ||
8614 | |||
8615 | codec->patch_ops = alc_patch_ops; | ||
8616 | |||
8617 | if (board_config == ALC861VD_AUTO) | ||
8618 | spec->init_hook = alc861vd_auto_init; | ||
8619 | |||
8620 | return 0; | ||
8621 | } | ||
8622 | |||
8623 | /* | ||
7928 | * patch entries | 8624 | * patch entries |
7929 | */ | 8625 | */ |
7930 | struct hda_codec_preset snd_hda_preset_realtek[] = { | 8626 | struct hda_codec_preset snd_hda_preset_realtek[] = { |
7931 | { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, | 8627 | { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, |
7932 | { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, | 8628 | { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, |
7933 | { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, | 8629 | { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", |
8630 | .patch = patch_alc861 }, | ||
8631 | { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, | ||
8632 | { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 }, | ||
8633 | { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd }, | ||
8634 | { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, | ||
7934 | { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, | 8635 | { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, |
7935 | { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, | 8636 | { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, |
7936 | { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, | 8637 | { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, |
7937 | { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, | 8638 | { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, |
7938 | { .id = 0x10ec0861, .rev = 0x100300, .name = "ALC861", | ||
7939 | .patch = patch_alc861 }, | ||
7940 | { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", | ||
7941 | .patch = patch_alc861 }, | ||
7942 | { .id = 0x10ec0660, .name = "ALC660", .patch = patch_alc861 }, | ||
7943 | {} /* terminator */ | 8639 | {} /* terminator */ |
7944 | }; | 8640 | }; |