aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/patch_conexant.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
-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");