aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2014-01-27 10:28:02 -0500
committerTakashi Iwai <tiwai@suse.de>2014-01-30 06:39:12 -0500
commit3a00c6605b7709f6c30437efceda3f439bc5ddc2 (patch)
treeac5006d89f43d005eb8089467348e094d54fc6c0 /sound/pci
parent8f0972dfa9b7378aaf14eebf2454abd93a6033ad (diff)
ALSA: hda/conexant - Re-implement OLPC XO workarounds via fixup
OLPC XO needs a few special handling. Now these are implemented as a fixup to the generic parser. Obviously, the DC BIAS mode had to be added manually. This is mainly implemented in the mic_autoswitch hook, where the mic pins are overwritten depending on the DC bias mode. This also required the override of the mic boost control, since the mic boost is applied only when the DC mode is disabled. In addition, the mic pins must be set dynamically at recording time because these also control the LED. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/hda/patch_conexant.c643
1 files changed, 269 insertions, 374 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index b103908c63d7..74b829b73c35 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -68,6 +68,12 @@ struct conexant_spec {
68 68
69 unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ 69 unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
70 70
71 /* OPLC XO specific */
72 bool recording;
73 bool dc_enable;
74 unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */
75 struct nid_path *dc_mode_path;
76
71#ifdef ENABLE_CXT_STATIC_QUIRKS 77#ifdef ENABLE_CXT_STATIC_QUIRKS
72 const struct snd_kcontrol_new *mixers[5]; 78 const struct snd_kcontrol_new *mixers[5];
73 int num_mixers; 79 int num_mixers;
@@ -123,19 +129,6 @@ struct conexant_spec {
123 unsigned int hp_laptop:1; 129 unsigned int hp_laptop:1;
124 unsigned int asus:1; 130 unsigned int asus:1;
125 131
126 unsigned int ext_mic_present;
127 unsigned int recording;
128 void (*capture_prepare)(struct hda_codec *codec);
129 void (*capture_cleanup)(struct hda_codec *codec);
130
131 /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
132 * through the microphone jack.
133 * When the user enables this through a mixer switch, both internal and
134 * external microphones are disabled. Gain is fixed at 0dB. In this mode,
135 * we also allow the bias to be configured through a separate mixer
136 * control. */
137 unsigned int dc_enable;
138 unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
139 unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ 132 unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
140#endif /* ENABLE_CXT_STATIC_QUIRKS */ 133#endif /* ENABLE_CXT_STATIC_QUIRKS */
141}; 134};
@@ -253,8 +246,6 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
253 struct snd_pcm_substream *substream) 246 struct snd_pcm_substream *substream)
254{ 247{
255 struct conexant_spec *spec = codec->spec; 248 struct conexant_spec *spec = codec->spec;
256 if (spec->capture_prepare)
257 spec->capture_prepare(codec);
258 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 249 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
259 stream_tag, 0, format); 250 stream_tag, 0, format);
260 return 0; 251 return 0;
@@ -266,8 +257,6 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
266{ 257{
267 struct conexant_spec *spec = codec->spec; 258 struct conexant_spec *spec = codec->spec;
268 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 259 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
269 if (spec->capture_cleanup)
270 spec->capture_cleanup(codec);
271 return 0; 260 return 0;
272} 261}
273 262
@@ -1940,11 +1929,6 @@ static const hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
1940static const hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 }; 1929static const hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
1941static const hda_nid_t cxt5066_digout_pin_nids[2] = { 0x20, 0x22 }; 1930static const hda_nid_t cxt5066_digout_pin_nids[2] = { 0x20, 0x22 };
1942 1931
1943/* OLPC's microphone port is DC coupled for use with external sensors,
1944 * therefore we use a 50% mic bias in order to center the input signal with
1945 * the DC input range of the codec. */
1946#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50
1947
1948static const struct hda_channel_mode cxt5066_modes[1] = { 1932static const struct hda_channel_mode cxt5066_modes[1] = {
1949 { 2, NULL }, 1933 { 2, NULL },
1950}; 1934};
@@ -1997,88 +1981,6 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1997 return 1; 1981 return 1;
1998} 1982}
1999 1983
2000static const struct hda_input_mux cxt5066_olpc_dc_bias = {
2001 .num_items = 3,
2002 .items = {
2003 { "Off", PIN_IN },
2004 { "50%", PIN_VREF50 },
2005 { "80%", PIN_VREF80 },
2006 },
2007};
2008
2009static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
2010{
2011 struct conexant_spec *spec = codec->spec;
2012 /* Even though port F is the DC input, the bias is controlled on port B.
2013 * we also leave that port as an active input (but unselected) in DC mode
2014 * just in case that is necessary to make the bias setting take effect. */
2015 return snd_hda_set_pin_ctl_cache(codec, 0x1a,
2016 cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
2017}
2018
2019/* OLPC defers mic widget control until when capture is started because the
2020 * microphone LED comes on as soon as these settings are put in place. if we
2021 * did this before recording, it would give the false indication that recording
2022 * is happening when it is not. */
2023static void cxt5066_olpc_select_mic(struct hda_codec *codec)
2024{
2025 struct conexant_spec *spec = codec->spec;
2026 if (!spec->recording)
2027 return;
2028
2029 if (spec->dc_enable) {
2030 /* in DC mode we ignore presence detection and just use the jack
2031 * through our special DC port */
2032 const struct hda_verb enable_dc_mode[] = {
2033 /* disble internal mic, port C */
2034 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2035
2036 /* enable DC capture, port F */
2037 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2038 {},
2039 };
2040
2041 snd_hda_sequence_write(codec, enable_dc_mode);
2042 /* port B input disabled (and bias set) through the following call */
2043 cxt5066_set_olpc_dc_bias(codec);
2044 return;
2045 }
2046
2047 /* disable DC (port F) */
2048 snd_hda_set_pin_ctl(codec, 0x1e, 0);
2049
2050 /* external mic, port B */
2051 snd_hda_set_pin_ctl(codec, 0x1a,
2052 spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
2053
2054 /* internal mic, port C */
2055 snd_hda_set_pin_ctl(codec, 0x1b,
2056 spec->ext_mic_present ? 0 : PIN_VREF80);
2057}
2058
2059/* toggle input of built-in and mic jack appropriately */
2060static void cxt5066_olpc_automic(struct hda_codec *codec)
2061{
2062 struct conexant_spec *spec = codec->spec;
2063 unsigned int present;
2064
2065 if (spec->dc_enable) /* don't do presence detection in DC mode */
2066 return;
2067
2068 present = snd_hda_codec_read(codec, 0x1a, 0,
2069 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
2070 if (present)
2071 snd_printdd("CXT5066: external microphone detected\n");
2072 else
2073 snd_printdd("CXT5066: external microphone absent\n");
2074
2075 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
2076 present ? 0 : 1);
2077 spec->ext_mic_present = !!present;
2078
2079 cxt5066_olpc_select_mic(codec);
2080}
2081
2082/* toggle input of built-in digital mic and mic jack appropriately */ 1984/* toggle input of built-in digital mic and mic jack appropriately */
2083static void cxt5066_vostro_automic(struct hda_codec *codec) 1985static void cxt5066_vostro_automic(struct hda_codec *codec)
2084{ 1986{
@@ -2252,23 +2154,6 @@ static void cxt5066_automic(struct hda_codec *codec)
2252} 2154}
2253 2155
2254/* unsolicited event for jack sensing */ 2156/* unsolicited event for jack sensing */
2255static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
2256{
2257 struct conexant_spec *spec = codec->spec;
2258 snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
2259 switch (res >> 26) {
2260 case CONEXANT_HP_EVENT:
2261 cxt5066_hp_automute(codec);
2262 break;
2263 case CONEXANT_MIC_EVENT:
2264 /* ignore mic events in DC mode; we're always using the jack */
2265 if (!spec->dc_enable)
2266 cxt5066_olpc_automic(codec);
2267 break;
2268 }
2269}
2270
2271/* unsolicited event for jack sensing */
2272static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) 2157static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
2273{ 2158{
2274 snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); 2159 snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
@@ -2338,124 +2223,10 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
2338 idx = imux->num_items - 1; 2223 idx = imux->num_items - 1;
2339 2224
2340 spec->mic_boost = idx; 2225 spec->mic_boost = idx;
2341 if (!spec->dc_enable)
2342 cxt5066_set_mic_boost(codec);
2343 return 1;
2344}
2345
2346static void cxt5066_enable_dc(struct hda_codec *codec)
2347{
2348 const struct hda_verb enable_dc_mode[] = {
2349 /* disable gain */
2350 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2351
2352 /* switch to DC input */
2353 {0x17, AC_VERB_SET_CONNECT_SEL, 3},
2354 {}
2355 };
2356
2357 /* configure as input source */
2358 snd_hda_sequence_write(codec, enable_dc_mode);
2359 cxt5066_olpc_select_mic(codec); /* also sets configured bias */
2360}
2361
2362static void cxt5066_disable_dc(struct hda_codec *codec)
2363{
2364 /* reconfigure input source */
2365 cxt5066_set_mic_boost(codec); 2226 cxt5066_set_mic_boost(codec);
2366 /* automic also selects the right mic if we're recording */
2367 cxt5066_olpc_automic(codec);
2368}
2369
2370static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
2371 struct snd_ctl_elem_value *ucontrol)
2372{
2373 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2374 struct conexant_spec *spec = codec->spec;
2375 ucontrol->value.integer.value[0] = spec->dc_enable;
2376 return 0;
2377}
2378
2379static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
2380 struct snd_ctl_elem_value *ucontrol)
2381{
2382 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2383 struct conexant_spec *spec = codec->spec;
2384 int dc_enable = !!ucontrol->value.integer.value[0];
2385
2386 if (dc_enable == spec->dc_enable)
2387 return 0;
2388
2389 spec->dc_enable = dc_enable;
2390 if (dc_enable)
2391 cxt5066_enable_dc(codec);
2392 else
2393 cxt5066_disable_dc(codec);
2394
2395 return 1; 2227 return 1;
2396} 2228}
2397 2229
2398static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
2399 struct snd_ctl_elem_info *uinfo)
2400{
2401 return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo);
2402}
2403
2404static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
2405 struct snd_ctl_elem_value *ucontrol)
2406{
2407 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2408 struct conexant_spec *spec = codec->spec;
2409 ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
2410 return 0;
2411}
2412
2413static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
2414 struct snd_ctl_elem_value *ucontrol)
2415{
2416 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2417 struct conexant_spec *spec = codec->spec;
2418 const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
2419 unsigned int idx;
2420
2421 idx = ucontrol->value.enumerated.item[0];
2422 if (idx >= imux->num_items)
2423 idx = imux->num_items - 1;
2424
2425 spec->dc_input_bias = idx;
2426 if (spec->dc_enable)
2427 cxt5066_set_olpc_dc_bias(codec);
2428 return 1;
2429}
2430
2431static void cxt5066_olpc_capture_prepare(struct hda_codec *codec)
2432{
2433 struct conexant_spec *spec = codec->spec;
2434 /* mark as recording and configure the microphone widget so that the
2435 * recording LED comes on. */
2436 spec->recording = 1;
2437 cxt5066_olpc_select_mic(codec);
2438}
2439
2440static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)
2441{
2442 struct conexant_spec *spec = codec->spec;
2443 const struct hda_verb disable_mics[] = {
2444 /* disable external mic, port B */
2445 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2446
2447 /* disble internal mic, port C */
2448 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2449
2450 /* disable DC capture, port F */
2451 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2452 {},
2453 };
2454
2455 snd_hda_sequence_write(codec, disable_mics);
2456 spec->recording = 0;
2457}
2458
2459static void conexant_check_dig_outs(struct hda_codec *codec, 2230static void conexant_check_dig_outs(struct hda_codec *codec,
2460 const hda_nid_t *dig_pins, 2231 const hda_nid_t *dig_pins,
2461 int num_pins) 2232 int num_pins)
@@ -2506,43 +2277,6 @@ static const struct snd_kcontrol_new cxt5066_mixer_master[] = {
2506 {} 2277 {}
2507}; 2278};
2508 2279
2509static const struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
2510 {
2511 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2512 .name = "Master Playback Volume",
2513 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
2514 SNDRV_CTL_ELEM_ACCESS_TLV_READ |
2515 SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
2516 .subdevice = HDA_SUBDEV_AMP_FLAG,
2517 .info = snd_hda_mixer_amp_volume_info,
2518 .get = snd_hda_mixer_amp_volume_get,
2519 .put = snd_hda_mixer_amp_volume_put,
2520 .tlv = { .c = snd_hda_mixer_amp_tlv },
2521 /* offset by 28 volume steps to limit minimum gain to -46dB */
2522 .private_value =
2523 HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28),
2524 },
2525 {}
2526};
2527
2528static const struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = {
2529 {
2530 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2531 .name = "DC Mode Enable Switch",
2532 .info = snd_ctl_boolean_mono_info,
2533 .get = cxt5066_olpc_dc_get,
2534 .put = cxt5066_olpc_dc_put,
2535 },
2536 {
2537 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2538 .name = "DC Input Bias Enum",
2539 .info = cxt5066_olpc_dc_bias_enum_info,
2540 .get = cxt5066_olpc_dc_bias_enum_get,
2541 .put = cxt5066_olpc_dc_bias_enum_put,
2542 },
2543 {}
2544};
2545
2546static const struct snd_kcontrol_new cxt5066_mixers[] = { 2280static const struct snd_kcontrol_new cxt5066_mixers[] = {
2547 { 2281 {
2548 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2282 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2633,67 +2367,6 @@ static const struct hda_verb cxt5066_init_verbs[] = {
2633 { } /* end */ 2367 { } /* end */
2634}; 2368};
2635 2369
2636static const struct hda_verb cxt5066_init_verbs_olpc[] = {
2637 /* Port A: headphones */
2638 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2639 {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
2640
2641 /* Port B: external microphone */
2642 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2643
2644 /* Port C: internal microphone */
2645 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2646
2647 /* Port D: unused */
2648 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2649
2650 /* Port E: unused, but has primary EAPD */
2651 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2652 {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
2653
2654 /* Port F: external DC input through microphone port */
2655 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2656
2657 /* Port G: internal speakers */
2658 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2659 {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
2660
2661 /* DAC1 */
2662 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2663
2664 /* DAC2: unused */
2665 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2666
2667 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
2668 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2669 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2670 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2671 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2672 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2673 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2674 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2675 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2676 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2677 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2678 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2679
2680 /* Disable digital microphone port */
2681 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2682
2683 /* Audio input selectors */
2684 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
2685 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2686
2687 /* Disable SPDIF */
2688 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2689 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2690
2691 /* enable unsolicited events for Port A and B */
2692 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
2693 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
2694 { } /* end */
2695};
2696
2697static const struct hda_verb cxt5066_init_verbs_vostro[] = { 2370static const struct hda_verb cxt5066_init_verbs_vostro[] = {
2698 /* Port A: headphones */ 2371 /* Port A: headphones */
2699 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, 2372 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
@@ -2889,25 +2562,9 @@ static int cxt5066_init(struct hda_codec *codec)
2889 return 0; 2562 return 0;
2890} 2563}
2891 2564
2892static int cxt5066_olpc_init(struct hda_codec *codec)
2893{
2894 struct conexant_spec *spec = codec->spec;
2895 snd_printdd("CXT5066: init\n");
2896 conexant_init(codec);
2897 cxt5066_hp_automute(codec);
2898 if (!spec->dc_enable) {
2899 cxt5066_set_mic_boost(codec);
2900 cxt5066_olpc_automic(codec);
2901 } else {
2902 cxt5066_enable_dc(codec);
2903 }
2904 return 0;
2905}
2906
2907enum { 2565enum {
2908 CXT5066_LAPTOP, /* Laptops w/ EAPD support */ 2566 CXT5066_LAPTOP, /* Laptops w/ EAPD support */
2909 CXT5066_DELL_LAPTOP, /* Dell Laptop */ 2567 CXT5066_DELL_LAPTOP, /* Dell Laptop */
2910 CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
2911 CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */ 2568 CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */
2912 CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ 2569 CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
2913 CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */ 2570 CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
@@ -2920,7 +2577,6 @@ enum {
2920static const char * const cxt5066_models[CXT5066_MODELS] = { 2577static const char * const cxt5066_models[CXT5066_MODELS] = {
2921 [CXT5066_LAPTOP] = "laptop", 2578 [CXT5066_LAPTOP] = "laptop",
2922 [CXT5066_DELL_LAPTOP] = "dell-laptop", 2579 [CXT5066_DELL_LAPTOP] = "dell-laptop",
2923 [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
2924 [CXT5066_DELL_VOSTRO] = "dell-vostro", 2580 [CXT5066_DELL_VOSTRO] = "dell-vostro",
2925 [CXT5066_IDEAPAD] = "ideapad", 2581 [CXT5066_IDEAPAD] = "ideapad",
2926 [CXT5066_THINKPAD] = "thinkpad", 2582 [CXT5066_THINKPAD] = "thinkpad",
@@ -2941,10 +2597,8 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
2941 SND_PCI_QUIRK(0x1043, 0x1643, "Asus K52JU", CXT5066_ASUS), 2597 SND_PCI_QUIRK(0x1043, 0x1643, "Asus K52JU", CXT5066_ASUS),
2942 SND_PCI_QUIRK(0x1043, 0x1993, "Asus U50F", CXT5066_ASUS), 2598 SND_PCI_QUIRK(0x1043, 0x1993, "Asus U50F", CXT5066_ASUS),
2943 SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD), 2599 SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD),
2944 SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
2945 SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", 2600 SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
2946 CXT5066_LAPTOP), 2601 CXT5066_LAPTOP),
2947 SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
2948 SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD), 2602 SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
2949 SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD), 2603 SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
2950 SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), 2604 SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS),
@@ -3030,32 +2684,11 @@ static int patch_cxt5066(struct hda_codec *codec)
3030 spec->mic_boost = 3; /* default 30dB gain */ 2684 spec->mic_boost = 3; /* default 30dB gain */
3031 break; 2685 break;
3032 2686
3033 case CXT5066_OLPC_XO_1_5:
3034 codec->patch_ops.init = cxt5066_olpc_init;
3035 codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
3036 spec->init_verbs[0] = cxt5066_init_verbs_olpc;
3037 spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
3038 spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc;
3039 spec->mixers[spec->num_mixers++] = cxt5066_mixers;
3040 spec->port_d_mode = 0;
3041 spec->mic_boost = 3; /* default 30dB gain */
3042
3043 /* no S/PDIF out */
3044 spec->multiout.dig_out_nid = 0;
3045
3046 /* input source automatically selected */
3047 spec->input_mux = NULL;
3048
3049 /* our capture hooks which allow us to turn on the microphone LED
3050 * at the right time */
3051 spec->capture_prepare = cxt5066_olpc_capture_prepare;
3052 spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
3053 break;
3054 case CXT5066_DELL_VOSTRO: 2687 case CXT5066_DELL_VOSTRO:
3055 codec->patch_ops.init = cxt5066_init; 2688 codec->patch_ops.init = cxt5066_init;
3056 codec->patch_ops.unsol_event = cxt5066_unsol_event; 2689 codec->patch_ops.unsol_event = cxt5066_unsol_event;
3057 spec->init_verbs[0] = cxt5066_init_verbs_vostro; 2690 spec->init_verbs[0] = cxt5066_init_verbs_vostro;
3058 spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; 2691 spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
3059 spec->mixers[spec->num_mixers++] = cxt5066_mixers; 2692 spec->mixers[spec->num_mixers++] = cxt5066_mixers;
3060 spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers; 2693 spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
3061 spec->port_d_mode = 0; 2694 spec->port_d_mode = 0;
@@ -3238,6 +2871,7 @@ enum {
3238 CXT_FIXUP_HEADPHONE_MIC, 2871 CXT_FIXUP_HEADPHONE_MIC,
3239 CXT_FIXUP_GPIO1, 2872 CXT_FIXUP_GPIO1,
3240 CXT_FIXUP_THINKPAD_ACPI, 2873 CXT_FIXUP_THINKPAD_ACPI,
2874 CXT_FIXUP_OLPC_XO,
3241}; 2875};
3242 2876
3243/* for hda_fixup_thinkpad_acpi() */ 2877/* for hda_fixup_thinkpad_acpi() */
@@ -3315,6 +2949,261 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec,
3315 } 2949 }
3316} 2950}
3317 2951
2952/* OPLC XO 1.5 fixup */
2953
2954/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
2955 * through the microphone jack.
2956 * When the user enables this through a mixer switch, both internal and
2957 * external microphones are disabled. Gain is fixed at 0dB. In this mode,
2958 * we also allow the bias to be configured through a separate mixer
2959 * control. */
2960
2961#define update_mic_pin(codec, nid, val) \
2962 snd_hda_codec_update_cache(codec, nid, 0, \
2963 AC_VERB_SET_PIN_WIDGET_CONTROL, val)
2964
2965static const struct hda_input_mux olpc_xo_dc_bias = {
2966 .num_items = 3,
2967 .items = {
2968 { "Off", PIN_IN },
2969 { "50%", PIN_VREF50 },
2970 { "80%", PIN_VREF80 },
2971 },
2972};
2973
2974static void olpc_xo_update_mic_boost(struct hda_codec *codec)
2975{
2976 struct conexant_spec *spec = codec->spec;
2977 int ch, val;
2978
2979 for (ch = 0; ch < 2; ch++) {
2980 val = AC_AMP_SET_OUTPUT |
2981 (ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT);
2982 if (!spec->dc_enable)
2983 val |= snd_hda_codec_amp_read(codec, 0x17, ch, HDA_OUTPUT, 0);
2984 snd_hda_codec_write(codec, 0x17, 0,
2985 AC_VERB_SET_AMP_GAIN_MUTE, val);
2986 }
2987}
2988
2989static void olpc_xo_update_mic_pins(struct hda_codec *codec)
2990{
2991 struct conexant_spec *spec = codec->spec;
2992 int cur_input, val;
2993 struct nid_path *path;
2994
2995 cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]];
2996
2997 /* Set up mic pins for port-B, C and F dynamically as the recording
2998 * LED is turned on/off by these pin controls
2999 */
3000 if (!spec->dc_enable) {
3001 /* disable DC bias path and pin for port F */
3002 update_mic_pin(codec, 0x1e, 0);
3003 snd_hda_activate_path(codec, spec->dc_mode_path, false, false);
3004
3005 /* update port B (ext mic) and C (int mic) */
3006 /* OLPC defers mic widget control until when capture is
3007 * started because the microphone LED comes on as soon as
3008 * these settings are put in place. if we did this before
3009 * recording, it would give the false indication that
3010 * recording is happening when it is not.
3011 */
3012 update_mic_pin(codec, 0x1a, spec->recording ?
3013 snd_hda_codec_get_pin_target(codec, 0x1a) : 0);
3014 update_mic_pin(codec, 0x1b, spec->recording ?
3015 snd_hda_codec_get_pin_target(codec, 0x1b) : 0);
3016 /* enable normal mic path */
3017 path = snd_hda_get_path_from_idx(codec, cur_input);
3018 if (path)
3019 snd_hda_activate_path(codec, path, true, false);
3020 } else {
3021 /* disable normal mic path */
3022 path = snd_hda_get_path_from_idx(codec, cur_input);
3023 if (path)
3024 snd_hda_activate_path(codec, path, false, false);
3025
3026 /* Even though port F is the DC input, the bias is controlled
3027 * on port B. We also leave that port as an active input (but
3028 * unselected) in DC mode just in case that is necessary to
3029 * make the bias setting take effect.
3030 */
3031 if (spec->recording)
3032 val = olpc_xo_dc_bias.items[spec->dc_input_bias].index;
3033 else
3034 val = 0;
3035 update_mic_pin(codec, 0x1a, val);
3036 update_mic_pin(codec, 0x1b, 0);
3037 /* enable DC bias path and pin */
3038 update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0);
3039 snd_hda_activate_path(codec, spec->dc_mode_path, true, false);
3040 }
3041}
3042
3043/* mic_autoswitch hook */
3044static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_tbl *jack)
3045{
3046 struct conexant_spec *spec = codec->spec;
3047 int saved_cached_write = codec->cached_write;
3048
3049 codec->cached_write = 1;
3050 /* in DC mode, we don't handle automic */
3051 if (!spec->dc_enable)
3052 snd_hda_gen_mic_autoswitch(codec, jack);
3053 olpc_xo_update_mic_pins(codec);
3054 snd_hda_codec_flush_cache(codec);
3055 codec->cached_write = saved_cached_write;
3056 if (spec->dc_enable)
3057 olpc_xo_update_mic_boost(codec);
3058}
3059
3060/* pcm_capture hook */
3061static void olpc_xo_capture_hook(struct hda_pcm_stream *hinfo,
3062 struct hda_codec *codec,
3063 struct snd_pcm_substream *substream,
3064 int action)
3065{
3066 struct conexant_spec *spec = codec->spec;
3067
3068 /* toggle spec->recording flag and update mic pins accordingly
3069 * for turning on/off LED
3070 */
3071 switch (action) {
3072 case HDA_GEN_PCM_ACT_PREPARE:
3073 spec->recording = 1;
3074 olpc_xo_update_mic_pins(codec);
3075 break;
3076 case HDA_GEN_PCM_ACT_CLEANUP:
3077 spec->recording = 0;
3078 olpc_xo_update_mic_pins(codec);
3079 break;
3080 }
3081}
3082
3083static int olpc_xo_dc_mode_get(struct snd_kcontrol *kcontrol,
3084 struct snd_ctl_elem_value *ucontrol)
3085{
3086 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3087 struct conexant_spec *spec = codec->spec;
3088 ucontrol->value.integer.value[0] = spec->dc_enable;
3089 return 0;
3090}
3091
3092static int olpc_xo_dc_mode_put(struct snd_kcontrol *kcontrol,
3093 struct snd_ctl_elem_value *ucontrol)
3094{
3095 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3096 struct conexant_spec *spec = codec->spec;
3097 int dc_enable = !!ucontrol->value.integer.value[0];
3098
3099 if (dc_enable == spec->dc_enable)
3100 return 0;
3101
3102 spec->dc_enable = dc_enable;
3103 olpc_xo_update_mic_pins(codec);
3104 olpc_xo_update_mic_boost(codec);
3105 return 1;
3106}
3107
3108static int olpc_xo_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
3109 struct snd_ctl_elem_value *ucontrol)
3110{
3111 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3112 struct conexant_spec *spec = codec->spec;
3113 ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
3114 return 0;
3115}
3116
3117static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
3118 struct snd_ctl_elem_info *uinfo)
3119{
3120 return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo);
3121}
3122
3123static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
3124 struct snd_ctl_elem_value *ucontrol)
3125{
3126 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3127 struct conexant_spec *spec = codec->spec;
3128 const struct hda_input_mux *imux = &olpc_xo_dc_bias;
3129 unsigned int idx;
3130
3131 idx = ucontrol->value.enumerated.item[0];
3132 if (idx >= imux->num_items)
3133 idx = imux->num_items - 1;
3134 if (spec->dc_input_bias == idx)
3135 return 0;
3136
3137 spec->dc_input_bias = idx;
3138 if (spec->dc_enable)
3139 olpc_xo_update_mic_pins(codec);
3140 return 1;
3141}
3142
3143static const struct snd_kcontrol_new olpc_xo_mixers[] = {
3144 {
3145 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3146 .name = "DC Mode Enable Switch",
3147 .info = snd_ctl_boolean_mono_info,
3148 .get = olpc_xo_dc_mode_get,
3149 .put = olpc_xo_dc_mode_put,
3150 },
3151 {
3152 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3153 .name = "DC Input Bias Enum",
3154 .info = olpc_xo_dc_bias_enum_info,
3155 .get = olpc_xo_dc_bias_enum_get,
3156 .put = olpc_xo_dc_bias_enum_put,
3157 },
3158 {}
3159};
3160
3161/* overriding mic boost put callback; update mic boost volume only when
3162 * DC mode is disabled
3163 */
3164static int olpc_xo_mic_boost_put(struct snd_kcontrol *kcontrol,
3165 struct snd_ctl_elem_value *ucontrol)
3166{
3167 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3168 struct conexant_spec *spec = codec->spec;
3169 int ret = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
3170 if (ret > 0 && spec->dc_enable)
3171 olpc_xo_update_mic_boost(codec);
3172 return ret;
3173}
3174
3175static void cxt_fixup_olpc_xo(struct hda_codec *codec,
3176 const struct hda_fixup *fix, int action)
3177{
3178 struct conexant_spec *spec = codec->spec;
3179 int i;
3180
3181 if (action != HDA_FIXUP_ACT_PROBE)
3182 return;
3183
3184 spec->gen.mic_autoswitch_hook = olpc_xo_automic;
3185 spec->gen.pcm_capture_hook = olpc_xo_capture_hook;
3186 spec->dc_mode_path = snd_hda_add_new_path(codec, 0x1e, 0x14, 0);
3187
3188 snd_hda_add_new_ctls(codec, olpc_xo_mixers);
3189
3190 /* OLPC's microphone port is DC coupled for use with external sensors,
3191 * therefore we use a 50% mic bias in order to center the input signal
3192 * with the DC input range of the codec.
3193 */
3194 snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50);
3195
3196 /* override mic boost control */
3197 for (i = 0; i < spec->gen.kctls.used; i++) {
3198 struct snd_kcontrol_new *kctl =
3199 snd_array_elem(&spec->gen.kctls, i);
3200 if (!strcmp(kctl->name, "Mic Boost Volume")) {
3201 kctl->put = olpc_xo_mic_boost_put;
3202 break;
3203 }
3204 }
3205}
3206
3318 3207
3319/* ThinkPad X200 & co with cxt5051 */ 3208/* ThinkPad X200 & co with cxt5051 */
3320static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { 3209static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
@@ -3400,6 +3289,10 @@ static const struct hda_fixup cxt_fixups[] = {
3400 .type = HDA_FIXUP_FUNC, 3289 .type = HDA_FIXUP_FUNC,
3401 .v.func = hda_fixup_thinkpad_acpi, 3290 .v.func = hda_fixup_thinkpad_acpi,
3402 }, 3291 },
3292 [CXT_FIXUP_OLPC_XO] = {
3293 .type = HDA_FIXUP_FUNC,
3294 .v.func = cxt_fixup_olpc_xo,
3295 },
3403}; 3296};
3404 3297
3405static const struct snd_pci_quirk cxt5051_fixups[] = { 3298static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -3416,6 +3309,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
3416 SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), 3309 SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
3417 SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_GPIO1), 3310 SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_GPIO1),
3418 SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), 3311 SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
3312 SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
3419 SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), 3313 SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
3420 SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), 3314 SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
3421 SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), 3315 SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
@@ -3439,6 +3333,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
3439 { .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" }, 3333 { .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" },
3440 { .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" }, 3334 { .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" },
3441 { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" }, 3335 { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
3336 { .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
3442 {} 3337 {}
3443}; 3338};
3444 3339