diff options
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 125 |
1 files changed, 85 insertions, 40 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 01e46ba72690..3521f33d43c3 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c | |||
@@ -111,8 +111,12 @@ struct conexant_spec { | |||
111 | 111 | ||
112 | unsigned int dell_automute; | 112 | unsigned int dell_automute; |
113 | unsigned int port_d_mode; | 113 | unsigned int port_d_mode; |
114 | unsigned char ext_mic_bias; | ||
115 | unsigned int dell_vostro; | 114 | unsigned int dell_vostro; |
115 | |||
116 | unsigned int ext_mic_present; | ||
117 | unsigned int recording; | ||
118 | void (*capture_prepare)(struct hda_codec *codec); | ||
119 | void (*capture_cleanup)(struct hda_codec *codec); | ||
116 | }; | 120 | }; |
117 | 121 | ||
118 | static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, | 122 | static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, |
@@ -185,6 +189,8 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo, | |||
185 | struct snd_pcm_substream *substream) | 189 | struct snd_pcm_substream *substream) |
186 | { | 190 | { |
187 | struct conexant_spec *spec = codec->spec; | 191 | struct conexant_spec *spec = codec->spec; |
192 | if (spec->capture_prepare) | ||
193 | spec->capture_prepare(codec); | ||
188 | snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], | 194 | snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], |
189 | stream_tag, 0, format); | 195 | stream_tag, 0, format); |
190 | return 0; | 196 | return 0; |
@@ -196,6 +202,8 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, | |||
196 | { | 202 | { |
197 | struct conexant_spec *spec = codec->spec; | 203 | struct conexant_spec *spec = codec->spec; |
198 | snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); | 204 | snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); |
205 | if (spec->capture_cleanup) | ||
206 | spec->capture_cleanup(codec); | ||
199 | return 0; | 207 | return 0; |
200 | } | 208 | } |
201 | 209 | ||
@@ -2016,53 +2024,53 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol, | |||
2016 | return 1; | 2024 | return 1; |
2017 | } | 2025 | } |
2018 | 2026 | ||
2019 | /* toggle input of built-in and mic jack appropriately */ | 2027 | /* OLPC defers mic widget control until when capture is started because the |
2020 | static void cxt5066_automic(struct hda_codec *codec) | 2028 | * microphone LED comes on as soon as these settings are put in place. if we |
2029 | * did this before recording, it would give the false indication that recording | ||
2030 | * is happening when it is not. */ | ||
2031 | static void cxt5066_olpc_select_mic(struct hda_codec *codec) | ||
2021 | { | 2032 | { |
2022 | struct conexant_spec *spec = codec->spec; | 2033 | struct conexant_spec *spec = codec->spec; |
2023 | struct hda_verb ext_mic_present[] = { | 2034 | if (!spec->recording) |
2024 | /* enable external mic, port B */ | 2035 | return; |
2025 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, | ||
2026 | |||
2027 | /* switch to external mic input */ | ||
2028 | {0x17, AC_VERB_SET_CONNECT_SEL, 0}, | ||
2029 | 2036 | ||
2030 | /* disable internal mic, port C */ | 2037 | /* external mic, port B */ |
2031 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | 2038 | snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, |
2032 | {} | 2039 | spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); |
2033 | }; | ||
2034 | static struct hda_verb ext_mic_absent[] = { | ||
2035 | /* enable internal mic, port C */ | ||
2036 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
2037 | 2040 | ||
2038 | /* switch to internal mic input */ | 2041 | /* internal mic, port C */ |
2039 | {0x17, AC_VERB_SET_CONNECT_SEL, 1}, | 2042 | snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, |
2043 | spec->ext_mic_present ? 0 : PIN_VREF80); | ||
2044 | } | ||
2040 | 2045 | ||
2041 | /* disable external mic, port B */ | 2046 | /* toggle input of built-in and mic jack appropriately */ |
2042 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | 2047 | static void cxt5066_olpc_automic(struct hda_codec *codec) |
2043 | {} | 2048 | { |
2044 | }; | 2049 | struct conexant_spec *spec = codec->spec; |
2045 | unsigned int present; | 2050 | unsigned int present; |
2046 | 2051 | ||
2047 | present = snd_hda_jack_detect(codec, 0x1a); | 2052 | present = snd_hda_codec_read(codec, 0x1a, 0, |
2048 | if (present) { | 2053 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; |
2054 | if (present) | ||
2049 | snd_printdd("CXT5066: external microphone detected\n"); | 2055 | snd_printdd("CXT5066: external microphone detected\n"); |
2050 | snd_hda_sequence_write(codec, ext_mic_present); | 2056 | else |
2051 | } else { | ||
2052 | snd_printdd("CXT5066: external microphone absent\n"); | 2057 | snd_printdd("CXT5066: external microphone absent\n"); |
2053 | snd_hda_sequence_write(codec, ext_mic_absent); | 2058 | |
2054 | } | 2059 | snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, |
2060 | present ? 0 : 1); | ||
2061 | spec->ext_mic_present = !!present; | ||
2062 | |||
2063 | cxt5066_olpc_select_mic(codec); | ||
2055 | } | 2064 | } |
2056 | 2065 | ||
2057 | /* toggle input of built-in digital mic and mic jack appropriately */ | 2066 | /* toggle input of built-in digital mic and mic jack appropriately */ |
2058 | static void cxt5066_vostro_automic(struct hda_codec *codec) | 2067 | static void cxt5066_vostro_automic(struct hda_codec *codec) |
2059 | { | 2068 | { |
2060 | struct conexant_spec *spec = codec->spec; | ||
2061 | unsigned int present; | 2069 | unsigned int present; |
2062 | 2070 | ||
2063 | struct hda_verb ext_mic_present[] = { | 2071 | struct hda_verb ext_mic_present[] = { |
2064 | /* enable external mic, port B */ | 2072 | /* enable external mic, port B */ |
2065 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, | 2073 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, |
2066 | 2074 | ||
2067 | /* switch to external mic input */ | 2075 | /* switch to external mic input */ |
2068 | {0x17, AC_VERB_SET_CONNECT_SEL, 0}, | 2076 | {0x17, AC_VERB_SET_CONNECT_SEL, 0}, |
@@ -2113,7 +2121,7 @@ static void cxt5066_hp_automute(struct hda_codec *codec) | |||
2113 | } | 2121 | } |
2114 | 2122 | ||
2115 | /* unsolicited event for jack sensing */ | 2123 | /* unsolicited event for jack sensing */ |
2116 | static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) | 2124 | static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res) |
2117 | { | 2125 | { |
2118 | snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); | 2126 | snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); |
2119 | switch (res >> 26) { | 2127 | switch (res >> 26) { |
@@ -2121,7 +2129,7 @@ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) | |||
2121 | cxt5066_hp_automute(codec); | 2129 | cxt5066_hp_automute(codec); |
2122 | break; | 2130 | break; |
2123 | case CONEXANT_MIC_EVENT: | 2131 | case CONEXANT_MIC_EVENT: |
2124 | cxt5066_automic(codec); | 2132 | cxt5066_olpc_automic(codec); |
2125 | break; | 2133 | break; |
2126 | } | 2134 | } |
2127 | } | 2135 | } |
@@ -2197,6 +2205,31 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, | |||
2197 | return 1; | 2205 | return 1; |
2198 | } | 2206 | } |
2199 | 2207 | ||
2208 | static void cxt5066_olpc_capture_prepare(struct hda_codec *codec) | ||
2209 | { | ||
2210 | struct conexant_spec *spec = codec->spec; | ||
2211 | /* mark as recording and configure the microphone widget so that the | ||
2212 | * recording LED comes on. */ | ||
2213 | spec->recording = 1; | ||
2214 | cxt5066_olpc_select_mic(codec); | ||
2215 | } | ||
2216 | |||
2217 | static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec) | ||
2218 | { | ||
2219 | struct conexant_spec *spec = codec->spec; | ||
2220 | const struct hda_verb disable_mics[] = { | ||
2221 | /* disable external mic, port B */ | ||
2222 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
2223 | |||
2224 | /* disble internal mic, port C */ | ||
2225 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
2226 | {}, | ||
2227 | }; | ||
2228 | |||
2229 | snd_hda_sequence_write(codec, disable_mics); | ||
2230 | spec->recording = 0; | ||
2231 | } | ||
2232 | |||
2200 | static struct hda_input_mux cxt5066_capture_source = { | 2233 | static struct hda_input_mux cxt5066_capture_source = { |
2201 | .num_items = 4, | 2234 | .num_items = 4, |
2202 | .items = { | 2235 | .items = { |
@@ -2347,10 +2380,10 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = { | |||
2347 | {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | 2380 | {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ |
2348 | 2381 | ||
2349 | /* Port B: external microphone */ | 2382 | /* Port B: external microphone */ |
2350 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, CXT5066_OLPC_EXT_MIC_BIAS}, | 2383 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, |
2351 | 2384 | ||
2352 | /* Port C: internal microphone */ | 2385 | /* Port C: internal microphone */ |
2353 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | 2386 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, |
2354 | 2387 | ||
2355 | /* Port D: unused */ | 2388 | /* Port D: unused */ |
2356 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | 2389 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, |
@@ -2479,12 +2512,19 @@ static int cxt5066_init(struct hda_codec *codec) | |||
2479 | cxt5066_hp_automute(codec); | 2512 | cxt5066_hp_automute(codec); |
2480 | if (spec->dell_vostro) | 2513 | if (spec->dell_vostro) |
2481 | cxt5066_vostro_automic(codec); | 2514 | cxt5066_vostro_automic(codec); |
2482 | else | ||
2483 | cxt5066_automic(codec); | ||
2484 | } | 2515 | } |
2485 | return 0; | 2516 | return 0; |
2486 | } | 2517 | } |
2487 | 2518 | ||
2519 | static int cxt5066_olpc_init(struct hda_codec *codec) | ||
2520 | { | ||
2521 | snd_printdd("CXT5066: init\n"); | ||
2522 | conexant_init(codec); | ||
2523 | cxt5066_hp_automute(codec); | ||
2524 | cxt5066_olpc_automic(codec); | ||
2525 | return 0; | ||
2526 | } | ||
2527 | |||
2488 | enum { | 2528 | enum { |
2489 | CXT5066_LAPTOP, /* Laptops w/ EAPD support */ | 2529 | CXT5066_LAPTOP, /* Laptops w/ EAPD support */ |
2490 | CXT5066_DELL_LAPTOP, /* Dell Laptop */ | 2530 | CXT5066_DELL_LAPTOP, /* Dell Laptop */ |
@@ -2521,7 +2561,7 @@ static int patch_cxt5066(struct hda_codec *codec) | |||
2521 | codec->spec = spec; | 2561 | codec->spec = spec; |
2522 | 2562 | ||
2523 | codec->patch_ops = conexant_patch_ops; | 2563 | codec->patch_ops = conexant_patch_ops; |
2524 | codec->patch_ops.init = cxt5066_init; | 2564 | codec->patch_ops.init = conexant_init; |
2525 | 2565 | ||
2526 | spec->dell_automute = 0; | 2566 | spec->dell_automute = 0; |
2527 | spec->multiout.max_channels = 2; | 2567 | spec->multiout.max_channels = 2; |
@@ -2534,7 +2574,6 @@ static int patch_cxt5066(struct hda_codec *codec) | |||
2534 | spec->input_mux = &cxt5066_capture_source; | 2574 | spec->input_mux = &cxt5066_capture_source; |
2535 | 2575 | ||
2536 | spec->port_d_mode = PIN_HP; | 2576 | spec->port_d_mode = PIN_HP; |
2537 | spec->ext_mic_bias = PIN_VREF80; | ||
2538 | 2577 | ||
2539 | spec->num_init_verbs = 1; | 2578 | spec->num_init_verbs = 1; |
2540 | spec->init_verbs[0] = cxt5066_init_verbs; | 2579 | spec->init_verbs[0] = cxt5066_init_verbs; |
@@ -2561,20 +2600,26 @@ static int patch_cxt5066(struct hda_codec *codec) | |||
2561 | spec->dell_automute = 1; | 2600 | spec->dell_automute = 1; |
2562 | break; | 2601 | break; |
2563 | case CXT5066_OLPC_XO_1_5: | 2602 | case CXT5066_OLPC_XO_1_5: |
2564 | codec->patch_ops.unsol_event = cxt5066_unsol_event; | 2603 | codec->patch_ops.init = cxt5066_olpc_init; |
2604 | codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event; | ||
2565 | spec->init_verbs[0] = cxt5066_init_verbs_olpc; | 2605 | spec->init_verbs[0] = cxt5066_init_verbs_olpc; |
2566 | spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; | 2606 | spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; |
2567 | spec->mixers[spec->num_mixers++] = cxt5066_mixers; | 2607 | spec->mixers[spec->num_mixers++] = cxt5066_mixers; |
2568 | spec->port_d_mode = 0; | 2608 | spec->port_d_mode = 0; |
2569 | spec->ext_mic_bias = CXT5066_OLPC_EXT_MIC_BIAS; | ||
2570 | 2609 | ||
2571 | /* no S/PDIF out */ | 2610 | /* no S/PDIF out */ |
2572 | spec->multiout.dig_out_nid = 0; | 2611 | spec->multiout.dig_out_nid = 0; |
2573 | 2612 | ||
2574 | /* input source automatically selected */ | 2613 | /* input source automatically selected */ |
2575 | spec->input_mux = NULL; | 2614 | spec->input_mux = NULL; |
2615 | |||
2616 | /* our capture hooks which allow us to turn on the microphone LED | ||
2617 | * at the right time */ | ||
2618 | spec->capture_prepare = cxt5066_olpc_capture_prepare; | ||
2619 | spec->capture_cleanup = cxt5066_olpc_capture_cleanup; | ||
2576 | break; | 2620 | break; |
2577 | case CXT5066_DELL_VOSTO: | 2621 | case CXT5066_DELL_VOSTO: |
2622 | codec->patch_ops.init = cxt5066_init; | ||
2578 | codec->patch_ops.unsol_event = cxt5066_vostro_event; | 2623 | codec->patch_ops.unsol_event = cxt5066_vostro_event; |
2579 | spec->init_verbs[0] = cxt5066_init_verbs_vostro; | 2624 | spec->init_verbs[0] = cxt5066_init_verbs_vostro; |
2580 | spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; | 2625 | spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; |