diff options
author | Daniel Drake <dsd@laptop.org> | 2009-07-16 09:46:57 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-07-16 10:03:08 -0400 |
commit | 0fb67e982a1749b2cd477709838bf866028970ce (patch) | |
tree | e0682778467f5d75c7f7b003da01ab1f01e1d652 /sound/pci/hda/patch_conexant.c | |
parent | 3c6aae4489e7c05f3685e9eaa538249be7c79ebe (diff) |
ALSA: hda - Add CX20582 and OLPC XO-1.5 support
This adds support for the Conexant CX20582 codec, based on code from
http://www.linuxant.com/alsa-driver/alsa-driver-linuxant-1.0.19ppch12-1.noarch.rpm.zip
This is the codec to be shipped in the OLPC XO-1.5, so this patch also
includes an XO-specific profile. Resultant configuration:
http://dev.laptop.org/~dsd/20090713/codec0.txt
http://dev.laptop.org/~dsd/20090713/codec0.svg
As the Linuxant code is structured differently to the other codecs,
I was unable to cleanly reimplement everything in the generic and Dell
profiles as more info is needed (e.g. codec graphs). I simplified those
profiles so that hopefully it will not break anyone's audio. If it does,
it may be worth returning -ENODEV from patch_cx5066 on non-OLPC systems,
and then fixing snd_hda_codec_configure() to fall back on the generic
parser, at least until support for other systems is figured out.
Signed-off-by: Daniel Drake <dsd@laptop.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index ac868c59f9e3..9d899eda44d7 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c | |||
@@ -108,6 +108,8 @@ struct conexant_spec { | |||
108 | struct hda_input_mux private_imux; | 108 | struct hda_input_mux private_imux; |
109 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; | 109 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; |
110 | 110 | ||
111 | unsigned int dell_automute; | ||
112 | unsigned int port_d_mode; | ||
111 | }; | 113 | }; |
112 | 114 | ||
113 | static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, | 115 | static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, |
@@ -1908,6 +1910,480 @@ static int patch_cxt5051(struct hda_codec *codec) | |||
1908 | return 0; | 1910 | return 0; |
1909 | } | 1911 | } |
1910 | 1912 | ||
1913 | /* Conexant 5066 specific */ | ||
1914 | |||
1915 | static hda_nid_t cxt5066_dac_nids[1] = { 0x10 }; | ||
1916 | static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 }; | ||
1917 | static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 }; | ||
1918 | #define CXT5066_SPDIF_OUT 0x21 | ||
1919 | |||
1920 | static struct hda_channel_mode cxt5066_modes[1] = { | ||
1921 | { 2, NULL }, | ||
1922 | }; | ||
1923 | |||
1924 | static void cxt5066_update_speaker(struct hda_codec *codec) | ||
1925 | { | ||
1926 | struct conexant_spec *spec = codec->spec; | ||
1927 | unsigned int pinctl; | ||
1928 | |||
1929 | snd_printdd("CXT5066: update speaker, hp_present=%d\n", | ||
1930 | spec->hp_present); | ||
1931 | |||
1932 | /* Port A (HP) */ | ||
1933 | pinctl = ((spec->hp_present & 1) && spec->cur_eapd) ? PIN_HP : 0; | ||
1934 | snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
1935 | pinctl); | ||
1936 | |||
1937 | /* Port D (HP/LO) */ | ||
1938 | pinctl = ((spec->hp_present & 2) && spec->cur_eapd) | ||
1939 | ? spec->port_d_mode : 0; | ||
1940 | snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
1941 | pinctl); | ||
1942 | |||
1943 | /* CLASS_D AMP */ | ||
1944 | pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; | ||
1945 | snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
1946 | pinctl); | ||
1947 | |||
1948 | if (spec->dell_automute) { | ||
1949 | /* DELL AIO Port Rule: PortA > PortD > IntSpk */ | ||
1950 | pinctl = (!(spec->hp_present & 1) && spec->cur_eapd) | ||
1951 | ? PIN_OUT : 0; | ||
1952 | snd_hda_codec_write(codec, 0x1c, 0, | ||
1953 | AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); | ||
1954 | } | ||
1955 | } | ||
1956 | |||
1957 | /* turn on/off EAPD (+ mute HP) as a master switch */ | ||
1958 | static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol, | ||
1959 | struct snd_ctl_elem_value *ucontrol) | ||
1960 | { | ||
1961 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
1962 | |||
1963 | if (!cxt_eapd_put(kcontrol, ucontrol)) | ||
1964 | return 0; | ||
1965 | |||
1966 | cxt5066_update_speaker(codec); | ||
1967 | return 1; | ||
1968 | } | ||
1969 | |||
1970 | /* toggle input of built-in and mic jack appropriately */ | ||
1971 | static void cxt5066_automic(struct hda_codec *codec) | ||
1972 | { | ||
1973 | static struct hda_verb ext_mic_present[] = { | ||
1974 | /* enable external mic, port B */ | ||
1975 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
1976 | |||
1977 | /* switch to external mic input */ | ||
1978 | {0x17, AC_VERB_SET_CONNECT_SEL, 0}, | ||
1979 | |||
1980 | /* disable internal mic, port C */ | ||
1981 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
1982 | {} | ||
1983 | }; | ||
1984 | static struct hda_verb ext_mic_absent[] = { | ||
1985 | /* enable internal mic, port C */ | ||
1986 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
1987 | |||
1988 | /* switch to internal mic input */ | ||
1989 | {0x17, AC_VERB_SET_CONNECT_SEL, 1}, | ||
1990 | |||
1991 | /* disable external mic, port B */ | ||
1992 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
1993 | {} | ||
1994 | }; | ||
1995 | unsigned int present; | ||
1996 | |||
1997 | present = snd_hda_codec_read(codec, 0x1a, 0, | ||
1998 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
1999 | if (present) { | ||
2000 | snd_printdd("CXT5066: external microphone detected\n"); | ||
2001 | snd_hda_sequence_write(codec, ext_mic_present); | ||
2002 | } else { | ||
2003 | snd_printdd("CXT5066: external microphone absent\n"); | ||
2004 | snd_hda_sequence_write(codec, ext_mic_absent); | ||
2005 | } | ||
2006 | } | ||
2007 | |||
2008 | /* mute internal speaker if HP is plugged */ | ||
2009 | static void cxt5066_hp_automute(struct hda_codec *codec) | ||
2010 | { | ||
2011 | struct conexant_spec *spec = codec->spec; | ||
2012 | unsigned int portA, portD; | ||
2013 | |||
2014 | /* Port A */ | ||
2015 | portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0) | ||
2016 | & AC_PINSENSE_PRESENCE; | ||
2017 | |||
2018 | /* Port D */ | ||
2019 | portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) | ||
2020 | & AC_PINSENSE_PRESENCE) << 1; | ||
2021 | |||
2022 | spec->hp_present = !!(portA | portD); | ||
2023 | snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", | ||
2024 | portA, portD, spec->hp_present); | ||
2025 | cxt5066_update_speaker(codec); | ||
2026 | } | ||
2027 | |||
2028 | /* unsolicited event for jack sensing */ | ||
2029 | static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) | ||
2030 | { | ||
2031 | snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); | ||
2032 | switch (res >> 26) { | ||
2033 | case CONEXANT_HP_EVENT: | ||
2034 | cxt5066_hp_automute(codec); | ||
2035 | break; | ||
2036 | case CONEXANT_MIC_EVENT: | ||
2037 | cxt5066_automic(codec); | ||
2038 | break; | ||
2039 | } | ||
2040 | } | ||
2041 | |||
2042 | static const struct hda_input_mux cxt5066_analog_mic_boost = { | ||
2043 | .num_items = 5, | ||
2044 | .items = { | ||
2045 | { "0dB", 0 }, | ||
2046 | { "10dB", 1 }, | ||
2047 | { "20dB", 2 }, | ||
2048 | { "30dB", 3 }, | ||
2049 | { "40dB", 4 }, | ||
2050 | }, | ||
2051 | }; | ||
2052 | |||
2053 | static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol, | ||
2054 | struct snd_ctl_elem_info *uinfo) | ||
2055 | { | ||
2056 | return snd_hda_input_mux_info(&cxt5066_analog_mic_boost, uinfo); | ||
2057 | } | ||
2058 | |||
2059 | static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol, | ||
2060 | struct snd_ctl_elem_value *ucontrol) | ||
2061 | { | ||
2062 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
2063 | int val; | ||
2064 | |||
2065 | val = snd_hda_codec_read(codec, 0x17, 0, | ||
2066 | AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_OUTPUT); | ||
2067 | |||
2068 | ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN; | ||
2069 | return 0; | ||
2070 | } | ||
2071 | |||
2072 | static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, | ||
2073 | struct snd_ctl_elem_value *ucontrol) | ||
2074 | { | ||
2075 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
2076 | const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; | ||
2077 | unsigned int idx; | ||
2078 | |||
2079 | if (!imux->num_items) | ||
2080 | return 0; | ||
2081 | idx = ucontrol->value.enumerated.item[0]; | ||
2082 | if (idx >= imux->num_items) | ||
2083 | idx = imux->num_items - 1; | ||
2084 | |||
2085 | snd_hda_codec_write_cache(codec, 0x17, 0, | ||
2086 | AC_VERB_SET_AMP_GAIN_MUTE, | ||
2087 | AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | | ||
2088 | imux->items[idx].index); | ||
2089 | |||
2090 | return 1; | ||
2091 | } | ||
2092 | |||
2093 | static struct hda_input_mux cxt5066_capture_source = { | ||
2094 | .num_items = 4, | ||
2095 | .items = { | ||
2096 | { "Mic B", 0 }, | ||
2097 | { "Mic C", 1 }, | ||
2098 | { "Mic E", 2 }, | ||
2099 | { "Mic F", 3 }, | ||
2100 | }, | ||
2101 | }; | ||
2102 | |||
2103 | static struct hda_bind_ctls cxt5066_bind_capture_vol_others = { | ||
2104 | .ops = &snd_hda_bind_vol, | ||
2105 | .values = { | ||
2106 | HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT), | ||
2107 | HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT), | ||
2108 | 0 | ||
2109 | }, | ||
2110 | }; | ||
2111 | |||
2112 | static struct hda_bind_ctls cxt5066_bind_capture_sw_others = { | ||
2113 | .ops = &snd_hda_bind_sw, | ||
2114 | .values = { | ||
2115 | HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT), | ||
2116 | HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT), | ||
2117 | 0 | ||
2118 | }, | ||
2119 | }; | ||
2120 | |||
2121 | static struct snd_kcontrol_new cxt5066_mixer_master[] = { | ||
2122 | HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), | ||
2123 | {} | ||
2124 | }; | ||
2125 | |||
2126 | static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { | ||
2127 | { | ||
2128 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
2129 | .name = "Master Playback Volume", | ||
2130 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
2131 | SNDRV_CTL_ELEM_ACCESS_TLV_READ | | ||
2132 | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, | ||
2133 | .info = snd_hda_mixer_amp_volume_info, | ||
2134 | .get = snd_hda_mixer_amp_volume_get, | ||
2135 | .put = snd_hda_mixer_amp_volume_put, | ||
2136 | .tlv = { .c = snd_hda_mixer_amp_tlv }, | ||
2137 | /* offset by 28 volume steps to limit minimum gain to -46dB */ | ||
2138 | .private_value = | ||
2139 | HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28), | ||
2140 | }, | ||
2141 | {} | ||
2142 | }; | ||
2143 | |||
2144 | static struct snd_kcontrol_new cxt5066_mixers[] = { | ||
2145 | { | ||
2146 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
2147 | .name = "Master Playback Switch", | ||
2148 | .info = cxt_eapd_info, | ||
2149 | .get = cxt_eapd_get, | ||
2150 | .put = cxt5066_hp_master_sw_put, | ||
2151 | .private_value = 0x1d, | ||
2152 | }, | ||
2153 | |||
2154 | { | ||
2155 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
2156 | .name = "Analog Mic Boost Capture Enum", | ||
2157 | .info = cxt5066_mic_boost_mux_enum_info, | ||
2158 | .get = cxt5066_mic_boost_mux_enum_get, | ||
2159 | .put = cxt5066_mic_boost_mux_enum_put, | ||
2160 | }, | ||
2161 | |||
2162 | HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others), | ||
2163 | HDA_BIND_SW("Capture Switch", &cxt5066_bind_capture_sw_others), | ||
2164 | {} | ||
2165 | }; | ||
2166 | |||
2167 | static struct hda_verb cxt5066_init_verbs[] = { | ||
2168 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */ | ||
2169 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */ | ||
2170 | {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */ | ||
2171 | {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */ | ||
2172 | |||
2173 | /* Speakers */ | ||
2174 | {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
2175 | {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||
2176 | |||
2177 | /* HP, Amp */ | ||
2178 | {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
2179 | {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||
2180 | |||
2181 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
2182 | {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||
2183 | |||
2184 | /* DAC1 */ | ||
2185 | {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
2186 | |||
2187 | /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */ | ||
2188 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, | ||
2189 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||
2190 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50}, | ||
2191 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, | ||
2192 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, | ||
2193 | |||
2194 | /* no digital microphone support yet */ | ||
2195 | {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
2196 | |||
2197 | /* Audio input selector */ | ||
2198 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, | ||
2199 | |||
2200 | /* SPDIF route: PCM */ | ||
2201 | {0x20, AC_VERB_SET_CONNECT_SEL, 0x0}, | ||
2202 | {0x22, AC_VERB_SET_CONNECT_SEL, 0x0}, | ||
2203 | |||
2204 | {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
2205 | {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
2206 | |||
2207 | /* EAPD */ | ||
2208 | {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ | ||
2209 | |||
2210 | /* not handling these yet */ | ||
2211 | {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||
2212 | {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||
2213 | {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||
2214 | {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||
2215 | {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||
2216 | {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||
2217 | {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||
2218 | {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||
2219 | { } /* end */ | ||
2220 | }; | ||
2221 | |||
2222 | static struct hda_verb cxt5066_init_verbs_olpc[] = { | ||
2223 | /* Port A: headphones */ | ||
2224 | {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
2225 | {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||
2226 | |||
2227 | /* Port B: external microphone */ | ||
2228 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
2229 | |||
2230 | /* Port C: internal microphone */ | ||
2231 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
2232 | |||
2233 | /* Port D: unused */ | ||
2234 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
2235 | |||
2236 | /* Port E: unused, but has primary EAPD */ | ||
2237 | {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
2238 | {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ | ||
2239 | |||
2240 | /* Port F: unused */ | ||
2241 | {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
2242 | |||
2243 | /* Port G: internal speakers */ | ||
2244 | {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
2245 | {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||
2246 | |||
2247 | /* DAC1 */ | ||
2248 | {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
2249 | |||
2250 | /* DAC2: unused */ | ||
2251 | {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||
2252 | |||
2253 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, | ||
2254 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||
2255 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, | ||
2256 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, | ||
2257 | {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, | ||
2258 | {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||
2259 | {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, | ||
2260 | {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, | ||
2261 | {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, | ||
2262 | {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||
2263 | {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, | ||
2264 | {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, | ||
2265 | |||
2266 | /* Disable digital microphone port */ | ||
2267 | {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
2268 | |||
2269 | /* Audio input selectors */ | ||
2270 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, | ||
2271 | {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, | ||
2272 | |||
2273 | /* Disable SPDIF */ | ||
2274 | {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
2275 | {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||
2276 | |||
2277 | /* enable unsolicited events for Port A and B */ | ||
2278 | {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, | ||
2279 | {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, | ||
2280 | { } /* end */ | ||
2281 | }; | ||
2282 | |||
2283 | static struct hda_verb cxt5066_init_verbs_portd_lo[] = { | ||
2284 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
2285 | { } /* end */ | ||
2286 | }; | ||
2287 | |||
2288 | /* initialize jack-sensing, too */ | ||
2289 | static int cxt5066_init(struct hda_codec *codec) | ||
2290 | { | ||
2291 | snd_printdd("CXT5066: init\n"); | ||
2292 | conexant_init(codec); | ||
2293 | if (codec->patch_ops.unsol_event) { | ||
2294 | cxt5066_hp_automute(codec); | ||
2295 | cxt5066_automic(codec); | ||
2296 | } | ||
2297 | return 0; | ||
2298 | } | ||
2299 | |||
2300 | enum { | ||
2301 | CXT5066_LAPTOP, /* Laptops w/ EAPD support */ | ||
2302 | CXT5066_DELL_LAPTOP, /* Dell Laptop */ | ||
2303 | CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ | ||
2304 | CXT5066_MODELS | ||
2305 | }; | ||
2306 | |||
2307 | static const char *cxt5066_models[CXT5066_MODELS] = { | ||
2308 | [CXT5066_LAPTOP] = "laptop", | ||
2309 | [CXT5066_DELL_LAPTOP] = "dell-laptop", | ||
2310 | [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", | ||
2311 | }; | ||
2312 | |||
2313 | static struct snd_pci_quirk cxt5066_cfg_tbl[] = { | ||
2314 | SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", | ||
2315 | CXT5066_LAPTOP), | ||
2316 | SND_PCI_QUIRK(0x1028, 0x02f5, "Dell", | ||
2317 | CXT5066_DELL_LAPTOP), | ||
2318 | {} | ||
2319 | }; | ||
2320 | |||
2321 | static int patch_cxt5066(struct hda_codec *codec) | ||
2322 | { | ||
2323 | struct conexant_spec *spec; | ||
2324 | int board_config; | ||
2325 | |||
2326 | spec = kzalloc(sizeof(*spec), GFP_KERNEL); | ||
2327 | if (!spec) | ||
2328 | return -ENOMEM; | ||
2329 | codec->spec = spec; | ||
2330 | |||
2331 | codec->patch_ops = conexant_patch_ops; | ||
2332 | codec->patch_ops.init = cxt5066_init; | ||
2333 | |||
2334 | spec->dell_automute = 0; | ||
2335 | spec->multiout.max_channels = 2; | ||
2336 | spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids); | ||
2337 | spec->multiout.dac_nids = cxt5066_dac_nids; | ||
2338 | spec->multiout.dig_out_nid = CXT5066_SPDIF_OUT; | ||
2339 | spec->num_adc_nids = 1; | ||
2340 | spec->adc_nids = cxt5066_adc_nids; | ||
2341 | spec->capsrc_nids = cxt5066_capsrc_nids; | ||
2342 | spec->input_mux = &cxt5066_capture_source; | ||
2343 | |||
2344 | spec->port_d_mode = PIN_HP; | ||
2345 | |||
2346 | spec->num_init_verbs = 1; | ||
2347 | spec->init_verbs[0] = cxt5066_init_verbs; | ||
2348 | spec->num_channel_mode = ARRAY_SIZE(cxt5066_modes); | ||
2349 | spec->channel_mode = cxt5066_modes; | ||
2350 | spec->cur_adc = 0; | ||
2351 | spec->cur_adc_idx = 0; | ||
2352 | |||
2353 | board_config = snd_hda_check_board_config(codec, CXT5066_MODELS, | ||
2354 | cxt5066_models, cxt5066_cfg_tbl); | ||
2355 | switch (board_config) { | ||
2356 | default: | ||
2357 | case CXT5066_LAPTOP: | ||
2358 | spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; | ||
2359 | spec->mixers[spec->num_mixers++] = cxt5066_mixers; | ||
2360 | break; | ||
2361 | case CXT5066_DELL_LAPTOP: | ||
2362 | spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; | ||
2363 | spec->mixers[spec->num_mixers++] = cxt5066_mixers; | ||
2364 | |||
2365 | spec->port_d_mode = PIN_OUT; | ||
2366 | spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_portd_lo; | ||
2367 | spec->num_init_verbs++; | ||
2368 | spec->dell_automute = 1; | ||
2369 | break; | ||
2370 | case CXT5066_OLPC_XO_1_5: | ||
2371 | codec->patch_ops.unsol_event = cxt5066_unsol_event; | ||
2372 | spec->init_verbs[0] = cxt5066_init_verbs_olpc; | ||
2373 | spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; | ||
2374 | spec->mixers[spec->num_mixers++] = cxt5066_mixers; | ||
2375 | spec->port_d_mode = 0; | ||
2376 | |||
2377 | /* no S/PDIF out */ | ||
2378 | spec->multiout.dig_out_nid = 0; | ||
2379 | |||
2380 | /* input source automatically selected */ | ||
2381 | spec->input_mux = NULL; | ||
2382 | break; | ||
2383 | } | ||
2384 | |||
2385 | return 0; | ||
2386 | } | ||
1911 | 2387 | ||
1912 | /* | 2388 | /* |
1913 | */ | 2389 | */ |
@@ -1919,12 +2395,15 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { | |||
1919 | .patch = patch_cxt5047 }, | 2395 | .patch = patch_cxt5047 }, |
1920 | { .id = 0x14f15051, .name = "CX20561 (Hermosa)", | 2396 | { .id = 0x14f15051, .name = "CX20561 (Hermosa)", |
1921 | .patch = patch_cxt5051 }, | 2397 | .patch = patch_cxt5051 }, |
2398 | { .id = 0x14f15066, .name = "CX20582 (Pebble)", | ||
2399 | .patch = patch_cxt5066 }, | ||
1922 | {} /* terminator */ | 2400 | {} /* terminator */ |
1923 | }; | 2401 | }; |
1924 | 2402 | ||
1925 | MODULE_ALIAS("snd-hda-codec-id:14f15045"); | 2403 | MODULE_ALIAS("snd-hda-codec-id:14f15045"); |
1926 | MODULE_ALIAS("snd-hda-codec-id:14f15047"); | 2404 | MODULE_ALIAS("snd-hda-codec-id:14f15047"); |
1927 | MODULE_ALIAS("snd-hda-codec-id:14f15051"); | 2405 | MODULE_ALIAS("snd-hda-codec-id:14f15051"); |
2406 | MODULE_ALIAS("snd-hda-codec-id:14f15066"); | ||
1928 | 2407 | ||
1929 | MODULE_LICENSE("GPL"); | 2408 | MODULE_LICENSE("GPL"); |
1930 | MODULE_DESCRIPTION("Conexant HD-audio codec"); | 2409 | MODULE_DESCRIPTION("Conexant HD-audio codec"); |