diff options
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
| -rw-r--r-- | sound/pci/hda/patch_conexant.c | 153 |
1 files changed, 149 insertions, 4 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 61682e1d09da..509a1049ff40 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c | |||
| @@ -115,6 +115,7 @@ struct conexant_spec { | |||
| 115 | unsigned int port_d_mode; | 115 | unsigned int port_d_mode; |
| 116 | unsigned int dell_vostro:1; | 116 | unsigned int dell_vostro:1; |
| 117 | unsigned int ideapad:1; | 117 | unsigned int ideapad:1; |
| 118 | unsigned int thinkpad:1; | ||
| 118 | 119 | ||
| 119 | unsigned int ext_mic_present; | 120 | unsigned int ext_mic_present; |
| 120 | unsigned int recording; | 121 | unsigned int recording; |
| @@ -2031,6 +2032,9 @@ static void cxt5066_update_speaker(struct hda_codec *codec) | |||
| 2031 | /* Port D (HP/LO) */ | 2032 | /* Port D (HP/LO) */ |
| 2032 | pinctl = ((spec->hp_present & 2) && spec->cur_eapd) | 2033 | pinctl = ((spec->hp_present & 2) && spec->cur_eapd) |
| 2033 | ? spec->port_d_mode : 0; | 2034 | ? spec->port_d_mode : 0; |
| 2035 | /* Mute if Port A is connected on Thinkpad */ | ||
| 2036 | if (spec->thinkpad && (spec->hp_present & 1)) | ||
| 2037 | pinctl = 0; | ||
| 2034 | snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | 2038 | snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, |
| 2035 | pinctl); | 2039 | pinctl); |
| 2036 | 2040 | ||
| @@ -2211,6 +2215,50 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec) | |||
| 2211 | } | 2215 | } |
| 2212 | } | 2216 | } |
| 2213 | 2217 | ||
| 2218 | /* toggle input of built-in digital mic and mic jack appropriately | ||
| 2219 | order is: external mic -> dock mic -> interal mic */ | ||
| 2220 | static void cxt5066_thinkpad_automic(struct hda_codec *codec) | ||
| 2221 | { | ||
| 2222 | unsigned int ext_present, dock_present; | ||
| 2223 | |||
| 2224 | static struct hda_verb ext_mic_present[] = { | ||
| 2225 | {0x14, AC_VERB_SET_CONNECT_SEL, 0}, | ||
| 2226 | {0x17, AC_VERB_SET_CONNECT_SEL, 1}, | ||
| 2227 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
| 2228 | {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
| 2229 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
| 2230 | {} | ||
| 2231 | }; | ||
| 2232 | static struct hda_verb dock_mic_present[] = { | ||
| 2233 | {0x14, AC_VERB_SET_CONNECT_SEL, 0}, | ||
| 2234 | {0x17, AC_VERB_SET_CONNECT_SEL, 0}, | ||
| 2235 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
| 2236 | {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
| 2237 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
| 2238 | {} | ||
| 2239 | }; | ||
| 2240 | static struct hda_verb ext_mic_absent[] = { | ||
| 2241 | {0x14, AC_VERB_SET_CONNECT_SEL, 2}, | ||
| 2242 | {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, | ||
| 2243 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
| 2244 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
| 2245 | {} | ||
| 2246 | }; | ||
| 2247 | |||
| 2248 | ext_present = snd_hda_jack_detect(codec, 0x1b); | ||
| 2249 | dock_present = snd_hda_jack_detect(codec, 0x1a); | ||
| 2250 | if (ext_present) { | ||
| 2251 | snd_printdd("CXT5066: external microphone detected\n"); | ||
| 2252 | snd_hda_sequence_write(codec, ext_mic_present); | ||
| 2253 | } else if (dock_present) { | ||
| 2254 | snd_printdd("CXT5066: dock microphone detected\n"); | ||
| 2255 | snd_hda_sequence_write(codec, dock_mic_present); | ||
| 2256 | } else { | ||
| 2257 | snd_printdd("CXT5066: external microphone absent\n"); | ||
| 2258 | snd_hda_sequence_write(codec, ext_mic_absent); | ||
| 2259 | } | ||
| 2260 | } | ||
| 2261 | |||
| 2214 | /* mute internal speaker if HP is plugged */ | 2262 | /* mute internal speaker if HP is plugged */ |
| 2215 | static void cxt5066_hp_automute(struct hda_codec *codec) | 2263 | static void cxt5066_hp_automute(struct hda_codec *codec) |
| 2216 | { | 2264 | { |
| @@ -2223,7 +2271,8 @@ static void cxt5066_hp_automute(struct hda_codec *codec) | |||
| 2223 | /* Port D */ | 2271 | /* Port D */ |
| 2224 | portD = snd_hda_jack_detect(codec, 0x1c); | 2272 | portD = snd_hda_jack_detect(codec, 0x1c); |
| 2225 | 2273 | ||
| 2226 | spec->hp_present = !!(portA | portD); | 2274 | spec->hp_present = !!(portA); |
| 2275 | spec->hp_present |= portD ? 2 : 0; | ||
| 2227 | snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", | 2276 | snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", |
| 2228 | portA, portD, spec->hp_present); | 2277 | portA, portD, spec->hp_present); |
| 2229 | cxt5066_update_speaker(codec); | 2278 | cxt5066_update_speaker(codec); |
| @@ -2274,6 +2323,20 @@ static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res) | |||
| 2274 | } | 2323 | } |
| 2275 | } | 2324 | } |
| 2276 | 2325 | ||
| 2326 | /* unsolicited event for jack sensing */ | ||
| 2327 | static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res) | ||
| 2328 | { | ||
| 2329 | snd_printdd("CXT5066_thinkpad: unsol event %x (%x)\n", res, res >> 26); | ||
| 2330 | switch (res >> 26) { | ||
| 2331 | case CONEXANT_HP_EVENT: | ||
| 2332 | cxt5066_hp_automute(codec); | ||
| 2333 | break; | ||
| 2334 | case CONEXANT_MIC_EVENT: | ||
| 2335 | cxt5066_thinkpad_automic(codec); | ||
| 2336 | break; | ||
| 2337 | } | ||
| 2338 | } | ||
| 2339 | |||
| 2277 | static const struct hda_input_mux cxt5066_analog_mic_boost = { | 2340 | static const struct hda_input_mux cxt5066_analog_mic_boost = { |
| 2278 | .num_items = 5, | 2341 | .num_items = 5, |
| 2279 | .items = { | 2342 | .items = { |
| @@ -2292,7 +2355,7 @@ static void cxt5066_set_mic_boost(struct hda_codec *codec) | |||
| 2292 | AC_VERB_SET_AMP_GAIN_MUTE, | 2355 | AC_VERB_SET_AMP_GAIN_MUTE, |
| 2293 | AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | | 2356 | AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | |
| 2294 | cxt5066_analog_mic_boost.items[spec->mic_boost].index); | 2357 | cxt5066_analog_mic_boost.items[spec->mic_boost].index); |
| 2295 | if (spec->ideapad) { | 2358 | if (spec->ideapad || spec->thinkpad) { |
| 2296 | /* adjust the internal mic as well...it is not through 0x17 */ | 2359 | /* adjust the internal mic as well...it is not through 0x17 */ |
| 2297 | snd_hda_codec_write_cache(codec, 0x23, 0, | 2360 | snd_hda_codec_write_cache(codec, 0x23, 0, |
| 2298 | AC_VERB_SET_AMP_GAIN_MUTE, | 2361 | AC_VERB_SET_AMP_GAIN_MUTE, |
| @@ -2780,6 +2843,64 @@ static struct hda_verb cxt5066_init_verbs_ideapad[] = { | |||
| 2780 | { } /* end */ | 2843 | { } /* end */ |
| 2781 | }; | 2844 | }; |
| 2782 | 2845 | ||
| 2846 | static struct hda_verb cxt5066_init_verbs_thinkpad[] = { | ||
| 2847 | {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */ | ||
| 2848 | {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */ | ||
| 2849 | |||
| 2850 | /* Port G: internal speakers */ | ||
| 2851 | {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
| 2852 | {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||
| 2853 | |||
| 2854 | /* Port A: HP, Amp */ | ||
| 2855 | {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
| 2856 | {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||
| 2857 | |||
| 2858 | /* Port B: Mic Dock */ | ||
| 2859 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
| 2860 | |||
| 2861 | /* Port C: Mic */ | ||
| 2862 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
| 2863 | |||
| 2864 | /* Port D: HP Dock, Amp */ | ||
| 2865 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
| 2866 | {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||
| 2867 | |||
| 2868 | /* DAC1 */ | ||
| 2869 | {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
| 2870 | |||
| 2871 | /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */ | ||
| 2872 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, | ||
| 2873 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||
| 2874 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50}, | ||
| 2875 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, | ||
| 2876 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, | ||
| 2877 | {0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */ | ||
| 2878 | |||
| 2879 | /* Audio input selector */ | ||
| 2880 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2}, | ||
| 2881 | {0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */ | ||
| 2882 | |||
| 2883 | /* SPDIF route: PCM */ | ||
| 2884 | {0x20, AC_VERB_SET_CONNECT_SEL, 0x0}, | ||
| 2885 | {0x22, AC_VERB_SET_CONNECT_SEL, 0x0}, | ||
| 2886 | |||
| 2887 | {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
| 2888 | {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
| 2889 | |||
| 2890 | /* internal microphone */ | ||
| 2891 | {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */ | ||
| 2892 | |||
| 2893 | /* EAPD */ | ||
| 2894 | {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ | ||
| 2895 | |||
| 2896 | /* enable unsolicited events for Port A, B, C and D */ | ||
| 2897 | {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, | ||
| 2898 | {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, | ||
| 2899 | {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, | ||
| 2900 | {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, | ||
| 2901 | { } /* end */ | ||
| 2902 | }; | ||
| 2903 | |||
| 2783 | static struct hda_verb cxt5066_init_verbs_portd_lo[] = { | 2904 | static struct hda_verb cxt5066_init_verbs_portd_lo[] = { |
| 2784 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | 2905 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, |
| 2785 | { } /* end */ | 2906 | { } /* end */ |
| @@ -2798,6 +2919,8 @@ static int cxt5066_init(struct hda_codec *codec) | |||
| 2798 | cxt5066_vostro_automic(codec); | 2919 | cxt5066_vostro_automic(codec); |
| 2799 | else if (spec->ideapad) | 2920 | else if (spec->ideapad) |
| 2800 | cxt5066_ideapad_automic(codec); | 2921 | cxt5066_ideapad_automic(codec); |
| 2922 | else if (spec->thinkpad) | ||
| 2923 | cxt5066_thinkpad_automic(codec); | ||
| 2801 | } | 2924 | } |
| 2802 | cxt5066_set_mic_boost(codec); | 2925 | cxt5066_set_mic_boost(codec); |
| 2803 | return 0; | 2926 | return 0; |
| @@ -2819,20 +2942,22 @@ static int cxt5066_olpc_init(struct hda_codec *codec) | |||
| 2819 | } | 2942 | } |
| 2820 | 2943 | ||
| 2821 | enum { | 2944 | enum { |
| 2822 | CXT5066_LAPTOP, /* Laptops w/ EAPD support */ | 2945 | CXT5066_LAPTOP, /* Laptops w/ EAPD support */ |
| 2823 | CXT5066_DELL_LAPTOP, /* Dell Laptop */ | 2946 | CXT5066_DELL_LAPTOP, /* Dell Laptop */ |
| 2824 | CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ | 2947 | CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ |
| 2825 | CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ | 2948 | CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ |
| 2826 | CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ | 2949 | CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ |
| 2950 | CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */ | ||
| 2827 | CXT5066_MODELS | 2951 | CXT5066_MODELS |
| 2828 | }; | 2952 | }; |
| 2829 | 2953 | ||
| 2830 | static const char *cxt5066_models[CXT5066_MODELS] = { | 2954 | static const char *cxt5066_models[CXT5066_MODELS] = { |
| 2831 | [CXT5066_LAPTOP] = "laptop", | 2955 | [CXT5066_LAPTOP] = "laptop", |
| 2832 | [CXT5066_DELL_LAPTOP] = "dell-laptop", | 2956 | [CXT5066_DELL_LAPTOP] = "dell-laptop", |
| 2833 | [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", | 2957 | [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", |
| 2834 | [CXT5066_DELL_VOSTO] = "dell-vostro", | 2958 | [CXT5066_DELL_VOSTO] = "dell-vostro", |
| 2835 | [CXT5066_IDEAPAD] = "ideapad", | 2959 | [CXT5066_IDEAPAD] = "ideapad", |
| 2960 | [CXT5066_THINKPAD] = "thinkpad", | ||
| 2836 | }; | 2961 | }; |
| 2837 | 2962 | ||
| 2838 | static struct snd_pci_quirk cxt5066_cfg_tbl[] = { | 2963 | static struct snd_pci_quirk cxt5066_cfg_tbl[] = { |
| @@ -2843,6 +2968,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { | |||
| 2843 | SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), | 2968 | SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), |
| 2844 | SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), | 2969 | SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), |
| 2845 | SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), | 2970 | SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), |
| 2971 | SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), | ||
| 2846 | {} | 2972 | {} |
| 2847 | }; | 2973 | }; |
| 2848 | 2974 | ||
| @@ -2950,6 +3076,22 @@ static int patch_cxt5066(struct hda_codec *codec) | |||
| 2950 | /* input source automatically selected */ | 3076 | /* input source automatically selected */ |
| 2951 | spec->input_mux = NULL; | 3077 | spec->input_mux = NULL; |
| 2952 | break; | 3078 | break; |
| 3079 | case CXT5066_THINKPAD: | ||
| 3080 | codec->patch_ops.init = cxt5066_init; | ||
| 3081 | codec->patch_ops.unsol_event = cxt5066_thinkpad_event; | ||
| 3082 | spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; | ||
| 3083 | spec->mixers[spec->num_mixers++] = cxt5066_mixers; | ||
| 3084 | spec->init_verbs[0] = cxt5066_init_verbs_thinkpad; | ||
| 3085 | spec->thinkpad = 1; | ||
| 3086 | spec->port_d_mode = PIN_OUT; | ||
| 3087 | spec->mic_boost = 2; /* default 20dB gain */ | ||
| 3088 | |||
| 3089 | /* no S/PDIF out */ | ||
| 3090 | spec->multiout.dig_out_nid = 0; | ||
| 3091 | |||
| 3092 | /* input source automatically selected */ | ||
| 3093 | spec->input_mux = NULL; | ||
| 3094 | break; | ||
| 2953 | } | 3095 | } |
| 2954 | 3096 | ||
| 2955 | return 0; | 3097 | return 0; |
| @@ -2969,6 +3111,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { | |||
| 2969 | .patch = patch_cxt5066 }, | 3111 | .patch = patch_cxt5066 }, |
| 2970 | { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)", | 3112 | { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)", |
| 2971 | .patch = patch_cxt5066 }, | 3113 | .patch = patch_cxt5066 }, |
| 3114 | { .id = 0x14f15069, .name = "CX20585", | ||
| 3115 | .patch = patch_cxt5066 }, | ||
| 2972 | {} /* terminator */ | 3116 | {} /* terminator */ |
| 2973 | }; | 3117 | }; |
| 2974 | 3118 | ||
| @@ -2977,6 +3121,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15047"); | |||
| 2977 | MODULE_ALIAS("snd-hda-codec-id:14f15051"); | 3121 | MODULE_ALIAS("snd-hda-codec-id:14f15051"); |
| 2978 | MODULE_ALIAS("snd-hda-codec-id:14f15066"); | 3122 | MODULE_ALIAS("snd-hda-codec-id:14f15066"); |
| 2979 | MODULE_ALIAS("snd-hda-codec-id:14f15067"); | 3123 | MODULE_ALIAS("snd-hda-codec-id:14f15067"); |
| 3124 | MODULE_ALIAS("snd-hda-codec-id:14f15069"); | ||
| 2980 | 3125 | ||
| 2981 | MODULE_LICENSE("GPL"); | 3126 | MODULE_LICENSE("GPL"); |
| 2982 | MODULE_DESCRIPTION("Conexant HD-audio codec"); | 3127 | MODULE_DESCRIPTION("Conexant HD-audio codec"); |
