Commit | Line | Data |
---|---|---|
e5f14248 TI |
1 | /* |
2 | * HD audio interface patch for Cirrus Logic CS420x chip | |
3 | * | |
4 | * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de> | |
5 | * | |
6 | * This driver is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This driver is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
20 | ||
21 | #include <linux/init.h> | |
e5f14248 TI |
22 | #include <linux/slab.h> |
23 | #include <linux/pci.h> | |
da155d5b | 24 | #include <linux/module.h> |
e5f14248 | 25 | #include <sound/core.h> |
1077a024 | 26 | #include <sound/tlv.h> |
e5f14248 TI |
27 | #include "hda_codec.h" |
28 | #include "hda_local.h" | |
128bc4ba | 29 | #include "hda_auto_parser.h" |
1835a0f9 | 30 | #include "hda_jack.h" |
1077a024 | 31 | #include "hda_generic.h" |
e5f14248 TI |
32 | |
33 | /* | |
34 | */ | |
35 | ||
36 | struct cs_spec { | |
1077a024 | 37 | struct hda_gen_spec gen; |
e5f14248 | 38 | |
ed208255 TI |
39 | unsigned int gpio_mask; |
40 | unsigned int gpio_dir; | |
41 | unsigned int gpio_data; | |
6dfeb703 TI |
42 | unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ |
43 | unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ | |
ed208255 | 44 | |
56487c27 TH |
45 | /* CS421x */ |
46 | unsigned int spdif_detect:1; | |
1077a024 | 47 | unsigned int spdif_present:1; |
56487c27 TH |
48 | unsigned int sense_b:1; |
49 | hda_nid_t vendor_nid; | |
e5f14248 TI |
50 | }; |
51 | ||
56487c27 | 52 | /* available models with CS420x */ |
a6bae205 | 53 | enum { |
4e7d7c60 | 54 | CS420X_MBP53, |
a6bae205 | 55 | CS420X_MBP55, |
1a5ba2e9 | 56 | CS420X_IMAC27, |
b35aabd7 TI |
57 | CS420X_GPIO_13, |
58 | CS420X_GPIO_23, | |
ef596a57 | 59 | CS420X_MBP101, |
ffe4d12b | 60 | CS420X_MBP81, |
6ab982e8 | 61 | CS420X_MBA42, |
a6bae205 | 62 | CS420X_AUTO, |
03efce75 TI |
63 | /* aliases */ |
64 | CS420X_IMAC27_122 = CS420X_GPIO_23, | |
65 | CS420X_APPLE = CS420X_GPIO_13, | |
a6bae205 TI |
66 | }; |
67 | ||
56487c27 TH |
68 | /* CS421x boards */ |
69 | enum { | |
70 | CS421X_CDB4210, | |
b35aabd7 | 71 | CS421X_SENSE_B, |
4af16107 | 72 | CS421X_STUMPY, |
56487c27 TH |
73 | }; |
74 | ||
40c20fa0 TI |
75 | /* Vendor-specific processing widget */ |
76 | #define CS420X_VENDOR_NID 0x11 | |
77 | #define CS_DIG_OUT1_PIN_NID 0x10 | |
78 | #define CS_DIG_OUT2_PIN_NID 0x15 | |
16337e02 DB |
79 | #define CS_DMIC1_PIN_NID 0x0e |
80 | #define CS_DMIC2_PIN_NID 0x12 | |
40c20fa0 TI |
81 | |
82 | /* coef indices */ | |
83 | #define IDX_SPDIF_STAT 0x0000 | |
84 | #define IDX_SPDIF_CTL 0x0001 | |
85 | #define IDX_ADC_CFG 0x0002 | |
86 | /* SZC bitmask, 4 modes below: | |
87 | * 0 = immediate, | |
88 | * 1 = digital immediate, analog zero-cross | |
89 | * 2 = digtail & analog soft-ramp | |
90 | * 3 = digital soft-ramp, analog zero-cross | |
91 | */ | |
92 | #define CS_COEF_ADC_SZC_MASK (3 << 0) | |
93 | #define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */ | |
94 | #define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */ | |
95 | /* PGA mode: 0 = differential, 1 = signle-ended */ | |
96 | #define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */ | |
97 | #define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */ | |
98 | #define IDX_DAC_CFG 0x0003 | |
99 | /* SZC bitmask, 4 modes below: | |
100 | * 0 = Immediate | |
101 | * 1 = zero-cross | |
102 | * 2 = soft-ramp | |
103 | * 3 = soft-ramp on zero-cross | |
104 | */ | |
105 | #define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */ | |
106 | #define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */ | |
107 | #define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */ | |
108 | ||
109 | #define IDX_BEEP_CFG 0x0004 | |
110 | /* 0x0008 - test reg key */ | |
111 | /* 0x0009 - 0x0014 -> 12 test regs */ | |
112 | /* 0x0015 - visibility reg */ | |
113 | ||
56487c27 TH |
114 | /* |
115 | * Cirrus Logic CS4210 | |
116 | * | |
117 | * 1 DAC => HP(sense) / Speakers, | |
118 | * 1 ADC <= LineIn(sense) / MicIn / DMicIn, | |
119 | * 1 SPDIF OUT => SPDIF Trasmitter(sense) | |
120 | */ | |
121 | #define CS4210_DAC_NID 0x02 | |
122 | #define CS4210_ADC_NID 0x03 | |
5660ffd0 | 123 | #define CS4210_VENDOR_NID 0x0B |
56487c27 TH |
124 | #define CS421X_DMIC_PIN_NID 0x09 /* Port E */ |
125 | #define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ | |
126 | ||
1077a024 TI |
127 | #define CS421X_IDX_DEV_CFG 0x01 |
128 | #define CS421X_IDX_ADC_CFG 0x02 | |
129 | #define CS421X_IDX_DAC_CFG 0x03 | |
130 | #define CS421X_IDX_SPK_CTL 0x04 | |
e5f14248 | 131 | |
1077a024 | 132 | #define SPDIF_EVENT 0x04 |
6a92934d | 133 | |
1077a024 TI |
134 | /* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ |
135 | #define CS4213_VENDOR_NID 0x09 | |
e5f14248 | 136 | |
21a4dc43 | 137 | |
1077a024 | 138 | static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) |
e5f14248 TI |
139 | { |
140 | struct cs_spec *spec = codec->spec; | |
1077a024 TI |
141 | snd_hda_codec_write(codec, spec->vendor_nid, 0, |
142 | AC_VERB_SET_COEF_INDEX, idx); | |
143 | return snd_hda_codec_read(codec, spec->vendor_nid, 0, | |
144 | AC_VERB_GET_PROC_COEF, 0); | |
e5f14248 TI |
145 | } |
146 | ||
1077a024 TI |
147 | static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, |
148 | unsigned int coef) | |
e5f14248 TI |
149 | { |
150 | struct cs_spec *spec = codec->spec; | |
1077a024 TI |
151 | snd_hda_codec_write(codec, spec->vendor_nid, 0, |
152 | AC_VERB_SET_COEF_INDEX, idx); | |
153 | snd_hda_codec_write(codec, spec->vendor_nid, 0, | |
154 | AC_VERB_SET_PROC_COEF, coef); | |
e5f14248 TI |
155 | } |
156 | ||
21a4dc43 TI |
157 | /* |
158 | * auto-mute and auto-mic switching | |
56487c27 TH |
159 | * CS421x auto-output redirecting |
160 | * HP/SPK/SPDIF | |
21a4dc43 TI |
161 | */ |
162 | ||
1077a024 | 163 | static void cs_automute(struct hda_codec *codec) |
e5f14248 TI |
164 | { |
165 | struct cs_spec *spec = codec->spec; | |
e5f14248 | 166 | |
1077a024 TI |
167 | /* mute HPs if spdif jack (SENSE_B) is present */ |
168 | spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b); | |
56487c27 | 169 | |
1077a024 | 170 | snd_hda_gen_update_outputs(codec); |
78e2a928 | 171 | |
be8cf445 | 172 | if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) { |
039eb753 | 173 | spec->gpio_data = spec->gen.hp_jack_present ? |
6dfeb703 | 174 | spec->gpio_eapd_hp : spec->gpio_eapd_speaker; |
3a385167 | 175 | snd_hda_codec_write(codec, 0x01, 0, |
039eb753 | 176 | AC_VERB_SET_GPIO_DATA, spec->gpio_data); |
3a385167 | 177 | } |
e5f14248 TI |
178 | } |
179 | ||
1077a024 | 180 | static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid) |
e5f14248 | 181 | { |
1077a024 TI |
182 | unsigned int val; |
183 | val = snd_hda_codec_get_pincfg(codec, nid); | |
184 | return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); | |
e5f14248 TI |
185 | } |
186 | ||
1077a024 | 187 | static void init_input_coef(struct hda_codec *codec) |
e5f14248 TI |
188 | { |
189 | struct cs_spec *spec = codec->spec; | |
40c20fa0 | 190 | unsigned int coef; |
e5f14248 | 191 | |
5660ffd0 DH |
192 | /* CS420x has multiple ADC, CS421x has single ADC */ |
193 | if (spec->vendor_nid == CS420X_VENDOR_NID) { | |
16337e02 | 194 | coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG); |
56487c27 | 195 | if (is_active_pin(codec, CS_DMIC2_PIN_NID)) |
16337e02 | 196 | coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */ |
56487c27 | 197 | if (is_active_pin(codec, CS_DMIC1_PIN_NID)) |
16337e02 | 198 | coef |= 1 << 3; /* DMIC1 2 chan on, GPIO0 off |
56487c27 TH |
199 | * No effect if SPDIF_OUT2 is |
200 | * selected in IDX_SPDIF_CTL. | |
201 | */ | |
16337e02 DB |
202 | |
203 | cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef); | |
56487c27 | 204 | } |
40c20fa0 TI |
205 | } |
206 | ||
c42d4782 | 207 | static const struct hda_verb cs_coef_init_verbs[] = { |
40c20fa0 TI |
208 | {0x11, AC_VERB_SET_PROC_STATE, 1}, |
209 | {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG}, | |
210 | {0x11, AC_VERB_SET_PROC_COEF, | |
211 | (0x002a /* DAC1/2/3 SZCMode Soft Ramp */ | |
212 | | 0x0040 /* Mute DACs on FIFO error */ | |
213 | | 0x1000 /* Enable DACs High Pass Filter */ | |
214 | | 0x0400 /* Disable Coefficient Auto increment */ | |
215 | )}, | |
829e87e0 TI |
216 | /* ADC1/2 - Digital and Analog Soft Ramp */ |
217 | {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG}, | |
218 | {0x11, AC_VERB_SET_PROC_COEF, 0x000a}, | |
40c20fa0 | 219 | /* Beep */ |
5a83b4b5 | 220 | {0x11, AC_VERB_SET_COEF_INDEX, IDX_BEEP_CFG}, |
40c20fa0 TI |
221 | {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */ |
222 | ||
223 | {} /* terminator */ | |
224 | }; | |
225 | ||
a769cbcf BA |
226 | /* Errata: CS4207 rev C0/C1/C2 Silicon |
227 | * | |
228 | * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf | |
229 | * | |
230 | * 6. At high temperature (TA > +85°C), the digital supply current (IVD) | |
231 | * may be excessive (up to an additional 200 μA), which is most easily | |
232 | * observed while the part is being held in reset (RESET# active low). | |
233 | * | |
234 | * Root Cause: At initial powerup of the device, the logic that drives | |
235 | * the clock and write enable to the S/PDIF SRC RAMs is not properly | |
236 | * initialized. | |
237 | * Certain random patterns will cause a steady leakage current in those | |
238 | * RAM cells. The issue will resolve once the SRCs are used (turned on). | |
239 | * | |
240 | * Workaround: The following verb sequence briefly turns on the S/PDIF SRC | |
241 | * blocks, which will alleviate the issue. | |
242 | */ | |
243 | ||
c42d4782 | 244 | static const struct hda_verb cs_errata_init_verbs[] = { |
a769cbcf BA |
245 | {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */ |
246 | {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ | |
247 | ||
248 | {0x11, AC_VERB_SET_COEF_INDEX, 0x0008}, | |
249 | {0x11, AC_VERB_SET_PROC_COEF, 0x9999}, | |
250 | {0x11, AC_VERB_SET_COEF_INDEX, 0x0017}, | |
251 | {0x11, AC_VERB_SET_PROC_COEF, 0xa412}, | |
252 | {0x11, AC_VERB_SET_COEF_INDEX, 0x0001}, | |
253 | {0x11, AC_VERB_SET_PROC_COEF, 0x0009}, | |
254 | ||
255 | {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */ | |
256 | {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */ | |
257 | ||
258 | {0x11, AC_VERB_SET_COEF_INDEX, 0x0017}, | |
259 | {0x11, AC_VERB_SET_PROC_COEF, 0x2412}, | |
260 | {0x11, AC_VERB_SET_COEF_INDEX, 0x0008}, | |
261 | {0x11, AC_VERB_SET_PROC_COEF, 0x0000}, | |
262 | {0x11, AC_VERB_SET_COEF_INDEX, 0x0001}, | |
263 | {0x11, AC_VERB_SET_PROC_COEF, 0x0008}, | |
264 | {0x11, AC_VERB_SET_PROC_STATE, 0x00}, | |
265 | ||
38c07641 | 266 | #if 0 /* Don't to set to D3 as we are in power-up sequence */ |
a769cbcf BA |
267 | {0x07, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Rx: D3 */ |
268 | {0x08, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Tx: D3 */ | |
269 | /*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */ | |
38c07641 | 270 | #endif |
a769cbcf BA |
271 | |
272 | {} /* terminator */ | |
273 | }; | |
274 | ||
40c20fa0 | 275 | /* SPDIF setup */ |
1077a024 | 276 | static void init_digital_coef(struct hda_codec *codec) |
40c20fa0 TI |
277 | { |
278 | unsigned int coef; | |
279 | ||
280 | coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */ | |
281 | coef |= 0x0008; /* Replace with mute on error */ | |
282 | if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID)) | |
283 | coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2 | |
284 | * SPDIF_OUT2 is shared with GPIO1 and | |
285 | * DMIC_SDA2. | |
286 | */ | |
287 | cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef); | |
e5f14248 TI |
288 | } |
289 | ||
290 | static int cs_init(struct hda_codec *codec) | |
291 | { | |
292 | struct cs_spec *spec = codec->spec; | |
293 | ||
be8cf445 TI |
294 | if (spec->vendor_nid == CS420X_VENDOR_NID) { |
295 | /* init_verb sequence for C0/C1/C2 errata*/ | |
296 | snd_hda_sequence_write(codec, cs_errata_init_verbs); | |
297 | snd_hda_sequence_write(codec, cs_coef_init_verbs); | |
298 | } | |
ed208255 | 299 | |
1077a024 | 300 | snd_hda_gen_init(codec); |
98415eac | 301 | |
ed208255 TI |
302 | if (spec->gpio_mask) { |
303 | snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, | |
304 | spec->gpio_mask); | |
305 | snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, | |
306 | spec->gpio_dir); | |
307 | snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, | |
308 | spec->gpio_data); | |
309 | } | |
310 | ||
be8cf445 TI |
311 | if (spec->vendor_nid == CS420X_VENDOR_NID) { |
312 | init_input_coef(codec); | |
313 | init_digital_coef(codec); | |
314 | } | |
01a61e12 TI |
315 | |
316 | return 0; | |
e5f14248 TI |
317 | } |
318 | ||
1077a024 | 319 | #define cs_free snd_hda_gen_free |
e5f14248 | 320 | |
c42d4782 | 321 | static const struct hda_codec_ops cs_patch_ops = { |
1077a024 TI |
322 | .build_controls = snd_hda_gen_build_controls, |
323 | .build_pcms = snd_hda_gen_build_pcms, | |
e5f14248 TI |
324 | .init = cs_init, |
325 | .free = cs_free, | |
5c2e4e0a | 326 | .unsol_event = snd_hda_jack_unsol_event, |
e5f14248 TI |
327 | }; |
328 | ||
329 | static int cs_parse_auto_config(struct hda_codec *codec) | |
330 | { | |
331 | struct cs_spec *spec = codec->spec; | |
332 | int err; | |
333 | ||
1077a024 | 334 | err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); |
ed208255 TI |
335 | if (err < 0) |
336 | return err; | |
337 | ||
1077a024 | 338 | err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); |
e5f14248 TI |
339 | if (err < 0) |
340 | return err; | |
1077a024 | 341 | |
e5f14248 TI |
342 | return 0; |
343 | } | |
344 | ||
b35aabd7 TI |
345 | static const struct hda_model_fixup cs420x_models[] = { |
346 | { .id = CS420X_MBP53, .name = "mbp53" }, | |
347 | { .id = CS420X_MBP55, .name = "mbp55" }, | |
348 | { .id = CS420X_IMAC27, .name = "imac27" }, | |
349 | { .id = CS420X_IMAC27_122, .name = "imac27_122" }, | |
350 | { .id = CS420X_APPLE, .name = "apple" }, | |
ef596a57 | 351 | { .id = CS420X_MBP101, .name = "mbp101" }, |
ffe4d12b | 352 | { .id = CS420X_MBP81, .name = "mbp81" }, |
6ab982e8 | 353 | { .id = CS420X_MBA42, .name = "mba42" }, |
b35aabd7 | 354 | {} |
a6bae205 TI |
355 | }; |
356 | ||
b35aabd7 | 357 | static const struct snd_pci_quirk cs420x_fixup_tbl[] = { |
4e7d7c60 | 358 | SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53), |
87232dd4 | 359 | SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55), |
a6bae205 | 360 | SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), |
f46119b7 | 361 | SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55), |
6dfeb703 TI |
362 | /* this conflicts with too many other models */ |
363 | /*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/ | |
6dfeb703 | 364 | |
b35aabd7 | 365 | /* codec SSID */ |
ffe4d12b | 366 | SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81), |
7e5bea19 | 367 | SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122), |
ef596a57 | 368 | SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101), |
6ab982e8 | 369 | SND_PCI_QUIRK(0x106b, 0x5b00, "MacBookAir 4,2", CS420X_MBA42), |
6dfeb703 | 370 | SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE), |
a6bae205 TI |
371 | {} /* terminator */ |
372 | }; | |
373 | ||
b35aabd7 | 374 | static const struct hda_pintbl mbp53_pincfgs[] = { |
4e7d7c60 VW |
375 | { 0x09, 0x012b4050 }, |
376 | { 0x0a, 0x90100141 }, | |
377 | { 0x0b, 0x90100140 }, | |
378 | { 0x0c, 0x018b3020 }, | |
379 | { 0x0d, 0x90a00110 }, | |
380 | { 0x0e, 0x400000f0 }, | |
381 | { 0x0f, 0x01cbe030 }, | |
382 | { 0x10, 0x014be060 }, | |
383 | { 0x12, 0x400000f0 }, | |
384 | { 0x15, 0x400000f0 }, | |
385 | {} /* terminator */ | |
386 | }; | |
387 | ||
b35aabd7 | 388 | static const struct hda_pintbl mbp55_pincfgs[] = { |
a6bae205 TI |
389 | { 0x09, 0x012b4030 }, |
390 | { 0x0a, 0x90100121 }, | |
391 | { 0x0b, 0x90100120 }, | |
392 | { 0x0c, 0x400000f0 }, | |
393 | { 0x0d, 0x90a00110 }, | |
394 | { 0x0e, 0x400000f0 }, | |
395 | { 0x0f, 0x400000f0 }, | |
396 | { 0x10, 0x014be040 }, | |
397 | { 0x12, 0x400000f0 }, | |
398 | { 0x15, 0x400000f0 }, | |
399 | {} /* terminator */ | |
400 | }; | |
401 | ||
b35aabd7 | 402 | static const struct hda_pintbl imac27_pincfgs[] = { |
1a5ba2e9 RAE |
403 | { 0x09, 0x012b4050 }, |
404 | { 0x0a, 0x90100140 }, | |
405 | { 0x0b, 0x90100142 }, | |
406 | { 0x0c, 0x018b3020 }, | |
407 | { 0x0d, 0x90a00110 }, | |
408 | { 0x0e, 0x400000f0 }, | |
409 | { 0x0f, 0x01cbe030 }, | |
410 | { 0x10, 0x014be060 }, | |
411 | { 0x12, 0x01ab9070 }, | |
412 | { 0x15, 0x400000f0 }, | |
413 | {} /* terminator */ | |
414 | }; | |
415 | ||
ef596a57 TI |
416 | static const struct hda_pintbl mbp101_pincfgs[] = { |
417 | { 0x0d, 0x40ab90f0 }, | |
418 | { 0x0e, 0x90a600f0 }, | |
419 | { 0x12, 0x50a600f0 }, | |
420 | {} /* terminator */ | |
421 | }; | |
422 | ||
6ab982e8 TI |
423 | static const struct hda_pintbl mba42_pincfgs[] = { |
424 | { 0x09, 0x012b4030 }, /* HP */ | |
425 | { 0x0a, 0x400000f0 }, | |
426 | { 0x0b, 0x90100120 }, /* speaker */ | |
427 | { 0x0c, 0x400000f0 }, | |
428 | { 0x0d, 0x90a00110 }, /* mic */ | |
429 | { 0x0e, 0x400000f0 }, | |
430 | { 0x0f, 0x400000f0 }, | |
431 | { 0x10, 0x400000f0 }, | |
432 | { 0x12, 0x400000f0 }, | |
433 | { 0x15, 0x400000f0 }, | |
434 | {} /* terminator */ | |
435 | }; | |
436 | ||
b35aabd7 TI |
437 | static void cs420x_fixup_gpio_13(struct hda_codec *codec, |
438 | const struct hda_fixup *fix, int action) | |
439 | { | |
440 | if (action == HDA_FIXUP_ACT_PRE_PROBE) { | |
441 | struct cs_spec *spec = codec->spec; | |
442 | spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */ | |
443 | spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ | |
444 | spec->gpio_mask = spec->gpio_dir = | |
445 | spec->gpio_eapd_hp | spec->gpio_eapd_speaker; | |
446 | } | |
447 | } | |
a6bae205 | 448 | |
b35aabd7 TI |
449 | static void cs420x_fixup_gpio_23(struct hda_codec *codec, |
450 | const struct hda_fixup *fix, int action) | |
a6bae205 | 451 | { |
b35aabd7 TI |
452 | if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
453 | struct cs_spec *spec = codec->spec; | |
454 | spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */ | |
455 | spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ | |
456 | spec->gpio_mask = spec->gpio_dir = | |
457 | spec->gpio_eapd_hp | spec->gpio_eapd_speaker; | |
458 | } | |
a6bae205 TI |
459 | } |
460 | ||
b35aabd7 TI |
461 | static const struct hda_fixup cs420x_fixups[] = { |
462 | [CS420X_MBP53] = { | |
463 | .type = HDA_FIXUP_PINS, | |
464 | .v.pins = mbp53_pincfgs, | |
465 | .chained = true, | |
466 | .chain_id = CS420X_APPLE, | |
467 | }, | |
468 | [CS420X_MBP55] = { | |
469 | .type = HDA_FIXUP_PINS, | |
470 | .v.pins = mbp55_pincfgs, | |
471 | .chained = true, | |
472 | .chain_id = CS420X_GPIO_13, | |
473 | }, | |
474 | [CS420X_IMAC27] = { | |
475 | .type = HDA_FIXUP_PINS, | |
476 | .v.pins = imac27_pincfgs, | |
477 | .chained = true, | |
478 | .chain_id = CS420X_GPIO_13, | |
479 | }, | |
480 | [CS420X_GPIO_13] = { | |
481 | .type = HDA_FIXUP_FUNC, | |
482 | .v.func = cs420x_fixup_gpio_13, | |
483 | }, | |
484 | [CS420X_GPIO_23] = { | |
485 | .type = HDA_FIXUP_FUNC, | |
486 | .v.func = cs420x_fixup_gpio_23, | |
487 | }, | |
ef596a57 TI |
488 | [CS420X_MBP101] = { |
489 | .type = HDA_FIXUP_PINS, | |
490 | .v.pins = mbp101_pincfgs, | |
491 | .chained = true, | |
ef596a57 TI |
492 | .chain_id = CS420X_GPIO_13, |
493 | }, | |
ffe4d12b TI |
494 | [CS420X_MBP81] = { |
495 | .type = HDA_FIXUP_VERBS, | |
496 | .v.verbs = (const struct hda_verb[]) { | |
497 | /* internal mic ADC2: right only, single ended */ | |
498 | {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG}, | |
499 | {0x11, AC_VERB_SET_PROC_COEF, 0x102a}, | |
500 | {} | |
501 | }, | |
502 | .chained = true, | |
503 | .chain_id = CS420X_GPIO_13, | |
504 | }, | |
6ab982e8 TI |
505 | [CS420X_MBA42] = { |
506 | .type = HDA_FIXUP_PINS, | |
507 | .v.pins = mba42_pincfgs, | |
508 | .chained = true, | |
509 | .chain_id = CS420X_GPIO_13, | |
510 | }, | |
b35aabd7 TI |
511 | }; |
512 | ||
1077a024 | 513 | static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid) |
e5f14248 TI |
514 | { |
515 | struct cs_spec *spec; | |
e5f14248 TI |
516 | |
517 | spec = kzalloc(sizeof(*spec), GFP_KERNEL); | |
518 | if (!spec) | |
1077a024 | 519 | return NULL; |
e5f14248 | 520 | codec->spec = spec; |
1077a024 TI |
521 | spec->vendor_nid = vendor_nid; |
522 | snd_hda_gen_spec_init(&spec->gen); | |
523 | ||
524 | return spec; | |
525 | } | |
526 | ||
527 | static int patch_cs420x(struct hda_codec *codec) | |
528 | { | |
529 | struct cs_spec *spec; | |
530 | int err; | |
e5f14248 | 531 | |
1077a024 TI |
532 | spec = cs_alloc_spec(codec, CS420X_VENDOR_NID); |
533 | if (!spec) | |
534 | return -ENOMEM; | |
56487c27 | 535 | |
6d3073e1 TI |
536 | spec->gen.automute_hook = cs_automute; |
537 | ||
b35aabd7 TI |
538 | snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, |
539 | cs420x_fixups); | |
540 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); | |
e5f14248 | 541 | |
ed208255 | 542 | err = cs_parse_auto_config(codec); |
21a4dc43 TI |
543 | if (err < 0) |
544 | goto error; | |
545 | ||
e5f14248 TI |
546 | codec->patch_ops = cs_patch_ops; |
547 | ||
b35aabd7 TI |
548 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); |
549 | ||
e5f14248 TI |
550 | return 0; |
551 | ||
552 | error: | |
c5e0b6db | 553 | cs_free(codec); |
e5f14248 TI |
554 | return err; |
555 | } | |
556 | ||
be8cf445 TI |
557 | /* |
558 | * CS4208 support: | |
559 | * Its layout is no longer compatible with CS4206/CS4207, and the generic | |
560 | * parser seems working fairly well, except for trivial fixups. | |
561 | */ | |
562 | enum { | |
563 | CS4208_GPIO0, | |
564 | }; | |
565 | ||
566 | static const struct hda_model_fixup cs4208_models[] = { | |
567 | { .id = CS4208_GPIO0, .name = "gpio0" }, | |
568 | {} | |
569 | }; | |
570 | ||
571 | static const struct snd_pci_quirk cs4208_fixup_tbl[] = { | |
572 | /* codec SSID */ | |
573 | SND_PCI_QUIRK(0x106b, 0x7100, "MacBookPro 6,1", CS4208_GPIO0), | |
574 | SND_PCI_QUIRK(0x106b, 0x7200, "MacBookPro 6,2", CS4208_GPIO0), | |
575 | {} /* terminator */ | |
576 | }; | |
577 | ||
578 | static void cs4208_fixup_gpio0(struct hda_codec *codec, | |
579 | const struct hda_fixup *fix, int action) | |
580 | { | |
581 | if (action == HDA_FIXUP_ACT_PRE_PROBE) { | |
582 | struct cs_spec *spec = codec->spec; | |
583 | spec->gpio_eapd_hp = 0; | |
584 | spec->gpio_eapd_speaker = 1; | |
585 | spec->gpio_mask = spec->gpio_dir = | |
586 | spec->gpio_eapd_hp | spec->gpio_eapd_speaker; | |
587 | } | |
588 | } | |
589 | ||
590 | static const struct hda_fixup cs4208_fixups[] = { | |
591 | [CS4208_GPIO0] = { | |
592 | .type = HDA_FIXUP_FUNC, | |
593 | .v.func = cs4208_fixup_gpio0, | |
594 | }, | |
595 | }; | |
596 | ||
597 | static int patch_cs4208(struct hda_codec *codec) | |
598 | { | |
599 | struct cs_spec *spec; | |
600 | int err; | |
601 | ||
602 | spec = cs_alloc_spec(codec, 0); /* no specific w/a */ | |
603 | if (!spec) | |
604 | return -ENOMEM; | |
605 | ||
606 | spec->gen.automute_hook = cs_automute; | |
607 | ||
608 | snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl, | |
609 | cs4208_fixups); | |
610 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); | |
611 | ||
612 | err = cs_parse_auto_config(codec); | |
613 | if (err < 0) | |
614 | goto error; | |
615 | ||
616 | codec->patch_ops = cs_patch_ops; | |
617 | ||
618 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); | |
619 | ||
620 | return 0; | |
621 | ||
622 | error: | |
623 | cs_free(codec); | |
624 | return err; | |
625 | } | |
626 | ||
56487c27 TH |
627 | /* |
628 | * Cirrus Logic CS4210 | |
629 | * | |
630 | * 1 DAC => HP(sense) / Speakers, | |
631 | * 1 ADC <= LineIn(sense) / MicIn / DMicIn, | |
632 | * 1 SPDIF OUT => SPDIF Trasmitter(sense) | |
633 | */ | |
634 | ||
635 | /* CS4210 board names */ | |
b35aabd7 TI |
636 | static const struct hda_model_fixup cs421x_models[] = { |
637 | { .id = CS421X_CDB4210, .name = "cdb4210" }, | |
4af16107 | 638 | { .id = CS421X_STUMPY, .name = "stumpy" }, |
b35aabd7 | 639 | {} |
56487c27 TH |
640 | }; |
641 | ||
b35aabd7 | 642 | static const struct snd_pci_quirk cs421x_fixup_tbl[] = { |
56487c27 TH |
643 | /* Test Intel board + CDB2410 */ |
644 | SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), | |
645 | {} /* terminator */ | |
646 | }; | |
647 | ||
648 | /* CS4210 board pinconfigs */ | |
649 | /* Default CS4210 (CDB4210)*/ | |
b35aabd7 | 650 | static const struct hda_pintbl cdb4210_pincfgs[] = { |
56487c27 TH |
651 | { 0x05, 0x0321401f }, |
652 | { 0x06, 0x90170010 }, | |
653 | { 0x07, 0x03813031 }, | |
654 | { 0x08, 0xb7a70037 }, | |
655 | { 0x09, 0xb7a6003e }, | |
656 | { 0x0a, 0x034510f0 }, | |
657 | {} /* terminator */ | |
658 | }; | |
659 | ||
4af16107 DR |
660 | /* Stumpy ChromeBox */ |
661 | static const struct hda_pintbl stumpy_pincfgs[] = { | |
662 | { 0x05, 0x022120f0 }, | |
663 | { 0x06, 0x901700f0 }, | |
664 | { 0x07, 0x02a120f0 }, | |
665 | { 0x08, 0x77a70037 }, | |
666 | { 0x09, 0x77a6003e }, | |
667 | { 0x0a, 0x434510f0 }, | |
668 | {} /* terminator */ | |
669 | }; | |
670 | ||
b35aabd7 TI |
671 | /* Setup GPIO/SENSE for each board (if used) */ |
672 | static void cs421x_fixup_sense_b(struct hda_codec *codec, | |
673 | const struct hda_fixup *fix, int action) | |
674 | { | |
675 | struct cs_spec *spec = codec->spec; | |
676 | if (action == HDA_FIXUP_ACT_PRE_PROBE) | |
677 | spec->sense_b = 1; | |
678 | } | |
679 | ||
680 | static const struct hda_fixup cs421x_fixups[] = { | |
681 | [CS421X_CDB4210] = { | |
682 | .type = HDA_FIXUP_PINS, | |
683 | .v.pins = cdb4210_pincfgs, | |
684 | .chained = true, | |
685 | .chain_id = CS421X_SENSE_B, | |
686 | }, | |
687 | [CS421X_SENSE_B] = { | |
688 | .type = HDA_FIXUP_FUNC, | |
689 | .v.func = cs421x_fixup_sense_b, | |
4af16107 DR |
690 | }, |
691 | [CS421X_STUMPY] = { | |
692 | .type = HDA_FIXUP_PINS, | |
693 | .v.pins = stumpy_pincfgs, | |
694 | }, | |
56487c27 TH |
695 | }; |
696 | ||
697 | static const struct hda_verb cs421x_coef_init_verbs[] = { | |
698 | {0x0B, AC_VERB_SET_PROC_STATE, 1}, | |
699 | {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG}, | |
700 | /* | |
701 | Disable Coefficient Index Auto-Increment(DAI)=1, | |
702 | PDREF=0 | |
703 | */ | |
704 | {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 }, | |
705 | ||
706 | {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG}, | |
707 | /* ADC SZCMode = Digital Soft Ramp */ | |
708 | {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 }, | |
709 | ||
710 | {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG}, | |
711 | {0x0B, AC_VERB_SET_PROC_COEF, | |
712 | (0x0002 /* DAC SZCMode = Digital Soft Ramp */ | |
713 | | 0x0004 /* Mute DAC on FIFO error */ | |
714 | | 0x0008 /* Enable DAC High Pass Filter */ | |
715 | )}, | |
716 | {} /* terminator */ | |
717 | }; | |
718 | ||
719 | /* Errata: CS4210 rev A1 Silicon | |
720 | * | |
721 | * http://www.cirrus.com/en/pubs/errata/ | |
722 | * | |
723 | * Description: | |
724 | * 1. Performance degredation is present in the ADC. | |
725 | * 2. Speaker output is not completely muted upon HP detect. | |
726 | * 3. Noise is present when clipping occurs on the amplified | |
727 | * speaker outputs. | |
728 | * | |
729 | * Workaround: | |
730 | * The following verb sequence written to the registers during | |
731 | * initialization will correct the issues listed above. | |
732 | */ | |
733 | ||
734 | static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = { | |
735 | {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ | |
736 | ||
737 | {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006}, | |
738 | {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */ | |
739 | ||
740 | {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A}, | |
741 | {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */ | |
742 | ||
743 | {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011}, | |
744 | {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */ | |
745 | ||
746 | {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A}, | |
747 | {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */ | |
748 | ||
749 | {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B}, | |
750 | {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */ | |
751 | ||
752 | {} /* terminator */ | |
753 | }; | |
754 | ||
755 | /* Speaker Amp Gain is controlled by the vendor widget's coef 4 */ | |
756 | static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0); | |
757 | ||
758 | static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol, | |
759 | struct snd_ctl_elem_info *uinfo) | |
760 | { | |
761 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
762 | uinfo->count = 1; | |
763 | uinfo->value.integer.min = 0; | |
764 | uinfo->value.integer.max = 3; | |
765 | return 0; | |
766 | } | |
767 | ||
768 | static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol, | |
769 | struct snd_ctl_elem_value *ucontrol) | |
770 | { | |
771 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
772 | ||
773 | ucontrol->value.integer.value[0] = | |
774 | cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003; | |
775 | return 0; | |
776 | } | |
777 | ||
778 | static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, | |
779 | struct snd_ctl_elem_value *ucontrol) | |
780 | { | |
781 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
782 | ||
783 | unsigned int vol = ucontrol->value.integer.value[0]; | |
784 | unsigned int coef = | |
785 | cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL); | |
786 | unsigned int original_coef = coef; | |
787 | ||
788 | coef &= ~0x0003; | |
789 | coef |= (vol & 0x0003); | |
790 | if (original_coef == coef) | |
791 | return 0; | |
792 | else { | |
793 | cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef); | |
794 | return 1; | |
795 | } | |
796 | } | |
797 | ||
1077a024 | 798 | static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = { |
56487c27 TH |
799 | |
800 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
801 | .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | | |
802 | SNDRV_CTL_ELEM_ACCESS_TLV_READ), | |
803 | .name = "Speaker Boost Playback Volume", | |
804 | .info = cs421x_boost_vol_info, | |
805 | .get = cs421x_boost_vol_get, | |
806 | .put = cs421x_boost_vol_put, | |
807 | .tlv = { .p = cs421x_speaker_boost_db_scale }, | |
808 | }; | |
809 | ||
5660ffd0 | 810 | static void cs4210_pinmux_init(struct hda_codec *codec) |
56487c27 TH |
811 | { |
812 | struct cs_spec *spec = codec->spec; | |
813 | unsigned int def_conf, coef; | |
814 | ||
815 | /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */ | |
816 | coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); | |
817 | ||
818 | if (spec->gpio_mask) | |
819 | coef |= 0x0008; /* B1,B2 are GPIOs */ | |
820 | else | |
821 | coef &= ~0x0008; | |
822 | ||
823 | if (spec->sense_b) | |
824 | coef |= 0x0010; /* B2 is SENSE_B, not inverted */ | |
825 | else | |
826 | coef &= ~0x0010; | |
827 | ||
828 | cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); | |
829 | ||
830 | if ((spec->gpio_mask || spec->sense_b) && | |
831 | is_active_pin(codec, CS421X_DMIC_PIN_NID)) { | |
832 | ||
833 | /* | |
834 | GPIO or SENSE_B forced - disconnect the DMIC pin. | |
835 | */ | |
836 | def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID); | |
837 | def_conf &= ~AC_DEFCFG_PORT_CONN; | |
838 | def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT); | |
839 | snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf); | |
840 | } | |
841 | } | |
842 | ||
1077a024 TI |
843 | static void cs4210_spdif_automute(struct hda_codec *codec, |
844 | struct hda_jack_tbl *tbl) | |
56487c27 TH |
845 | { |
846 | struct cs_spec *spec = codec->spec; | |
1077a024 TI |
847 | bool spdif_present = false; |
848 | hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0]; | |
849 | ||
850 | /* detect on spdif is specific to CS4210 */ | |
851 | if (!spec->spdif_detect || | |
852 | spec->vendor_nid != CS4210_VENDOR_NID) | |
853 | return; | |
854 | ||
855 | spdif_present = snd_hda_jack_detect(codec, spdif_pin); | |
856 | if (spdif_present == spec->spdif_present) | |
857 | return; | |
858 | ||
859 | spec->spdif_present = spdif_present; | |
860 | /* SPDIF TX on/off */ | |
861 | if (spdif_present) | |
862 | snd_hda_set_pin_ctl(codec, spdif_pin, | |
863 | spdif_present ? PIN_OUT : 0); | |
56487c27 | 864 | |
1077a024 TI |
865 | cs_automute(codec); |
866 | } | |
867 | ||
868 | static void parse_cs421x_digital(struct hda_codec *codec) | |
869 | { | |
870 | struct cs_spec *spec = codec->spec; | |
871 | struct auto_pin_cfg *cfg = &spec->gen.autocfg; | |
872 | int i; | |
56487c27 TH |
873 | |
874 | for (i = 0; i < cfg->dig_outs; i++) { | |
875 | hda_nid_t nid = cfg->dig_out_pins[i]; | |
56487c27 | 876 | if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { |
56487c27 | 877 | spec->spdif_detect = 1; |
1077a024 TI |
878 | snd_hda_jack_detect_enable_callback(codec, nid, |
879 | SPDIF_EVENT, | |
880 | cs4210_spdif_automute); | |
56487c27 TH |
881 | } |
882 | } | |
883 | } | |
884 | ||
885 | static int cs421x_init(struct hda_codec *codec) | |
886 | { | |
887 | struct cs_spec *spec = codec->spec; | |
888 | ||
5660ffd0 DH |
889 | if (spec->vendor_nid == CS4210_VENDOR_NID) { |
890 | snd_hda_sequence_write(codec, cs421x_coef_init_verbs); | |
891 | snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes); | |
892 | cs4210_pinmux_init(codec); | |
893 | } | |
56487c27 | 894 | |
1077a024 TI |
895 | snd_hda_gen_init(codec); |
896 | ||
56487c27 TH |
897 | if (spec->gpio_mask) { |
898 | snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, | |
899 | spec->gpio_mask); | |
900 | snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, | |
901 | spec->gpio_dir); | |
902 | snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, | |
903 | spec->gpio_data); | |
904 | } | |
905 | ||
1077a024 | 906 | init_input_coef(codec); |
56487c27 | 907 | |
1077a024 | 908 | cs4210_spdif_automute(codec, NULL); |
56487c27 TH |
909 | |
910 | return 0; | |
911 | } | |
912 | ||
1077a024 | 913 | static int cs421x_build_controls(struct hda_codec *codec) |
56487c27 | 914 | { |
56487c27 | 915 | struct cs_spec *spec = codec->spec; |
56487c27 | 916 | int err; |
56487c27 | 917 | |
1077a024 | 918 | err = snd_hda_gen_build_controls(codec); |
56487c27 TH |
919 | if (err < 0) |
920 | return err; | |
56487c27 | 921 | |
1077a024 TI |
922 | if (spec->gen.autocfg.speaker_outs && |
923 | spec->vendor_nid == CS4210_VENDOR_NID) { | |
56487c27 | 924 | err = snd_hda_ctl_add(codec, 0, |
1077a024 | 925 | snd_ctl_new1(&cs421x_speaker_boost_ctl, codec)); |
56487c27 TH |
926 | if (err < 0) |
927 | return err; | |
928 | } | |
01a61e12 | 929 | return 0; |
56487c27 TH |
930 | } |
931 | ||
1077a024 | 932 | static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) |
56487c27 | 933 | { |
1077a024 | 934 | unsigned int caps; |
56487c27 | 935 | |
1077a024 TI |
936 | /* set the upper-limit for mixer amp to 0dB */ |
937 | caps = query_amp_caps(codec, dac, HDA_OUTPUT); | |
938 | caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); | |
939 | caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) | |
940 | << AC_AMPCAP_NUM_STEPS_SHIFT; | |
941 | snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); | |
56487c27 TH |
942 | } |
943 | ||
944 | static int cs421x_parse_auto_config(struct hda_codec *codec) | |
945 | { | |
946 | struct cs_spec *spec = codec->spec; | |
1077a024 | 947 | hda_nid_t dac = CS4210_DAC_NID; |
56487c27 TH |
948 | int err; |
949 | ||
1077a024 TI |
950 | fix_volume_caps(codec, dac); |
951 | ||
952 | err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); | |
56487c27 TH |
953 | if (err < 0) |
954 | return err; | |
1077a024 TI |
955 | |
956 | err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); | |
56487c27 TH |
957 | if (err < 0) |
958 | return err; | |
1077a024 TI |
959 | |
960 | parse_cs421x_digital(codec); | |
56487c27 TH |
961 | return 0; |
962 | } | |
963 | ||
964 | #ifdef CONFIG_PM | |
965 | /* | |
966 | Manage PDREF, when transitioning to D3hot | |
967 | (DAC,ADC) -> D3, PDREF=1, AFG->D3 | |
968 | */ | |
68cb2b55 | 969 | static int cs421x_suspend(struct hda_codec *codec) |
56487c27 | 970 | { |
5660ffd0 | 971 | struct cs_spec *spec = codec->spec; |
56487c27 TH |
972 | unsigned int coef; |
973 | ||
974 | snd_hda_shutup_pins(codec); | |
975 | ||
976 | snd_hda_codec_write(codec, CS4210_DAC_NID, 0, | |
977 | AC_VERB_SET_POWER_STATE, AC_PWRST_D3); | |
978 | snd_hda_codec_write(codec, CS4210_ADC_NID, 0, | |
979 | AC_VERB_SET_POWER_STATE, AC_PWRST_D3); | |
980 | ||
5660ffd0 DH |
981 | if (spec->vendor_nid == CS4210_VENDOR_NID) { |
982 | coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); | |
983 | coef |= 0x0004; /* PDREF */ | |
984 | cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); | |
985 | } | |
56487c27 TH |
986 | |
987 | return 0; | |
988 | } | |
989 | #endif | |
990 | ||
00e17f76 | 991 | static const struct hda_codec_ops cs421x_patch_ops = { |
56487c27 | 992 | .build_controls = cs421x_build_controls, |
1077a024 | 993 | .build_pcms = snd_hda_gen_build_pcms, |
56487c27 TH |
994 | .init = cs421x_init, |
995 | .free = cs_free, | |
5c2e4e0a | 996 | .unsol_event = snd_hda_jack_unsol_event, |
56487c27 TH |
997 | #ifdef CONFIG_PM |
998 | .suspend = cs421x_suspend, | |
999 | #endif | |
1000 | }; | |
1001 | ||
5660ffd0 | 1002 | static int patch_cs4210(struct hda_codec *codec) |
56487c27 TH |
1003 | { |
1004 | struct cs_spec *spec; | |
1005 | int err; | |
1006 | ||
1077a024 | 1007 | spec = cs_alloc_spec(codec, CS4210_VENDOR_NID); |
56487c27 TH |
1008 | if (!spec) |
1009 | return -ENOMEM; | |
56487c27 | 1010 | |
6d3073e1 TI |
1011 | spec->gen.automute_hook = cs_automute; |
1012 | ||
b35aabd7 TI |
1013 | snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, |
1014 | cs421x_fixups); | |
1015 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); | |
56487c27 TH |
1016 | |
1017 | /* | |
1018 | Update the GPIO/DMIC/SENSE_B pinmux before the configuration | |
1019 | is auto-parsed. If GPIO or SENSE_B is forced, DMIC input | |
1020 | is disabled. | |
1021 | */ | |
5660ffd0 | 1022 | cs4210_pinmux_init(codec); |
56487c27 TH |
1023 | |
1024 | err = cs421x_parse_auto_config(codec); | |
1025 | if (err < 0) | |
1026 | goto error; | |
1027 | ||
5660ffd0 DH |
1028 | codec->patch_ops = cs421x_patch_ops; |
1029 | ||
b35aabd7 TI |
1030 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); |
1031 | ||
5660ffd0 DH |
1032 | return 0; |
1033 | ||
1034 | error: | |
c5e0b6db | 1035 | cs_free(codec); |
5660ffd0 DH |
1036 | return err; |
1037 | } | |
1038 | ||
1039 | static int patch_cs4213(struct hda_codec *codec) | |
1040 | { | |
1041 | struct cs_spec *spec; | |
1042 | int err; | |
1043 | ||
1077a024 | 1044 | spec = cs_alloc_spec(codec, CS4213_VENDOR_NID); |
5660ffd0 DH |
1045 | if (!spec) |
1046 | return -ENOMEM; | |
5660ffd0 DH |
1047 | |
1048 | err = cs421x_parse_auto_config(codec); | |
1049 | if (err < 0) | |
1050 | goto error; | |
56487c27 | 1051 | |
5660ffd0 | 1052 | codec->patch_ops = cs421x_patch_ops; |
56487c27 TH |
1053 | return 0; |
1054 | ||
1055 | error: | |
c5e0b6db | 1056 | cs_free(codec); |
56487c27 TH |
1057 | return err; |
1058 | } | |
1059 | ||
e5f14248 TI |
1060 | |
1061 | /* | |
1062 | * patch entries | |
1063 | */ | |
c42d4782 | 1064 | static const struct hda_codec_preset snd_hda_preset_cirrus[] = { |
e5f14248 TI |
1065 | { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x }, |
1066 | { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x }, | |
be8cf445 | 1067 | { .id = 0x10134208, .name = "CS4208", .patch = patch_cs4208 }, |
5660ffd0 DH |
1068 | { .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 }, |
1069 | { .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 }, | |
e5f14248 TI |
1070 | {} /* terminator */ |
1071 | }; | |
1072 | ||
1073 | MODULE_ALIAS("snd-hda-codec-id:10134206"); | |
1074 | MODULE_ALIAS("snd-hda-codec-id:10134207"); | |
be8cf445 | 1075 | MODULE_ALIAS("snd-hda-codec-id:10134208"); |
56487c27 | 1076 | MODULE_ALIAS("snd-hda-codec-id:10134210"); |
5660ffd0 | 1077 | MODULE_ALIAS("snd-hda-codec-id:10134213"); |
e5f14248 TI |
1078 | |
1079 | MODULE_LICENSE("GPL"); | |
1080 | MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); | |
1081 | ||
1082 | static struct hda_codec_preset_list cirrus_list = { | |
1083 | .preset = snd_hda_preset_cirrus, | |
1084 | .owner = THIS_MODULE, | |
1085 | }; | |
1086 | ||
1087 | static int __init patch_cirrus_init(void) | |
1088 | { | |
1089 | return snd_hda_add_codec_preset(&cirrus_list); | |
1090 | } | |
1091 | ||
1092 | static void __exit patch_cirrus_exit(void) | |
1093 | { | |
1094 | snd_hda_delete_codec_preset(&cirrus_list); | |
1095 | } | |
1096 | ||
1097 | module_init(patch_cirrus_init) | |
1098 | module_exit(patch_cirrus_exit) |