diff options
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 32 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 75 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 339 |
3 files changed, 430 insertions, 16 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ba2098d..501cbc4 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1565,6 +1565,17 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); #endif /* SND_HDA_NEEDS_RESUME */ +static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int ofs) +{ + u32 caps = query_amp_caps(codec, nid, dir); + /* get num steps */ + caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + if (ofs < caps) + caps -= ofs; + return caps; +} + /** * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer * @@ -1579,23 +1590,17 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, u8 chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); unsigned int ofs = get_amp_offset(kcontrol); - u32 caps; - caps = query_amp_caps(codec, nid, dir); - /* num steps */ - caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - if (!caps) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chs == 3 ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs); + if (!uinfo->value.integer.max) { printk(KERN_WARNING "hda_codec: " "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid, kcontrol->id.name); return -EINVAL; } - if (ofs < caps) - caps -= ofs; - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chs == 3 ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = caps; return 0; } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info); @@ -1620,8 +1625,13 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid, int ch, int dir, int idx, unsigned int ofs, unsigned int val) { + unsigned int maxval; + if (val > 0) val += ofs; + maxval = get_amp_max_value(codec, nid, dir, ofs); + if (val > maxval) + val = maxval; return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, HDA_AMP_VOLMASK, val); } diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 2bf2cb5..3b789ee 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -131,6 +131,8 @@ struct conexant_spec { unsigned int dc_enable; unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */ unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ + + unsigned int beep_amp; }; static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -515,6 +517,15 @@ static struct snd_kcontrol_new cxt_capture_mixers[] = { {} }; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/* additional beep mixers; the actual parameters are overwritten at build */ +static struct snd_kcontrol_new cxt_beep_mixer[] = { + HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), + { } /* end */ +}; +#endif + static const char *slave_vols[] = { "Headphone Playback Volume", "Speaker Playback Volume", @@ -580,6 +591,23 @@ static int conexant_build_controls(struct hda_codec *codec) return err; } +#ifdef CONFIG_SND_HDA_INPUT_BEEP + /* create beep controls if needed */ + if (spec->beep_amp) { + struct snd_kcontrol_new *knew; + for (knew = cxt_beep_mixer; knew->name; knew++) { + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = spec->beep_amp; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + } + } +#endif + return 0; } @@ -590,6 +618,13 @@ static struct hda_codec_ops conexant_patch_ops = { .free = conexant_free, }; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +#define set_beep_amp(spec, nid, idx, dir) \ + ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif + /* * EAPD control * the private value = nid | (invert << 8) @@ -1130,9 +1165,10 @@ static int patch_cxt5045(struct hda_codec *codec) spec->num_init_verbs = 1; spec->init_verbs[0] = cxt5045_init_verbs; spec->spdif_route = 0; - spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes), - spec->channel_mode = cxt5045_modes, + spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes); + spec->channel_mode = cxt5045_modes; + set_beep_amp(spec, 0x16, 0, 1); codec->patch_ops = conexant_patch_ops; @@ -1211,6 +1247,9 @@ static int patch_cxt5045(struct hda_codec *codec) break; } + if (spec->beep_amp) + snd_hda_attach_beep_device(codec, spec->beep_amp); + return 0; } @@ -1632,6 +1671,11 @@ static void cxt5051_update_speaker(struct hda_codec *codec) pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); + /* on ideapad there is an aditional speaker (subwoofer) to mute */ + if (spec->ideapad) + snd_hda_codec_write(codec, 0x1b, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl); } /* turn on/off EAPD (+ mute HP) as a master switch */ @@ -1888,6 +1932,13 @@ static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid, #endif } +static struct hda_verb cxt5051_ideapad_init_verbs[] = { + /* Subwoofer */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } /* end */ +}; + /* initialize jack-sensing, too */ static int cxt5051_init(struct hda_codec *codec) { @@ -1917,6 +1968,7 @@ enum { CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */ CXT5051_F700, /* HP Compaq Presario F700 */ CXT5051_TOSHIBA, /* Toshiba M300 & co */ + CXT5051_IDEAPAD, /* Lenovo IdeaPad Y430 */ CXT5051_MODELS }; @@ -1927,6 +1979,7 @@ static const char *cxt5051_models[CXT5051_MODELS] = { [CXT5051_LENOVO_X200] = "lenovo-x200", [CXT5051_F700] = "hp-700", [CXT5051_TOSHIBA] = "toshiba", + [CXT5051_IDEAPAD] = "ideapad", }; static struct snd_pci_quirk cxt5051_cfg_tbl[] = { @@ -1938,6 +1991,7 @@ static struct snd_pci_quirk cxt5051_cfg_tbl[] = { CXT5051_LAPTOP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD), {} }; @@ -1972,6 +2026,8 @@ static int patch_cxt5051(struct hda_codec *codec) spec->cur_adc = 0; spec->cur_adc_idx = 0; + set_beep_amp(spec, 0x13, 0, HDA_OUTPUT); + codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, @@ -1999,8 +2055,16 @@ static int patch_cxt5051(struct hda_codec *codec) spec->mixers[0] = cxt5051_toshiba_mixers; spec->auto_mic = AUTO_MIC_PORTB; break; + case CXT5051_IDEAPAD: + spec->init_verbs[spec->num_init_verbs++] = + cxt5051_ideapad_init_verbs; + spec->ideapad = 1; + break; } + if (spec->beep_amp) + snd_hda_attach_beep_device(codec, spec->beep_amp); + return 0; } @@ -2616,7 +2680,6 @@ static struct snd_kcontrol_new cxt5066_vostro_mixers[] = { .put = cxt5066_mic_boost_mux_enum_put, .private_value = 0x23 | 0x100, }, - HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), {} }; @@ -3014,6 +3077,8 @@ static int patch_cxt5066(struct hda_codec *codec) spec->cur_adc = 0; spec->cur_adc_idx = 0; + set_beep_amp(spec, 0x13, 0, HDA_OUTPUT); + board_config = snd_hda_check_board_config(codec, CXT5066_MODELS, cxt5066_models, cxt5066_cfg_tbl); switch (board_config) { @@ -3062,7 +3127,6 @@ static int patch_cxt5066(struct hda_codec *codec) spec->port_d_mode = 0; spec->dell_vostro = 1; spec->mic_boost = 3; /* default 30dB gain */ - snd_hda_attach_beep_device(codec, 0x13); /* no S/PDIF out */ spec->multiout.dig_out_nid = 0; @@ -3104,6 +3168,9 @@ static int patch_cxt5066(struct hda_codec *codec) break; } + if (spec->beep_amp) + snd_hda_attach_beep_device(codec, spec->beep_amp); + return 0; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ff614dd..a7592f5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -256,6 +256,13 @@ enum { ALC882_MODEL_LAST, }; +/* ALC680 models */ +enum { + ALC680_BASE, + ALC680_AUTO, + ALC680_MODEL_LAST, +}; + /* for GPIO Poll */ #define GPIO_MASK 0x03 @@ -18615,7 +18622,7 @@ static int alc662_parse_auto_config(struct hda_codec *codec) add_verb(spec, alc662_init_verbs); if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || - codec->vendor_id == 0x10ec0665) + codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670) add_verb(spec, alc663_init_verbs); if (codec->vendor_id == 0x10ec0272) @@ -18759,6 +18766,335 @@ static int patch_alc888(struct hda_codec *codec) } /* + * ALC680 support + */ +#define ALC680_DIGOUT_NID ALC880_DIGOUT_NID +#define alc680_modes alc260_modes + +static hda_nid_t alc680_dac_nids[3] = { + /* Lout1, Lout2, hp */ + 0x02, 0x03, 0x04 +}; + +static hda_nid_t alc680_adc_nids[3] = { + /* ADC0-2 */ + /* DMIC, MIC, Line-in*/ + 0x07, 0x08, 0x09 +}; + +static struct snd_kcontrol_new alc680_base_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + { } +}; + +static struct snd_kcontrol_new alc680_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), + { } /* end */ +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb alc680_init_verbs[] = { + /* Unmute DAC0-1 and set vol = 0 */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } +}; + +/* create input playback/capture controls for the given pin */ +static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid, + const char *ctlname, int idx) +{ + hda_nid_t dac; + int err; + + switch (nid) { + case 0x14: + dac = 0x02; + break; + case 0x15: + dac = 0x03; + break; + case 0x16: + dac = 0x04; + break; + default: + return 0; + } + if (spec->multiout.dac_nids[0] != dac && + spec->multiout.dac_nids[1] != dac) { + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, + HDA_COMPOSE_AMP_VAL(dac, 3, idx, + HDA_OUTPUT)); + if (err < 0) + return err; + + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); + + if (err < 0) + return err; + spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac; + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) +{ + hda_nid_t nid; + int err; + + spec->multiout.dac_nids = spec->private_dac_nids; + + nid = cfg->line_out_pins[0]; + if (nid) { + const char *name; + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + name = "Speaker"; + else + name = "Front"; + err = alc680_new_analog_output(spec, nid, name, 0); + if (err < 0) + return err; + } + + nid = cfg->speaker_pins[0]; + if (nid) { + err = alc680_new_analog_output(spec, nid, "Speaker", 0); + if (err < 0) + return err; + } + nid = cfg->hp_pins[0]; + if (nid) { + err = alc680_new_analog_output(spec, nid, "Headphone", 0); + if (err < 0) + return err; + } + + return 0; +} + +static void alc680_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type) +{ + alc_set_pin_output(codec, nid, pin_type); +} + +static void alc680_auto_init_multi_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid = spec->autocfg.line_out_pins[0]; + if (nid) { + int pin_type = get_pin_type(spec->autocfg.line_out_type); + alc680_auto_set_output_and_unmute(codec, nid, pin_type); + } +} + +static void alc680_auto_init_hp_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.hp_pins[0]; + if (pin) + alc680_auto_set_output_and_unmute(codec, pin, PIN_HP); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc680_auto_set_output_and_unmute(codec, pin, PIN_OUT); +} + +/* pcm configuration: identical with ALC880 */ +#define alc680_pcm_analog_playback alc880_pcm_analog_playback +#define alc680_pcm_analog_capture alc880_pcm_analog_capture +#define alc680_pcm_analog_alt_capture alc880_pcm_analog_alt_capture +#define alc680_pcm_digital_playback alc880_pcm_digital_playback + +static struct hda_input_mux alc680_capture_source = { + .num_items = 1, + .items = { + { "Mic", 0x0 }, + }, +}; + +/* + * BIOS auto configuration + */ +static int alc680_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + static hda_nid_t alc680_ignore[] = { 0 }; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc680_ignore); + if (err < 0) + return err; + if (!spec->autocfg.line_outs) { + if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) { + spec->multiout.max_channels = 2; + spec->no_analog = 1; + goto dig_only; + } + return 0; /* can't find valid BIOS pin config */ + } + err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = 2; + + dig_only: + /* digital only support output */ + if (spec->autocfg.dig_outs) { + spec->multiout.dig_out_nid = ALC680_DIGOUT_NID; + spec->dig_out_type = spec->autocfg.dig_out_type[0]; + } + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); + + add_verb(spec, alc680_init_verbs); + spec->num_mux_defs = 1; + spec->input_mux = &alc680_capture_source; + + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + + return 1; +} + +#define alc680_auto_init_analog_input alc882_auto_init_analog_input + +/* init callback for auto-configuration model -- overriding the default init */ +static void alc680_auto_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc680_auto_init_multi_out(codec); + alc680_auto_init_hp_out(codec); + alc680_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_inithook(codec); +} + +/* + * configuration and preset + */ +static const char *alc680_models[ALC680_MODEL_LAST] = { + [ALC680_BASE] = "base", + [ALC680_AUTO] = "auto", +}; + +static struct snd_pci_quirk alc680_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE), + {} +}; + +static struct alc_config_preset alc680_presets[] = { + [ALC680_BASE] = { + .mixers = { alc680_base_mixer }, + .cap_mixer = alc680_capture_mixer, + .init_verbs = { alc680_init_verbs }, + .num_dacs = ARRAY_SIZE(alc680_dac_nids), + .dac_nids = alc680_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc680_adc_nids), + .adc_nids = alc680_adc_nids, + .hp_nid = 0x04, + .dig_out_nid = ALC680_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc680_modes), + .channel_mode = alc680_modes, + .input_mux = &alc680_capture_source, + }, +}; + +static int patch_alc680(struct hda_codec *codec) +{ + struct alc_spec *spec; + int board_config; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST, + alc680_models, + alc680_cfg_tbl); + + if (board_config < 0 || board_config >= ALC680_MODEL_LAST) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = ALC680_AUTO; + } + + if (board_config == ALC680_AUTO) { + /* automatic parse from the BIOS config */ + err = alc680_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC680_BASE; + } + } + + if (board_config != ALC680_AUTO) + setup_preset(codec, &alc680_presets[board_config]); + + spec->stream_analog_playback = &alc680_pcm_analog_playback; + spec->stream_analog_capture = &alc680_pcm_analog_capture; + spec->stream_analog_alt_capture = &alc680_pcm_analog_alt_capture; + spec->stream_digital_playback = &alc680_pcm_digital_playback; + + if (!spec->adc_nids) { + spec->adc_nids = alc680_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc680_adc_nids); + } + + if (!spec->cap_mixer) + set_capture_mixer(codec); + + spec->vmaster_nid = 0x02; + + codec->patch_ops = alc_patch_ops; + if (board_config == ALC680_AUTO) + spec->init_hook = alc680_auto_init; + + return 0; +} + +/* * patch entries */ static struct hda_codec_preset snd_hda_preset_realtek[] = { @@ -18782,6 +19118,7 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 }, { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 }, { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 }, + { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 }, { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 }, |