diff options
author | Takashi Iwai <tiwai@suse.de> | 2014-02-03 09:47:03 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2014-02-03 09:47:03 +0100 |
commit | 63f4b3a475e84c5aa77f720f0f66989c686a14fb (patch) | |
tree | 404dd893b043c49fdd85d27782eee90b438272a5 /sound | |
parent | 4fa71c1550a857ff1dbfe9e99acc1f4cfec5f0d0 (diff) | |
parent | 3d692451eaf213e44f343aa2be06501b56ab2077 (diff) |
Merge branch 'topic/hda' into for-next
The updates of HD-audio drivers for 3.15
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/hda_generic.c | 2 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 23 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 804 |
3 files changed, 395 insertions, 434 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8321a97d5c05..437ef13c5a90 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -762,7 +762,7 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, AC_PWRST_D0); } if (enable && path->multi[i]) - snd_hda_codec_write_cache(codec, nid, 0, + snd_hda_codec_update_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, path->idx[i]); if (has_amp_in(codec, path, i)) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index fa2879a21a50..e87eac552ec5 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -834,18 +834,6 @@ static unsigned int azx_command_addr(u32 cmd) return addr; } -static unsigned int azx_response_addr(u32 res) -{ - unsigned int addr = res & 0xf; - - if (addr >= AZX_MAX_CODECS) { - snd_BUG(); - addr = 0; - } - - return addr; -} - /* send a command */ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) { @@ -907,8 +895,15 @@ static void azx_update_rirb(struct azx *chip) rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */ res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]); res = le32_to_cpu(chip->rirb.buf[rp]); - addr = azx_response_addr(res_ex); - if (res_ex & ICH6_RIRB_EX_UNSOL_EV) + addr = res_ex & 0xf; + if ((addr >= AZX_MAX_CODECS) || !(chip->codec_mask & (1 << addr))) { + snd_printk(KERN_ERR SFX "%s: spurious response %#x:%#x, rp = %d, wp = %d", + pci_name(chip->pci), + res, res_ex, + chip->rirb.rp, wp); + snd_BUG(); + } + else if (res_ex & ICH6_RIRB_EX_UNSOL_EV) snd_hda_queue_unsol_event(chip->bus, res, res_ex); else if (chip->rirb.cmds[addr]) { chip->rirb.res[addr] = res; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 4e0ec146553d..6cc646531ce8 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -35,7 +35,7 @@ #include "hda_jack.h" #include "hda_generic.h" -#define ENABLE_CXT_STATIC_QUIRKS +#undef ENABLE_CXT_STATIC_QUIRKS #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 @@ -68,6 +68,12 @@ struct conexant_spec { 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; @@ -123,19 +129,6 @@ struct conexant_spec { unsigned int hp_laptop:1; unsigned int asus: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 */ }; @@ -253,8 +246,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; @@ -266,8 +257,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; } @@ -673,14 +662,6 @@ static const struct hda_input_mux cxt5045_capture_source_benq = { } }; -static const struct hda_input_mux cxt5045_capture_source_hp530 = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "Internal Mic", 0x2 }, - } -}; - /* 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) @@ -796,28 +777,6 @@ static const struct snd_kcontrol_new cxt5045_benq_mixers[] = { {} }; -static const struct snd_kcontrol_new cxt5045_mixers_hp530[] = { - 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, 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, - }, - - {} -}; - static const struct hda_verb cxt5045_init_verbs[] = { /* Line in, Mic */ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, @@ -1000,7 +959,6 @@ enum { CXT5045_LAPTOP_MICSENSE, CXT5045_LAPTOP_HPMICSENSE, CXT5045_BENQ, - CXT5045_LAPTOP_HP530, #ifdef CONFIG_SND_DEBUG CXT5045_TEST, #endif @@ -1013,7 +971,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 @@ -1021,8 +978,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), @@ -1113,14 +1068,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; @@ -1940,11 +1887,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 }, }; @@ -1997,88 +1939,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_set_pin_ctl_cache(codec, 0x1a, - 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_set_pin_ctl(codec, 0x1e, 0); - - /* external mic, port B */ - snd_hda_set_pin_ctl(codec, 0x1a, - spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); - - /* internal mic, port C */ - snd_hda_set_pin_ctl(codec, 0x1b, - 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) { @@ -2252,23 +2112,6 @@ 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); @@ -2338,124 +2181,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) @@ -2506,43 +2235,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, @@ -2633,67 +2325,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}, @@ -2889,25 +2520,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? */ @@ -2920,7 +2535,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", @@ -2941,10 +2555,8 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { 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), @@ -3030,32 +2642,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; @@ -3238,6 +2829,11 @@ enum { 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, }; /* for hda_fixup_thinkpad_acpi() */ @@ -3315,6 +2911,288 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec, } } +/* OPLC XO 1.5 fixup */ + +/* 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. */ + +#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 void olpc_xo_update_mic_boost(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + 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); + } +} + +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; + + cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]]; + + /* 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); + } +} + +/* mic_autoswitch hook */ +static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct conexant_spec *spec = codec->spec; + int saved_cached_write = codec->cached_write; + + 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); +} + +/* 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; + + /* 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; + } +} + +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; + ucontrol->value.integer.value[0] = spec->dc_enable; + return 0; +} + +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; + int dc_enable = !!ucontrol->value.integer.value[0]; + + if (dc_enable == spec->dc_enable) + return 0; + + spec->dc_enable = dc_enable; + olpc_xo_update_mic_pins(codec); + olpc_xo_update_mic_boost(codec); + return 1; +} + +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; + ucontrol->value.enumerated.item[0] = spec->dc_input_bias; + return 0; +} + +static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo); +} + +static int olpc_xo_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 = &olpc_xo_dc_bias; + unsigned int idx; + + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (spec->dc_input_bias == idx) + return 0; + + spec->dc_input_bias = idx; + if (spec->dc_enable) + olpc_xo_update_mic_pins(codec); + return 1; +} + +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, + }, + {} +}; + +/* 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; + 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 cxt_fixup_olpc_xo(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + int i; + + if (action != HDA_FIXUP_ACT_PROBE) + return; + + 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); + + snd_hda_add_new_ctls(codec, olpc_xo_mixers); + + /* 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; + } + } +} + +/* + * 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) +{ + 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)); +} + +/* + * 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) +{ + 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)); +} /* ThinkPad X200 & co with cxt5051 */ static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { @@ -3400,6 +3278,68 @@ static const struct hda_fixup cxt_fixups[] = { .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, + }, +}; + +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 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 const struct hda_model_fixup cxt5047_fixup_models[] = { + { .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" }, + {} }; static const struct snd_pci_quirk cxt5051_fixups[] = { @@ -3407,10 +3347,16 @@ static const struct snd_pci_quirk cxt5051_fixups[] = { {} }; +static const struct hda_model_fixup cxt5051_fixup_models[] = { + { .id = CXT_PINCFG_LENOVO_X200, .name = "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), @@ -3427,6 +3373,17 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { {} }; +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) */ @@ -3466,19 +3423,28 @@ static int patch_conexant_auto(struct hda_codec *codec) switch (codec->vendor_id) { case 0x14f15045: 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, NULL, cxt5051_fixups, cxt_fixups); + snd_hda_pick_fixup(codec, cxt5051_fixup_models, + cxt5051_fixups, cxt_fixups); break; default: codec->pin_amp_workaround = 1; - snd_hda_pick_fixup(codec, NULL, cxt5066_fixups, cxt_fixups); + snd_hda_pick_fixup(codec, cxt5066_fixup_models, + cxt5066_fixups, cxt_fixups); break; } |