aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sound/pci/hda/patch_conexant.c213
1 files changed, 190 insertions, 23 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 3521f33d43c3..685015a53292 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -117,6 +117,16 @@ struct conexant_spec {
117 unsigned int recording; 117 unsigned int recording;
118 void (*capture_prepare)(struct hda_codec *codec); 118 void (*capture_prepare)(struct hda_codec *codec);
119 void (*capture_cleanup)(struct hda_codec *codec); 119 void (*capture_cleanup)(struct hda_codec *codec);
120
121 /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
122 * through the microphone jack.
123 * When the user enables this through a mixer switch, both internal and
124 * external microphones are disabled. Gain is fixed at 0dB. In this mode,
125 * we also allow the bias to be configured through a separate mixer
126 * control. */
127 unsigned int dc_enable;
128 unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
129 unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
120}; 130};
121 131
122static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, 132static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -2024,6 +2034,26 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
2024 return 1; 2034 return 1;
2025} 2035}
2026 2036
2037static const struct hda_input_mux cxt5066_olpc_dc_bias = {
2038 .num_items = 3,
2039 .items = {
2040 { "Off", PIN_IN },
2041 { "50%", PIN_VREF50 },
2042 { "80%", PIN_VREF80 },
2043 },
2044};
2045
2046static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
2047{
2048 struct conexant_spec *spec = codec->spec;
2049 /* Even though port F is the DC input, the bias is controlled on port B.
2050 * we also leave that port as an active input (but unselected) in DC mode
2051 * just in case that is necessary to make the bias setting take effect. */
2052 return snd_hda_codec_write_cache(codec, 0x1a, 0,
2053 AC_VERB_SET_PIN_WIDGET_CONTROL,
2054 cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
2055}
2056
2027/* OLPC defers mic widget control until when capture is started because the 2057/* OLPC defers mic widget control until when capture is started because the
2028 * microphone LED comes on as soon as these settings are put in place. if we 2058 * 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 2059 * did this before recording, it would give the false indication that recording
@@ -2034,6 +2064,27 @@ static void cxt5066_olpc_select_mic(struct hda_codec *codec)
2034 if (!spec->recording) 2064 if (!spec->recording)
2035 return; 2065 return;
2036 2066
2067 if (spec->dc_enable) {
2068 /* in DC mode we ignore presence detection and just use the jack
2069 * through our special DC port */
2070 const struct hda_verb enable_dc_mode[] = {
2071 /* disble internal mic, port C */
2072 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2073
2074 /* enable DC capture, port F */
2075 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2076 {},
2077 };
2078
2079 snd_hda_sequence_write(codec, enable_dc_mode);
2080 /* port B input disabled (and bias set) through the following call */
2081 cxt5066_set_olpc_dc_bias(codec);
2082 return;
2083 }
2084
2085 /* disable DC (port F) */
2086 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
2087
2037 /* external mic, port B */ 2088 /* external mic, port B */
2038 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 2089 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2039 spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); 2090 spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
@@ -2049,6 +2100,9 @@ static void cxt5066_olpc_automic(struct hda_codec *codec)
2049 struct conexant_spec *spec = codec->spec; 2100 struct conexant_spec *spec = codec->spec;
2050 unsigned int present; 2101 unsigned int present;
2051 2102
2103 if (spec->dc_enable) /* don't do presence detection in DC mode */
2104 return;
2105
2052 present = snd_hda_codec_read(codec, 0x1a, 0, 2106 present = snd_hda_codec_read(codec, 0x1a, 0,
2053 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; 2107 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
2054 if (present) 2108 if (present)
@@ -2123,13 +2177,16 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
2123/* unsolicited event for jack sensing */ 2177/* unsolicited event for jack sensing */
2124static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res) 2178static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
2125{ 2179{
2180 struct conexant_spec *spec = codec->spec;
2126 snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); 2181 snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
2127 switch (res >> 26) { 2182 switch (res >> 26) {
2128 case CONEXANT_HP_EVENT: 2183 case CONEXANT_HP_EVENT:
2129 cxt5066_hp_automute(codec); 2184 cxt5066_hp_automute(codec);
2130 break; 2185 break;
2131 case CONEXANT_MIC_EVENT: 2186 case CONEXANT_MIC_EVENT:
2132 cxt5066_olpc_automic(codec); 2187 /* ignore mic events in DC mode; we're always using the jack */
2188 if (!spec->dc_enable)
2189 cxt5066_olpc_automic(codec);
2133 break; 2190 break;
2134 } 2191 }
2135} 2192}
@@ -2159,6 +2216,15 @@ static const struct hda_input_mux cxt5066_analog_mic_boost = {
2159 }, 2216 },
2160}; 2217};
2161 2218
2219static int cxt5066_set_mic_boost(struct hda_codec *codec)
2220{
2221 struct conexant_spec *spec = codec->spec;
2222 return snd_hda_codec_write_cache(codec, 0x17, 0,
2223 AC_VERB_SET_AMP_GAIN_MUTE,
2224 AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
2225 cxt5066_analog_mic_boost.items[spec->mic_boost].index);
2226}
2227
2162static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol, 2228static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
2163 struct snd_ctl_elem_info *uinfo) 2229 struct snd_ctl_elem_info *uinfo)
2164{ 2230{
@@ -2169,15 +2235,8 @@ static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
2169 struct snd_ctl_elem_value *ucontrol) 2235 struct snd_ctl_elem_value *ucontrol)
2170{ 2236{
2171 struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2237 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2172 int val; 2238 struct conexant_spec *spec = codec->spec;
2173 hda_nid_t nid = kcontrol->private_value & 0xff; 2239 ucontrol->value.enumerated.item[0] = spec->mic_boost;
2174 int inout = (kcontrol->private_value & 0x100) ?
2175 AC_AMP_GET_INPUT : AC_AMP_GET_OUTPUT;
2176
2177 val = snd_hda_codec_read(codec, nid, 0,
2178 AC_VERB_GET_AMP_GAIN_MUTE, inout);
2179
2180 ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN;
2181 return 0; 2240 return 0;
2182} 2241}
2183 2242
@@ -2185,23 +2244,101 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
2185 struct snd_ctl_elem_value *ucontrol) 2244 struct snd_ctl_elem_value *ucontrol)
2186{ 2245{
2187 struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2246 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2247 struct conexant_spec *spec = codec->spec;
2188 const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; 2248 const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
2189 unsigned int idx; 2249 unsigned int idx;
2190 hda_nid_t nid = kcontrol->private_value & 0xff; 2250 idx = ucontrol->value.enumerated.item[0];
2191 int inout = (kcontrol->private_value & 0x100) ? 2251 if (idx >= imux->num_items)
2192 AC_AMP_SET_INPUT : AC_AMP_SET_OUTPUT; 2252 idx = imux->num_items - 1;
2253
2254 spec->mic_boost = idx;
2255 if (!spec->dc_enable)
2256 cxt5066_set_mic_boost(codec);
2257 return 1;
2258}
2259
2260static void cxt5066_enable_dc(struct hda_codec *codec)
2261{
2262 const struct hda_verb enable_dc_mode[] = {
2263 /* disable gain */
2264 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2265
2266 /* switch to DC input */
2267 {0x17, AC_VERB_SET_CONNECT_SEL, 3},
2268 {}
2269 };
2270
2271 /* configure as input source */
2272 snd_hda_sequence_write(codec, enable_dc_mode);
2273 cxt5066_olpc_select_mic(codec); /* also sets configured bias */
2274}
2275
2276static void cxt5066_disable_dc(struct hda_codec *codec)
2277{
2278 /* reconfigure input source */
2279 cxt5066_set_mic_boost(codec);
2280 /* automic also selects the right mic if we're recording */
2281 cxt5066_olpc_automic(codec);
2282}
2283
2284static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
2285 struct snd_ctl_elem_value *ucontrol)
2286{
2287 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2288 struct conexant_spec *spec = codec->spec;
2289 ucontrol->value.integer.value[0] = spec->dc_enable;
2290 return 0;
2291}
2193 2292
2194 if (!imux->num_items) 2293static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
2294 struct snd_ctl_elem_value *ucontrol)
2295{
2296 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2297 struct conexant_spec *spec = codec->spec;
2298 int dc_enable = !!ucontrol->value.integer.value[0];
2299
2300 if (dc_enable == spec->dc_enable)
2195 return 0; 2301 return 0;
2302
2303 spec->dc_enable = dc_enable;
2304 if (dc_enable)
2305 cxt5066_enable_dc(codec);
2306 else
2307 cxt5066_disable_dc(codec);
2308
2309 return 1;
2310}
2311
2312static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
2313 struct snd_ctl_elem_info *uinfo)
2314{
2315 return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo);
2316}
2317
2318static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
2319 struct snd_ctl_elem_value *ucontrol)
2320{
2321 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2322 struct conexant_spec *spec = codec->spec;
2323 ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
2324 return 0;
2325}
2326
2327static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
2328 struct snd_ctl_elem_value *ucontrol)
2329{
2330 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2331 struct conexant_spec *spec = codec->spec;
2332 const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
2333 unsigned int idx;
2334
2196 idx = ucontrol->value.enumerated.item[0]; 2335 idx = ucontrol->value.enumerated.item[0];
2197 if (idx >= imux->num_items) 2336 if (idx >= imux->num_items)
2198 idx = imux->num_items - 1; 2337 idx = imux->num_items - 1;
2199 2338
2200 snd_hda_codec_write_cache(codec, nid, 0, 2339 spec->dc_input_bias = idx;
2201 AC_VERB_SET_AMP_GAIN_MUTE, 2340 if (spec->dc_enable)
2202 AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | inout | 2341 cxt5066_set_olpc_dc_bias(codec);
2203 imux->items[idx].index);
2204
2205 return 1; 2342 return 1;
2206} 2343}
2207 2344
@@ -2223,6 +2360,9 @@ static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)
2223 2360
2224 /* disble internal mic, port C */ 2361 /* disble internal mic, port C */
2225 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, 2362 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2363
2364 /* disable DC capture, port F */
2365 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2226 {}, 2366 {},
2227 }; 2367 };
2228 2368
@@ -2282,6 +2422,24 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
2282 {} 2422 {}
2283}; 2423};
2284 2424
2425static struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = {
2426 {
2427 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2428 .name = "DC Mode Enable Switch",
2429 .info = snd_ctl_boolean_mono_info,
2430 .get = cxt5066_olpc_dc_get,
2431 .put = cxt5066_olpc_dc_put,
2432 },
2433 {
2434 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2435 .name = "DC Input Bias Enum",
2436 .info = cxt5066_olpc_dc_bias_enum_info,
2437 .get = cxt5066_olpc_dc_bias_enum_get,
2438 .put = cxt5066_olpc_dc_bias_enum_put,
2439 },
2440 {}
2441};
2442
2285static struct snd_kcontrol_new cxt5066_mixers[] = { 2443static struct snd_kcontrol_new cxt5066_mixers[] = {
2286 { 2444 {
2287 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2445 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2294,11 +2452,10 @@ static struct snd_kcontrol_new cxt5066_mixers[] = {
2294 2452
2295 { 2453 {
2296 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2454 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2297 .name = "Ext Mic Boost Capture Enum", 2455 .name = "Analog Mic Boost Capture Enum",
2298 .info = cxt5066_mic_boost_mux_enum_info, 2456 .info = cxt5066_mic_boost_mux_enum_info,
2299 .get = cxt5066_mic_boost_mux_enum_get, 2457 .get = cxt5066_mic_boost_mux_enum_get,
2300 .put = cxt5066_mic_boost_mux_enum_put, 2458 .put = cxt5066_mic_boost_mux_enum_put,
2301 .private_value = 0x17,
2302 }, 2459 },
2303 2460
2304 HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others), 2461 HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
@@ -2392,7 +2549,7 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
2392 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, 2549 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2393 {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ 2550 {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
2394 2551
2395 /* Port F: unused */ 2552 /* Port F: external DC input through microphone port */
2396 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, 2553 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2397 2554
2398 /* Port G: internal speakers */ 2555 /* Port G: internal speakers */
@@ -2513,15 +2670,22 @@ static int cxt5066_init(struct hda_codec *codec)
2513 if (spec->dell_vostro) 2670 if (spec->dell_vostro)
2514 cxt5066_vostro_automic(codec); 2671 cxt5066_vostro_automic(codec);
2515 } 2672 }
2673 cxt5066_set_mic_boost(codec);
2516 return 0; 2674 return 0;
2517} 2675}
2518 2676
2519static int cxt5066_olpc_init(struct hda_codec *codec) 2677static int cxt5066_olpc_init(struct hda_codec *codec)
2520{ 2678{
2679 struct conexant_spec *spec = codec->spec;
2521 snd_printdd("CXT5066: init\n"); 2680 snd_printdd("CXT5066: init\n");
2522 conexant_init(codec); 2681 conexant_init(codec);
2523 cxt5066_hp_automute(codec); 2682 cxt5066_hp_automute(codec);
2524 cxt5066_olpc_automic(codec); 2683 if (!spec->dc_enable) {
2684 cxt5066_set_mic_boost(codec);
2685 cxt5066_olpc_automic(codec);
2686 } else {
2687 cxt5066_enable_dc(codec);
2688 }
2525 return 0; 2689 return 0;
2526} 2690}
2527 2691
@@ -2604,8 +2768,10 @@ static int patch_cxt5066(struct hda_codec *codec)
2604 codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event; 2768 codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
2605 spec->init_verbs[0] = cxt5066_init_verbs_olpc; 2769 spec->init_verbs[0] = cxt5066_init_verbs_olpc;
2606 spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; 2770 spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
2771 spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc;
2607 spec->mixers[spec->num_mixers++] = cxt5066_mixers; 2772 spec->mixers[spec->num_mixers++] = cxt5066_mixers;
2608 spec->port_d_mode = 0; 2773 spec->port_d_mode = 0;
2774 spec->mic_boost = 3; /* default 30dB gain */
2609 2775
2610 /* no S/PDIF out */ 2776 /* no S/PDIF out */
2611 spec->multiout.dig_out_nid = 0; 2777 spec->multiout.dig_out_nid = 0;
@@ -2627,6 +2793,7 @@ static int patch_cxt5066(struct hda_codec *codec)
2627 spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers; 2793 spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
2628 spec->port_d_mode = 0; 2794 spec->port_d_mode = 0;
2629 spec->dell_vostro = 1; 2795 spec->dell_vostro = 1;
2796 spec->mic_boost = 3; /* default 30dB gain */
2630 snd_hda_attach_beep_device(codec, 0x13); 2797 snd_hda_attach_beep_device(codec, 0x13);
2631 2798
2632 /* no S/PDIF out */ 2799 /* no S/PDIF out */