diff options
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 2631 |
1 files changed, 9 insertions, 2622 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 1dc7e974f3b1..7627a69ca6d7 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -34,27 +34,6 @@ #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 -#define CXT_PIN_DIR_INOUT 0x02 -#define CXT_PIN_DIR_IN_NOMICBIAS 0x03 -#define CXT_PIN_DIR_INOUT_NOMICBIAS 0x04 - -#define CONEXANT_HP_EVENT 0x37 -#define CONEXANT_MIC_EVENT 0x38 -#define CONEXANT_LINE_EVENT 0x39 - -/* Conexant 5051 specific */ - -#define CXT5051_SPDIF_OUT 0x12 -#define CXT5051_PORTB_EVENT 0x38 -#define CXT5051_PORTC_EVENT 0x39 - -#define AUTO_MIC_PORTB (1 << 1) -#define AUTO_MIC_PORTC (1 << 2) - struct conexant_spec { struct hda_gen_spec gen; @@ -72,64 +51,6 @@ struct conexant_spec { 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; - - const struct hda_verb *init_verbs[5]; /* initialization verbs - * don't forget NULL - * termination! - */ - unsigned int num_init_verbs; - - /* playback */ - struct hda_multi_out multiout; /* playback set-up - * max_channels, dacs must be set - * dig_out_nid and hp_nid are optional - */ - unsigned int cur_eapd; - unsigned int hp_present; - unsigned int line_present; - unsigned int auto_mic; - - /* capture */ - unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - hda_nid_t dig_in_nid; /* digital-in NID; optional */ - - unsigned int cur_adc_idx; - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - - const struct hda_pcm_stream *capture_stream; - - /* capture source */ - const struct hda_input_mux *input_mux; - const hda_nid_t *capsrc_nids; - unsigned int cur_mux[3]; - - /* channel model */ - const struct hda_channel_mode *channel_mode; - int num_channel_mode; - - /* PCM information */ - struct hda_pcm pcm_rec[2]; /* used in build_pcms() */ - - unsigned int spdif_route; - - unsigned int port_d_mode; - 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 mic_boost; /* offset into cxt5066_analog_mic_boost */ -#endif /* ENABLE_CXT_STATIC_QUIRKS */ }; @@ -173,2533 +94,6 @@ static int add_beep_ctls(struct hda_codec *codec) #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) -{ - struct conexant_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int conexant_playback_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; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, - format, substream); -} - -static int conexant_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int conexant_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int conexant_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int conexant_dig_playback_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; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, - format, substream); -} - -/* - * Analog capture - */ -static int conexant_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; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - stream_tag, 0, format); - return 0; -} - -static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct conexant_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); - return 0; -} - - - -static const struct hda_pcm_stream conexant_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .open = conexant_playback_pcm_open, - .prepare = conexant_playback_pcm_prepare, - .cleanup = conexant_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream conexant_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = conexant_capture_pcm_prepare, - .cleanup = conexant_capture_pcm_cleanup - }, -}; - - -static const struct hda_pcm_stream conexant_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .open = conexant_dig_playback_pcm_open, - .close = conexant_dig_playback_pcm_close, - .prepare = conexant_dig_playback_pcm_prepare - }, -}; - -static const struct hda_pcm_stream conexant_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static int cx5051_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; - spec->cur_adc = spec->adc_nids[spec->cur_adc_idx]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); - return 0; -} - -static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - 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 cx5051_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = cx5051_capture_pcm_prepare, - .cleanup = cx5051_capture_pcm_cleanup - }, -}; - -static int conexant_build_pcms(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "CONEXANT Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dac_nids[0]; - if (spec->capture_stream) - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *spec->capture_stream; - else { - if (codec->vendor_id == 0x14f15051) - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - cx5051_pcm_analog_capture; - else { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - conexant_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = - spec->num_adc_nids; - } - } - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - - if (spec->multiout.dig_out_nid) { - info++; - codec->num_pcms++; - info->name = "Conexant Digital"; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - conexant_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dig_out_nid; - if (spec->dig_in_nid) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - conexant_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->dig_in_nid; - } - } - - return 0; -} - -static int conexant_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} - -static int conexant_mux_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; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static int conexant_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; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->capsrc_nids[adc_idx], - &spec->cur_mux[adc_idx]); -} - -static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - if (power_state == AC_PWRST_D3) - msleep(100); - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, - power_state); - /* 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); -} - -static int conexant_init(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - return 0; -} - -static void conexant_free(struct hda_codec *codec) -{ - kfree(codec->spec); -} - -static const struct snd_kcontrol_new cxt_capture_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = conexant_mux_enum_info, - .get = conexant_mux_enum_get, - .put = conexant_mux_enum_put - }, - {} -}; - -static const char * const slave_pfxs[] = { - "Headphone", "Speaker", "Bass Speaker", "Front", "Surround", "CLFE", - NULL -}; - -static int conexant_build_controls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int i; - int err; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid); - if (err < 0) - return err; - } - - /* if we have no master control, let's create it */ - if (spec->vmaster_nid && - !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - 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_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_pfxs, - "Playback Switch"); - if (err < 0) - return err; - } - - if (spec->input_mux) { - err = snd_hda_add_new_ctls(codec, cxt_capture_mixers); - if (err < 0) - return err; - } - - err = add_beep_ctls(codec); - if (err < 0) - return err; - - return 0; -} - -static const struct hda_codec_ops conexant_patch_ops = { - .build_controls = conexant_build_controls, - .build_pcms = conexant_build_pcms, - .init = conexant_init, - .free = conexant_free, - .set_power_state = conexant_set_power, -}; - -static int patch_conexant_auto(struct hda_codec *codec); -/* - * EAPD control - * the private value = nid | (invert << 8) - */ - -#define cxt_eapd_info snd_ctl_boolean_mono_info - -static int cxt_eapd_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 invert = (kcontrol->private_value >> 8) & 1; - if (invert) - ucontrol->value.integer.value[0] = !spec->cur_eapd; - else - ucontrol->value.integer.value[0] = spec->cur_eapd; - return 0; - -} - -static int cxt_eapd_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 invert = (kcontrol->private_value >> 8) & 1; - hda_nid_t nid = kcontrol->private_value & 0xff; - unsigned int eapd; - - eapd = !!ucontrol->value.integer.value[0]; - if (invert) - eapd = !eapd; - if (eapd == spec->cur_eapd) - return 0; - - spec->cur_eapd = eapd; - snd_hda_codec_write_cache(codec, nid, - 0, AC_VERB_SET_EAPD_BTLENABLE, - eapd ? 0x02 : 0x00); - return 1; -} - -/* controls for test mode */ -#ifdef CONFIG_SND_DEBUG - -#define CXT_EAPD_SWITCH(xname, nid, mask) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .info = cxt_eapd_info, \ - .get = cxt_eapd_get, \ - .put = cxt_eapd_put, \ - .private_value = nid | (mask<<16) } - - - -static int conexant_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode, - spec->num_channel_mode); -} - -static int conexant_ch_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; - return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - spec->multiout.max_channels); -} - -static int conexant_ch_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; - int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, - &spec->multiout.max_channels); - return err; -} - -#define CXT_PIN_MODE(xname, nid, dir) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .info = conexant_ch_mode_info, \ - .get = conexant_ch_mode_get, \ - .put = conexant_ch_mode_put, \ - .private_value = nid | (dir<<16) } - -#endif /* CONFIG_SND_DEBUG */ - -/* Conexant 5045 specific */ - -static const hda_nid_t cxt5045_dac_nids[1] = { 0x19 }; -static const hda_nid_t cxt5045_adc_nids[1] = { 0x1a }; -static const hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a }; -#define CXT5045_SPDIF_OUT 0x18 - -static const struct hda_channel_mode cxt5045_modes[1] = { - { 2, NULL }, -}; - -static const struct hda_input_mux cxt5045_capture_source = { - .num_items = 2, - .items = { - { "Internal Mic", 0x1 }, - { "Mic", 0x2 }, - } -}; - -static const struct hda_input_mux cxt5045_capture_source_benq = { - .num_items = 4, - .items = { - { "Internal Mic", 0x1 }, - { "Mic", 0x2 }, - { "Line", 0x3 }, - { "Mixer", 0x0 }, - } -}; - -/* turn on/off EAPD (+ mute HP) as a master switch */ -static int cxt5045_hp_master_sw_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; - unsigned int bits; - - if (!cxt_eapd_put(kcontrol, ucontrol)) - return 0; - - /* toggle internal speakers mute depending of presence of - * the headphone jack - */ - bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - - bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, 0x11, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - return 1; -} - -/* bind volumes of both NID 0x10 and 0x11 */ -static const struct hda_bind_ctls cxt5045_hp_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -/* toggle input of built-in and mic jack appropriately */ -static void cxt5045_hp_automic(struct hda_codec *codec) -{ - static const struct hda_verb mic_jack_on[] = { - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {} - }; - static const struct hda_verb mic_jack_off[] = { - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {} - }; - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x12); - if (present) - snd_hda_sequence_write(codec, mic_jack_on); - else - snd_hda_sequence_write(codec, mic_jack_off); -} - - -/* mute internal speaker if HP is plugged */ -static void cxt5045_hp_automute(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int bits; - - spec->hp_present = snd_hda_jack_detect(codec, 0x11); - - bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); -} - -/* unsolicited event for HP jack sensing */ -static void cxt5045_hp_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - res >>= 26; - switch (res) { - case CONEXANT_HP_EVENT: - cxt5045_hp_automute(codec); - break; - case CONEXANT_MIC_EVENT: - cxt5045_hp_automic(codec); - break; - - } -} - -static const struct snd_kcontrol_new cxt5045_mixers[] = { - 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), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x17, 0x2, 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, - }, - - {} -}; - -static const struct snd_kcontrol_new cxt5045_benq_mixers[] = { - HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x3, HDA_INPUT), - - {} -}; - -static const struct hda_verb cxt5045_init_verbs[] = { - /* Line in, Mic */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, - /* HP, Amp */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x10, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Record selector: Internal mic */ - {0x1a, AC_VERB_SET_CONNECT_SEL,0x1}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, - /* SPDIF route: PCM */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - { 0x13, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* EAPD */ - {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2 }, /* default on */ - { } /* end */ -}; - -static const struct hda_verb cxt5045_benq_init_verbs[] = { - /* Internal Mic, Mic */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, - /* Line In,HP, Amp */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x10, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x11, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Record selector: Internal mic */ - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, - /* SPDIF route: PCM */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* EAPD */ - {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - { } /* end */ -}; - -static const struct hda_verb cxt5045_hp_sense_init_verbs[] = { - /* pin sensing on HP jack */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, - { } /* end */ -}; - -static const struct hda_verb cxt5045_mic_sense_init_verbs[] = { - /* pin sensing on HP jack */ - {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, - { } /* end */ -}; - -#ifdef CONFIG_SND_DEBUG -/* Test configuration for debugging, modelled after the ALC260 test - * configuration. - */ -static const struct hda_input_mux cxt5045_test_capture_source = { - .num_items = 5, - .items = { - { "MIXER", 0x0 }, - { "MIC1 pin", 0x1 }, - { "LINE1 pin", 0x2 }, - { "HP-OUT pin", 0x3 }, - { "CD pin", 0x4 }, - }, -}; - -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("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), - CXT_PIN_MODE("LINE1 pin mode", 0x12, CXT_PIN_DIR_INOUT), - - /* EAPD Switch Control */ - CXT_EAPD_SWITCH("External Amplifier", 0x10, 0x0), - - /* Loopback mixer controls */ - - 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", - .info = conexant_mux_enum_info, - .get = conexant_mux_enum_get, - .put = conexant_mux_enum_put, - }, - /* Audio input controls */ - HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct hda_verb cxt5045_test_init_verbs[] = { - /* Set connections */ - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 }, - { 0x11, AC_VERB_SET_CONNECT_SEL, 0x0 }, - { 0x12, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* Enable retasking pins as output, initially without power amp */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - - /* Disable digital (SPDIF) pins initially, but users can enable - * them via a mixer switch. In the case of SPDIF-out, this initverb - * payload also sets the generation to 0, output to be in "consumer" - * PCM format, copyright asserted, no pre-emphasis and no validity - * control. - */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0}, - - /* 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 - * input/output buffers as needed. - */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - - /* Mute capture amp left and right */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - - /* Set ADC connection select to match default mixer setting (mic1 - * pin) - */ - {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 */ - {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 */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ - - { } -}; -#endif - - -/* initialize jack-sensing, too */ -static int cxt5045_init(struct hda_codec *codec) -{ - conexant_init(codec); - cxt5045_hp_automute(codec); - return 0; -} - - -enum { - CXT5045_LAPTOP_HPSENSE, - CXT5045_LAPTOP_MICSENSE, - CXT5045_LAPTOP_HPMICSENSE, - CXT5045_BENQ, -#ifdef CONFIG_SND_DEBUG - CXT5045_TEST, -#endif - CXT5045_AUTO, - CXT5045_MODELS -}; - -static const char * const cxt5045_models[CXT5045_MODELS] = { - [CXT5045_LAPTOP_HPSENSE] = "laptop-hpsense", - [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense", - [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense", - [CXT5045_BENQ] = "benq", -#ifdef CONFIG_SND_DEBUG - [CXT5045_TEST] = "test", -#endif - [CXT5045_AUTO] = "auto", -}; - -static const struct snd_pci_quirk cxt5045_cfg_tbl[] = { - 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), - SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", - CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK_MASK(0x1631, 0xff00, 0xc100, "Packard Bell", - CXT5045_LAPTOP_HPMICSENSE), - SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE), - {} -}; - -static int patch_cxt5045(struct hda_codec *codec) -{ - struct conexant_spec *spec; - int board_config; - - board_config = snd_hda_check_board_config(codec, CXT5045_MODELS, - cxt5045_models, - cxt5045_cfg_tbl); - if (board_config < 0) - board_config = CXT5045_AUTO; /* model=auto as default */ - if (board_config == CXT5045_AUTO) - return patch_conexant_auto(codec); - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - codec->single_adc_amp = 1; - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids); - spec->multiout.dac_nids = cxt5045_dac_nids; - spec->multiout.dig_out_nid = CXT5045_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = cxt5045_adc_nids; - spec->capsrc_nids = cxt5045_capsrc_nids; - spec->input_mux = &cxt5045_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = cxt5045_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = cxt5045_init_verbs; - spec->spdif_route = 0; - spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes); - spec->channel_mode = cxt5045_modes; - - set_beep_amp(spec, 0x16, 0, 1); - - codec->patch_ops = conexant_patch_ops; - - switch (board_config) { - case CXT5045_LAPTOP_HPSENSE: - codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - spec->input_mux = &cxt5045_capture_source; - spec->num_init_verbs = 2; - spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; - spec->mixers[0] = cxt5045_mixers; - codec->patch_ops.init = cxt5045_init; - break; - case CXT5045_LAPTOP_MICSENSE: - codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - spec->input_mux = &cxt5045_capture_source; - spec->num_init_verbs = 2; - spec->init_verbs[1] = cxt5045_mic_sense_init_verbs; - spec->mixers[0] = cxt5045_mixers; - codec->patch_ops.init = cxt5045_init; - break; - default: - case CXT5045_LAPTOP_HPMICSENSE: - codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - spec->input_mux = &cxt5045_capture_source; - spec->num_init_verbs = 3; - spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; - spec->init_verbs[2] = cxt5045_mic_sense_init_verbs; - spec->mixers[0] = cxt5045_mixers; - codec->patch_ops.init = cxt5045_init; - break; - case CXT5045_BENQ: - codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; - spec->input_mux = &cxt5045_capture_source_benq; - spec->num_init_verbs = 1; - spec->init_verbs[0] = cxt5045_benq_init_verbs; - spec->mixers[0] = cxt5045_mixers; - spec->mixers[1] = cxt5045_benq_mixers; - spec->num_mixers = 2; - codec->patch_ops.init = cxt5045_init; - break; -#ifdef CONFIG_SND_DEBUG - case CXT5045_TEST: - spec->input_mux = &cxt5045_test_capture_source; - spec->mixers[0] = cxt5045_test_mixer; - spec->init_verbs[0] = cxt5045_test_init_verbs; - break; - -#endif - } - - switch (codec->subsystem_id >> 16) { - case 0x103c: - case 0x1631: - case 0x1734: - case 0x17aa: - /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have - * really bad sound over 0dB on NID 0x17. Fix max PCM level to - * 0 dB (originally it has 0x2b steps with 0dB offset 0x14) - */ - 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)); - break; - } - - if (spec->beep_amp) - snd_hda_attach_beep_device(codec, get_amp_nid_(spec->beep_amp)); - - return 0; -} - - -/* Conexant 5047 specific */ -#define CXT5047_SPDIF_OUT 0x11 - -static const hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */ -static const hda_nid_t cxt5047_adc_nids[1] = { 0x12 }; -static const hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a }; - -static const struct hda_channel_mode cxt5047_modes[1] = { - { 2, NULL }, -}; - -static const struct hda_input_mux cxt5047_toshiba_capture_source = { - .num_items = 2, - .items = { - { "ExtMic", 0x2 }, - { "Line-In", 0x1 }, - } -}; - -/* turn on/off EAPD (+ mute HP) as a master switch */ -static int cxt5047_hp_master_sw_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; - unsigned int bits; - - if (!cxt_eapd_put(kcontrol, ucontrol)) - return 0; - - /* toggle internal speakers mute depending of presence of - * the headphone jack - */ - bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE; - /* NOTE: Conexat codec needs the index for *OUTPUT* amp of - * pin widgets unlike other codecs. In this case, we need to - * set index 0x01 for the volume from the mixer amp 0x19. - */ - snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01, - HDA_AMP_MUTE, bits); - bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, - HDA_AMP_MUTE, bits); - return 1; -} - -/* mute internal speaker if HP is plugged */ -static void cxt5047_hp_automute(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int bits; - - spec->hp_present = snd_hda_jack_detect(codec, 0x13); - - bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; - /* See the note in cxt5047_hp_master_sw_put */ - snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01, - HDA_AMP_MUTE, bits); -} - -/* toggle input of built-in and mic jack appropriately */ -static void cxt5047_hp_automic(struct hda_codec *codec) -{ - static const struct hda_verb mic_jack_on[] = { - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {} - }; - static const struct hda_verb mic_jack_off[] = { - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {} - }; - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x15); - if (present) - snd_hda_sequence_write(codec, mic_jack_on); - else - snd_hda_sequence_write(codec, mic_jack_off); -} - -/* unsolicited event for HP jack sensing */ -static void cxt5047_hp_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case CONEXANT_HP_EVENT: - cxt5047_hp_automute(codec); - break; - case CONEXANT_MIC_EVENT: - cxt5047_hp_automic(codec); - break; - } -} - -static const struct snd_kcontrol_new cxt5047_base_mixers[] = { - HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT), - { |