aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Drake <dsd@laptop.org>2010-01-07 07:46:25 -0500
committerJaroslav Kysela <perex@perex.cz>2010-01-08 03:11:34 -0500
commit75f8991d0e6969407d51501d5a0537f104075c99 (patch)
tree6c09e0ae2431951d1815caf27acbf7aa41c3bba1
parenta4ad68d57e4dc4138304df23d1817eb094149389 (diff)
ALSA: hda - Configure XO-1.5 microphones at capture time
The XO-1.5 has a microphone LED designed to indicate to the user when something is being recorded. This light is controlled by the microphone bias voltage and it is currently coming on all the time. This patch defers the microphone port configuration until when recording is actually taking place, fixing the behaviour of the LED. Signed-off-by: Daniel Drake <dsd@laptop.org> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--sound/pci/hda/patch_conexant.c125
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
118static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, 122static 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
2020static 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. */
2031static 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}, 2047static 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 */
2058static void cxt5066_vostro_automic(struct hda_codec *codec) 2067static 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 */
2116static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) 2124static 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
2208static 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
2217static 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
2200static struct hda_input_mux cxt5066_capture_source = { 2233static 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
2519static 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
2488enum { 2528enum {
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;