diff options
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 383 |
1 files changed, 323 insertions, 60 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index c578c28f368..685015a5329 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c | |||
@@ -111,8 +111,22 @@ struct conexant_spec { | |||
111 | 111 | ||
112 | unsigned int dell_automute; | 112 | unsigned int dell_automute; |
113 | unsigned int port_d_mode; | 113 | unsigned int port_d_mode; |
114 | unsigned char ext_mic_bias; | ||
115 | unsigned int dell_vostro; | 114 | unsigned int dell_vostro; |
115 | |||
116 | unsigned int ext_mic_present; | ||
117 | unsigned int recording; | ||
118 | void (*capture_prepare)(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 */ | ||
116 | }; | 130 | }; |
117 | 131 | ||
118 | static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, | 132 | static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, |
@@ -185,6 +199,8 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo, | |||
185 | struct snd_pcm_substream *substream) | 199 | struct snd_pcm_substream *substream) |
186 | { | 200 | { |
187 | struct conexant_spec *spec = codec->spec; | 201 | struct conexant_spec *spec = codec->spec; |
202 | if (spec->capture_prepare) | ||
203 | spec->capture_prepare(codec); | ||
188 | snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], | 204 | snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], |
189 | stream_tag, 0, format); | 205 | stream_tag, 0, format); |
190 | return 0; | 206 | return 0; |
@@ -196,6 +212,8 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, | |||
196 | { | 212 | { |
197 | struct conexant_spec *spec = codec->spec; | 213 | struct conexant_spec *spec = codec->spec; |
198 | snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); | 214 | snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); |
215 | if (spec->capture_cleanup) | ||
216 | spec->capture_cleanup(codec); | ||
199 | return 0; | 217 | return 0; |
200 | } | 218 | } |
201 | 219 | ||
@@ -1723,6 +1741,22 @@ static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = { | |||
1723 | {} | 1741 | {} |
1724 | }; | 1742 | }; |
1725 | 1743 | ||
1744 | static struct snd_kcontrol_new cxt5051_f700_mixers[] = { | ||
1745 | HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT), | ||
1746 | HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT), | ||
1747 | HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), | ||
1748 | { | ||
1749 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1750 | .name = "Master Playback Switch", | ||
1751 | .info = cxt_eapd_info, | ||
1752 | .get = cxt_eapd_get, | ||
1753 | .put = cxt5051_hp_master_sw_put, | ||
1754 | .private_value = 0x1a, | ||
1755 | }, | ||
1756 | |||
1757 | {} | ||
1758 | }; | ||
1759 | |||
1726 | static struct hda_verb cxt5051_init_verbs[] = { | 1760 | static struct hda_verb cxt5051_init_verbs[] = { |
1727 | /* Line in, Mic */ | 1761 | /* Line in, Mic */ |
1728 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, | 1762 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, |
@@ -1813,6 +1847,32 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { | |||
1813 | { } /* end */ | 1847 | { } /* end */ |
1814 | }; | 1848 | }; |
1815 | 1849 | ||
1850 | static struct hda_verb cxt5051_f700_init_verbs[] = { | ||
1851 | /* Line in, Mic */ | ||
1852 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x03}, | ||
1853 | {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
1854 | {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, | ||
1855 | {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, | ||
1856 | /* SPK */ | ||
1857 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
1858 | {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
1859 | /* HP, Amp */ | ||
1860 | {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
1861 | {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
1862 | /* DAC1 */ | ||
1863 | {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
1864 | /* Record selector: Int mic */ | ||
1865 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, | ||
1866 | {0x14, AC_VERB_SET_CONNECT_SEL, 0x1}, | ||
1867 | /* SPDIF route: PCM */ | ||
1868 | {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, | ||
1869 | /* EAPD */ | ||
1870 | {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ | ||
1871 | {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, | ||
1872 | {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, | ||
1873 | { } /* end */ | ||
1874 | }; | ||
1875 | |||
1816 | /* initialize jack-sensing, too */ | 1876 | /* initialize jack-sensing, too */ |
1817 | static int cxt5051_init(struct hda_codec *codec) | 1877 | static int cxt5051_init(struct hda_codec *codec) |
1818 | { | 1878 | { |
@@ -1832,6 +1892,7 @@ enum { | |||
1832 | CXT5051_HP, /* no docking */ | 1892 | CXT5051_HP, /* no docking */ |
1833 | CXT5051_HP_DV6736, /* HP without mic switch */ | 1893 | CXT5051_HP_DV6736, /* HP without mic switch */ |
1834 | CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ | 1894 | CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ |
1895 | CXT5051_F700, /* HP Compaq Presario F700 */ | ||
1835 | CXT5051_MODELS | 1896 | CXT5051_MODELS |
1836 | }; | 1897 | }; |
1837 | 1898 | ||
@@ -1840,6 +1901,7 @@ static const char *cxt5051_models[CXT5051_MODELS] = { | |||
1840 | [CXT5051_HP] = "hp", | 1901 | [CXT5051_HP] = "hp", |
1841 | [CXT5051_HP_DV6736] = "hp-dv6736", | 1902 | [CXT5051_HP_DV6736] = "hp-dv6736", |
1842 | [CXT5051_LENOVO_X200] = "lenovo-x200", | 1903 | [CXT5051_LENOVO_X200] = "lenovo-x200", |
1904 | [CXT5051_F700] = "hp 700" | ||
1843 | }; | 1905 | }; |
1844 | 1906 | ||
1845 | static struct snd_pci_quirk cxt5051_cfg_tbl[] = { | 1907 | static struct snd_pci_quirk cxt5051_cfg_tbl[] = { |
@@ -1849,6 +1911,7 @@ static struct snd_pci_quirk cxt5051_cfg_tbl[] = { | |||
1849 | CXT5051_LAPTOP), | 1911 | CXT5051_LAPTOP), |
1850 | SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), | 1912 | SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), |
1851 | SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200), | 1913 | SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200), |
1914 | SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700), | ||
1852 | {} | 1915 | {} |
1853 | }; | 1916 | }; |
1854 | 1917 | ||
@@ -1899,6 +1962,11 @@ static int patch_cxt5051(struct hda_codec *codec) | |||
1899 | case CXT5051_LENOVO_X200: | 1962 | case CXT5051_LENOVO_X200: |
1900 | spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; | 1963 | spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; |
1901 | break; | 1964 | break; |
1965 | case CXT5051_F700: | ||
1966 | spec->init_verbs[0] = cxt5051_f700_init_verbs; | ||
1967 | spec->mixers[0] = cxt5051_f700_mixers; | ||
1968 | spec->no_auto_mic = 1; | ||
1969 | break; | ||
1902 | } | 1970 | } |
1903 | 1971 | ||
1904 | return 0; | 1972 | return 0; |
@@ -1966,53 +2034,97 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol, | |||
1966 | return 1; | 2034 | return 1; |
1967 | } | 2035 | } |
1968 | 2036 | ||
1969 | /* toggle input of built-in and mic jack appropriately */ | 2037 | static const struct hda_input_mux cxt5066_olpc_dc_bias = { |
1970 | static void cxt5066_automic(struct hda_codec *codec) | 2038 | .num_items = 3, |
2039 | .items = { | ||
2040 | { "Off", PIN_IN }, | ||
2041 | { "50%", PIN_VREF50 }, | ||
2042 | { "80%", PIN_VREF80 }, | ||
2043 | }, | ||
2044 | }; | ||
2045 | |||
2046 | static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec) | ||
1971 | { | 2047 | { |
1972 | struct conexant_spec *spec = codec->spec; | 2048 | struct conexant_spec *spec = codec->spec; |
1973 | struct hda_verb ext_mic_present[] = { | 2049 | /* Even though port F is the DC input, the bias is controlled on port B. |
1974 | /* enable external mic, port B */ | 2050 | * we also leave that port as an active input (but unselected) in DC mode |
1975 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, | 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 | } | ||
1976 | 2056 | ||
1977 | /* switch to external mic input */ | 2057 | /* OLPC defers mic widget control until when capture is started because the |
1978 | {0x17, AC_VERB_SET_CONNECT_SEL, 0}, | 2058 | * microphone LED comes on as soon as these settings are put in place. if we |
2059 | * did this before recording, it would give the false indication that recording | ||
2060 | * is happening when it is not. */ | ||
2061 | static void cxt5066_olpc_select_mic(struct hda_codec *codec) | ||
2062 | { | ||
2063 | struct conexant_spec *spec = codec->spec; | ||
2064 | if (!spec->recording) | ||
2065 | return; | ||
1979 | 2066 | ||
1980 | /* disable internal mic, port C */ | 2067 | if (spec->dc_enable) { |
1981 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | 2068 | /* in DC mode we ignore presence detection and just use the jack |
1982 | {} | 2069 | * through our special DC port */ |
1983 | }; | 2070 | const struct hda_verb enable_dc_mode[] = { |
1984 | static struct hda_verb ext_mic_absent[] = { | 2071 | /* disble internal mic, port C */ |
1985 | /* enable internal mic, port C */ | 2072 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, |
1986 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | 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 | } | ||
1987 | 2084 | ||
1988 | /* switch to internal mic input */ | 2085 | /* disable DC (port F) */ |
1989 | {0x17, AC_VERB_SET_CONNECT_SEL, 1}, | 2086 | snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); |
1990 | 2087 | ||
1991 | /* disable external mic, port B */ | 2088 | /* external mic, port B */ |
1992 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | 2089 | snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, |
1993 | {} | 2090 | spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); |
1994 | }; | 2091 | |
2092 | /* internal mic, port C */ | ||
2093 | snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
2094 | spec->ext_mic_present ? 0 : PIN_VREF80); | ||
2095 | } | ||
2096 | |||
2097 | /* toggle input of built-in and mic jack appropriately */ | ||
2098 | static void cxt5066_olpc_automic(struct hda_codec *codec) | ||
2099 | { | ||
2100 | struct conexant_spec *spec = codec->spec; | ||
1995 | unsigned int present; | 2101 | unsigned int present; |
1996 | 2102 | ||
1997 | present = snd_hda_jack_detect(codec, 0x1a); | 2103 | if (spec->dc_enable) /* don't do presence detection in DC mode */ |
1998 | if (present) { | 2104 | return; |
2105 | |||
2106 | present = snd_hda_codec_read(codec, 0x1a, 0, | ||
2107 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
2108 | if (present) | ||
1999 | snd_printdd("CXT5066: external microphone detected\n"); | 2109 | snd_printdd("CXT5066: external microphone detected\n"); |
2000 | snd_hda_sequence_write(codec, ext_mic_present); | 2110 | else |
2001 | } else { | ||
2002 | snd_printdd("CXT5066: external microphone absent\n"); | 2111 | snd_printdd("CXT5066: external microphone absent\n"); |
2003 | snd_hda_sequence_write(codec, ext_mic_absent); | 2112 | |
2004 | } | 2113 | snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, |
2114 | present ? 0 : 1); | ||
2115 | spec->ext_mic_present = !!present; | ||
2116 | |||
2117 | cxt5066_olpc_select_mic(codec); | ||
2005 | } | 2118 | } |
2006 | 2119 | ||
2007 | /* toggle input of built-in digital mic and mic jack appropriately */ | 2120 | /* toggle input of built-in digital mic and mic jack appropriately */ |
2008 | static void cxt5066_vostro_automic(struct hda_codec *codec) | 2121 | static void cxt5066_vostro_automic(struct hda_codec *codec) |
2009 | { | 2122 | { |
2010 | struct conexant_spec *spec = codec->spec; | ||
2011 | unsigned int present; | 2123 | unsigned int present; |
2012 | 2124 | ||
2013 | struct hda_verb ext_mic_present[] = { | 2125 | struct hda_verb ext_mic_present[] = { |
2014 | /* enable external mic, port B */ | 2126 | /* enable external mic, port B */ |
2015 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, | 2127 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, |
2016 | 2128 | ||
2017 | /* switch to external mic input */ | 2129 | /* switch to external mic input */ |
2018 | {0x17, AC_VERB_SET_CONNECT_SEL, 0}, | 2130 | {0x17, AC_VERB_SET_CONNECT_SEL, 0}, |
@@ -2063,15 +2175,18 @@ static void cxt5066_hp_automute(struct hda_codec *codec) | |||
2063 | } | 2175 | } |
2064 | 2176 | ||
2065 | /* unsolicited event for jack sensing */ | 2177 | /* unsolicited event for jack sensing */ |
2066 | static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) | 2178 | static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res) |
2067 | { | 2179 | { |
2180 | struct conexant_spec *spec = codec->spec; | ||
2068 | snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); | 2181 | snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); |
2069 | switch (res >> 26) { | 2182 | switch (res >> 26) { |
2070 | case CONEXANT_HP_EVENT: | 2183 | case CONEXANT_HP_EVENT: |
2071 | cxt5066_hp_automute(codec); | 2184 | cxt5066_hp_automute(codec); |
2072 | break; | 2185 | break; |
2073 | case CONEXANT_MIC_EVENT: | 2186 | case CONEXANT_MIC_EVENT: |
2074 | cxt5066_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); | ||
2075 | break; | 2190 | break; |
2076 | } | 2191 | } |
2077 | } | 2192 | } |
@@ -2101,6 +2216,15 @@ static const struct hda_input_mux cxt5066_analog_mic_boost = { | |||
2101 | }, | 2216 | }, |
2102 | }; | 2217 | }; |
2103 | 2218 | ||
2219 | static 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 | |||
2104 | static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol, | 2228 | static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol, |
2105 | struct snd_ctl_elem_info *uinfo) | 2229 | struct snd_ctl_elem_info *uinfo) |
2106 | { | 2230 | { |
@@ -2111,15 +2235,8 @@ static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol, | |||
2111 | struct snd_ctl_elem_value *ucontrol) | 2235 | struct snd_ctl_elem_value *ucontrol) |
2112 | { | 2236 | { |
2113 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | 2237 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
2114 | int val; | 2238 | struct conexant_spec *spec = codec->spec; |
2115 | hda_nid_t nid = kcontrol->private_value & 0xff; | 2239 | ucontrol->value.enumerated.item[0] = spec->mic_boost; |
2116 | int inout = (kcontrol->private_value & 0x100) ? | ||
2117 | AC_AMP_GET_INPUT : AC_AMP_GET_OUTPUT; | ||
2118 | |||
2119 | val = snd_hda_codec_read(codec, nid, 0, | ||
2120 | AC_VERB_GET_AMP_GAIN_MUTE, inout); | ||
2121 | |||
2122 | ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN; | ||
2123 | return 0; | 2240 | return 0; |
2124 | } | 2241 | } |
2125 | 2242 | ||
@@ -2127,26 +2244,132 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, | |||
2127 | struct snd_ctl_elem_value *ucontrol) | 2244 | struct snd_ctl_elem_value *ucontrol) |
2128 | { | 2245 | { |
2129 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | 2246 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
2247 | struct conexant_spec *spec = codec->spec; | ||
2130 | const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; | 2248 | const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; |
2131 | unsigned int idx; | 2249 | unsigned int idx; |
2132 | hda_nid_t nid = kcontrol->private_value & 0xff; | 2250 | idx = ucontrol->value.enumerated.item[0]; |
2133 | int inout = (kcontrol->private_value & 0x100) ? | 2251 | if (idx >= imux->num_items) |
2134 | 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 | |||
2260 | static 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 | |||
2276 | static 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 | |||
2284 | static 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 | } | ||
2292 | |||
2293 | static 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]; | ||
2135 | 2299 | ||
2136 | if (!imux->num_items) | 2300 | if (dc_enable == spec->dc_enable) |
2137 | 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 | |||
2312 | static 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 | |||
2318 | static 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 | |||
2327 | static 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 | |||
2138 | idx = ucontrol->value.enumerated.item[0]; | 2335 | idx = ucontrol->value.enumerated.item[0]; |
2139 | if (idx >= imux->num_items) | 2336 | if (idx >= imux->num_items) |
2140 | idx = imux->num_items - 1; | 2337 | idx = imux->num_items - 1; |
2141 | 2338 | ||
2142 | snd_hda_codec_write_cache(codec, nid, 0, | 2339 | spec->dc_input_bias = idx; |
2143 | AC_VERB_SET_AMP_GAIN_MUTE, | 2340 | if (spec->dc_enable) |
2144 | AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | inout | | 2341 | cxt5066_set_olpc_dc_bias(codec); |
2145 | imux->items[idx].index); | ||
2146 | |||
2147 | return 1; | 2342 | return 1; |
2148 | } | 2343 | } |
2149 | 2344 | ||
2345 | static void cxt5066_olpc_capture_prepare(struct hda_codec *codec) | ||
2346 | { | ||
2347 | struct conexant_spec *spec = codec->spec; | ||
2348 | /* mark as recording and configure the microphone widget so that the | ||
2349 | * recording LED comes on. */ | ||
2350 | spec->recording = 1; | ||
2351 | cxt5066_olpc_select_mic(codec); | ||
2352 | } | ||
2353 | |||
2354 | static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec) | ||
2355 | { | ||
2356 | struct conexant_spec *spec = codec->spec; | ||
2357 | const struct hda_verb disable_mics[] = { | ||
2358 | /* disable external mic, port B */ | ||
2359 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
2360 | |||
2361 | /* disble internal mic, port C */ | ||
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}, | ||
2366 | {}, | ||
2367 | }; | ||
2368 | |||
2369 | snd_hda_sequence_write(codec, disable_mics); | ||
2370 | spec->recording = 0; | ||
2371 | } | ||
2372 | |||
2150 | static struct hda_input_mux cxt5066_capture_source = { | 2373 | static struct hda_input_mux cxt5066_capture_source = { |
2151 | .num_items = 4, | 2374 | .num_items = 4, |
2152 | .items = { | 2375 | .items = { |
@@ -2187,6 +2410,7 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { | |||
2187 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | 2410 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | |
2188 | SNDRV_CTL_ELEM_ACCESS_TLV_READ | | 2411 | SNDRV_CTL_ELEM_ACCESS_TLV_READ | |
2189 | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, | 2412 | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, |
2413 | .subdevice = HDA_SUBDEV_AMP_FLAG, | ||
2190 | .info = snd_hda_mixer_amp_volume_info, | 2414 | .info = snd_hda_mixer_amp_volume_info, |
2191 | .get = snd_hda_mixer_amp_volume_get, | 2415 | .get = snd_hda_mixer_amp_volume_get, |
2192 | .put = snd_hda_mixer_amp_volume_put, | 2416 | .put = snd_hda_mixer_amp_volume_put, |
@@ -2198,6 +2422,24 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { | |||
2198 | {} | 2422 | {} |
2199 | }; | 2423 | }; |
2200 | 2424 | ||
2425 | static 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 | |||
2201 | static struct snd_kcontrol_new cxt5066_mixers[] = { | 2443 | static struct snd_kcontrol_new cxt5066_mixers[] = { |
2202 | { | 2444 | { |
2203 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 2445 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
@@ -2210,11 +2452,10 @@ static struct snd_kcontrol_new cxt5066_mixers[] = { | |||
2210 | 2452 | ||
2211 | { | 2453 | { |
2212 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 2454 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
2213 | .name = "Ext Mic Boost Capture Enum", | 2455 | .name = "Analog Mic Boost Capture Enum", |
2214 | .info = cxt5066_mic_boost_mux_enum_info, | 2456 | .info = cxt5066_mic_boost_mux_enum_info, |
2215 | .get = cxt5066_mic_boost_mux_enum_get, | 2457 | .get = cxt5066_mic_boost_mux_enum_get, |
2216 | .put = cxt5066_mic_boost_mux_enum_put, | 2458 | .put = cxt5066_mic_boost_mux_enum_put, |
2217 | .private_value = 0x17, | ||
2218 | }, | 2459 | }, |
2219 | 2460 | ||
2220 | HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others), | 2461 | HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others), |
@@ -2296,10 +2537,10 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = { | |||
2296 | {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | 2537 | {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ |
2297 | 2538 | ||
2298 | /* Port B: external microphone */ | 2539 | /* Port B: external microphone */ |
2299 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, CXT5066_OLPC_EXT_MIC_BIAS}, | 2540 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, |
2300 | 2541 | ||
2301 | /* Port C: internal microphone */ | 2542 | /* Port C: internal microphone */ |
2302 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | 2543 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, |
2303 | 2544 | ||
2304 | /* Port D: unused */ | 2545 | /* Port D: unused */ |
2305 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | 2546 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, |
@@ -2308,7 +2549,7 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = { | |||
2308 | {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | 2549 | {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, |
2309 | {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ | 2550 | {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ |
2310 | 2551 | ||
2311 | /* Port F: unused */ | 2552 | /* Port F: external DC input through microphone port */ |
2312 | {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | 2553 | {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, |
2313 | 2554 | ||
2314 | /* Port G: internal speakers */ | 2555 | /* Port G: internal speakers */ |
@@ -2428,8 +2669,22 @@ static int cxt5066_init(struct hda_codec *codec) | |||
2428 | cxt5066_hp_automute(codec); | 2669 | cxt5066_hp_automute(codec); |
2429 | if (spec->dell_vostro) | 2670 | if (spec->dell_vostro) |
2430 | cxt5066_vostro_automic(codec); | 2671 | cxt5066_vostro_automic(codec); |
2431 | else | 2672 | } |
2432 | cxt5066_automic(codec); | 2673 | cxt5066_set_mic_boost(codec); |
2674 | return 0; | ||
2675 | } | ||
2676 | |||
2677 | static int cxt5066_olpc_init(struct hda_codec *codec) | ||
2678 | { | ||
2679 | struct conexant_spec *spec = codec->spec; | ||
2680 | snd_printdd("CXT5066: init\n"); | ||
2681 | conexant_init(codec); | ||
2682 | cxt5066_hp_automute(codec); | ||
2683 | if (!spec->dc_enable) { | ||
2684 | cxt5066_set_mic_boost(codec); | ||
2685 | cxt5066_olpc_automic(codec); | ||
2686 | } else { | ||
2687 | cxt5066_enable_dc(codec); | ||
2433 | } | 2688 | } |
2434 | return 0; | 2689 | return 0; |
2435 | } | 2690 | } |
@@ -2470,7 +2725,7 @@ static int patch_cxt5066(struct hda_codec *codec) | |||
2470 | codec->spec = spec; | 2725 | codec->spec = spec; |
2471 | 2726 | ||
2472 | codec->patch_ops = conexant_patch_ops; | 2727 | codec->patch_ops = conexant_patch_ops; |
2473 | codec->patch_ops.init = cxt5066_init; | 2728 | codec->patch_ops.init = conexant_init; |
2474 | 2729 | ||
2475 | spec->dell_automute = 0; | 2730 | spec->dell_automute = 0; |
2476 | spec->multiout.max_channels = 2; | 2731 | spec->multiout.max_channels = 2; |
@@ -2483,7 +2738,6 @@ static int patch_cxt5066(struct hda_codec *codec) | |||
2483 | spec->input_mux = &cxt5066_capture_source; | 2738 | spec->input_mux = &cxt5066_capture_source; |
2484 | 2739 | ||
2485 | spec->port_d_mode = PIN_HP; | 2740 | spec->port_d_mode = PIN_HP; |
2486 | spec->ext_mic_bias = PIN_VREF80; | ||
2487 | 2741 | ||
2488 | spec->num_init_verbs = 1; | 2742 | spec->num_init_verbs = 1; |
2489 | spec->init_verbs[0] = cxt5066_init_verbs; | 2743 | spec->init_verbs[0] = cxt5066_init_verbs; |
@@ -2510,20 +2764,28 @@ static int patch_cxt5066(struct hda_codec *codec) | |||
2510 | spec->dell_automute = 1; | 2764 | spec->dell_automute = 1; |
2511 | break; | 2765 | break; |
2512 | case CXT5066_OLPC_XO_1_5: | 2766 | case CXT5066_OLPC_XO_1_5: |
2513 | codec->patch_ops.unsol_event = cxt5066_unsol_event; | 2767 | codec->patch_ops.init = cxt5066_olpc_init; |
2768 | codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event; | ||
2514 | spec->init_verbs[0] = cxt5066_init_verbs_olpc; | 2769 | spec->init_verbs[0] = cxt5066_init_verbs_olpc; |
2515 | 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; | ||
2516 | spec->mixers[spec->num_mixers++] = cxt5066_mixers; | 2772 | spec->mixers[spec->num_mixers++] = cxt5066_mixers; |
2517 | spec->port_d_mode = 0; | 2773 | spec->port_d_mode = 0; |
2518 | spec->ext_mic_bias = CXT5066_OLPC_EXT_MIC_BIAS; | 2774 | spec->mic_boost = 3; /* default 30dB gain */ |
2519 | 2775 | ||
2520 | /* no S/PDIF out */ | 2776 | /* no S/PDIF out */ |
2521 | spec->multiout.dig_out_nid = 0; | 2777 | spec->multiout.dig_out_nid = 0; |
2522 | 2778 | ||
2523 | /* input source automatically selected */ | 2779 | /* input source automatically selected */ |
2524 | spec->input_mux = NULL; | 2780 | spec->input_mux = NULL; |
2781 | |||
2782 | /* our capture hooks which allow us to turn on the microphone LED | ||
2783 | * at the right time */ | ||
2784 | spec->capture_prepare = cxt5066_olpc_capture_prepare; | ||
2785 | spec->capture_cleanup = cxt5066_olpc_capture_cleanup; | ||
2525 | break; | 2786 | break; |
2526 | case CXT5066_DELL_VOSTO: | 2787 | case CXT5066_DELL_VOSTO: |
2788 | codec->patch_ops.init = cxt5066_init; | ||
2527 | codec->patch_ops.unsol_event = cxt5066_vostro_event; | 2789 | codec->patch_ops.unsol_event = cxt5066_vostro_event; |
2528 | spec->init_verbs[0] = cxt5066_init_verbs_vostro; | 2790 | spec->init_verbs[0] = cxt5066_init_verbs_vostro; |
2529 | spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; | 2791 | spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; |
@@ -2531,6 +2793,7 @@ static int patch_cxt5066(struct hda_codec *codec) | |||
2531 | spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers; | 2793 | spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers; |
2532 | spec->port_d_mode = 0; | 2794 | spec->port_d_mode = 0; |
2533 | spec->dell_vostro = 1; | 2795 | spec->dell_vostro = 1; |
2796 | spec->mic_boost = 3; /* default 30dB gain */ | ||
2534 | snd_hda_attach_beep_device(codec, 0x13); | 2797 | snd_hda_attach_beep_device(codec, 0x13); |
2535 | 2798 | ||
2536 | /* no S/PDIF out */ | 2799 | /* no S/PDIF out */ |