aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Taprogge <jens.taprogge@taprogge.org>2010-04-14 17:42:04 -0400
committerTakashi Iwai <tiwai@suse.de>2010-04-15 03:10:29 -0400
commit7b2bfdbc0dee5a321b5c02febe157adebd33ab3a (patch)
tree371857c94d67efd82d282c3dbb7e8af6b79a17cc
parent039f0f3a5b724a1ed022247301565f1a43d27544 (diff)
ALSA: hda - Add initial support for Thinkpad T410s HDA codec
attached please find a patch that adds support for at least the T410s HDA codec. Most likely it will also add support for the T410 and T510 based models. The patch was derived from Ideapad support. Support for the laptop's and docking-station output connectors as well as the docking-station microphone connector and the laptops internal devices has been tested. Since it has been developed without a data-sheet available, support for digital outputs and the laptop's microphone input may well be incorrect. Microphone mute functionality is not included: The microphone mute button seems to be reported through thinkpad_acpi key 0000101b. The mute button LED seems to be wired to thinkpad_acpi led number 15. Signed-off-by: Jens Taprogge <jens.taprogge@taprogge.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/pci/hda/patch_conexant.c153
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 */
2220static 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 */
2215static void cxt5066_hp_automute(struct hda_codec *codec) 2263static 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 */
2327static 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
2277static const struct hda_input_mux cxt5066_analog_mic_boost = { 2340static 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
2846static 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
2783static struct hda_verb cxt5066_init_verbs_portd_lo[] = { 2904static 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
2821enum { 2944enum {
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
2830static const char *cxt5066_models[CXT5066_MODELS] = { 2954static 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
2838static struct snd_pci_quirk cxt5066_cfg_tbl[] = { 2963static 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");
2977MODULE_ALIAS("snd-hda-codec-id:14f15051"); 3121MODULE_ALIAS("snd-hda-codec-id:14f15051");
2978MODULE_ALIAS("snd-hda-codec-id:14f15066"); 3122MODULE_ALIAS("snd-hda-codec-id:14f15066");
2979MODULE_ALIAS("snd-hda-codec-id:14f15067"); 3123MODULE_ALIAS("snd-hda-codec-id:14f15067");
3124MODULE_ALIAS("snd-hda-codec-id:14f15069");
2980 3125
2981MODULE_LICENSE("GPL"); 3126MODULE_LICENSE("GPL");
2982MODULE_DESCRIPTION("Conexant HD-audio codec"); 3127MODULE_DESCRIPTION("Conexant HD-audio codec");