diff options
Diffstat (limited to 'sound/pci/hda/patch_realtek.c')
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 1663 |
1 files changed, 923 insertions, 740 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a378c014512..0bd4e6bf354 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -30,7 +30,6 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" -#include "hda_patch.h" #define ALC880_FRONT_EVENT 0x01 #define ALC880_DCVOL_EVENT 0x02 @@ -114,6 +113,7 @@ enum { ALC268_3ST, ALC268_TOSHIBA, ALC268_ACER, + ALC268_ACER_DMIC, ALC268_ACER_ASPIRE_ONE, ALC268_DELL, ALC268_ZEPTO, @@ -130,6 +130,8 @@ enum { ALC269_QUANTA_FL1, ALC269_ASUS_EEEPC_P703, ALC269_ASUS_EEEPC_P901, + ALC269_FUJITSU, + ALC269_LIFEBOOK, ALC269_AUTO, ALC269_MODEL_LAST /* last tag */ }; @@ -152,6 +154,7 @@ enum { enum { ALC660VD_3ST, ALC660VD_3ST_DIG, + ALC660VD_ASUS_V1S, ALC861VD_3ST, ALC861VD_3ST_DIG, ALC861VD_6ST_DIG, @@ -212,6 +215,7 @@ enum { ALC883_TARGA_2ch_DIG, ALC883_ACER, ALC883_ACER_ASPIRE, + ALC888_ACER_ASPIRE_4930G, ALC883_MEDION, ALC883_MEDION_MD2, ALC883_LAPTOP_EAPD, @@ -225,9 +229,11 @@ enum { ALC883_MITAC, ALC883_CLEVO_M720, ALC883_FUJITSU_PI2515, + ALC888_FUJITSU_XA3530, ALC883_3ST_6ch_INTEL, ALC888_ASUS_M90V, ALC888_ASUS_EEE1601, + ALC1200_ASUS_P5Q, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -239,6 +245,7 @@ struct alc_spec { /* codec parameterization */ struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; + struct snd_kcontrol_new *cap_mixer; /* capture mixer */ const struct hda_verb *init_verbs[5]; /* initialization verbs * don't forget NULL @@ -268,6 +275,7 @@ struct alc_spec { hda_nid_t *adc_nids; hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ + unsigned char is_mix_capture; /* matrix-style capture (non-mux) */ /* capture source */ unsigned int num_mux_defs; @@ -284,8 +292,7 @@ struct alc_spec { /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; - unsigned int num_kctl_alloc, num_kctl_used; - struct snd_kcontrol_new *kctl_alloc; + struct snd_array kctls; struct hda_input_mux private_imux; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; @@ -323,6 +330,7 @@ struct alc_config_preset { struct snd_kcontrol_new *mixers[5]; /* should be identical size * with spec */ + struct snd_kcontrol_new *cap_mixer; /* capture mixer */ const struct hda_verb *init_verbs[5]; unsigned int num_dacs; hda_nid_t *dac_nids; @@ -375,14 +383,39 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; + unsigned int mux_idx; hda_nid_t nid = spec->capsrc_nids ? spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; - return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol, - nid, &spec->cur_mux[adc_idx]); -} + mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; + imux = &spec->input_mux[mux_idx]; + + if (spec->is_mix_capture) { + /* Matrix-mixer style (e.g. ALC882) */ + unsigned int *cur_val = &spec->cur_mux[adc_idx]; + unsigned int i, idx; + + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (*cur_val == idx) + return 0; + for (i = 0; i < imux->num_items; i++) { + unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, + imux->items[i].index, + HDA_AMP_MUTE, v); + } + *cur_val = idx; + return 1; + } else { + /* MUX style (e.g. ALC880) */ + return snd_hda_input_mux_put(codec, imux, ucontrol, nid, + &spec->cur_mux[adc_idx]); + } +} /* * channel mode setting @@ -717,6 +750,43 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, #endif /* CONFIG_SND_DEBUG */ /* + */ +static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix) +{ + if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers))) + return; + spec->mixers[spec->num_mixers++] = mix; +} + +static void add_verb(struct alc_spec *spec, const struct hda_verb *verb) +{ + if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs))) + return; + spec->init_verbs[spec->num_init_verbs++] = verb; +} + +#ifdef CONFIG_PROC_FS +/* + * hook for proc + */ +static void print_realtek_coef(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int coeff; + + if (nid != 0x20) + return; + coeff = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); + snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff); + coeff = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_COEF_INDEX, 0); + snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff); +} +#else +#define print_realtek_coef NULL +#endif + +/* * set up from the preset table */ static void setup_preset(struct alc_spec *spec, @@ -725,11 +795,11 @@ static void setup_preset(struct alc_spec *spec, int i; for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) - spec->mixers[spec->num_mixers++] = preset->mixers[i]; + add_mixer(spec, preset->mixers[i]); + spec->cap_mixer = preset->cap_mixer; for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; i++) - spec->init_verbs[spec->num_init_verbs++] = - preset->init_verbs[i]; + add_verb(spec, preset->init_verbs[i]); spec->channel_mode = preset->channel_mode; spec->num_channel_mode = preset->num_channel_mode; @@ -1107,6 +1177,226 @@ static void alc_fix_pincfg(struct hda_codec *codec, } /* + * ALC888 + */ + +/* + * 2ch mode + */ +static struct hda_verb alc888_4ST_ch2_intel_init[] = { +/* Mic-in jack as mic in */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-in jack as Line in */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-Out as Front */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } /* end */ +}; + +/* + * 4ch mode + */ +static struct hda_verb alc888_4ST_ch4_intel_init[] = { +/* Mic-in jack as mic in */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as Front */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } /* end */ +}; + +/* + * 6ch mode + */ +static struct hda_verb alc888_4ST_ch6_intel_init[] = { +/* Mic-in jack as CLFE */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + { } /* end */ +}; + +/* + * 8ch mode + */ +static struct hda_verb alc888_4ST_ch8_intel_init[] = { +/* Mic-in jack as CLFE */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as Side */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + { } /* end */ +}; + +static struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = { + { 2, alc888_4ST_ch2_intel_init }, + { 4, alc888_4ST_ch4_intel_init }, + { 6, alc888_4ST_ch6_intel_init }, + { 8, alc888_4ST_ch8_intel_init }, +}; + +/* + * ALC888 Fujitsu Siemens Amillo xa3530 + */ + +static struct hda_verb alc888_fujitsu_xa3530_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Connect Internal HP to Front */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Connect Bass HP to Front */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Connect Line-Out side jack (SPDIF) to Side */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, +/* Connect Mic jack to CLFE */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, +/* Connect Line-in jack to Surround */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, +/* Connect HP out jack to Front */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Enable unsolicited event for HP jack and Line-out jack */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {} +}; + +static void alc888_fujitsu_xa3530_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned int bits; + /* Line out presence */ + present = snd_hda_codec_read(codec, 0x17, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + /* HP out presence */ + present = present || snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + bits = present ? HDA_AMP_MUTE : 0; + /* Toggle internal speakers muting */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); + /* Toggle internal bass muting */ + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); +} + +static void alc888_fujitsu_xa3530_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if (res >> 26 == ALC880_HP_EVENT) + alc888_fujitsu_xa3530_automute(codec); +} + + +/* + * ALC888 Acer Aspire 4930G model + */ + +static struct hda_verb alc888_acer_aspire_4930g_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, +/* Connect Internal HP to front */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Connect HP out to front */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } +}; + +static struct hda_input_mux alc888_2_capture_sources[2] = { + /* Front mic only available on one ADC */ + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Front Mic", 0xb }, + }, + }, + { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, + } +}; + +static struct snd_kcontrol_new alc888_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ +}; + +static void alc888_acer_aspire_4930g_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned int bits; + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); +} + +static void alc888_acer_aspire_4930g_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if (res >> 26 == ALC880_HP_EVENT) + alc888_acer_aspire_4930g_automute(codec); +} + +/* * ALC880 3-stack model * * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e) @@ -1205,49 +1495,126 @@ static struct snd_kcontrol_new alc880_three_stack_mixer[] = { }; /* capture mixer elements */ -static struct snd_kcontrol_new alc880_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 3, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ -}; +static int alc_cap_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int err; -/* capture mixer elements (in case NID 0x07 not available) */ -static struct snd_kcontrol_new alc880_capture_alt_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ -}; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, + HDA_INPUT); + err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); + mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */ + return err; +} + +static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int err; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, + HDA_INPUT); + err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); + mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */ + return err; +} + +typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + getput_call_t func) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + int err; + + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[adc_idx], + 3, 0, HDA_INPUT); + err = func(kcontrol, ucontrol); + mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */ + return err; +} + +static int alc_cap_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_volume_get); +} + +static int alc_cap_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_volume_put); +} + +/* capture mixer elements */ +#define alc_cap_sw_info snd_ctl_boolean_stereo_info + +static int alc_cap_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_switch_get); +} + +static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_switch_put); +} + +#define DEFINE_CAPMIX(num) \ +static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Capture Switch", \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .count = num, \ + .info = alc_cap_sw_info, \ + .get = alc_cap_sw_get, \ + .put = alc_cap_sw_put, \ + }, \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Capture Volume", \ + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \ + .count = num, \ + .info = alc_cap_vol_info, \ + .get = alc_cap_vol_get, \ + .put = alc_cap_vol_put, \ + .tlv = { .c = alc_cap_vol_tlv }, \ + }, \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + /* .name = "Capture Source", */ \ + .name = "Input Source", \ + .count = num, \ + .info = alc_mux_enum_info, \ + .get = alc_mux_enum_get, \ + .put = alc_mux_enum_put, \ + }, \ + { } /* end */ \ +} + +/* up to three ADCs */ +DEFINE_CAPMIX(1); +DEFINE_CAPMIX(2); +DEFINE_CAPMIX(3); /* @@ -1533,18 +1900,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, { } /* end */ }; @@ -1619,6 +1974,7 @@ static const char *alc_slave_vols[] = { "Speaker Playback Volume", "Mono Playback Volume", "Line-Out Playback Volume", + "PCM Playback Volume", NULL, }; @@ -1638,6 +1994,9 @@ static const char *alc_slave_sws[] = { /* * build control elements */ + +static void alc_free_kctls(struct hda_codec *codec); + static int alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1649,7 +2008,11 @@ static int alc_build_controls(struct hda_codec *codec) if (err < 0) return err; } - + if (spec->cap_mixer) { + err = snd_hda_add_new_ctls(codec, spec->cap_mixer); + if (err < 0) + return err; + } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); @@ -1684,6 +2047,7 @@ static int alc_build_controls(struct hda_codec *codec) return err; } + alc_free_kctls(codec); /* no longer needed */ return 0; } @@ -2774,19 +3138,27 @@ static int alc_build_pcms(struct hda_codec *codec) return 0; } +static void alc_free_kctls(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); +} + static void alc_free(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int i; if (!spec) return; - if (spec->kctl_alloc) { - for (i = 0; i < spec->num_kctl_used; i++) - kfree(spec->kctl_alloc[i].name); - kfree(spec->kctl_alloc); - } + alc_free_kctls(codec); kfree(spec); codec->spec = NULL; /* to be sure */ } @@ -3268,6 +3640,8 @@ static struct alc_config_preset alc880_presets[] = { alc880_gpio2_init_verbs }, .num_dacs = ARRAY_SIZE(alc880_dac_nids), .dac_nids = alc880_dac_nids, + .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */ + .num_adc_nids = 1, /* single ADC */ .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), .channel_mode = alc880_2_jack_modes, @@ -3532,9 +3906,6 @@ static struct alc_config_preset alc880_presets[] = { * Automatic parse of I/O pins from the BIOS configuration */ -#define NUM_CONTROL_ALLOC 32 -#define NUM_VERB_ALLOC 32 - enum { ALC_CTL_WIDGET_VOL, ALC_CTL_WIDGET_MUTE, @@ -3552,29 +3923,15 @@ static int add_control(struct alc_spec *spec, int type, const char *name, { struct snd_kcontrol_new *knew; - if (spec->num_kctl_used >= spec->num_kctl_alloc) { - int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; - - /* array + terminator */ - knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); - if (!knew) - return -ENOMEM; - if (spec->kctl_alloc) { - memcpy(knew, spec->kctl_alloc, - sizeof(*knew) * spec->num_kctl_alloc); - kfree(spec->kctl_alloc); - } - spec->kctl_alloc = knew; - spec->num_kctl_alloc = num; - } - - knew = &spec->kctl_alloc[spec->num_kctl_used]; + snd_array_init(&spec->kctls, sizeof(*knew), 32); + knew = snd_array_new(&spec->kctls); + if (!knew) + return -ENOMEM; *knew = alc880_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; knew->private_value = val; - spec->num_kctl_used++; return 0; } @@ -3898,10 +4255,10 @@ static int alc880_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) spec->dig_in_nid = ALC880_DIGIN_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); - spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs; + add_verb(spec, alc880_volume_init_verbs); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; @@ -3925,6 +4282,17 @@ static void alc880_auto_init(struct hda_codec *codec) * OK, here we have finally the patch for ALC880 */ +static void set_capture_mixer(struct alc_spec *spec) +{ + static struct snd_kcontrol_new *caps[3] = { + alc_capture_mixer1, + alc_capture_mixer2, + alc_capture_mixer3, + }; + if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) + spec->cap_mixer = caps[spec->num_adc_nids - 1]; +} + static int patch_alc880(struct hda_codec *codec) { struct alc_spec *spec; @@ -3980,16 +4348,12 @@ static int patch_alc880(struct hda_codec *codec) if (wcap != AC_WID_AUD_IN) { spec->adc_nids = alc880_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt); - spec->mixers[spec->num_mixers] = - alc880_capture_alt_mixer; - spec->num_mixers++; } else { spec->adc_nids = alc880_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids); - spec->mixers[spec->num_mixers] = alc880_capture_mixer; - spec->num_mixers++; } } + set_capture_mixer(spec); spec->vmaster_nid = 0x0c; @@ -4000,6 +4364,7 @@ static int patch_alc880(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc880_loopbacks; #endif + codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -4024,11 +4389,6 @@ static hda_nid_t alc260_adc_nids_alt[1] = { 0x05, }; -static hda_nid_t alc260_hp_adc_nids[2] = { - /* ADC1, 0 */ - 0x05, 0x04 -}; - /* NIDs used when simultaneous access to both ADCs makes sense. Note that * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC. */ @@ -4157,13 +4517,13 @@ static void alc260_hp_master_update(struct hda_codec *codec, struct alc_spec *spec = codec->spec; unsigned int val = spec->master_sw ? PIN_HP : 0; /* change HP and line-out pins */ - snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_codec_write(codec, line, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); /* mono (speaker) depending on the HP jack sense */ val = (val && !spec->jack_present) ? PIN_OUT : 0; - snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_codec_write(codec, mono, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); } @@ -4242,7 +4602,7 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = { .info = snd_ctl_boolean_mono_info, .get = alc260_hp_master_sw_get, .put = alc260_hp_master_sw_put, - .private_value = (0x10 << 16) | (0x15 << 8) | 0x11 + .private_value = (0x15 << 16) | (0x10 << 8) | 0x11 }, HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), @@ -4295,7 +4655,7 @@ static void alc260_hp_3013_automute(struct hda_codec *codec) present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0); spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; - alc260_hp_master_update(codec, 0x10, 0x15, 0x11); + alc260_hp_master_update(codec, 0x15, 0x10, 0x11); } static void alc260_hp_3013_unsol_event(struct hda_codec *codec, @@ -4427,45 +4787,6 @@ static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = { { } /* end */ }; -/* capture mixer elements */ -static struct snd_kcontrol_new alc260_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ -}; - -static struct snd_kcontrol_new alc260_capture_alt_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ -}; - /* * initialization verbs */ @@ -5282,7 +5603,6 @@ static struct hda_verb alc260_volume_init_verbs[] = { static int alc260_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int wcap; int err; static hda_nid_t alc260_ignore[] = { 0x17, 0 }; @@ -5293,7 +5613,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec) err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; - if (!spec->kctl_alloc) + if (!spec->kctls.list) return 0; /* can't find valid BIOS pin config */ err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg); if (err < 0) @@ -5303,28 +5623,14 @@ static int alc260_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = ALC260_DIGOUT_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); - spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs; + add_verb(spec, alc260_volume_init_verbs); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; - /* check whether NID 0x04 is valid */ - wcap = get_wcaps(codec, 0x04); - wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */ - if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { - spec->adc_nids = alc260_adc_nids_alt; - spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); - spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer; - } else { - spec->adc_nids = alc260_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); - spec->mixers[spec->num_mixers] = alc260_capture_mixer; - } - spec->num_mixers++; - store_pin_configs(codec); return 1; } @@ -5394,12 +5700,11 @@ static struct alc_config_preset alc260_presets[] = { [ALC260_BASIC] = { .mixers = { alc260_base_output_mixer, alc260_input_mixer, - alc260_pc_beep_mixer, - alc260_capture_mixer }, + alc260_pc_beep_mixer }, .init_verbs = { alc260_init_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), + .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), .adc_nids = alc260_adc_nids, .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, @@ -5407,14 +5712,13 @@ static struct alc_config_preset alc260_presets[] = { }, [ALC260_HP] = { .mixers = { alc260_hp_output_mixer, - alc260_input_mixer, - alc260_capture_alt_mixer }, + alc260_input_mixer }, .init_verbs = { alc260_init_verbs, alc260_hp_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), - .adc_nids = alc260_hp_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, @@ -5423,14 +5727,13 @@ static struct alc_config_preset alc260_presets[] = { }, [ALC260_HP_DC7600] = { .mixers = { alc260_hp_dc7600_mixer, - alc260_input_mixer, - alc260_capture_alt_mixer }, + alc260_input_mixer }, .init_verbs = { alc260_init_verbs, alc260_hp_dc7600_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), - .adc_nids = alc260_hp_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, @@ -5439,14 +5742,13 @@ static struct alc_config_preset alc260_presets[] = { }, [ALC260_HP_3013] = { .mixers = { alc260_hp_3013_mixer, - alc260_input_mixer, - alc260_capture_alt_mixer }, + alc260_input_mixer }, .init_verbs = { alc260_hp_3013_init_verbs, alc260_hp_3013_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), - .adc_nids = alc260_hp_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, @@ -5454,8 +5756,7 @@ static struct alc_config_preset alc260_presets[] = { .init_hook = alc260_hp_3013_automute, }, [ALC260_FUJITSU_S702X] = { - .mixers = { alc260_fujitsu_mixer, - alc260_capture_mixer }, + .mixers = { alc260_fujitsu_mixer }, .init_verbs = { alc260_fujitsu_init_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, @@ -5467,8 +5768,7 @@ static struct alc_config_preset alc260_presets[] = { .input_mux = alc260_fujitsu_capture_sources, }, [ALC260_ACER] = { - .mixers = { alc260_acer_mixer, - alc260_capture_mixer }, + .mixers = { alc260_acer_mixer }, .init_verbs = { alc260_acer_init_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, @@ -5480,8 +5780,7 @@ static struct alc_config_preset alc260_presets[] = { .input_mux = alc260_acer_capture_sources, }, [ALC260_WILL] = { - .mixers = { alc260_will_mixer, - alc260_capture_mixer }, + .mixers = { alc260_will_mixer }, .init_verbs = { alc260_init_verbs, alc260_will_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, @@ -5493,8 +5792,7 @@ static struct alc_config_preset alc260_presets[] = { .input_mux = &alc260_capture_source, }, [ALC260_REPLACER_672V] = { - .mixers = { alc260_replacer_672v_mixer, - alc260_capture_mixer }, + .mixers = { alc260_replacer_672v_mixer }, .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, @@ -5509,8 +5807,7 @@ static struct alc_config_preset alc260_presets[] = { }, #ifdef CONFIG_SND_DEBUG [ALC260_TEST] = { - .mixers = { alc260_test_mixer, - alc260_capture_mixer }, + .mixers = { alc260_test_mixer }, .init_verbs = { alc260_test_init_verbs }, .num_dacs = ARRAY_SIZE(alc260_test_dac_nids), .dac_nids = alc260_test_dac_nids, @@ -5569,6 +5866,21 @@ static int patch_alc260(struct hda_codec *codec) spec->stream_digital_playback = &alc260_pcm_digital_playback; spec->stream_digital_capture = &alc260_pcm_digital_capture; + if (!spec->adc_nids && spec->input_mux) { + /* check whether NID 0x04 is valid */ + unsigned int wcap = get_wcaps(codec, 0x04); + wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + /* get type */ + if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { + spec->adc_nids = alc260_adc_nids_alt; + spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); + } else { + spec->adc_nids = alc260_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); + } + } + set_capture_mixer(spec); + spec->vmaster_nid = 0x08; codec->patch_ops = alc_patch_ops; @@ -5578,6 +5890,7 @@ static int patch_alc260(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc260_loopbacks; #endif + codec->proc_widget_hook = print_realtek_coef; |