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.c157
1 files changed, 152 insertions, 5 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index feabb44c7ca4..e863649d31f5 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;
@@ -1784,6 +1785,7 @@ static struct hda_verb cxt5051_init_verbs[] = {
1784 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, 1785 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
1785 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, 1786 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
1786 /* SPDIF route: PCM */ 1787 /* SPDIF route: PCM */
1788 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1787 {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, 1789 {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
1788 /* EAPD */ 1790 /* EAPD */
1789 {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ 1791 {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
@@ -1840,6 +1842,7 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
1840 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, 1842 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
1841 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, 1843 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
1842 /* SPDIF route: PCM */ 1844 /* SPDIF route: PCM */
1845 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* needed for W500 Advanced Mini Dock 250410 */
1843 {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, 1846 {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
1844 /* EAPD */ 1847 /* EAPD */
1845 {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ 1848 {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
@@ -1911,7 +1914,7 @@ enum {
1911 CXT5051_LAPTOP, /* Laptops w/ EAPD support */ 1914 CXT5051_LAPTOP, /* Laptops w/ EAPD support */
1912 CXT5051_HP, /* no docking */ 1915 CXT5051_HP, /* no docking */
1913 CXT5051_HP_DV6736, /* HP without mic switch */ 1916 CXT5051_HP_DV6736, /* HP without mic switch */
1914 CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ 1917 CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */
1915 CXT5051_F700, /* HP Compaq Presario F700 */ 1918 CXT5051_F700, /* HP Compaq Presario F700 */
1916 CXT5051_TOSHIBA, /* Toshiba M300 & co */ 1919 CXT5051_TOSHIBA, /* Toshiba M300 & co */
1917 CXT5051_MODELS 1920 CXT5051_MODELS
@@ -2033,6 +2036,9 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
2033 /* Port D (HP/LO) */ 2036 /* Port D (HP/LO) */
2034 pinctl = ((spec->hp_present & 2) && spec->cur_eapd) 2037 pinctl = ((spec->hp_present & 2) && spec->cur_eapd)
2035 ? spec->port_d_mode : 0; 2038 ? spec->port_d_mode : 0;
2039 /* Mute if Port A is connected on Thinkpad */
2040 if (spec->thinkpad && (spec->hp_present & 1))
2041 pinctl = 0;
2036 snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 2042 snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
2037 pinctl); 2043 pinctl);
2038 2044
@@ -2213,6 +2219,50 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec)
2213 } 2219 }
2214} 2220}
2215 2221
2222/* toggle input of built-in digital mic and mic jack appropriately
2223 order is: external mic -> dock mic -> interal mic */
2224static void cxt5066_thinkpad_automic(struct hda_codec *codec)
2225{
2226 unsigned int ext_present, dock_present;
2227
2228 static struct hda_verb ext_mic_present[] = {
2229 {0x14, AC_VERB_SET_CONNECT_SEL, 0},
2230 {0x17, AC_VERB_SET_CONNECT_SEL, 1},
2231 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2232 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2233 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2234 {}
2235 };
2236 static struct hda_verb dock_mic_present[] = {
2237 {0x14, AC_VERB_SET_CONNECT_SEL, 0},
2238 {0x17, AC_VERB_SET_CONNECT_SEL, 0},
2239 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2240 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2241 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2242 {}
2243 };
2244 static struct hda_verb ext_mic_absent[] = {
2245 {0x14, AC_VERB_SET_CONNECT_SEL, 2},
2246 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2247 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2248 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2249 {}
2250 };
2251
2252 ext_present = snd_hda_jack_detect(codec, 0x1b);
2253 dock_present = snd_hda_jack_detect(codec, 0x1a);
2254 if (ext_present) {
2255 snd_printdd("CXT5066: external microphone detected\n");
2256 snd_hda_sequence_write(codec, ext_mic_present);
2257 } else if (dock_present) {
2258 snd_printdd("CXT5066: dock microphone detected\n");
2259 snd_hda_sequence_write(codec, dock_mic_present);
2260 } else {
2261 snd_printdd("CXT5066: external microphone absent\n");
2262 snd_hda_sequence_write(codec, ext_mic_absent);
2263 }
2264}
2265
2216/* mute internal speaker if HP is plugged */ 2266/* mute internal speaker if HP is plugged */
2217static void cxt5066_hp_automute(struct hda_codec *codec) 2267static void cxt5066_hp_automute(struct hda_codec *codec)
2218{ 2268{
@@ -2225,7 +2275,8 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
2225 /* Port D */ 2275 /* Port D */
2226 portD = snd_hda_jack_detect(codec, 0x1c); 2276 portD = snd_hda_jack_detect(codec, 0x1c);
2227 2277
2228 spec->hp_present = !!(portA | portD); 2278 spec->hp_present = !!(portA);
2279 spec->hp_present |= portD ? 2 : 0;
2229 snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", 2280 snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
2230 portA, portD, spec->hp_present); 2281 portA, portD, spec->hp_present);
2231 cxt5066_update_speaker(codec); 2282 cxt5066_update_speaker(codec);
@@ -2276,6 +2327,20 @@ static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res)
2276 } 2327 }
2277} 2328}
2278 2329
2330/* unsolicited event for jack sensing */
2331static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res)
2332{
2333 snd_printdd("CXT5066_thinkpad: unsol event %x (%x)\n", res, res >> 26);
2334 switch (res >> 26) {
2335 case CONEXANT_HP_EVENT:
2336 cxt5066_hp_automute(codec);
2337 break;
2338 case CONEXANT_MIC_EVENT:
2339 cxt5066_thinkpad_automic(codec);
2340 break;
2341 }
2342}
2343
2279static const struct hda_input_mux cxt5066_analog_mic_boost = { 2344static const struct hda_input_mux cxt5066_analog_mic_boost = {
2280 .num_items = 5, 2345 .num_items = 5,
2281 .items = { 2346 .items = {
@@ -2294,7 +2359,7 @@ static void cxt5066_set_mic_boost(struct hda_codec *codec)
2294 AC_VERB_SET_AMP_GAIN_MUTE, 2359 AC_VERB_SET_AMP_GAIN_MUTE,
2295 AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | 2360 AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
2296 cxt5066_analog_mic_boost.items[spec->mic_boost].index); 2361 cxt5066_analog_mic_boost.items[spec->mic_boost].index);
2297 if (spec->ideapad) { 2362 if (spec->ideapad || spec->thinkpad) {
2298 /* adjust the internal mic as well...it is not through 0x17 */ 2363 /* adjust the internal mic as well...it is not through 0x17 */
2299 snd_hda_codec_write_cache(codec, 0x23, 0, 2364 snd_hda_codec_write_cache(codec, 0x23, 0,
2300 AC_VERB_SET_AMP_GAIN_MUTE, 2365 AC_VERB_SET_AMP_GAIN_MUTE,
@@ -2782,6 +2847,64 @@ static struct hda_verb cxt5066_init_verbs_ideapad[] = {
2782 { } /* end */ 2847 { } /* end */
2783}; 2848};
2784 2849
2850static struct hda_verb cxt5066_init_verbs_thinkpad[] = {
2851 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
2852 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
2853
2854 /* Port G: internal speakers */
2855 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2856 {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
2857
2858 /* Port A: HP, Amp */
2859 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2860 {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
2861
2862 /* Port B: Mic Dock */
2863 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2864
2865 /* Port C: Mic */
2866 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2867
2868 /* Port D: HP Dock, Amp */
2869 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
2870 {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
2871
2872 /* DAC1 */
2873 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2874
2875 /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
2876 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
2877 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2878 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
2879 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2880 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2881 {0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */
2882
2883 /* Audio input selector */
2884 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
2885 {0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */
2886
2887 /* SPDIF route: PCM */
2888 {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
2889 {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
2890
2891 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2892 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2893
2894 /* internal microphone */
2895 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
2896
2897 /* EAPD */
2898 {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
2899
2900 /* enable unsolicited events for Port A, B, C and D */
2901 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
2902 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
2903 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
2904 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
2905 { } /* end */
2906};
2907
2785static struct hda_verb cxt5066_init_verbs_portd_lo[] = { 2908static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
2786 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, 2909 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2787 { } /* end */ 2910 { } /* end */
@@ -2800,6 +2923,8 @@ static int cxt5066_init(struct hda_codec *codec)
2800 cxt5066_vostro_automic(codec); 2923 cxt5066_vostro_automic(codec);
2801 else if (spec->ideapad) 2924 else if (spec->ideapad)
2802 cxt5066_ideapad_automic(codec); 2925 cxt5066_ideapad_automic(codec);
2926 else if (spec->thinkpad)
2927 cxt5066_thinkpad_automic(codec);
2803 } 2928 }
2804 cxt5066_set_mic_boost(codec); 2929 cxt5066_set_mic_boost(codec);
2805 return 0; 2930 return 0;
@@ -2821,20 +2946,22 @@ static int cxt5066_olpc_init(struct hda_codec *codec)
2821} 2946}
2822 2947
2823enum { 2948enum {
2824 CXT5066_LAPTOP, /* Laptops w/ EAPD support */ 2949 CXT5066_LAPTOP, /* Laptops w/ EAPD support */
2825 CXT5066_DELL_LAPTOP, /* Dell Laptop */ 2950 CXT5066_DELL_LAPTOP, /* Dell Laptop */
2826 CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ 2951 CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
2827 CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ 2952 CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */
2828 CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ 2953 CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
2954 CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
2829 CXT5066_MODELS 2955 CXT5066_MODELS
2830}; 2956};
2831 2957
2832static const char *cxt5066_models[CXT5066_MODELS] = { 2958static const char *cxt5066_models[CXT5066_MODELS] = {
2833 [CXT5066_LAPTOP] = "laptop", 2959 [CXT5066_LAPTOP] = "laptop",
2834 [CXT5066_DELL_LAPTOP] = "dell-laptop", 2960 [CXT5066_DELL_LAPTOP] = "dell-laptop",
2835 [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", 2961 [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
2836 [CXT5066_DELL_VOSTO] = "dell-vostro", 2962 [CXT5066_DELL_VOSTO] = "dell-vostro",
2837 [CXT5066_IDEAPAD] = "ideapad", 2963 [CXT5066_IDEAPAD] = "ideapad",
2964 [CXT5066_THINKPAD] = "thinkpad",
2838}; 2965};
2839 2966
2840static struct snd_pci_quirk cxt5066_cfg_tbl[] = { 2967static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
@@ -2849,6 +2976,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
2849 SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), 2976 SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
2850 SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD), 2977 SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
2851 SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), 2978 SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
2979 SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
2852 {} 2980 {}
2853}; 2981};
2854 2982
@@ -2956,6 +3084,22 @@ static int patch_cxt5066(struct hda_codec *codec)
2956 /* input source automatically selected */ 3084 /* input source automatically selected */
2957 spec->input_mux = NULL; 3085 spec->input_mux = NULL;
2958 break; 3086 break;
3087 case CXT5066_THINKPAD:
3088 codec->patch_ops.init = cxt5066_init;
3089 codec->patch_ops.unsol_event = cxt5066_thinkpad_event;
3090 spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
3091 spec->mixers[spec->num_mixers++] = cxt5066_mixers;
3092 spec->init_verbs[0] = cxt5066_init_verbs_thinkpad;
3093 spec->thinkpad = 1;
3094 spec->port_d_mode = PIN_OUT;
3095 spec->mic_boost = 2; /* default 20dB gain */
3096
3097 /* no S/PDIF out */
3098 spec->multiout.dig_out_nid = 0;
3099
3100 /* input source automatically selected */
3101 spec->input_mux = NULL;
3102 break;
2959 } 3103 }
2960 3104
2961 return 0; 3105 return 0;
@@ -2975,6 +3119,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
2975 .patch = patch_cxt5066 }, 3119 .patch = patch_cxt5066 },
2976 { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)", 3120 { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
2977 .patch = patch_cxt5066 }, 3121 .patch = patch_cxt5066 },
3122 { .id = 0x14f15069, .name = "CX20585",
3123 .patch = patch_cxt5066 },
2978 {} /* terminator */ 3124 {} /* terminator */
2979}; 3125};
2980 3126
@@ -2983,6 +3129,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15047");
2983MODULE_ALIAS("snd-hda-codec-id:14f15051"); 3129MODULE_ALIAS("snd-hda-codec-id:14f15051");
2984MODULE_ALIAS("snd-hda-codec-id:14f15066"); 3130MODULE_ALIAS("snd-hda-codec-id:14f15066");
2985MODULE_ALIAS("snd-hda-codec-id:14f15067"); 3131MODULE_ALIAS("snd-hda-codec-id:14f15067");
3132MODULE_ALIAS("snd-hda-codec-id:14f15069");
2986 3133
2987MODULE_LICENSE("GPL"); 3134MODULE_LICENSE("GPL");
2988MODULE_DESCRIPTION("Conexant HD-audio codec"); 3135MODULE_DESCRIPTION("Conexant HD-audio codec");