diff options
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
| -rw-r--r-- | sound/pci/hda/patch_conexant.c | 2416 |
1 files changed, 757 insertions, 1659 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index a7a5733aa4d..1dc7e974f3b 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -23,15 +23,18 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/pci.h> #include <linux/module.h> #include <sound/core.h> #include <sound/jack.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" +#include "hda_generic.h" + +#undef ENABLE_CXT_STATIC_QUIRKS #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 @@ -52,21 +55,25 @@ #define AUTO_MIC_PORTB (1 << 1) #define AUTO_MIC_PORTC (1 << 2) -struct pin_dac_pair { - hda_nid_t pin; - hda_nid_t dac; - int type; -}; +struct conexant_spec { + struct hda_gen_spec gen; -struct imux_info { - hda_nid_t pin; /* input pin NID */ - hda_nid_t adc; /* connected ADC NID */ - hda_nid_t boost; /* optional boost volume NID */ - int index; /* corresponding to autocfg.input */ -}; + unsigned int beep_amp; -struct conexant_spec { + /* extra EAPD pins */ + unsigned int num_eapds; + hda_nid_t eapds[4]; + bool dynamic_eapd; + + unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ + /* OPLC XO specific */ + bool recording; + bool dc_enable; + unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */ + struct nid_path *dc_mode_path; + +#ifdef ENABLE_CXT_STATIC_QUIRKS const struct snd_kcontrol_new *mixers[5]; int num_mixers; hda_nid_t vmaster_nid; @@ -86,11 +93,6 @@ struct conexant_spec { unsigned int hp_present; unsigned int line_present; unsigned int auto_mic; - int auto_mic_ext; /* imux_pins[] index for ext mic */ - int auto_mic_dock; /* imux_pins[] index for dock mic */ - int auto_mic_int; /* imux_pins[] index for int mic */ - unsigned int need_dac_fix; - hda_nid_t slave_dig_outs[2]; /* capture */ unsigned int num_adc_nids; @@ -118,53 +120,61 @@ struct conexant_spec { unsigned int spdif_route; - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct hda_input_mux private_imux; - struct imux_info imux_info[HDA_MAX_NUM_INPUTS]; - hda_nid_t private_adc_nids[HDA_MAX_NUM_INPUTS]; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - struct pin_dac_pair dac_info[8]; - int dac_info_filled; - unsigned int port_d_mode; - unsigned int auto_mute:1; /* used in auto-parser */ - unsigned int detect_line:1; /* Line-out detection enabled */ - unsigned int automute_lines:1; /* automute line-out as well */ - unsigned int automute_hp_lo:1; /* both HP and LO available */ unsigned int dell_automute:1; unsigned int dell_vostro:1; unsigned int ideapad:1; unsigned int thinkpad:1; unsigned int hp_laptop:1; unsigned int asus:1; - unsigned int pin_eapd_ctrls:1; - unsigned int single_adc_amp:1; - - unsigned int adc_switching:1; - - unsigned int ext_mic_present; - unsigned int recording; - void (*capture_prepare)(struct hda_codec *codec); - void (*capture_cleanup)(struct hda_codec *codec); - - /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors) - * through the microphone jack. - * When the user enables this through a mixer switch, both internal and - * external microphones are disabled. Gain is fixed at 0dB. In this mode, - * we also allow the bias to be configured through a separate mixer - * control. */ - 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 */ +#endif /* ENABLE_CXT_STATIC_QUIRKS */ +}; - unsigned int beep_amp; - /* extra EAPD pins */ - unsigned int num_eapds; - hda_nid_t eapds[4]; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static inline void set_beep_amp(struct conexant_spec *spec, hda_nid_t nid, + int idx, int dir) +{ + spec->gen.beep_nid = nid; + spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); +} +/* additional beep mixers; the actual parameters are overwritten at build */ +static const 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 */ }; +/* create beep controls if needed */ +static int add_beep_ctls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int err; + + if (spec->beep_amp) { + const 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; + } + } + return 0; +} +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#define add_beep_ctls(codec) 0 +#endif + + +#ifdef ENABLE_CXT_STATIC_QUIRKS static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) @@ -235,8 +245,6 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - if (spec->capture_prepare) - spec->capture_prepare(codec); snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], stream_tag, 0, format); return 0; @@ -248,8 +256,6 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct conexant_spec *spec = codec->spec; snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); - if (spec->capture_cleanup) - spec->capture_cleanup(codec); return 0; } @@ -377,8 +383,6 @@ static int conexant_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } - if (spec->slave_dig_outs[0]) - codec->slave_dig_outs = spec->slave_dig_outs; } return 0; @@ -426,7 +430,7 @@ static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg, /* partial workaround for "azx_get_response timeout" */ if (power_state == AC_PWRST_D0) msleep(10); - snd_hda_codec_set_power_to_all(codec, fg, power_state, true); + snd_hda_codec_set_power_to_all(codec, fg, power_state); } static int conexant_init(struct hda_codec *codec) @@ -441,7 +445,6 @@ static int conexant_init(struct hda_codec *codec) static void conexant_free(struct hda_codec *codec) { - snd_hda_detach_beep_device(codec); kfree(codec->spec); } @@ -456,30 +459,8 @@ static const struct snd_kcontrol_new cxt_capture_mixers[] = { {} }; -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const 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 * const slave_vols[] = { - "Headphone Playback Volume", - "Speaker Playback Volume", - "Front Playback Volume", - "Surround Playback Volume", - "CLFE Playback Volume", - NULL -}; - -static const char * const slave_sws[] = { - "Headphone Playback Switch", - "Speaker Playback Switch", - "Front Playback Switch", - "Surround Playback Switch", - "CLFE Playback Switch", +static const char * const slave_pfxs[] = { + "Headphone", "Speaker", "Bass Speaker", "Front", "Surround", "CLFE", NULL }; @@ -519,14 +500,16 @@ static int conexant_build_controls(struct hda_codec *codec) snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, slave_vols); + vmaster_tlv, slave_pfxs, + "Playback Volume"); if (err < 0) return err; } if (spec->vmaster_nid && !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_sws); + NULL, slave_pfxs, + "Playback Switch"); if (err < 0) return err; } @@ -537,33 +520,12 @@ 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) { - const 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; -} + err = add_beep_ctls(codec); + if (err < 0) + return err; -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int conexant_suspend(struct hda_codec *codec, pm_message_t state) -{ - snd_hda_shutup_pins(codec); return 0; } -#endif static const struct hda_codec_ops conexant_patch_ops = { .build_controls = conexant_build_controls, @@ -571,19 +533,8 @@ static const struct hda_codec_ops conexant_patch_ops = { .init = conexant_init, .free = conexant_free, .set_power_state = conexant_set_power, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .suspend = conexant_suspend, -#endif - .reboot_notify = snd_hda_shutup_pins, }; -#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 - static int patch_conexant_auto(struct hda_codec *codec); /* * EAPD control @@ -667,8 +618,6 @@ static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol, int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, spec->num_channel_mode, &spec->multiout.max_channels); - if (err >= 0 && spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; return err; } @@ -695,27 +644,18 @@ static const struct hda_channel_mode cxt5045_modes[1] = { static const struct hda_input_mux cxt5045_capture_source = { .num_items = 2, .items = { - { "IntMic", 0x1 }, - { "ExtMic", 0x2 }, + { "Internal Mic", 0x1 }, + { "Mic", 0x2 }, } }; static const struct hda_input_mux cxt5045_capture_source_benq = { - .num_items = 5, - .items = { - { "IntMic", 0x1 }, - { "ExtMic", 0x2 }, - { "LineIn", 0x3 }, - { "CD", 0x4 }, - { "Mixer", 0x0 }, - } -}; - -static const struct hda_input_mux cxt5045_capture_source_hp530 = { - .num_items = 2, + .num_items = 4, .items = { - { "ExtMic", 0x1 }, - { "IntMic", 0x2 }, + { "Internal Mic", 0x1 }, + { "Mic", 0x2 }, + { "Line", 0x3 }, + { "Mixer", 0x0 }, } }; @@ -806,10 +746,8 @@ static void cxt5045_hp_unsol_event(struct hda_codec *codec, } static const struct snd_kcontrol_new cxt5045_mixers[] = { - HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x1a, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Capture Switch", 0x1a, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Capture Volume", 0x1a, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Mic Capture Switch", 0x1a, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x1, HDA_INPUT), @@ -830,42 +768,8 @@ static const struct snd_kcontrol_new cxt5045_mixers[] = { }; static const struct snd_kcontrol_new cxt5045_benq_mixers[] = { - HDA_CODEC_VOLUME("CD Capture Volume", 0x1a, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Capture Switch", 0x1a, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x4, HDA_INPUT), - - HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT), - - HDA_CODEC_VOLUME("Mixer Capture Volume", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mixer Capture Switch", 0x1a, 0x0, HDA_INPUT), - - {} -}; - -static const struct snd_kcontrol_new cxt5045_mixers_hp530[] = { - HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x1a, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Capture Switch", 0x1a, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Capture Volume", 0x1a, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Capture Switch", 0x1a, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x17, 0x1, HDA_INPUT), - HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5045_hp_master_sw_put, - .private_value = 0x10, - }, + HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x3, HDA_INPUT), {} }; @@ -954,10 +858,10 @@ static const struct snd_kcontrol_new cxt5045_test_mixer[] = { /* Output controls */ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x10, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Speaker Playback Switch", 0x10, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Node 11 Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Node 11 Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Node 12 Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Node 12 Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("HP-OUT Playback Volume", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("HP-OUT Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("LINE1 Playback Switch", 0x12, 0x0, HDA_OUTPUT), /* Modes for retasking pin widgets */ CXT_PIN_MODE("HP-OUT pin mode", 0x11, CXT_PIN_DIR_INOUT), @@ -968,16 +872,16 @@ static const struct snd_kcontrol_new cxt5045_test_mixer[] = { /* Loopback mixer controls */ - HDA_CODEC_VOLUME("Mixer-1 Volume", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mixer-1 Switch", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mixer-2 Volume", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mixer-2 Switch", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mixer-3 Volume", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Mixer-3 Switch", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Mixer-4 Volume", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Mixer-4 Switch", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("Mixer-5 Volume", 0x17, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Mixer-5 Switch", 0x17, 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("PCM Volume", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("PCM Switch", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("MIC1 pin Volume", 0x17, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("MIC1 pin Switch", 0x17, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("LINE1 pin Volume", 0x17, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("LINE1 pin Switch", 0x17, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("HP-OUT pin Volume", 0x17, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("HP-OUT pin Switch", 0x17, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("CD pin Volume", 0x17, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD pin Switch", 0x17, 0x4, HDA_INPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Source", @@ -986,16 +890,8 @@ static const struct snd_kcontrol_new cxt5045_test_mixer[] = { .put = conexant_mux_enum_put, }, /* Audio input controls */ - HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT), { } /* end */ }; @@ -1017,10 +913,6 @@ static const struct hda_verb cxt5045_test_init_verbs[] = { {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0}, - /* Start with output sum widgets muted and their output gains at min */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Unmute retasking pin widget output buffers since the default * state appears to be output. As the pin mode is changed by the * user the pin mode control will take care of enabling the pin's @@ -1035,11 +927,11 @@ static const struct hda_verb cxt5045_test_init_verbs[] = { /* Set ADC connection select to match default mixer setting (mic1 * pin) */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Mute all inputs to mixer widget (even unconnected ones) */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer pin */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer */ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Mic1 pin */ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* Line pin */ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* HP pin */ @@ -1064,7 +956,6 @@ enum { CXT5045_LAPTOP_MICSENSE, CXT5045_LAPTOP_HPMICSENSE, CXT5045_BENQ, - CXT5045_LAPTOP_HP530, #ifdef CONFIG_SND_DEBUG CXT5045_TEST, #endif @@ -1077,7 +968,6 @@ static const char * const cxt5045_models[CXT5045_MODELS] = { [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense", [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense", [CXT5045_BENQ] = "benq", - [CXT5045_LAPTOP_HP530] = "laptop-hp530", #ifdef CONFIG_SND_DEBUG [CXT5045_TEST] = "test", #endif @@ -1085,8 +975,6 @@ static const char * const cxt5045_models[CXT5045_MODELS] = { }; static const struct snd_pci_quirk cxt5045_cfg_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530), - SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE), SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ), SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE), @@ -1118,7 +1006,7 @@ static int patch_cxt5045(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - codec->pin_amp_workaround = 1; + codec->single_adc_amp = 1; spec->multiout.max_channels = 2; spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids); @@ -1177,14 +1065,6 @@ static int patch_cxt5045(struct hda_codec *codec) spec->num_mixers = 2; codec->patch_ops.init = cxt5045_init; break; - case CXT5045_LAPTOP_HP530: - codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - spec->input_mux = &cxt5045_capture_source_hp530; - spec->num_init_verbs = 2; - spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; - spec->mixers[0] = cxt5045_mixers_hp530; - codec->patch_ops.init = cxt5045_init; - break; #ifdef CONFIG_SND_DEBUG case CXT5045_TEST: spec->input_mux = &cxt5045_test_capture_source; @@ -1213,7 +1093,7 @@ static int patch_cxt5045(struct hda_codec *codec) } if (spec->beep_amp) - snd_hda_attach_beep_device(codec, spec->beep_amp); + snd_hda_attach_beep_device(codec, get_amp_nid_(spec->beep_amp)); return 0; } @@ -1637,17 +1517,13 @@ static void cxt5051_update_speaker(struct hda_codec *codec) unsigned int pinctl; /* headphone pin */ pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0; - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x16, pinctl); /* speaker pin */ 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 */ + snd_hda_set_pin_ctl(codec, 0x1a, pinctl); + /* on ideapad there is an additional speaker (subwoofer) to mute */ if (spec->ideapad) - snd_hda_codec_write(codec, 0x1b, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1b, pinctl); } /* turn on/off EAPD (+ mute HP) as a master switch */ @@ -1745,8 +1621,8 @@ static const struct snd_kcontrol_new cxt5051_capture_mixers[] = { HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Volume", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Dock Mic Switch", 0x15, 0x00, HDA_INPUT), {} }; @@ -1996,7 +1872,7 @@ static int patch_cxt5051(struct hda_codec *codec) } if (spec->beep_amp) - snd_hda_attach_beep_device(codec, spec->beep_amp); + snd_hda_attach_beep_device(codec, get_amp_nid_(spec->beep_amp)); return 0; } @@ -2008,11 +1884,6 @@ static const hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 }; static const hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 }; static const hda_nid_t cxt5066_digout_pin_nids[2] = { 0x20, 0x22 }; -/* OLPC's microphone port is DC coupled for use with external sensors, - * therefore we use a 50% mic bias in order to center the input signal with - * the DC input range of the codec. */ -#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50 - static const struct hda_channel_mode cxt5066_modes[1] = { { 2, NULL }, }; @@ -2027,13 +1898,13 @@ static void cxt5066_update_speaker(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; unsigned int pinctl; - snd_printdd("CXT5066: update speaker, hp_present=%d, cur_eapd=%d\n", + codec_dbg(codec, + "CXT5066: update speaker, hp_present=%d, cur_eapd=%d\n", spec->hp_present, spec->cur_eapd); /* Port A (HP) */ pinctl = (hp_port_a_present(spec) && spec->cur_eapd) ? PIN_HP : 0; - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x19, pinctl); /* Port D (HP/LO) */ pinctl = spec->cur_eapd ? spec->port_d_mode : 0; @@ -2046,13 +1917,11 @@ static void cxt5066_update_speaker(struct hda_codec *codec) if (!hp_port_d_present(spec)) pinctl = 0; } - snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1c, pinctl); /* CLASS_D AMP */ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; - snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1f, pinctl); } /* turn on/off EAPD (+ mute HP) as a master switch */ @@ -2068,89 +1937,6 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol, return 1; } -static const struct hda_input_mux cxt5066_olpc_dc_bias = { - .num_items = 3, - .items = { - { "Off", PIN_IN }, - { "50%", PIN_VREF50 }, - { "80%", PIN_VREF80 }, - }, -}; - -static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - /* Even though port F is the DC input, the bias is controlled on port B. - * we also leave that port as an active input (but unselected) in DC mode - * just in case that is necessary to make the bias setting take effect. */ - return snd_hda_codec_write_cache(codec, 0x1a, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index); -} - -/* OLPC defers mic widget control until when capture is started because the - * microphone LED comes on as soon as these settings are put in place. if we - * did this before recording, it would give the false indication that recording - * is happening when it is not. */ -static void cxt5066_olpc_select_mic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - if (!spec->recording) - return; - - if (spec->dc_enable) { - /* in DC mode we ignore presence detection and just use the jack - * through our special DC port */ - const struct hda_verb enable_dc_mode[] = { - /* disble internal mic, port C */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* enable DC capture, port F */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {}, - }; - - snd_hda_sequence_write(codec, enable_dc_mode); - /* port B input disabled (and bias set) through the following call */ - cxt5066_set_olpc_dc_bias(codec); - return; - } - - /* disable DC (port F) */ - snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - - /* external mic, port B */ - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); - - /* internal mic, port C */ - snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->ext_mic_present ? 0 : PIN_VREF80); -} - -/* toggle input of built-in and mic jack appropriately */ -static void cxt5066_olpc_automic(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int present; - - if (spec->dc_enable) /* don't do presence detection in DC mode */ - return; - - present = snd_hda_codec_read(codec, 0x1a, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - if (present) - snd_printdd("CXT5066: external microphone detected\n"); - else - snd_printdd("CXT5066: external microphone absent\n"); - - snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, - present ? 0 : 1); - spec->ext_mic_present = !!present; - - cxt5066_olpc_select_mic(codec); -} - /* toggle input of built-in digital mic and mic jack appropriately */ static void cxt5066_vostro_automic(struct hda_codec *codec) { @@ -2182,10 +1968,10 @@ static void cxt5066_vostro_automic(struct hda_codec *codec) present = snd_hda_jack_detect(codec, 0x1a); if (present) { - snd_printdd("CXT5066: external microphone detected\n"); + codec_dbg(codec, "CXT5066: external microphone detected\n"); snd_hda_sequence_write(codec, ext_mic_present); } else { - snd_printdd("CXT5066: external microphone absent\n"); + codec_dbg(codec, "CXT5066: external microphone absent\n"); snd_hda_sequence_write(codec, ext_mic_absent); } } @@ -2210,10 +1996,10 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec) present = snd_hda_jack_detect(codec, 0x1b); if (present) { - snd_printdd("CXT5066: external microphone detected\n"); + codec_dbg(codec, "CXT5066: external microphone detected\n"); snd_hda_sequence_write(codec, ext_mic_present); } else { - snd_printdd("CXT5066: external microphone absent\n"); + codec_dbg(codec, "CXT5066: external microphone absent\n"); snd_hda_sequence_write(codec, ext_mic_absent); } } @@ -2225,7 +2011,7 @@ static void cxt5066_asus_automic(struct hda_codec *codec) unsigned int present; present = snd_hda_jack_detect(codec, 0x1b); - snd_printdd("CXT5066: external microphone present=%d\n", present); + codec_dbg(codec, "CXT5066: external microphone present=%d\n", present); snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, present ? 1 : 0); } @@ -2237,7 +2023,7 @@ static void cxt5066_hp_laptop_automic(struct hda_codec *codec) unsigned int present; present = snd_hda_jack_detect(codec, 0x1b); - snd_printdd("CXT5066: external microphone present=%d\n", present); + codec_dbg(codec, "CXT5066: external microphone present=%d\n", present); snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, present ? 1 : 3); } @@ -2276,13 +2062,13 @@ static void cxt5066_thinkpad_automic(struct hda_codec *codec) ext_present = snd_hda_jack_detect(codec, 0x1b); dock_present = snd_hda_jack_detect(codec, 0x1a); if (ext_present) { - snd_printdd("CXT5066: external microphone detected\n"); + codec_dbg(codec, "CXT5066: external microphone detected\n"); snd_hda_sequence_write(codec, ext_mic_present); } else if (dock_present) { - snd_printdd("CXT5066: dock microphone detected\n"); + codec_dbg(codec, "CXT5066: dock microphone detected\n"); snd_hda_sequence_write(codec, dock_mic_present); } else { - snd_printdd("CXT5066: external microphone absent\n"); + codec_dbg(codec, "CXT5066: external microphone absent\n"); snd_hda_sequence_write(codec, ext_mic_absent); } } @@ -2301,7 +2087,7 @@ static void cxt5066_hp_automute(struct hda_codec *codec) spec->hp_present = portA ? HP_PRESENT_PORT_A : 0; spec->hp_present |= portD ? HP_PRESENT_PORT_D : 0; - snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", + codec_dbg(codec, "CXT5066: hp automute portA=%x portD=%x present=%d\n", portA, portD, spec->hp_present); cxt5066_update_speaker(codec); } @@ -2324,26 +2110,9 @@ static void cxt5066_automic(struct hda_codec *codec) } /* unsolicited event for jack sensing */ -static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct conexant_spec *spec = codec->spec; - snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); - switch (res >> 26) { - case CONEXANT_HP_EVENT: - cxt5066_hp_automute(codec); - break; - case CONEXANT_MIC_EVENT: - /* ignore mic events in DC mode; we're always using the jack */ - if (!spec->dc_enable) - cxt5066_olpc_automic(codec); - break; - } -} - -/* unsolicited event for jack sensing */ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) { - snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); + codec_dbg(codec, "CXT5066: unsol event %x (%x)\n", res, res >> 26); switch (res >> 26) { case CONEXANT_HP_EVENT: cxt5066_hp_automute(codec); @@ -2410,124 +2179,10 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, idx = imux->num_items - 1; spec->mic_boost = idx; - if (!spec->dc_enable) - cxt5066_set_mic_boost(codec); - return 1; -} - -static void cxt5066_enable_dc(struct hda_codec *codec) -{ - const struct hda_verb enable_dc_mode[] = { - /* disable gain */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* switch to DC input */ - {0x17, AC_VERB_SET_CONNECT_SEL, 3}, - {} - }; - - /* configure as input source */ - snd_hda_sequence_write(codec, enable_dc_mode); - cxt5066_olpc_select_mic(codec); /* also sets configured bias */ -} - -static void cxt5066_disable_dc(struct hda_codec *codec) -{ - /* reconfigure input source */ cxt5066_set_mic_boost(codec); - /* automic also selects the right mic if we're recording */ - cxt5066_olpc_automic(codec); -} - -static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = spec->dc_enable; - return 0; -} - -static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - int dc_enable = !!ucontrol->value.integer.value[0]; - - if (dc_enable == spec->dc_enable) - return 0; - - spec->dc_enable = dc_enable; - if (dc_enable) - cxt5066_enable_dc(codec); - else - cxt5066_disable_dc(codec); - return 1; } -static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo); -} - -static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->dc_input_bias; - return 0; -} - -static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; - unsigned int idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - - spec->dc_input_bias = idx; - if (spec->dc_enable) - cxt5066_set_olpc_dc_bias(codec); - return 1; -} - -static void cxt5066_olpc_capture_prepare(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - /* mark as recording and configure the microphone widget so that the - * recording LED comes on. */ - spec->recording = 1; - cxt5066_olpc_select_mic(codec); -} - -static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - const struct hda_verb disable_mics[] = { - /* disable external mic, port B */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* disble internal mic, port C */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* disable DC capture, port F */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {}, - }; - - snd_hda_sequence_write(codec, disable_mics); - spec->recording = 0; -} - static void conexant_check_dig_outs(struct hda_codec *codec, const hda_nid_t *dig_pins, int num_pins) @@ -2542,10 +2197,6 @@ static void conexant_check_dig_outs(struct hda_codec *codec, continue; if (snd_hda_get_connections(codec, *dig_pins, nid_loc, 1) != 1) continue; - if (spec->slave_dig_outs[0]) - nid_loc++; - else - nid_loc = spec->slave_dig_outs; } } @@ -2582,43 +2233,6 @@ static const struct snd_kcontrol_new cxt5066_mixer_master[] = { {} }; -static const struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = snd_hda_mixer_amp_volume_put, - .tlv = { .c = snd_hda_mixer_amp_tlv }, - /* offset by 28 volume steps to limit minimum gain to -46dB */ - .private_value = - HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28), - }, - {} -}; - -static const struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DC Mode Enable Switch", - .info = snd_ctl_boolean_mono_info, - .get = cxt5066_olpc_dc_get, - .put = cxt5066_olpc_dc_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DC Input Bias Enum", - .info = cxt5066_olpc_dc_bias_enum_info, - .get = cxt5066_olpc_dc_bias_enum_get, - .put = cxt5066_olpc_dc_bias_enum_put, - }, - {} -}; - static const struct snd_kcontrol_new cxt5066_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -2709,67 +2323,6 @@ static const struct hda_verb cxt5066_init_verbs[] = { { } /* end */ }; -static const struct hda_verb cxt5066_init_verbs_olpc[] = { - /* Port A: headphones */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* Port B: external microphone */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port C: internal microphone */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port D: unused */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port E: unused, but has primary EAPD */ - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - - /* Port F: external DC input through microphone port */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Port G: internal speakers */ - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ - - /* DAC1 */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* DAC2: unused */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - - /* Disable digital microphone port */ - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* Audio input selectors */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - - /* Disable SPDIF */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - - /* enable unsolicited events for Port A and B */ - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, - { } /* end */ -}; - static const struct hda_verb cxt5066_init_verbs_vostro[] = { /* Port A: headphones */ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, @@ -2955,7 +2508,7 @@ static const struct hda_verb cxt5066_init_verbs_hp_laptop[] = { /* initialize jack-sensing, too */ static int cxt5066_init(struct hda_codec *codec) { - snd_printdd("CXT5066: init\n"); + codec_dbg(codec, "CXT5066: init\n"); conexant_init(codec); if (codec->patch_ops.unsol_event) { cxt5066_hp_automute(codec); @@ -2965,25 +2518,9 @@ static int cxt5066_init(struct hda_codec *codec) return 0; } -static int cxt5066_olpc_init(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - snd_printdd("CXT5066: init\n"); - conexant_init(codec); - cxt5066_hp_automute(codec); - if (!spec->dc_enable) { - cxt5066_set_mic_boost(codec); - cxt5066_olpc_automic(codec); - } else { - cxt5066_enable_dc(codec); - } - return 0; -} - enum { CXT5066_LAPTOP, /* Laptops w/ EAPD support */ CXT5066_DELL_LAPTOP, /* Dell Laptop */ - CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */ CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */ @@ -2996,7 +2533,6 @@ enum { static const char * const cxt5066_models[CXT5066_MODELS] = { [CXT5066_LAPTOP] = "laptop", [CXT5066_DELL_LAPTOP] = "dell-laptop", - [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", [CXT5066_DELL_VOSTRO] = "dell-vostro", [CXT5066_IDEAPAD] = "ideapad", [CXT5066_THINKPAD] = "thinkpad", @@ -3006,36 +2542,24 @@ static const char * const cxt5066_models[CXT5066_MODELS] = { }; static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT5066_AUTO), SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO), SND_PCI_QUIRK(0x1028, 0x02f5, "Dell Vostro 320", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1028, 0x0401, "Dell Vostro 1014", CXT5066_DELL_VOSTRO), - SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO), SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1028, 0x050f, "Dell Inspiron", CXT5066_IDEAPAD), - SND_PCI_QUIRK(0x1028, 0x0510, "Dell Vostro", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP), SND_PCI_QUIRK(0x1043, 0x13f3, "Asus A52J", CXT5066_ASUS), SND_PCI_QUIRK(0x1043, 0x1643, "Asus K52JU", CXT5066_ASUS), SND_PCI_QUIRK(0x1043, 0x1993, "Asus U50F", CXT5066_ASUS), SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD), - SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", CXT5066_LAPTOP), - SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), - SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T510", CXT5066_AUTO), - SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520 & W520", CXT5066_AUTO), - SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), - SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */ - SND_PCI_QUIRK(0x1b0a, 0x2092, "CyberpowerPC Gamer Xplorer N57001", CXT5066_AUTO), {} }; @@ -3116,32 +2640,11 @@ static int patch_cxt5066(struct hda_codec *codec) spec->mic_boost = 3; /* default 30dB gain */ break; - case CXT5066_OLPC_XO_1_5: - codec->patch_ops.init = cxt5066_olpc_init; - codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event; - spec->init_verbs[0] = cxt5066_init_verbs_olpc; - spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; - spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc; - spec->mixers[spec->num_mixers++] = cxt5066_mixers; - spec->port_d_mode = 0; - spec->mic_boost = 3; /* default 30dB gain */ - - /* no S/PDIF out */ - spec->multiout.dig_out_nid = 0; - - /* input source automatically selected */ - spec->input_mux = NULL; - - /* our capture hooks which allow us to turn on the microphone LED - * at the right time */ - spec->capture_prepare = cxt5066_olpc_capture_prepare; - spec->capture_cleanup = cxt5066_olpc_capture_cleanup; - break; case CXT5066_DELL_VOSTRO: codec->patch_ops.init = cxt5066_init; codec->patch_ops.unsol_event = cxt5066_unsol_event; spec->init_verbs[0] = cxt5066_init_verbs_vostro; - spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; + spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; spec->mixers[spec->num_mixers++] = cxt5066_mixers; spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers; spec->port_d_mode = 0; @@ -3189,1234 +2692,808 @@ static int patch_cxt5066(struct hda_codec *codec) } if (spec->beep_amp) - snd_hda_attach_beep_device(codec, spec->beep_amp); + snd_hda_attach_beep_device(codec, get_amp_nid_(spec->beep_amp)); return 0; } +#endif /* ENABLE_CXT_STATIC_QUIRKS */ + + /* * Automatic parser for CX20641 & co */ -static int cx_auto_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t adc = spec->imux_info[spec->cur_mux[0]].adc; - if (spec->adc_switching) { - spec->cur_adc = adc; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - } - snd_hda_codec_setup_stream(codec, adc, stream_tag, 0, format); - return 0; -} - -static int cx_auto_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static void cx_auto_parse_beep(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - return 0; -} - -static const struct hda_pcm_stream cx_auto_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = cx_auto_capture_pcm_prepare, - .cleanup = cx_auto_capture_pcm_cleanup - }, -}; - -static const hda_nid_t cx_auto_adc_nids[] = { 0x14 }; - -#define get_connection_index(codec, mux, nid)\ - snd_hda_get_conn_index(codec, mux, nid, 0) - -/* get an unassigned DAC from the given list. - * Return the nid if found and reduce the DAC list, or return zero if - * not found - */ -static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t *dacs, int *num_dacs) -{ - int i, nums = *num_dacs; - hda_nid_t ret = 0; + hda_nid_t nid, end_nid; - for (i = 0; i < nums; i++) { - if (get_connection_index(codec, pin, dacs[i]) >= 0) { - ret = dacs[i]; + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { + set_beep_amp(spec, nid, 0, HDA_OUTPUT); break; } - } - if (!ret) - return 0; - if (--nums > 0) - memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t)); - *num_dacs = nums; - return ret; } +#else +#define cx_auto_parse_beep(codec) +#endif -#define MAX_AUTO_DACS 5 - -#define DAC_SLAVE_FLAG 0x8000 /* filled dac is a slave */ - -/* fill analog DAC list from the widget tree */ -static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs) +/* parse EAPDs */ +static void cx_auto_parse_eapd(struct hda_codec *codec) { + struct conexant_spec *spec = codec->spec; hda_nid_t nid, end_nid; - int nums = 0; end_nid = codec->start_nid + codec->num_nodes; for (nid = codec->start_nid; nid < end_nid; nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(wcaps); - if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) { - dacs[nums++] = nid; - if (nums >= MAX_AUTO_DACS) - break; - } - } - return nums; -} - -/* fill pin_dac_pair list from the pin and dac list */ -static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins, - int num_pins, hda_nid_t *dacs, int *rest, - struct pin_dac_pair *filled, int nums, - int type) -{ - int i, start = nums; - - for (i = 0; i < num_pins; i++, nums++) { - filled[nums].pin = pins[i]; - filled[nums].type = type; - filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest); - if (filled[nums].dac) - continue; - if (filled[start].dac && get_connection_index(codec, pins[i], filled[start].dac) >= 0) { - filled[nums].dac = filled[start].dac | DAC_SLAVE_FLAG; - continue; - } - if (filled[0].dac && get_connection_index(codec, pins[i], filled[0].dac) >= 0) { - filled[nums].dac = filled[0].dac | DAC_SLAVE_FLAG; + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) continue; - } - snd_printdd("Failed to find a DAC for pin 0x%x", pins[i]); - } - return nums; -} - -/* parse analog output paths */ -static void cx_auto_parse_output(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t dacs[MAX_AUTO_DACS]; - int i, j, nums, rest; - - rest = fill_cx_auto_dacs(codec, dacs); - /* parse all analog output pins */ - nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs, - dacs, &rest, spec->dac_info, 0, - AUTO_PIN_LINE_OUT); - nums = fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs, - dacs, &rest, spec->dac_info, nums, - AUTO_PIN_HP_OUT); - nums = fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs, - dacs, &rest, spec->dac_info, nums, - AUTO_PIN_SPEAKER_OUT); - spec->dac_info_filled = nums; - /* fill multiout struct */ - for (i = 0; i < nums; i++) { - hda_nid_t dac = spec->dac_info[i].dac; - if (!dac || (dac & DAC_SLAVE_FLAG)) + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) continue; - switch (spec->dac_info[i].type) { - case AUTO_PIN_LINE_OUT: - spec->private_dac_nids[spec->multiout.num_dacs] = dac; - spec->multiout.num_dacs++; - break; - case AUTO_PIN_HP_OUT: - case AUTO_PIN_SPEAKER_OUT: - if (!spec->multiout.hp_nid) { - spec->multiout.hp_nid = dac; - break; - } - for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++) - if (!spec->multiout.extra_out_nid[j]) { - spec->multiout.extra_out_nid[j] = dac; - break; - } - break; - } - } - spec->multiout.dac_nids = spec->private_dac_nids; - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - for (i = 0; i < cfg->hp_outs; i++) { - if (is_jack_detectable(codec, cfg->hp_pins[i])) { - spec->auto_mute = 1; + spec->eapds[spec->num_eapds++] = nid; + if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) break; - } - } - if (spec->auto_mute && - cfg->line_out_pins[0] && - cfg->line_out_type != AUTO_PIN_SPEAKER_OUT && - cfg->line_out_pins[0] != cfg->hp_pins[0] && - cfg->line_out_pins[0] != cfg->speaker_pins[0]) { - for (i = 0; i < cfg->line_outs; i++) { - if (is_jack_detectable(codec, cfg->line_out_pins[i])) { - spec->detect_line = 1; - break; - } - } - spec->automute_lines = spec->detect_line; } - spec->vmaster_nid = spec->private_dac_nids[0]; + /* NOTE: below is a wild guess; if we have more than two EAPDs, + * it's a new chip, where EAPDs are supposed to be associated to + * pins, and we can control EAPD per pin. + * OTOH, if only one or two EAPDs are found, it's an old chip, + * thus it might control over all pins. + */ + if (spec->num_eapds > 2) + spec->dynamic_eapd = 1; } static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool on); - -static void do_automute(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool on) + hda_nid_t *pins, bool on) { - struct conexant_spec *spec = codec->spec; int i; - for (i = 0; i < num_pins; i++) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - on ? PIN_OUT : 0); - if (spec->pin_eapd_ctrls) - cx_auto_turn_eapd(codec, num_pins, pins, on); -} - -static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) -{ - int i, present = 0; - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - if (!nid || !is_jack_detectable(codec, nid)) - break; - present |= snd_hda_jack_detect(codec, nid); + if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_EAPD_BTLENABLE, + on ? 0x02 : 0); } - return present; } -/* auto-mute/unmute speaker and line outs according to headphone jack */ -static void cx_auto_update_speakers(struct hda_codec *codec) +/* turn on/off EAPD according to Master switch */ +static void cx_auto_vmaster_hook(void *private_data, int enabled) { + struct hda_codec *codec = private_data; struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int on = 1; - - /* turn on HP EAPD when HP jacks are present */ - if (spec->pin_eapd_ctrls) { - if (spec->auto_mute) - on = spec->hp_present; - cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on); - } - - /* mute speakers in auto-mode if HP or LO jacks are plugged */ - if (spec->auto_mute) - on = !(spec->hp_present || - (spec->detect_line && spec->line_present)); - do_automute(codec, cfg->speaker_outs, cfg->speaker_pins, on); - /* toggle line-out mutes if needed, too */ - /* if LO is a copy of either HP or Speaker, don't need to handle it */ - if (cfg->line_out_pins[0] == cfg->hp_pins[0] || - cfg->line_out_pins[0] == cfg->speaker_pins[0]) - return; - if (spec->auto_mute) { - /* mute LO in auto-mode when HP jack is present */ - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT || - spec->automute_lines) - on = !spec->hp_present; - else - on = 1; - } - do_automute(codec, cfg->line_outs, cfg->line_out_pins, on); + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); } -static void cx_auto_hp_automute(struct hda_codec *codec) +static int cx_auto_build_controls(struct hda_codec *codec) { - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; + int err; - if (!spec->auto_mute) - return; - spec->hp_present = detect_jacks(codec, cfg->hp_outs, cfg->hp_pins); - cx_auto_update_speakers(codec); -} + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; -static void cx_auto_line_automute(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; + err = add_beep_ctls(codec); + if (err < 0) + return err; - if (!spec->auto_mute || !spec->detect_line) - return; - spec->line_present = detect_jacks(codec, cfg->line_outs, - cfg->line_out_pins); - cx_auto_update_speakers(codec); + return 0; } -static int cx_automute_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int cx_auto_init(struct hda_codec *codec) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct conexant_spec *spec = codec->spec; - static const char * const texts2[] = { - "Disabled", "Enabled" - }; - static const char * const texts3[] = { - "Disabled", "Speaker Only", "Line-Out+Speaker" - }; - const char * const *texts; + snd_hda_gen_init(codec); + if (!spec->dynamic_eapd) + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - if (spec->automute_hp_lo) { - uinfo->value.enumerated.items = 3; - texts = texts3; - } else { - uinfo->value.enumerated.items = 2; - texts = texts2; - } - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; -} + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); -static int cx_automute_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - unsigned int val; - if (!spec->auto_mute) - val = 0; - else if (!spec->automute_lines) - val = 1; - else - val = 2; - ucontrol->value.enumerated.item[0] = val; return 0; } -static int cx_automute_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; +#define cx_auto_free snd_hda_gen_free - switch (ucontrol->value.enumerated.item[0]) { - case 0: - if (!spec->auto_mute) - return 0; - spec->auto_mute = 0; - break; - case 1: - if (spec->auto_mute && !spec->automute_lines) - return 0; - spec->auto_mute = 1; - spec->automute_lines = 0; - break; - case 2: - if (!spec->automute_hp_lo) - return -EINVAL; - if (spec->auto_mute && spec->automute_lines) - return 0; - spec->auto_mute = 1; - spec->automute_lines = 1; - break; - default: - return -EINVAL; - } - cx_auto_update_speakers(codec); - return 1; -} +static const struct hda_codec_ops cx_auto_patch_ops = { + .build_controls = cx_auto_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cx_auto_init, + .free = cx_auto_free, + .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM + .check_power_status = snd_hda_gen_check_power_status, +#endif +}; -static const struct snd_kcontrol_new cx_automute_mode_enum[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Auto-Mute Mode", - .info = cx_automute_mode_info, - .get = cx_automute_mode_get, - .put = cx_automute_mode_put, - }, - { } +/* + * pin fix-up + */ +enum { + CXT_PINCFG_LENOVO_X200, + CXT_PINCFG_LENOVO_TP410, + CXT_PINCFG_LEMOTE_A1004, + CXT_PINCFG_LEMOTE_A1205, + CXT_FIXUP_STEREO_DMIC, + CXT_FIXUP_INC_MIC_BOOST, + CXT_FIXUP_HEADPHONE_MIC_PIN, + CXT_FIXUP_HEADPHONE_MIC, + CXT_FIXUP_GPIO1, + CXT_FIXUP_THINKPAD_ACPI, + CXT_FIXUP_OLPC_XO, + CXT_FIXUP_CAP_MIX_AMP, + CXT_FIXUP_TOSHIBA_P105, + CXT_FIXUP_HP_530, + CXT_FIXUP_CAP_MIX_AMP_5047, }; -static int cx_auto_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +/* for hda_fixup_thinkpad_acpi() */ +#include "thinkpad_helper.c" + +static void cxt_fixup_stereo_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct conexant_spec *spec = codec->spec; - - return snd_hda_input_mux_info(&spec->private_imux, uinfo); + spec->gen.inv_dmic_split = 1; } -static int cx_auto_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void cxt5066_increase_mic_boost(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; - ucontrol->value.enumerated.item[0] = spec->cur_mux[0]; - return 0; + snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT, + (0x3 << AC_AMPCAP_OFFSET_SHIFT) | + (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); } -/* look for the route the given pin from mux and return the index; - * if do_select is set, actually select the route. - */ -static int __select_input_connection(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t pin, hda_nid_t *srcp, - bool do_select, int depth) +static void cxt_update_headset_mode(struct hda_codec *codec) { - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; - int i, nums; + /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */ + int i; + bool mic_mode = false; + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; - switch (get_wcaps_type(get_wcaps(codec, mux))) { - case AC_WID_AUD_IN: - case AC_WID_AUD_SEL: - case AC_WID_AUD_MIX: - break; - default: - return -1; - } + hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; - nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) - if (conn[i] == pin) { - if (do_select) - snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_CONNECT_SEL, i); - if (srcp) - *srcp = mux; - return i; - } - depth++; - if (depth == 2) - return -1; - for (i = 0; i < nums; i++) { - int ret = __select_input_connection(codec, conn[i], pin, srcp, - do_select, depth); - if (ret >= 0) { - if (do_select) - snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_CONNECT_SEL, i); - return i; + for (i = 0; i < cfg->num_inputs; i++) + if (cfg->inputs[i].pin == mux_pin) { + mic_mode = !!cfg->inputs[i].is_headphone_mic; + break; } + + if (mic_mode) { + snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */ + spec->gen.hp_jack_present = false; + } else { + snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */ + spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]); } - return -1; -} -static void select_input_connection(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t pin) -{ - __select_input_connection(codec, mux, pin, NULL, true, 0); + snd_hda_gen_update_outputs(codec); } -static int get_input_connection(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t pin) +static void cxt_update_headset_mode_hook(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - return __select_input_connection(codec, mux, pin, NULL, false, 0); + cxt_update_headset_mode(codec); } -static int cx_auto_mux_enum_update(struct hda_codec *codec, - const struct hda_input_mux *imux, - unsigned int idx) +static void cxt_fixup_headphone_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct conexant_spec *spec = codec->spec; - hda_nid_t adc; - int changed = 1; - if (!imux->num_items) - return 0; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (spec->cur_mux[0] == idx) - changed = 0; - adc = spec->imux_info[idx].adc; - select_input_connection(codec, spec->imux_info[idx].adc, - spec->imux_info[idx].pin); - if (spec->cur_adc && spec->cur_adc != adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = adc; - snd_hda_codec_setup_stream(codec, adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC; + break; + case HDA_FIXUP_ACT_PROBE: + spec->gen.cap_sync_hook = cxt_update_headset_mode_hook; + spec->gen.automute_hook = cxt_update_headset_mode; + break; + case HDA_FIXUP_ACT_INIT: + cxt_update_headset_mode(codec); + break; } - spec->cur_mux[0] = idx; - return changed; } -static int cx_auto_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; +/* OPLC XO 1.5 fixup */ - return cx_auto_mux_enum_update(codec, &spec->private_imux, - ucontrol->value.enumerated.item[0]); -} +/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors) + * through the microphone jack. + * When the user enables this through a mixer switch, both internal and + * external microphones are disabled. Gain is fixed at 0dB. In this mode, + * we also allow the bias to be configured through a separate mixer + * control. */ -static const struct snd_kcontrol_new cx_auto_capture_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = cx_auto_mux_enum_info, - .get = cx_auto_mux_enum_get, - .put = cx_auto_mux_enum_put +#define update_mic_pin(codec, nid, val) \ + snd_hda_codec_update_cache(codec, nid, 0, \ + AC_VERB_SET_PIN_WIDGET_CONTROL, val) + +static const struct hda_input_mux olpc_xo_dc_bias = { + .num_items = 3, + .items = { + { "Off", PIN_IN }, + { "50%", PIN_VREF50 }, + { "80%", PIN_VREF80 }, }, - {} }; -static bool select_automic(struct hda_codec *codec, int idx, bool detect) +static void olpc_xo_update_mic_boost(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - if (idx < 0) - return false; - if (detect && !snd_hda_jack_detect(codec, spec->imux_info[idx].pin)) - return false; - cx_auto_mux_enum_update(codec, &spec->private_imux, idx); - return true; + int ch, val; + + for (ch = 0; ch < 2; ch++) { + val = AC_AMP_SET_OUTPUT | + (ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT); + if (!spec->dc_enable) + val |= snd_hda_codec_amp_read(codec, 0x17, ch, HDA_OUTPUT, 0); + snd_hda_codec_write(codec, 0x17, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } } -/* automatic switch internal and external mic */ -static void cx_auto_automic(struct hda_codec *codec) +static void olpc_xo_update_mic_pins(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; + int cur_input, val; + struct nid_path *path; - if (!spec->auto_mic) - return; - if (!select_automic(codec, spec->auto_mic_ext, true)) - if (!select_automic(codec, spec->auto_mic_dock, true)) - select_automic(codec, spec->auto_mic_int, false); -} + cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]]; -static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (snd_hda_jack_get_action(codec, res >> 26)) { - case CONEXANT_HP_EVENT: - cx_auto_hp_automute(codec); - break; - case CONEXANT_LINE_EVENT: - cx_auto_line_automute(codec); - break; - case CONEXANT_MIC_EVENT: - cx_auto_automic(codec); - break; + /* Set up mic pins for port-B, C and F dynamically as the recording + * LED is turned on/off by these pin controls + */ + if (!spec->dc_enable) { + /* disable DC bias path and pin for port F */ + update_mic_pin(codec, 0x1e, 0); + snd_hda_activate_path(codec, spec->dc_mode_path, false, false); + + /* update port B (ext mic) and C (int mic) */ + /* OLPC defers mic widget control until when capture is + * started because the microphone LED comes on as soon as + * these settings are put in place. if we did this before + * recording, it would give the false indication that + * recording is happening when it is not. + */ + update_mic_pin(codec, 0x1a, spec->recording ? + snd_hda_codec_get_pin_target(codec, 0x1a) : 0); + update_mic_pin(codec, 0x1b, spec->recording ? + snd_hda_codec_get_pin_target(codec, 0x1b) : 0); + /* enable normal mic path */ + path = snd_hda_get_path_from_idx(codec, cur_input); + if (path) + snd_hda_activate_path(codec, path, true, false); + } else { + /* disable normal mic path */ + path = snd_hda_get_path_from_idx(codec, cur_input); + if (path) + snd_hda_activate_path(codec, path, false, false); + + /* Even though port F is the DC input, the bias is controlled + * on port B. We also leave that port as an active input (but + * unselected) in DC mode just in case that is necessary to + * make the bias setting take effect. + */ + if (spec->recording) + val = olpc_xo_dc_bias.items[spec->dc_input_bias].index; + else + val = 0; + update_mic_pin(codec, 0x1a, val); + update_mic_pin(codec, 0x1b, 0); + /* enable DC bias path and pin */ + update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0); + snd_hda_activate_path(codec, spec->dc_mode_path, true, false); } - snd_hda_jack_report_sync(codec); } -/* check whether the pin config is suitable for auto-mic switching; - * auto-mic is enabled only when one int-mic and one ext- and/or - * one dock-mic exist - */ -static void cx_auto_check_auto_mic(struct hda_codec *codec) +/* mic_autoswitch hook */ +static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct conexant_spec *spec = codec->spec; - int pset[INPUT_PIN_ATTR_NORMAL + 1]; - int i; + int saved_cached_write = codec->cached_write; - for (i = 0; i < ARRAY_SIZE(pset); i++) - pset[i] = -1; - for (i = 0; i < spec->private_imux.num_items; i++) { - hda_nid_t pin = spec->imux_info[i].pin; - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); - int type, attr; - attr = snd_hda_get_input_pin_attr(def_conf); - if (attr == INPUT_PIN_ATTR_UNUSED) - return; /* invalid entry */ - if (attr > INPUT_PIN_ATTR_NORMAL) - attr = INPUT_PIN_ATTR_NORMAL; - if (attr != INPUT_PIN_ATTR_INT && - !is_jack_detectable(codec, pin)) - return; /* non-detectable pin */ - type = get_defcfg_device(def_conf); - if (type != AC_JACK_MIC_IN && - (attr != INPUT_PIN_ATTR_DOCK || type != AC_JACK_LINE_IN)) - return; /* no valid input type */ - if (pset[attr] >= 0) - return; /* already occupied */ - pset[attr] = i; - } - if (pset[INPUT_PIN_ATTR_INT] < 0 || - (pset[INPUT_PIN_ATTR_NORMAL] < 0 && pset[INPUT_PIN_ATTR_DOCK])) - return; /* no input to switch*/ - spec->auto_mic = 1; - spec->auto_mic_ext = pset[INPUT_PIN_ATTR_NORMAL]; - spec->auto_mic_dock = pset[INPUT_PIN_ATTR_DOCK]; - spec->auto_mic_int = pset[INPUT_PIN_ATTR_INT]; + codec->cached_write = 1; + /* in DC mode, we don't handle automic */ + if (!spec->dc_enable) + snd_hda_gen_mic_autoswitch(codec, jack); + olpc_xo_update_mic_pins(codec); + snd_hda_codec_flush_cache(codec); + codec->cached_write = saved_cached_write; + if (spec->dc_enable) + olpc_xo_update_mic_boost(codec); } -static void cx_auto_parse_input(struct hda_codec *codec) +/* pcm_capture hook */ +static void olpc_xo_capture_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) { struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct hda_input_mux *imux; - int i, j; - - imux = &spec->private_imux; - for (i = 0; i < cfg->num_inputs; i++) { - for (j = 0; j < spec->num_adc_nids; j++) { - hda_nid_t adc = spec->adc_nids[j]; - int idx = get_input_connection(codec, adc, - cfg->inputs[i].pin); - if (idx >= 0) { - const char *label; - label = hda_get_autocfg_input_label(codec, cfg, i); - spec->imux_info[imux->num_items].index = i; - spec->imux_info[imux->num_items].boost = 0; - spec->imux_info[imux->num_items].adc = adc; - spec->imux_info[imux->num_items].pin = - cfg->inputs[i].pin; - snd_hda_add_imux_item(imux, label, idx, NULL); - break; - } - } - } - if (imux->num_items >= 2 && cfg->num_inputs == imux->num_items) - cx_auto_check_auto_mic(codec); - if (imux->num_items > 1) { - for (i = 1; i < imux->num_items; i++) { - if (spec->imux_info[i].adc != spec->imux_info[0].adc) { - spec->adc_switching = 1; - break; - } - } - } -} -/* get digital-input audio widget corresponding to the given pin */ -static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t nid, end_nid; - - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(wcaps); - if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) { - if (get_connection_index(codec, nid, pin) >= 0) - return nid; - } + /* toggle spec->recording flag and update mic pins accordingly + * for turning on/off LED + */ + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + spec->recording = 1; + olpc_xo_update_mic_pins(codec); + break; + case HDA_GEN_PCM_ACT_CLEANUP: + spec->recording = 0; + olpc_xo_update_mic_pins(codec); + break; } - return 0; } -static void cx_auto_parse_digital(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - - if (cfg->dig_outs && - snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1) - spec->multiout.dig_out_nid = nid; - if (cfg->dig_in_pin) - spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin); -} - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -static void cx_auto_parse_beep(struct hda_codec *codec) +static int olpc_xo_dc_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; - - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { - set_beep_amp(spec, nid, 0, HDA_OUTPUT); - break; - } + ucontrol->value.integer.value[0] = spec->dc_enable; + return 0; } -#else -#define cx_auto_parse_beep(codec) -#endif -/* parse EAPDs */ -static void cx_auto_parse_eapd(struct hda_codec *codec) +static int olpc_xo_dc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + int dc_enable = !!ucontrol->value.integer.value[0]; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - continue; - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) - continue; - spec->eapds[spec->num_eapds++] = nid; - if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) - break; - } + if (dc_enable == spec->dc_enable) + return 0; - /* NOTE: below is a wild guess; if we have more than two EAPDs, - * it's a new chip, where EAPDs are supposed to be associated to - * pins, and we can control EAPD per pin. - * OTOH, if only one or two EAPDs are found, it's an old chip, - * thus it might control over all pins. - */ - spec->pin_eapd_ctrls = spec->num_eapds > 2; + spec->dc_enable = dc_enable; + olpc_xo_update_mic_pins(codec); + olpc_xo_update_mic_boost(codec); + return 1; } -static int cx_auto_parse_auto_config(struct hda_codec *codec) +static int olpc_xo_dc_bias_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct conexant_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - return err; - - cx_auto_parse_output(codec); - cx_auto_parse_input(codec); - cx_auto_parse_digital(codec); - cx_auto_parse_beep(codec); - cx_auto_parse_eapd(codec); + ucontrol->value.enumerated.item[0] = spec->dc_input_bias; return 0; } -static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool on) -{ - int i; - for (i = 0; i < num_pins; i++) { - if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_EAPD_BTLENABLE, - on ? 0x02 : 0); - } -} - -static void select_connection(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t src) +static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { - int idx = get_connection_index(codec, pin, src); - if (idx >= 0) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_CONNECT_SEL, idx); + return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo); } -static void mute_outputs(struct hda_codec *codec, int num_nids, - const hda_nid_t *nids) +static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - int i, val; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + const struct hda_input_mux *imux = &olpc_xo_dc_bias; + unsigned int idx; - for (i = 0; i < num_nids; i++) { - hda_nid_t nid = nids[i]; - if (!(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) - continue; - if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE) - val = AMP_OUT_MUTE; - else - val = AMP_OUT_ZERO; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); - } -} + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (spec->dc_input_bias == idx) + return 0; -static void enable_unsol_pins(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, unsigned int action) -{ - int i; - for (i = 0; i < num_pins; i++) - snd_hda_jack_detect_enable(codec, pins[i], action); + spec->dc_input_bias = idx; + if (spec->dc_enable) + olpc_xo_update_mic_pins(codec); + return 1; } -static void cx_auto_init_output(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid; - int i; - - mute_outputs(codec, spec->multiout.num_dacs, spec->multiout.dac_nids); - for (i = 0; i < cfg->hp_outs; i++) - snd_hda_codec_write(codec, cfg->hp_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); - mute_outputs(codec, cfg->hp_outs, cfg->hp_pins); - mute_outputs(codec, cfg->line_outs, cfg->line_out_pins); - mute_outputs(codec, cfg->speaker_outs, cfg->speaker_pins); - for (i = 0; i < spec->dac_info_filled; i++) { - nid = spec->dac_info[i].dac; - if (!nid) - nid = spec->multiout.dac_nids[0]; - else if (nid & DAC_SLAVE_FLAG) - nid &= ~DAC_SLAVE_FLAG; - select_connection(codec, spec->dac_info[i].pin, nid); - } - if (spec->auto_mute) { - enable_unsol_pins(codec, cfg->hp_outs, cfg->hp_pins, - CONEXANT_HP_EVENT); - spec->hp_present = detect_jacks(codec, cfg->hp_outs, - cfg->hp_pins); - if (spec->detect_line) { - enable_unsol_pins(codec, cfg->line_outs, - cfg->line_out_pins, - CONEXANT_LINE_EVENT); - spec->line_present = - detect_jacks(codec, cfg->line_outs, - cfg->line_out_pins); - } - } - cx_auto_update_speakers(codec); - /* turn on all EAPDs if no individual EAPD control is available */ - if (!spec->pin_eapd_ctrls) - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); -} +static const struct snd_kcontrol_new olpc_xo_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DC Mode Enable Switch", + .info = snd_ctl_boolean_mono_info, + .get = olpc_xo_dc_mode_get, + .put = olpc_xo_dc_mode_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DC Input Bias Enum", + .info = olpc_xo_dc_bias_enum_info, + .get = olpc_xo_dc_bias_enum_get, + .put = olpc_xo_dc_bias_enum_put, + }, + {} +}; -static void cx_auto_init_input(struct hda_codec *codec) +/* overriding mic boost put callback; update mic boost volume only when + * DC mode is disabled + */ +static int olpc_xo_mic_boost_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, val; - - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t nid = spec->adc_nids[i]; - if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) - continue; - if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) - val = AMP_IN_MUTE(0); - else - val = AMP_IN_UNMUTE(0); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - val); - } - - for (i = 0; i < cfg->num_inputs; i++) { - unsigned int type; - if (cfg->inputs[i].type == AUTO_PIN_MIC) - type = PIN_VREF80; - else - type = PIN_IN; - snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, type); - } - - if (spec->auto_mic) { - if (spec->auto_mic_ext >= 0) { - snd_hda_jack_detect_enable(codec, - cfg->inputs[spec->auto_mic_ext].pin, - CONEXANT_MIC_EVENT); - } - if (spec->auto_mic_dock >= 0) { - snd_hda_jack_detect_enable(codec, - cfg->inputs[spec->auto_mic_dock].pin, - CONEXANT_MIC_EVENT); - } - cx_auto_automic(codec); - } else { - select_input_connection(codec, spec->imux_info[0].adc, - spec->imux_info[0].pin); - } + int ret = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); + if (ret > 0 && spec->dc_enable) + olpc_xo_update_mic_boost(codec); + return ret; } -static void cx_auto_init_digital(struct hda_codec *codec) +static void cxt_fixup_olpc_xo(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (spec->multiout.dig_out_nid) - snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (spec->dig_in_nid) - snd_hda_codec_write(codec, cfg->dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); -} - -static int cx_auto_init(struct hda_codec *codec) -{ - /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/ - cx_auto_init_output(codec); - cx_auto_init_input(codec); - cx_auto_init_digital(codec); - snd_hda_jack_report_sync(codec); - return 0; -} - -static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, - const char *dir, int cidx, - hda_nid_t nid, int hda_dir, int amp_idx) -{ - static char name[32]; - static struct snd_kcontrol_new knew[] = { - HDA_CODEC_VOLUME(name, 0, 0, 0), - HDA_CODEC_MUTE(name, 0, 0, 0), - }; - static const char * const sfx[2] = { "Volume", "Switch" }; - int i, err; - - for (i = 0; i < 2; i++) { - struct snd_kcontrol *kctl; - knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx, - hda_dir); - knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; - knew[i].index = cidx; - snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]); - kctl = snd_ctl_new1(&knew[i], codec); - if (!kctl) - return -ENOMEM; - err = snd_hda_ctl_add(codec, nid, kctl); - if (err < 0) - return err; - if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE)) - break; - } - return 0; -} + int i; -#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \ - cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0) + if (action != HDA_FIXUP_ACT_PROBE) + return; -#define cx_auto_add_pb_volume(codec, nid, str, idx) \ - cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) + spec->gen.mic_autoswitch_hook = olpc_xo_automic; + spec->gen.pcm_capture_hook = olpc_xo_capture_hook; + spec->dc_mode_path = snd_hda_add_new_path(codec, 0x1e, 0x14, 0); -static int try_add_pb_volume(struct hda_codec *codec, hda_nid_t dac, - hda_nid_t pin, const char *name, int idx) -{ - unsigned int caps; - if (dac && !(dac & DAC_SLAVE_FLAG)) { - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - if (caps & AC_AMPCAP_NUM_STEPS) - return cx_auto_add_pb_volume(codec, dac, name, idx); - } - caps = query_amp_caps(codec, pin, HDA_OUTPUT); - if (caps & AC_AMPCAP_NUM_STEPS) - return cx_auto_add_pb_volume(codec, pin, name, idx); - return 0; -} + snd_hda_add_new_ctls(codec, olpc_xo_mixers); -static int cx_auto_build_output_controls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int i, err; - int num_line = 0, num_hp = 0, num_spk = 0; - static const char * const texts[3] = { "Front", "Surround", "CLFE" }; - - if (spec->dac_info_filled == 1) - return try_add_pb_volume(codec, spec->dac_info[0].dac, - spec->dac_info[0].pin, - "Master", 0); - - for (i = 0; i < spec->dac_info_filled; i++) { - const char *label; - int idx, type; - hda_nid_t dac = spec->dac_info[i].dac; - type = spec->dac_info[i].type; - if (type == AUTO_PIN_LINE_OUT) - type = spec->autocfg.line_out_type; - switch (type) { - case AUTO_PIN_LINE_OUT: - default: - label = texts[num_line++]; - idx = 0; - break; - case AUTO_PIN_HP_OUT: - label = "Headphone"; - idx = num_hp++; - break; - case AUTO_PIN_SPEAKER_OUT: - label = "Speaker"; - idx = num_spk++; + /* OLPC's microphone port is DC coupled for use with external sensors, + * therefore we use a 50% mic bias in order to center the input signal + * with the DC input range of the codec. + */ + snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50); + + /* override mic boost control */ + for (i = 0; i < spec->gen.kctls.used; i++) { + struct snd_kcontrol_new *kctl = + snd_array_elem(&spec->gen.kctls, i); + if (!strcmp(kctl->name, "Mic Boost Volume")) { + kctl->put = olpc_xo_mic_boost_put; break; } - err = try_add_pb_volume(codec, dac, - spec->dac_info[i].pin, - label, idx); - if (err < 0) - return err; - } - - if (spec->auto_mute) { - err = snd_hda_add_new_ctls(codec, cx_automute_mode_enum); - if (err < 0) - return err; } - - return 0; } -static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, - const char *label, const char *pfx, - int cidx) +/* + * Fix max input level on mixer widget to 0dB + * (originally it has 0x2b steps with 0dB offset 0x14) + */ +static void cxt_fixup_cap_mix_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct conexant_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t adc_nid = spec->adc_nids[i]; - int idx = get_input_connection(codec, adc_nid, nid); - if (idx < 0) - continue; - if (spec->single_adc_amp) - idx = 0; - return cx_auto_add_volume_idx(codec, label, pfx, - cidx, adc_nid, HDA_INPUT, idx); - } - return 0; + snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT, + (0x14 << AC_AMPCAP_OFFSET_SHIFT) | + (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); } -static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx, - const char *label, int cidx) +/* + * Fix max input level on mixer widget to 0dB + * (originally it has 0x1e steps with 0 dB offset 0x17) + */ +static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct conexant_spec *spec = codec->spec; - hda_nid_t mux, nid; - int i, con; - - nid = spec->imux_info[idx].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) - return cx_auto_add_volume(codec, label, " Boost", cidx, - nid, HDA_INPUT); - con = __select_input_connection(codec, spec->imux_info[idx].adc, nid, - &mux, false, 0); - if (con < 0) - return 0; - for (i = 0; i < idx; i++) { - if (spec->imux_info[i].boost == mux) - return 0; /* already present */ - } - - if (get_wcaps(codec, mux) & AC_WCAP_OUT_AMP) { - spec->imux_info[idx].boost = mux; - return cx_auto_add_volume(codec, label, " Boost", 0, - mux, HDA_OUTPUT); - } - return 0; + snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); } -static int cx_auto_build_input_controls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux; - const char *prev_label; - int input_conn[HDA_MAX_NUM_INPUTS]; - int i, j, err, cidx; - int multi_connection; - - if (!imux->num_items) - return 0; - - multi_connection = 0; - for (i = 0; i < imux->num_items; i++) { - cidx = get_input_connection(codec, spec->imux_info[i].adc, - spec->imux_info[i].pin); - if (cidx < 0) - continue; - input_conn[i] = spec->imux_info[i].adc; - if (!spec->single_adc_amp) - input_conn[i] |= cidx << 8; - if (i > 0 && input_conn[i] != input_conn[0]) - multi_connection = 1; - } - - prev_label = NULL; - cidx = 0; - for (i = 0; i < imux->num_items; i++) { - hda_nid_t nid = spec->imux_info[i].pin; - const char *label; +/* ThinkPad X200 & co with cxt5051 */ +static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { + { 0x16, 0x042140ff }, /* HP (seq# overridden) */ + { 0x17, 0x21a11000 }, /* dock-mic */ + { 0x19, 0x2121103f }, /* dock-HP */ + { 0x1c, 0x21440100 }, /* dock SPDIF out */ + {} +}; - label = hda_get_autocfg_input_label(codec, &spec->autocfg, - spec->imux_info[i].index); - if (label == prev_label) - cidx++; - else - cidx = 0; - prev_label = label; +/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */ +static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = { + { 0x19, 0x042110ff }, /* HP (seq# overridden) */ + { 0x1a, 0x21a190f0 }, /* dock-mic */ + { 0x1c, 0x212140ff }, /* dock-HP */ + {} +}; - err = cx_auto_add_boost_volume(codec, i, label, cidx); - if (err < 0) - return err; +/* Lemote A1004/A1205 with cxt5066 */ +static const struct hda_pintbl cxt_pincfg_lemote[] = { + { 0x1a, 0x90a10020 }, /* Internal mic */ + { 0x1b, 0x03a11020 }, /* External mic */ + { 0x1d, 0x400101f0 }, /* Not used */ + { 0x1e, 0x40a701f0 }, /* Not used */ + { 0x20, 0x404501f0 }, /* Not used */ + { 0x22, 0x404401f0 }, /* Not used */ + { 0x23, 0x40a701f0 }, /* Not used */ + {} +}; - if (!multi_connection) { - if (i > 0) - continue; - err = cx_auto_add_capture_volume(codec, nid, - "Capture", "", cidx); - } else { - bool dup_found = false; - for (j = 0; j < i; j++) { - if (input_conn[j] == input_conn[i]) { - dup_found = true; - break; - } - } - if (dup_found) - continue; - err = cx_auto_add_capture_volume(codec, nid, - label, " Capture", cidx); +static const struct hda_fixup cxt_fixups[] = { + [CXT_PINCFG_LENOVO_X200] = { + .type = HDA_FIXUP_PINS, + .v.pins = cxt_pincfg_lenovo_x200, + }, + [CXT_PINCFG_LENOVO_TP410] = { + .type = HDA_FIXUP_PINS, + .v.pins = cxt_pincfg_lenovo_tp410, + .chained = true, + .chain_id = CXT_FIXUP_THINKPAD_ACPI, + }, + [CXT_PINCFG_LEMOTE_A1004] = { + .type = HDA_FIXUP_PINS, + .chained = true, + .chain_id = CXT_FIXUP_INC_MIC_BOOST, + .v.pins = cxt_pincfg_lemote, + }, + [CXT_PINCFG_LEMOTE_A1205] = { + .type = HDA_FIXUP_PINS, + .v.pins = cxt_pincfg_lemote, + }, + [CXT_FIXUP_STEREO_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_stereo_dmic, + }, + [CXT_FIXUP_INC_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt5066_increase_mic_boost, + }, + [CXT_FIXUP_HEADPHONE_MIC_PIN] = { + .type = HDA_FIXUP_PINS, + .chained = true, + .chain_id = CXT_FIXUP_HEADPHONE_MIC, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { } } - if (err < 0) - return err; - } - - if (spec->private_imux.num_items > 1 && !spec->auto_mic) { - err = snd_hda_add_new_ctls(codec, cx_auto_capture_mixers); - if (err < 0) - return err; - } - - return 0; -} - -static int cx_auto_build_controls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int err; - - err = cx_auto_build_output_controls(codec); - if (err < 0) - return err; - err = cx_auto_build_input_controls(codec); - if (err < 0) - return err; - err = conexant_build_controls(codec); - if (err < 0) - return err; - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - return 0; -} - -static int cx_auto_search_adcs(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; - - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { - unsigned int caps = get_wcaps(codec, nid); - if (get_wcaps_type(caps) != AC_WID_AUD_IN) - continue; - if (caps & AC_WCAP_DIGITAL) - continue; - if (snd_BUG_ON(spec->num_adc_nids >= - ARRAY_SIZE(spec->private_adc_nids))) - break; - spec->private_adc_nids[spec->num_adc_nids++] = nid; - } - spec->adc_nids = spec->private_adc_nids; - return 0; -} - - -static const struct hda_codec_ops cx_auto_patch_ops = { - .build_controls = cx_auto_build_controls, - .build_pcms = conexant_build_pcms, - .init = cx_auto_init, - .free = conexant_free, - .unsol_event = cx_auto_unsol_event, -#ifdef CONFIG_SND_HDA_POWER_SAVE - .suspend = conexant_suspend, -#endif - .reboot_notify = snd_hda_shutup_pins, + }, + [CXT_FIXUP_HEADPHONE_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_headphone_mic, + }, + [CXT_FIXUP_GPIO1] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 }, + { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 }, + { 0x01, AC_VERB_SET_GPIO_DATA, 0x01 }, + { } + }, + }, + [CXT_FIXUP_THINKPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = hda_fixup_thinkpad_acpi, + }, + [CXT_FIXUP_OLPC_XO] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_olpc_xo, + }, + [CXT_FIXUP_CAP_MIX_AMP] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_cap_mix_amp, + }, + [CXT_FIXUP_TOSHIBA_P105] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x10, 0x961701f0 }, /* speaker/hp */ + { 0x12, 0x02a1901e }, /* ext mic */ + { 0x14, 0x95a70110 }, /* int mic */ + {} + }, + }, + [CXT_FIXUP_HP_530] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x90a60160 }, /* int mic */ + {} + }, + .chained = true, + .chain_id = CXT_FIXUP_CAP_MIX_AMP, + }, + [CXT_FIXUP_CAP_MIX_AMP_5047] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_cap_mix_amp_5047, + }, }; -/* - * pin fix-up - */ -struct cxt_pincfg { - hda_nid_t nid; - u32 val; +static const struct snd_pci_quirk cxt5045_fixups[] = { + SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105), + /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have + * really bad sound over 0dB on NID 0x17. + */ + SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP), + SND_PCI_QUIRK_VENDOR(0x1631, "Packard Bell", CXT_FIXUP_CAP_MIX_AMP), + SND_PCI_QUIRK_VENDOR(0x1734, "Fujitsu", CXT_FIXUP_CAP_MIX_AMP), + SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT_FIXUP_CAP_MIX_AMP), + {} }; -static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg) -{ - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); +static const struct hda_model_fixup cxt5045_fixup_models[] = { + { .id = CXT_FIXUP_CAP_MIX_AMP, .name = "cap-mix-amp" }, + { .id = CXT_FIXUP_TOSHIBA_P105, .name = "toshiba-p105" }, + { .id = CXT_FIXUP_HP_530, .name = "hp-530" }, + {} +}; -} +static const struct snd_pci_quirk cxt5047_fixups[] = { + /* HP laptops have really bad sound over 0 dB on NID 0x10. + */ + SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047), + {} +}; -static void apply_pin_fixup(struct hda_codec *codec, - const struct snd_pci_quirk *quirk, - const struct cxt_pincfg **table) -{ - quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); - if (quirk) { - snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n", - quirk->name); - apply_pincfg(codec, table[quirk->value]); - } -} +static const struct hda_model_fixup cxt5047_fixup_models[] = { + { .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" }, + {} +}; -enum { - CXT_PINCFG_LENOVO_X200, +static const struct snd_pci_quirk cxt5051_fixups[] = { + SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), + {} }; -static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { - { 0x16, 0x042140ff }, /* HP (seq# overridden) */ - { 0x17, 0x21a11000 }, /* dock-mic */ - { 0x19, 0x2121103f }, /* dock-HP */ +static const struct hda_model_fixup cxt5051_fixup_models[] = { + { .id = CXT_PINCFG_LENOVO_X200, .name = "lenovo-x200" }, {} }; -static const struct cxt_pincfg *cxt_pincfg_tbl[] = { - [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200, +static const struct snd_pci_quirk cxt5066_fixups[] = { + SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_GPIO1), + SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), + SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO), + SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI), + SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004), + SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205), + {} }; -static const struct snd_pci_quirk cxt_fixups[] = { - SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), +static const struct hda_model_fixup cxt5066_fixup_models[] = { + { .id = CXT_FIXUP_STEREO_DMIC, .name = "stereo-dmic" }, + { .id = CXT_FIXUP_GPIO1, .name = "gpio1" }, + { .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" }, + { .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" }, + { .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" }, + { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" }, + { .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" }, {} }; +/* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches + * can be created (bko#42825) + */ +static void add_cx5051_fake_mutes(struct hda_codec *codec) +{ + static hda_nid_t out_nids[] = { + 0x10, 0x11, 0 + }; + hda_nid_t *p; + + for (p = out_nids; *p; p++) + snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT, + AC_AMPCAP_MIN_MUTE | + query_amp_caps(codec, *p, HDA_OUTPUT)); +} + static int patch_conexant_auto(struct hda_codec *codec) { struct conexant_spec *spec; int err; - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); + codec_info(codec, "%s: BIOS auto-probing.\n", codec->chip_name); spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; + snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; - codec->pin_amp_workaround = 1; + + cx_auto_parse_beep(codec); + cx_auto_parse_eapd(codec); + spec->gen.own_eapd_ctl = 1; + if (spec->dynamic_eapd) + spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; switch (codec->vendor_id) { case 0x14f15045: - spec->single_adc_amp = 1; + codec->single_adc_amp = 1; + spec->gen.mixer_nid = 0x17; + spec->gen.add_stereo_mix_input = 1; + snd_hda_pick_fixup(codec, cxt5045_fixup_models, + cxt5045_fixups, cxt_fixups); + break; + case 0x14f15047: + codec->pin_amp_workaround = 1; + spec->gen.mixer_nid = 0x19; + spec->gen.add_stereo_mix_input = 1; + snd_hda_pick_fixup(codec, cxt5047_fixup_models, + cxt5047_fixups, cxt_fixups); + break; + case 0x14f15051: + add_cx5051_fake_mutes(codec); + codec->pin_amp_workaround = 1; + snd_hda_pick_fixup(codec, cxt5051_fixup_models, + cxt5051_fixups, cxt_fixups); + break; + default: + codec->pin_amp_workaround = 1; + snd_hda_pick_fixup(codec, cxt5066_fixup_models, + cxt5066_fixups, cxt_fixups); + break; + } + + /* Show mute-led control only on HP laptops + * This is a sort of white-list: on HP laptops, EAPD corresponds + * only to the mute-LED without actualy amp function. Meanwhile, + * others may use EAPD really as an amp switch, so it might be + * not good to expose it blindly. + */ + switch (codec->subsystem_id >> 16) { + case 0x103c: + spec->gen.vmaster_mute_enum = 1; break; } - apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = cx_auto_search_adcs(codec); + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, + spec->parse_flags); if (err < 0) - return err; - err = cx_auto_parse_auto_config(codec); - if (err < 0) { - kfree(codec->spec); - codec->spec = NULL; - return err; - } - spec->capture_stream = &cx_auto_pcm_analog_capture; + goto error; + + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + goto error; + codec->patch_ops = cx_auto_patch_ops; - if (spec->beep_amp) - snd_hda_attach_beep_device(codec, spec->beep_amp); + + /* Some laptops with Conexant chips show stalls in S3 resume, + * which falls into the single-cmd mode. + * Better to make reset, then. + */ + if (!codec->bus->sync_write) { + codec_info(codec, + "Enable sync_write for stable communication\n"); + codec->bus->sync_write = 1; + codec->bus->allow_bus_reset = 1; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; + + error: + cx_auto_free(codec); + return err; } +#ifndef ENABLE_CXT_STATIC_QUIRKS +#define patch_cxt5045 patch_conexant_auto +#define patch_cxt5047 patch_conexant_auto +#define patch_cxt5051 patch_conexant_auto +#define patch_cxt5066 patch_conexant_auto +#endif + /* */ @@ -4455,6 +3532,20 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_conexant_auto }, { .id = 0x14f150b9, .name = "CX20665", .patch = patch_conexant_auto }, + { .id = 0x14f1510f, .name = "CX20751/2", + .patch = patch_conexant_auto }, + { .id = 0x14f15110, .name = "CX20751/2", + .patch = patch_conexant_auto }, + { .id = 0x14f15111, .name = "CX20753/4", + .patch = patch_conexant_auto }, + { .id = 0x14f15113, .name = "CX20755", + .patch = patch_conexant_auto }, + { .id = 0x14f15114, .name = "CX20756", + .patch = patch_conexant_auto }, + { .id = 0x14f15115, .name = "CX20757", + .patch = patch_conexant_auto }, + { .id = 0x14f151d7, .name = "CX20952", + .patch = patch_conexant_auto }, {} /* terminator */ }; @@ -4475,6 +3566,13 @@ MODULE_ALIAS("snd-hda-codec-id:14f150ab"); MODULE_ALIAS("snd-hda-codec-id:14f150ac"); MODULE_ALIAS("snd-hda-codec-id:14f150b8"); MODULE_ALIAS("snd-hda-codec-id:14f150b9"); +MODULE_ALIAS("snd-hda-codec-id:14f1510f"); +MODULE_ALIAS("snd-hda-codec-id:14f15110"); +MODULE_ALIAS("snd-hda-codec-id:14f15111"); +MODULE_ALIAS("snd-hda-codec-id:14f15113"); +MODULE_ALIAS("snd-hda-codec-id:14f15114"); +MODULE_ALIAS("snd-hda-codec-id:14f15115"); +MODULE_ALIAS("snd-hda-codec-id:14f151d7"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); |
