Commit | Line | Data |
---|---|---|
8c70461b LT |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip | |
4 | * | |
5 | * Copyright (C) 2021 Cirrus Logic, Inc. and | |
6 | * Cirrus Logic International Semiconductor Ltd. | |
7 | */ | |
8 | ||
9 | #include <linux/init.h> | |
10 | #include <linux/slab.h> | |
11 | #include <linux/module.h> | |
12 | #include <sound/core.h> | |
13 | #include <linux/mutex.h> | |
4ff2ae3a | 14 | #include <linux/iopoll.h> |
8c70461b LT |
15 | |
16 | #include "patch_cs8409.h" | |
17 | ||
636eb9d2 LT |
18 | /****************************************************************************** |
19 | * CS8409 Specific Functions | |
20 | ******************************************************************************/ | |
21 | ||
8c70461b LT |
22 | static int cs8409_parse_auto_config(struct hda_codec *codec) |
23 | { | |
24 | struct cs8409_spec *spec = codec->spec; | |
25 | int err; | |
26 | int i; | |
27 | ||
28 | err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); | |
29 | if (err < 0) | |
30 | return err; | |
31 | ||
32 | err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); | |
33 | if (err < 0) | |
34 | return err; | |
35 | ||
36 | /* keep the ADCs powered up when it's dynamically switchable */ | |
37 | if (spec->gen.dyn_adc_switch) { | |
38 | unsigned int done = 0; | |
39 | ||
40 | for (i = 0; i < spec->gen.input_mux.num_items; i++) { | |
41 | int idx = spec->gen.dyn_adc_idx[i]; | |
42 | ||
43 | if (done & (1 << idx)) | |
44 | continue; | |
45 | snd_hda_gen_fix_pin_power(codec, spec->gen.adc_nids[idx]); | |
46 | done |= 1 << idx; | |
47 | } | |
48 | } | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
647d50a0 LT |
53 | static void cs8409_disable_i2c_clock_worker(struct work_struct *work); |
54 | ||
8c70461b LT |
55 | static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec) |
56 | { | |
57 | struct cs8409_spec *spec; | |
58 | ||
59 | spec = kzalloc(sizeof(*spec), GFP_KERNEL); | |
60 | if (!spec) | |
61 | return NULL; | |
62 | codec->spec = spec; | |
647d50a0 | 63 | spec->codec = codec; |
8c70461b | 64 | codec->power_save_node = 1; |
636eb9d2 | 65 | mutex_init(&spec->i2c_mux); |
647d50a0 | 66 | INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock_worker); |
8c70461b LT |
67 | snd_hda_gen_spec_init(&spec->gen); |
68 | ||
69 | return spec; | |
70 | } | |
71 | ||
8c70461b LT |
72 | static inline int cs8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx) |
73 | { | |
ccff0064 SB |
74 | snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx); |
75 | return snd_hda_codec_read(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_GET_PROC_COEF, 0); | |
8c70461b LT |
76 | } |
77 | ||
78 | static inline void cs8409_vendor_coef_set(struct hda_codec *codec, unsigned int idx, | |
79 | unsigned int coef) | |
80 | { | |
ccff0064 SB |
81 | snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx); |
82 | snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_PROC_COEF, coef); | |
8c70461b LT |
83 | } |
84 | ||
647d50a0 LT |
85 | /* |
86 | * cs8409_enable_i2c_clock - Disable I2C clocks | |
87 | * @codec: the codec instance | |
88 | * Disable I2C clocks. | |
89 | * This must be called when the i2c mutex is unlocked. | |
90 | */ | |
91 | static void cs8409_disable_i2c_clock(struct hda_codec *codec) | |
92 | { | |
93 | struct cs8409_spec *spec = codec->spec; | |
94 | ||
165b81c4 | 95 | mutex_lock(&spec->i2c_mux); |
647d50a0 LT |
96 | if (spec->i2c_clck_enabled) { |
97 | cs8409_vendor_coef_set(spec->codec, 0x0, | |
98 | cs8409_vendor_coef_get(spec->codec, 0x0) & 0xfffffff7); | |
99 | spec->i2c_clck_enabled = 0; | |
100 | } | |
165b81c4 | 101 | mutex_unlock(&spec->i2c_mux); |
647d50a0 LT |
102 | } |
103 | ||
104 | /* | |
105 | * cs8409_disable_i2c_clock_worker - Worker that disable the I2C Clock after 25ms without use | |
106 | */ | |
107 | static void cs8409_disable_i2c_clock_worker(struct work_struct *work) | |
108 | { | |
109 | struct cs8409_spec *spec = container_of(work, struct cs8409_spec, i2c_clk_work.work); | |
110 | ||
111 | cs8409_disable_i2c_clock(spec->codec); | |
112 | } | |
113 | ||
114 | /* | |
8c70461b LT |
115 | * cs8409_enable_i2c_clock - Enable I2C clocks |
116 | * @codec: the codec instance | |
647d50a0 LT |
117 | * Enable I2C clocks. |
118 | * This must be called when the i2c mutex is locked. | |
8c70461b | 119 | */ |
647d50a0 | 120 | static void cs8409_enable_i2c_clock(struct hda_codec *codec) |
8c70461b | 121 | { |
647d50a0 | 122 | struct cs8409_spec *spec = codec->spec; |
8c70461b | 123 | |
647d50a0 LT |
124 | /* Cancel the disable timer, but do not wait for any running disable functions to finish. |
125 | * If the disable timer runs out before cancel, the delayed work thread will be blocked, | |
126 | * waiting for the mutex to become unlocked. This mutex will be locked for the duration of | |
127 | * any i2c transaction, so the disable function will run to completion immediately | |
128 | * afterwards in the scenario. The next enable call will re-enable the clock, regardless. | |
129 | */ | |
130 | cancel_delayed_work(&spec->i2c_clk_work); | |
131 | ||
132 | if (!spec->i2c_clck_enabled) { | |
133 | cs8409_vendor_coef_set(codec, 0x0, cs8409_vendor_coef_get(codec, 0x0) | 0x8); | |
134 | spec->i2c_clck_enabled = 1; | |
135 | } | |
136 | queue_delayed_work(system_power_efficient_wq, &spec->i2c_clk_work, msecs_to_jiffies(25)); | |
8c70461b LT |
137 | } |
138 | ||
139 | /** | |
140 | * cs8409_i2c_wait_complete - Wait for I2C transaction | |
141 | * @codec: the codec instance | |
142 | * | |
143 | * Wait for I2C transaction to complete. | |
928adf0e | 144 | * Return -ETIMEDOUT if transaction wait times out. |
8c70461b LT |
145 | */ |
146 | static int cs8409_i2c_wait_complete(struct hda_codec *codec) | |
147 | { | |
8c70461b LT |
148 | unsigned int retval; |
149 | ||
928adf0e SB |
150 | return read_poll_timeout(cs8409_vendor_coef_get, retval, retval & 0x18, |
151 | CS42L42_I2C_SLEEP_US, CS42L42_I2C_TIMEOUT_US, false, codec, CS8409_I2C_STS); | |
8c70461b LT |
152 | } |
153 | ||
d395fd78 LT |
154 | /** |
155 | * cs8409_set_i2c_dev_addr - Set i2c address for transaction | |
156 | * @codec: the codec instance | |
157 | * @addr: I2C Address | |
158 | */ | |
159 | static void cs8409_set_i2c_dev_addr(struct hda_codec *codec, unsigned int addr) | |
160 | { | |
161 | struct cs8409_spec *spec = codec->spec; | |
162 | ||
163 | if (spec->dev_addr != addr) { | |
164 | cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, addr); | |
165 | spec->dev_addr = addr; | |
166 | } | |
167 | } | |
168 | ||
8de4e5a6 LT |
169 | /** |
170 | * cs8409_i2c_set_page - CS8409 I2C set page register. | |
24f7ac3d | 171 | * @scodec: the codec instance |
8de4e5a6 LT |
172 | * @i2c_reg: Page register |
173 | * | |
174 | * Returns negative on error. | |
175 | */ | |
24f7ac3d | 176 | static int cs8409_i2c_set_page(struct sub_codec *scodec, unsigned int i2c_reg) |
8de4e5a6 | 177 | { |
24f7ac3d | 178 | struct hda_codec *codec = scodec->codec; |
8de4e5a6 | 179 | |
24f7ac3d | 180 | if (scodec->paged && (scodec->last_page != (i2c_reg >> 8))) { |
8de4e5a6 LT |
181 | cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8); |
182 | if (cs8409_i2c_wait_complete(codec) < 0) | |
183 | return -EIO; | |
24f7ac3d | 184 | scodec->last_page = i2c_reg >> 8; |
8de4e5a6 LT |
185 | } |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
8c70461b LT |
190 | /** |
191 | * cs8409_i2c_read - CS8409 I2C Read. | |
24f7ac3d | 192 | * @scodec: the codec instance |
165b81c4 | 193 | * @addr: Register to read |
8c70461b | 194 | * |
8c70461b LT |
195 | * Returns negative on error, otherwise returns read value in bits 0-7. |
196 | */ | |
24f7ac3d | 197 | static int cs8409_i2c_read(struct sub_codec *scodec, unsigned int addr) |
8c70461b | 198 | { |
24f7ac3d | 199 | struct hda_codec *codec = scodec->codec; |
a1a6c7df | 200 | struct cs8409_spec *spec = codec->spec; |
8c70461b LT |
201 | unsigned int i2c_reg_data; |
202 | unsigned int read_data; | |
203 | ||
24f7ac3d | 204 | if (scodec->suspended) |
a1a6c7df LT |
205 | return -EPERM; |
206 | ||
165b81c4 | 207 | mutex_lock(&spec->i2c_mux); |
647d50a0 | 208 | cs8409_enable_i2c_clock(codec); |
24f7ac3d | 209 | cs8409_set_i2c_dev_addr(codec, scodec->addr); |
8c70461b | 210 | |
24f7ac3d LT |
211 | if (cs8409_i2c_set_page(scodec, addr)) |
212 | goto error; | |
8c70461b | 213 | |
165b81c4 | 214 | i2c_reg_data = (addr << 8) & 0x0ffff; |
ccff0064 | 215 | cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data); |
165b81c4 LT |
216 | if (cs8409_i2c_wait_complete(codec) < 0) |
217 | goto error; | |
8c70461b LT |
218 | |
219 | /* Register in bits 15-8 and the data in 7-0 */ | |
ccff0064 | 220 | read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD); |
8c70461b | 221 | |
165b81c4 | 222 | mutex_unlock(&spec->i2c_mux); |
4ff2ae3a | 223 | |
8c70461b | 224 | return read_data & 0x0ff; |
165b81c4 LT |
225 | |
226 | error: | |
227 | mutex_unlock(&spec->i2c_mux); | |
24f7ac3d | 228 | codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr); |
165b81c4 LT |
229 | return -EIO; |
230 | } | |
231 | ||
232 | /** | |
233 | * cs8409_i2c_bulk_read - CS8409 I2C Read Sequence. | |
24f7ac3d | 234 | * @scodec: the codec instance |
165b81c4 LT |
235 | * @seq: Register Sequence to read |
236 | * @count: Number of registeres to read | |
237 | * | |
238 | * Returns negative on error, values are read into value element of cs8409_i2c_param sequence. | |
239 | */ | |
24f7ac3d | 240 | static int cs8409_i2c_bulk_read(struct sub_codec *scodec, struct cs8409_i2c_param *seq, int count) |
165b81c4 | 241 | { |
24f7ac3d | 242 | struct hda_codec *codec = scodec->codec; |
165b81c4 LT |
243 | struct cs8409_spec *spec = codec->spec; |
244 | unsigned int i2c_reg_data; | |
245 | int i; | |
246 | ||
24f7ac3d | 247 | if (scodec->suspended) |
165b81c4 LT |
248 | return -EPERM; |
249 | ||
250 | mutex_lock(&spec->i2c_mux); | |
24f7ac3d | 251 | cs8409_set_i2c_dev_addr(codec, scodec->addr); |
165b81c4 LT |
252 | |
253 | for (i = 0; i < count; i++) { | |
254 | cs8409_enable_i2c_clock(codec); | |
24f7ac3d | 255 | if (cs8409_i2c_set_page(scodec, seq[i].addr)) |
165b81c4 LT |
256 | goto error; |
257 | ||
258 | i2c_reg_data = (seq[i].addr << 8) & 0x0ffff; | |
259 | cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data); | |
260 | ||
261 | if (cs8409_i2c_wait_complete(codec) < 0) | |
262 | goto error; | |
263 | ||
264 | seq[i].value = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD) & 0xff; | |
265 | } | |
266 | ||
267 | mutex_unlock(&spec->i2c_mux); | |
268 | ||
269 | return 0; | |
270 | ||
271 | error: | |
272 | mutex_unlock(&spec->i2c_mux); | |
24f7ac3d | 273 | codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr); |
165b81c4 | 274 | return -EIO; |
8c70461b LT |
275 | } |
276 | ||
277 | /** | |
278 | * cs8409_i2c_write - CS8409 I2C Write. | |
24f7ac3d | 279 | * @scodec: the codec instance |
165b81c4 LT |
280 | * @addr: Register to write to |
281 | * @value: Data to write | |
8c70461b | 282 | * |
8c70461b LT |
283 | * Returns negative on error, otherwise returns 0. |
284 | */ | |
24f7ac3d | 285 | static int cs8409_i2c_write(struct sub_codec *scodec, unsigned int addr, unsigned int value) |
8c70461b | 286 | { |
24f7ac3d | 287 | struct hda_codec *codec = scodec->codec; |
a1a6c7df | 288 | struct cs8409_spec *spec = codec->spec; |
8c70461b LT |
289 | unsigned int i2c_reg_data; |
290 | ||
24f7ac3d | 291 | if (scodec->suspended) |
a1a6c7df LT |
292 | return -EPERM; |
293 | ||
165b81c4 LT |
294 | mutex_lock(&spec->i2c_mux); |
295 | ||
647d50a0 | 296 | cs8409_enable_i2c_clock(codec); |
24f7ac3d | 297 | cs8409_set_i2c_dev_addr(codec, scodec->addr); |
8c70461b | 298 | |
24f7ac3d LT |
299 | if (cs8409_i2c_set_page(scodec, addr)) |
300 | goto error; | |
8c70461b | 301 | |
165b81c4 | 302 | i2c_reg_data = ((addr << 8) & 0x0ff00) | (value & 0x0ff); |
ccff0064 | 303 | cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data); |
8c70461b | 304 | |
165b81c4 LT |
305 | if (cs8409_i2c_wait_complete(codec) < 0) |
306 | goto error; | |
307 | ||
308 | mutex_unlock(&spec->i2c_mux); | |
309 | return 0; | |
310 | ||
311 | error: | |
312 | mutex_unlock(&spec->i2c_mux); | |
24f7ac3d | 313 | codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr); |
165b81c4 LT |
314 | return -EIO; |
315 | } | |
316 | ||
317 | /** | |
318 | * cs8409_i2c_bulk_write - CS8409 I2C Write Sequence. | |
24f7ac3d | 319 | * @scodec: the codec instance |
165b81c4 LT |
320 | * @seq: Register Sequence to write |
321 | * @count: Number of registeres to write | |
322 | * | |
323 | * Returns negative on error. | |
324 | */ | |
24f7ac3d LT |
325 | static int cs8409_i2c_bulk_write(struct sub_codec *scodec, const struct cs8409_i2c_param *seq, |
326 | int count) | |
165b81c4 | 327 | { |
24f7ac3d | 328 | struct hda_codec *codec = scodec->codec; |
165b81c4 LT |
329 | struct cs8409_spec *spec = codec->spec; |
330 | unsigned int i2c_reg_data; | |
331 | int i; | |
332 | ||
24f7ac3d | 333 | if (scodec->suspended) |
165b81c4 LT |
334 | return -EPERM; |
335 | ||
336 | mutex_lock(&spec->i2c_mux); | |
24f7ac3d | 337 | cs8409_set_i2c_dev_addr(codec, scodec->addr); |
165b81c4 LT |
338 | |
339 | for (i = 0; i < count; i++) { | |
340 | cs8409_enable_i2c_clock(codec); | |
24f7ac3d | 341 | if (cs8409_i2c_set_page(scodec, seq[i].addr)) |
165b81c4 LT |
342 | goto error; |
343 | ||
344 | i2c_reg_data = ((seq[i].addr << 8) & 0x0ff00) | (seq[i].value & 0x0ff); | |
345 | cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data); | |
346 | ||
347 | if (cs8409_i2c_wait_complete(codec) < 0) | |
348 | goto error; | |
8c70461b LT |
349 | } |
350 | ||
165b81c4 LT |
351 | mutex_unlock(&spec->i2c_mux); |
352 | ||
8c70461b | 353 | return 0; |
165b81c4 LT |
354 | |
355 | error: | |
356 | mutex_unlock(&spec->i2c_mux); | |
24f7ac3d | 357 | codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr); |
165b81c4 | 358 | return -EIO; |
8c70461b LT |
359 | } |
360 | ||
636eb9d2 LT |
361 | static int cs8409_init(struct hda_codec *codec) |
362 | { | |
363 | int ret = snd_hda_gen_init(codec); | |
364 | ||
365 | if (!ret) | |
366 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); | |
367 | ||
368 | return ret; | |
369 | } | |
370 | ||
371 | static int cs8409_build_controls(struct hda_codec *codec) | |
372 | { | |
373 | int err; | |
374 | ||
375 | err = snd_hda_gen_build_controls(codec); | |
376 | if (err < 0) | |
377 | return err; | |
378 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); | |
379 | ||
380 | return 0; | |
381 | } | |
382 | ||
c076e201 | 383 | /* Enable/Disable Unsolicited Response */ |
636eb9d2 LT |
384 | static void cs8409_enable_ur(struct hda_codec *codec, int flag) |
385 | { | |
c076e201 SB |
386 | struct cs8409_spec *spec = codec->spec; |
387 | unsigned int ur_gpios = 0; | |
388 | int i; | |
389 | ||
390 | for (i = 0; i < spec->num_scodecs; i++) | |
391 | ur_gpios |= spec->scodecs[i]->irq_mask; | |
392 | ||
636eb9d2 | 393 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, |
c076e201 | 394 | flag ? ur_gpios : 0); |
636eb9d2 LT |
395 | |
396 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE, | |
397 | flag ? AC_UNSOL_ENABLED : 0); | |
636eb9d2 LT |
398 | } |
399 | ||
400 | static void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid) | |
401 | { | |
402 | int caps; | |
403 | ||
404 | /* CS8409 is simple HDA bridge and intended to be used with a remote | |
405 | * companion codec. Most of input/output PIN(s) have only basic | |
406 | * capabilities. Receive and Transmit NID(s) have only OUTC and INC | |
407 | * capabilities and no presence detect capable (PDC) and call to | |
408 | * snd_hda_gen_build_controls() will mark them as non detectable | |
409 | * phantom jacks. However, a companion codec may be | |
410 | * connected to these pins which supports jack detect | |
411 | * capabilities. We have to override pin capabilities, | |
412 | * otherwise they will not be created as input devices. | |
413 | */ | |
414 | caps = snd_hdac_read_parm(&codec->core, nid, AC_PAR_PIN_CAP); | |
415 | if (caps >= 0) | |
416 | snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, | |
417 | (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT))); | |
418 | ||
419 | snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP)); | |
420 | } | |
421 | ||
f129f26f SB |
422 | static int cs8409_spk_sw_gpio_get(struct snd_kcontrol *kcontrol, |
423 | struct snd_ctl_elem_value *ucontrol) | |
424 | { | |
425 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
426 | struct cs8409_spec *spec = codec->spec; | |
427 | ||
428 | ucontrol->value.integer.value[0] = !!(spec->gpio_data & spec->speaker_pdn_gpio); | |
429 | return 0; | |
430 | } | |
431 | ||
432 | static int cs8409_spk_sw_gpio_put(struct snd_kcontrol *kcontrol, | |
433 | struct snd_ctl_elem_value *ucontrol) | |
434 | { | |
435 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
436 | struct cs8409_spec *spec = codec->spec; | |
437 | unsigned int gpio_data; | |
438 | ||
439 | gpio_data = (spec->gpio_data & ~spec->speaker_pdn_gpio) | | |
440 | (ucontrol->value.integer.value[0] ? spec->speaker_pdn_gpio : 0); | |
441 | if (gpio_data == spec->gpio_data) | |
442 | return 0; | |
443 | spec->gpio_data = gpio_data; | |
444 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); | |
445 | return 1; | |
446 | } | |
447 | ||
448 | static const struct snd_kcontrol_new cs8409_spk_sw_ctrl = { | |
449 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
450 | .info = snd_ctl_boolean_mono_info, | |
451 | .get = cs8409_spk_sw_gpio_get, | |
452 | .put = cs8409_spk_sw_gpio_put, | |
453 | }; | |
454 | ||
636eb9d2 LT |
455 | /****************************************************************************** |
456 | * CS42L42 Specific Functions | |
457 | ******************************************************************************/ | |
458 | ||
459 | int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo) | |
8c70461b | 460 | { |
b2a88774 | 461 | unsigned int ofs = get_amp_offset(kctrl); |
8c70461b LT |
462 | u8 chs = get_amp_channels(kctrl); |
463 | ||
b2a88774 LT |
464 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
465 | uinfo->value.integer.step = 1; | |
466 | uinfo->count = chs == 3 ? 2 : 1; | |
467 | ||
468 | switch (ofs) { | |
469 | case CS42L42_VOL_DAC: | |
636eb9d2 LT |
470 | uinfo->value.integer.min = CS42L42_HP_VOL_REAL_MIN; |
471 | uinfo->value.integer.max = CS42L42_HP_VOL_REAL_MAX; | |
8c70461b | 472 | break; |
b2a88774 | 473 | case CS42L42_VOL_ADC: |
636eb9d2 LT |
474 | uinfo->value.integer.min = CS42L42_AMIC_VOL_REAL_MIN; |
475 | uinfo->value.integer.max = CS42L42_AMIC_VOL_REAL_MAX; | |
8c70461b LT |
476 | break; |
477 | default: | |
478 | break; | |
479 | } | |
8c70461b | 480 | |
b2a88774 | 481 | return 0; |
8c70461b LT |
482 | } |
483 | ||
636eb9d2 | 484 | int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl) |
8c70461b LT |
485 | { |
486 | struct hda_codec *codec = snd_kcontrol_chip(kctrl); | |
487 | struct cs8409_spec *spec = codec->spec; | |
24f7ac3d | 488 | struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)]; |
8c70461b | 489 | int chs = get_amp_channels(kctrl); |
b2a88774 | 490 | unsigned int ofs = get_amp_offset(kctrl); |
8c70461b LT |
491 | long *valp = uctrl->value.integer.value; |
492 | ||
b2a88774 LT |
493 | switch (ofs) { |
494 | case CS42L42_VOL_DAC: | |
8c70461b | 495 | if (chs & BIT(0)) |
24f7ac3d | 496 | *valp++ = cs42l42->vol[ofs]; |
8c70461b | 497 | if (chs & BIT(1)) |
24f7ac3d | 498 | *valp = cs42l42->vol[ofs+1]; |
8c70461b | 499 | break; |
b2a88774 | 500 | case CS42L42_VOL_ADC: |
8c70461b | 501 | if (chs & BIT(0)) |
24f7ac3d | 502 | *valp = cs42l42->vol[ofs]; |
8c70461b LT |
503 | break; |
504 | default: | |
505 | break; | |
506 | } | |
b2a88774 | 507 | |
8c70461b LT |
508 | return 0; |
509 | } | |
510 | ||
7482ec71 SB |
511 | static void cs42l42_mute(struct sub_codec *cs42l42, int vol_type, |
512 | unsigned int chs, bool mute) | |
513 | { | |
514 | if (mute) { | |
515 | if (vol_type == CS42L42_VOL_DAC) { | |
516 | if (chs & BIT(0)) | |
9cd82738 | 517 | cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, 0x3f); |
7482ec71 | 518 | if (chs & BIT(1)) |
9cd82738 | 519 | cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, 0x3f); |
7482ec71 SB |
520 | } else if (vol_type == CS42L42_VOL_ADC) { |
521 | if (chs & BIT(0)) | |
9cd82738 | 522 | cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, 0x9f); |
7482ec71 SB |
523 | } |
524 | } else { | |
525 | if (vol_type == CS42L42_VOL_DAC) { | |
526 | if (chs & BIT(0)) | |
9cd82738 | 527 | cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, |
7482ec71 | 528 | -(cs42l42->vol[CS42L42_DAC_CH0_VOL_OFFSET]) |
9cd82738 | 529 | & CS42L42_MIXER_CH_VOL_MASK); |
7482ec71 | 530 | if (chs & BIT(1)) |
9cd82738 | 531 | cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, |
7482ec71 | 532 | -(cs42l42->vol[CS42L42_DAC_CH1_VOL_OFFSET]) |
9cd82738 | 533 | & CS42L42_MIXER_CH_VOL_MASK); |
7482ec71 SB |
534 | } else if (vol_type == CS42L42_VOL_ADC) { |
535 | if (chs & BIT(0)) | |
9cd82738 | 536 | cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, |
7482ec71 SB |
537 | cs42l42->vol[CS42L42_ADC_VOL_OFFSET] |
538 | & CS42L42_REG_AMIC_VOL_MASK); | |
539 | } | |
540 | } | |
541 | } | |
542 | ||
636eb9d2 | 543 | int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl) |
8c70461b LT |
544 | { |
545 | struct hda_codec *codec = snd_kcontrol_chip(kctrl); | |
546 | struct cs8409_spec *spec = codec->spec; | |
24f7ac3d | 547 | struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)]; |
8c70461b | 548 | int chs = get_amp_channels(kctrl); |
b2a88774 | 549 | unsigned int ofs = get_amp_offset(kctrl); |
8c70461b | 550 | long *valp = uctrl->value.integer.value; |
8c70461b | 551 | |
b2a88774 LT |
552 | switch (ofs) { |
553 | case CS42L42_VOL_DAC: | |
7482ec71 | 554 | if (chs & BIT(0)) |
24f7ac3d | 555 | cs42l42->vol[ofs] = *valp; |
8c70461b | 556 | if (chs & BIT(1)) { |
b2a88774 | 557 | valp++; |
7482ec71 | 558 | cs42l42->vol[ofs + 1] = *valp; |
8c70461b | 559 | } |
7482ec71 SB |
560 | if (spec->playback_started) |
561 | cs42l42_mute(cs42l42, CS42L42_VOL_DAC, chs, false); | |
8c70461b | 562 | break; |
b2a88774 | 563 | case CS42L42_VOL_ADC: |
7482ec71 | 564 | if (chs & BIT(0)) |
24f7ac3d | 565 | cs42l42->vol[ofs] = *valp; |
7482ec71 SB |
566 | if (spec->capture_started) |
567 | cs42l42_mute(cs42l42, CS42L42_VOL_ADC, chs, false); | |
8c70461b LT |
568 | break; |
569 | default: | |
570 | break; | |
571 | } | |
8c70461b | 572 | |
b2a88774 LT |
573 | return 0; |
574 | } | |
8c70461b | 575 | |
7482ec71 SB |
576 | static void cs42l42_playback_pcm_hook(struct hda_pcm_stream *hinfo, |
577 | struct hda_codec *codec, | |
578 | struct snd_pcm_substream *substream, | |
579 | int action) | |
580 | { | |
581 | struct cs8409_spec *spec = codec->spec; | |
582 | struct sub_codec *cs42l42; | |
583 | int i; | |
584 | bool mute; | |
585 | ||
586 | switch (action) { | |
587 | case HDA_GEN_PCM_ACT_PREPARE: | |
588 | mute = false; | |
589 | spec->playback_started = 1; | |
590 | break; | |
591 | case HDA_GEN_PCM_ACT_CLEANUP: | |
592 | mute = true; | |
593 | spec->playback_started = 0; | |
594 | break; | |
595 | default: | |
596 | return; | |
597 | } | |
598 | ||
599 | for (i = 0; i < spec->num_scodecs; i++) { | |
600 | cs42l42 = spec->scodecs[i]; | |
601 | cs42l42_mute(cs42l42, CS42L42_VOL_DAC, 0x3, mute); | |
602 | } | |
603 | } | |
604 | ||
605 | static void cs42l42_capture_pcm_hook(struct hda_pcm_stream *hinfo, | |
606 | struct hda_codec *codec, | |
607 | struct snd_pcm_substream *substream, | |
608 | int action) | |
609 | { | |
610 | struct cs8409_spec *spec = codec->spec; | |
611 | struct sub_codec *cs42l42; | |
612 | int i; | |
613 | bool mute; | |
614 | ||
615 | switch (action) { | |
616 | case HDA_GEN_PCM_ACT_PREPARE: | |
617 | mute = false; | |
618 | spec->capture_started = 1; | |
619 | break; | |
620 | case HDA_GEN_PCM_ACT_CLEANUP: | |
621 | mute = true; | |
622 | spec->capture_started = 0; | |
623 | break; | |
624 | default: | |
625 | return; | |
626 | } | |
627 | ||
628 | for (i = 0; i < spec->num_scodecs; i++) { | |
629 | cs42l42 = spec->scodecs[i]; | |
630 | cs42l42_mute(cs42l42, CS42L42_VOL_ADC, 0x3, mute); | |
631 | } | |
632 | } | |
633 | ||
8c70461b | 634 | /* Configure CS42L42 slave codec for jack autodetect */ |
24f7ac3d | 635 | static void cs42l42_enable_jack_detect(struct sub_codec *cs42l42) |
8c70461b | 636 | { |
9cd82738 | 637 | cs8409_i2c_write(cs42l42, CS42L42_HSBIAS_SC_AUTOCTL, cs42l42->hsbias_hiz); |
8c70461b | 638 | /* Clear WAKE# */ |
9cd82738 | 639 | cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C1); |
8c70461b LT |
640 | /* Wait ~2.5ms */ |
641 | usleep_range(2500, 3000); | |
642 | /* Set mode WAKE# output follows the combination logic directly */ | |
9cd82738 | 643 | cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C0); |
8c70461b | 644 | /* Clear interrupts status */ |
9cd82738 | 645 | cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); |
8c70461b | 646 | /* Enable interrupt */ |
9cd82738 | 647 | cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3); |
8c70461b LT |
648 | } |
649 | ||
650 | /* Enable and run CS42L42 slave codec jack auto detect */ | |
24f7ac3d | 651 | static void cs42l42_run_jack_detect(struct sub_codec *cs42l42) |
8c70461b | 652 | { |
8c70461b | 653 | /* Clear interrupts */ |
9cd82738 SB |
654 | cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS); |
655 | cs8409_i2c_read(cs42l42, CS42L42_DET_STATUS1); | |
656 | cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xFF); | |
657 | cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); | |
658 | ||
659 | cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x87); | |
660 | cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x86); | |
661 | cs8409_i2c_write(cs42l42, CS42L42_MISC_DET_CTL, 0x07); | |
662 | cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFD); | |
663 | cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80); | |
8cd07657 CE |
664 | /* Wait ~20ms*/ |
665 | usleep_range(20000, 25000); | |
9cd82738 SB |
666 | cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, 0x77); |
667 | cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0xc0); | |
8c70461b LT |
668 | } |
669 | ||
ec6a8aaa | 670 | static int cs42l42_manual_hs_det(struct sub_codec *cs42l42) |
404e770a | 671 | { |
ec6a8aaa SB |
672 | unsigned int hs_det_status; |
673 | unsigned int hs_det_comp1; | |
674 | unsigned int hs_det_comp2; | |
675 | unsigned int hs_det_sw; | |
676 | unsigned int hs_type; | |
677 | ||
678 | /* Set hs detect to manual, active mode */ | |
679 | cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, | |
680 | (1 << CS42L42_HSDET_CTRL_SHIFT) | | |
681 | (0 << CS42L42_HSDET_SET_SHIFT) | | |
682 | (0 << CS42L42_HSBIAS_REF_SHIFT) | | |
683 | (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); | |
684 | ||
685 | /* Configure HS DET comparator reference levels. */ | |
686 | cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, | |
687 | (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) | | |
688 | (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT)); | |
689 | ||
690 | /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */ | |
691 | cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1); | |
692 | ||
693 | msleep(100); | |
694 | ||
695 | hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); | |
696 | ||
697 | hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> | |
698 | CS42L42_HSDET_COMP1_OUT_SHIFT; | |
699 | hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> | |
700 | CS42L42_HSDET_COMP2_OUT_SHIFT; | |
701 | ||
702 | /* Close the SW_HSB_HS3 switch for a Type 2 headset. */ | |
703 | cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2); | |
704 | ||
705 | msleep(100); | |
706 | ||
707 | hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); | |
708 | ||
709 | hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> | |
710 | CS42L42_HSDET_COMP1_OUT_SHIFT) << 1; | |
711 | hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> | |
712 | CS42L42_HSDET_COMP2_OUT_SHIFT) << 1; | |
713 | ||
714 | /* Use Comparator 1 with 1.25V Threshold. */ | |
715 | switch (hs_det_comp1) { | |
716 | case CS42L42_HSDET_COMP_TYPE1: | |
717 | hs_type = CS42L42_PLUG_CTIA; | |
718 | hs_det_sw = CS42L42_HSDET_SW_TYPE1; | |
719 | break; | |
720 | case CS42L42_HSDET_COMP_TYPE2: | |
721 | hs_type = CS42L42_PLUG_OMTP; | |
722 | hs_det_sw = CS42L42_HSDET_SW_TYPE2; | |
723 | break; | |
724 | default: | |
725 | /* Fallback to Comparator 2 with 1.75V Threshold. */ | |
726 | switch (hs_det_comp2) { | |
727 | case CS42L42_HSDET_COMP_TYPE1: | |
728 | hs_type = CS42L42_PLUG_CTIA; | |
729 | hs_det_sw = CS42L42_HSDET_SW_TYPE1; | |
730 | break; | |
731 | case CS42L42_HSDET_COMP_TYPE2: | |
732 | hs_type = CS42L42_PLUG_OMTP; | |
733 | hs_det_sw = CS42L42_HSDET_SW_TYPE2; | |
734 | break; | |
735 | case CS42L42_HSDET_COMP_TYPE3: | |
736 | hs_type = CS42L42_PLUG_HEADPHONE; | |
737 | hs_det_sw = CS42L42_HSDET_SW_TYPE3; | |
738 | break; | |
739 | default: | |
740 | hs_type = CS42L42_PLUG_INVALID; | |
741 | hs_det_sw = CS42L42_HSDET_SW_TYPE4; | |
742 | break; | |
743 | } | |
744 | } | |
57f23424 | 745 | |
ec6a8aaa SB |
746 | /* Set Switches */ |
747 | cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, hs_det_sw); | |
748 | ||
749 | /* Set HSDET mode to Manual—Disabled */ | |
750 | cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, | |
751 | (0 << CS42L42_HSDET_CTRL_SHIFT) | | |
752 | (0 << CS42L42_HSDET_SET_SHIFT) | | |
753 | (0 << CS42L42_HSBIAS_REF_SHIFT) | | |
754 | (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); | |
755 | ||
756 | /* Configure HS DET comparator reference levels. */ | |
757 | cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, | |
758 | (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) | | |
759 | (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT)); | |
760 | ||
761 | return hs_type; | |
762 | } | |
763 | ||
764 | static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status) | |
765 | { | |
766 | int status_changed = 0; | |
404e770a SB |
767 | |
768 | /* TIP_SENSE INSERT/REMOVE */ | |
769 | switch (reg_ts_status) { | |
9cd82738 | 770 | case CS42L42_TS_PLUG: |
ec6a8aaa SB |
771 | if (cs42l42->no_type_dect) { |
772 | status_changed = 1; | |
773 | cs42l42->hp_jack_in = 1; | |
774 | cs42l42->mic_jack_in = 0; | |
775 | } else { | |
776 | cs42l42_run_jack_detect(cs42l42); | |
404e770a SB |
777 | } |
778 | break; | |
779 | ||
9cd82738 | 780 | case CS42L42_TS_UNPLUG: |
ec6a8aaa SB |
781 | status_changed = 1; |
782 | cs42l42->hp_jack_in = 0; | |
783 | cs42l42->mic_jack_in = 0; | |
404e770a SB |
784 | break; |
785 | default: | |
786 | /* jack in transition */ | |
787 | break; | |
788 | } | |
789 | ||
ec6a8aaa SB |
790 | codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status); |
791 | ||
404e770a SB |
792 | return status_changed; |
793 | } | |
794 | ||
24f7ac3d | 795 | static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42) |
8c70461b | 796 | { |
9cd82738 | 797 | int current_plug_status; |
8c70461b LT |
798 | int status_changed = 0; |
799 | int reg_cdc_status; | |
800 | int reg_hs_status; | |
801 | int reg_ts_status; | |
802 | int type; | |
8c70461b | 803 | |
8c70461b | 804 | /* Read jack detect status registers */ |
9cd82738 SB |
805 | reg_cdc_status = cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS); |
806 | reg_hs_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); | |
807 | reg_ts_status = cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); | |
8c70461b | 808 | |
8c70461b LT |
809 | /* If status values are < 0, read error has occurred. */ |
810 | if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0) | |
636eb9d2 | 811 | return -EIO; |
8c70461b | 812 | |
9cd82738 SB |
813 | current_plug_status = (reg_ts_status & (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) |
814 | >> CS42L42_TS_PLUG_SHIFT; | |
815 | ||
8c70461b | 816 | /* HSDET_AUTO_DONE */ |
9cd82738 | 817 | if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE_MASK) { |
8c70461b | 818 | |
db0ae848 | 819 | /* Disable HSDET_AUTO_DONE */ |
9cd82738 | 820 | cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFF); |
db0ae848 | 821 | |
9cd82738 | 822 | type = (reg_hs_status & CS42L42_HSDET_TYPE_MASK) >> CS42L42_HSDET_TYPE_SHIFT; |
404e770a | 823 | |
ec6a8aaa SB |
824 | /* Configure the HSDET mode. */ |
825 | cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80); | |
826 | ||
404e770a | 827 | if (cs42l42->no_type_dect) { |
9cd82738 | 828 | status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status); |
404e770a | 829 | } else { |
ec6a8aaa SB |
830 | if (type == CS42L42_PLUG_INVALID || type == CS42L42_PLUG_HEADPHONE) { |
831 | codec_dbg(cs42l42->codec, | |
832 | "Auto detect value not valid (%d), running manual det\n", | |
833 | type); | |
834 | type = cs42l42_manual_hs_det(cs42l42); | |
8c70461b | 835 | } |
ec6a8aaa SB |
836 | |
837 | switch (type) { | |
838 | case CS42L42_PLUG_CTIA: | |
839 | case CS42L42_PLUG_OMTP: | |
8c70461b | 840 | status_changed = 1; |
ec6a8aaa | 841 | cs42l42->hp_jack_in = 1; |
24f7ac3d | 842 | cs42l42->mic_jack_in = 1; |
ec6a8aaa SB |
843 | break; |
844 | case CS42L42_PLUG_HEADPHONE: | |
845 | status_changed = 1; | |
846 | cs42l42->hp_jack_in = 1; | |
847 | cs42l42->mic_jack_in = 0; | |
848 | break; | |
849 | default: | |
850 | status_changed = 1; | |
851 | cs42l42->hp_jack_in = 0; | |
852 | cs42l42->mic_jack_in = 0; | |
853 | break; | |
8c70461b | 854 | } |
ec6a8aaa | 855 | codec_dbg(cs42l42->codec, "Detection done (%d)\n", type); |
8c70461b | 856 | } |
ec6a8aaa | 857 | |
1a048301 | 858 | /* Enable the HPOUT ground clamp and configure the HP pull-down */ |
9cd82738 | 859 | cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x02); |
db0ae848 | 860 | /* Re-Enable Tip Sense Interrupt */ |
9cd82738 | 861 | cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3); |
8c70461b | 862 | } else { |
9cd82738 | 863 | status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status); |
8c70461b LT |
864 | } |
865 | ||
636eb9d2 LT |
866 | return status_changed; |
867 | } | |
868 | ||
24f7ac3d | 869 | static void cs42l42_resume(struct sub_codec *cs42l42) |
636eb9d2 | 870 | { |
c076e201 | 871 | struct hda_codec *codec = cs42l42->codec; |
f129f26f | 872 | struct cs8409_spec *spec = codec->spec; |
636eb9d2 | 873 | struct cs8409_i2c_param irq_regs[] = { |
9cd82738 SB |
874 | { CS42L42_CODEC_STATUS, 0x00 }, |
875 | { CS42L42_DET_INT_STATUS1, 0x00 }, | |
876 | { CS42L42_DET_INT_STATUS2, 0x00 }, | |
877 | { CS42L42_TSRS_PLUG_STATUS, 0x00 }, | |
636eb9d2 | 878 | }; |
342b6b61 | 879 | int fsv_old, fsv_new; |
636eb9d2 | 880 | |
c076e201 | 881 | /* Bring CS42L42 out of Reset */ |
f129f26f SB |
882 | spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); |
883 | spec->gpio_data |= cs42l42->reset_gpio; | |
884 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); | |
c076e201 SB |
885 | usleep_range(10000, 15000); |
886 | ||
24f7ac3d | 887 | cs42l42->suspended = 0; |
636eb9d2 | 888 | |
24f7ac3d LT |
889 | /* Initialize CS42L42 companion codec */ |
890 | cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num); | |
99bf5b0b | 891 | msleep(CS42L42_INIT_TIMEOUT_MS); |
636eb9d2 LT |
892 | |
893 | /* Clear interrupts, by reading interrupt status registers */ | |
24f7ac3d LT |
894 | cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs)); |
895 | ||
9cd82738 | 896 | fsv_old = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL); |
342b6b61 SB |
897 | if (cs42l42->full_scale_vol == CS42L42_FULL_SCALE_VOL_0DB) |
898 | fsv_new = fsv_old & ~CS42L42_FULL_SCALE_VOL_MASK; | |
899 | else | |
900 | fsv_new = fsv_old & CS42L42_FULL_SCALE_VOL_MASK; | |
901 | if (fsv_new != fsv_old) | |
9cd82738 | 902 | cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv_new); |
24f7ac3d | 903 | |
65cc4ad6 SB |
904 | /* we have to explicitly allow unsol event handling even during the |
905 | * resume phase so that the jack event is processed properly | |
906 | */ | |
907 | snd_hda_codec_allow_unsol_events(cs42l42->codec); | |
908 | ||
24f7ac3d | 909 | cs42l42_enable_jack_detect(cs42l42); |
636eb9d2 LT |
910 | } |
911 | ||
24f7ac3d | 912 | static void cs42l42_suspend(struct sub_codec *cs42l42) |
636eb9d2 | 913 | { |
c076e201 | 914 | struct hda_codec *codec = cs42l42->codec; |
f129f26f | 915 | struct cs8409_spec *spec = codec->spec; |
4ff2ae3a SB |
916 | int reg_cdc_status = 0; |
917 | const struct cs8409_i2c_param cs42l42_pwr_down_seq[] = { | |
9cd82738 SB |
918 | { CS42L42_DAC_CTL2, 0x02 }, |
919 | { CS42L42_HS_CLAMP_DISABLE, 0x00 }, | |
920 | { CS42L42_MIXER_CHA_VOL, 0x3F }, | |
921 | { CS42L42_MIXER_ADC_VOL, 0x3F }, | |
922 | { CS42L42_MIXER_CHB_VOL, 0x3F }, | |
923 | { CS42L42_HP_CTL, 0x0F }, | |
924 | { CS42L42_ASP_RX_DAI0_EN, 0x00 }, | |
925 | { CS42L42_ASP_CLK_CFG, 0x00 }, | |
926 | { CS42L42_PWR_CTL1, 0xFE }, | |
927 | { CS42L42_PWR_CTL2, 0x8C }, | |
928 | { CS42L42_PWR_CTL1, 0xFF }, | |
4ff2ae3a SB |
929 | }; |
930 | ||
931 | cs8409_i2c_bulk_write(cs42l42, cs42l42_pwr_down_seq, ARRAY_SIZE(cs42l42_pwr_down_seq)); | |
932 | ||
933 | if (read_poll_timeout(cs8409_i2c_read, reg_cdc_status, | |
934 | (reg_cdc_status & 0x1), CS42L42_PDN_SLEEP_US, CS42L42_PDN_TIMEOUT_US, | |
9cd82738 | 935 | true, cs42l42, CS42L42_CODEC_STATUS) < 0) |
4ff2ae3a | 936 | codec_warn(codec, "Timeout waiting for PDN_DONE for CS42L42\n"); |
c076e201 | 937 | |
636eb9d2 | 938 | /* Power down CS42L42 ASP/EQ/MIX/HP */ |
9cd82738 | 939 | cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x9C); |
24f7ac3d LT |
940 | cs42l42->suspended = 1; |
941 | cs42l42->last_page = 0; | |
424e531b SB |
942 | cs42l42->hp_jack_in = 0; |
943 | cs42l42->mic_jack_in = 0; | |
c076e201 SB |
944 | |
945 | /* Put CS42L42 into Reset */ | |
f129f26f SB |
946 | spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); |
947 | spec->gpio_data &= ~cs42l42->reset_gpio; | |
948 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); | |
636eb9d2 | 949 | } |
636eb9d2 LT |
950 | |
951 | static void cs8409_free(struct hda_codec *codec) | |
952 | { | |
953 | struct cs8409_spec *spec = codec->spec; | |
954 | ||
955 | /* Cancel i2c clock disable timer, and disable clock if left enabled */ | |
956 | cancel_delayed_work_sync(&spec->i2c_clk_work); | |
957 | cs8409_disable_i2c_clock(codec); | |
958 | ||
959 | snd_hda_gen_free(codec); | |
960 | } | |
961 | ||
962 | /****************************************************************************** | |
963 | * BULLSEYE / WARLOCK / CYBORG Specific Functions | |
964 | * CS8409/CS42L42 | |
965 | ******************************************************************************/ | |
966 | ||
967 | /* | |
968 | * In the case of CS8409 we do not have unsolicited events from NID's 0x24 | |
969 | * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will | |
970 | * generate interrupt via gpio 4 to notify jack events. We have to overwrite | |
971 | * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers | |
972 | * and then notify status via generic snd_hda_jack_unsol_event() call. | |
973 | */ | |
24f7ac3d | 974 | static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned int res) |
636eb9d2 LT |
975 | { |
976 | struct cs8409_spec *spec = codec->spec; | |
24f7ac3d | 977 | struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; |
636eb9d2 LT |
978 | struct hda_jack_tbl *jk; |
979 | ||
980 | /* jack_unsol_event() will be called every time gpio line changing state. | |
981 | * In this case gpio4 line goes up as a result of reading interrupt status | |
982 | * registers in previous cs8409_jack_unsol_event() call. | |
983 | * We don't need to handle this event, ignoring... | |
984 | */ | |
24f7ac3d | 985 | if (res & cs42l42->irq_mask) |
636eb9d2 | 986 | return; |
8c70461b | 987 | |
24f7ac3d | 988 | if (cs42l42_jack_unsol_event(cs42l42)) { |
8c70461b | 989 | snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID, |
24f7ac3d | 990 | cs42l42->hp_jack_in ? 0 : PIN_OUT); |
8c70461b LT |
991 | /* Report jack*/ |
992 | jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0); | |
636eb9d2 | 993 | if (jk) |
8c70461b LT |
994 | snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & |
995 | AC_UNSOL_RES_TAG); | |
8c70461b LT |
996 | /* Report jack*/ |
997 | jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0); | |
636eb9d2 | 998 | if (jk) |
8c70461b LT |
999 | snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & |
1000 | AC_UNSOL_RES_TAG); | |
8c70461b LT |
1001 | } |
1002 | } | |
1003 | ||
8c70461b | 1004 | /* Manage PDREF, when transition to D3hot */ |
636eb9d2 | 1005 | static int cs8409_cs42l42_suspend(struct hda_codec *codec) |
8c70461b LT |
1006 | { |
1007 | struct cs8409_spec *spec = codec->spec; | |
c076e201 | 1008 | int i; |
8c70461b | 1009 | |
424e531b SB |
1010 | spec->init_done = 0; |
1011 | ||
cc7df162 SB |
1012 | cs8409_enable_ur(codec, 0); |
1013 | ||
c076e201 SB |
1014 | for (i = 0; i < spec->num_scodecs; i++) |
1015 | cs42l42_suspend(spec->scodecs[i]); | |
8c70461b | 1016 | |
647d50a0 LT |
1017 | /* Cancel i2c clock disable timer, and disable clock if left enabled */ |
1018 | cancel_delayed_work_sync(&spec->i2c_clk_work); | |
1019 | cs8409_disable_i2c_clock(codec); | |
1020 | ||
8c70461b LT |
1021 | snd_hda_shutup_pins(codec); |
1022 | ||
1023 | return 0; | |
1024 | } | |
8c70461b | 1025 | |
8c70461b LT |
1026 | /* Vendor specific HW configuration |
1027 | * PLL, ASP, I2C, SPI, GPIOs, DMIC etc... | |
1028 | */ | |
1029 | static void cs8409_cs42l42_hw_init(struct hda_codec *codec) | |
1030 | { | |
1031 | const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg; | |
1032 | const struct cs8409_cir_param *seq_bullseye = cs8409_cs42l42_bullseye_atn; | |
1033 | struct cs8409_spec *spec = codec->spec; | |
24f7ac3d | 1034 | struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; |
8c70461b LT |
1035 | |
1036 | if (spec->gpio_mask) { | |
ccff0064 SB |
1037 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, |
1038 | spec->gpio_mask); | |
1039 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, | |
1040 | spec->gpio_dir); | |
1041 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, | |
1042 | spec->gpio_data); | |
8c70461b LT |
1043 | } |
1044 | ||
1045 | for (; seq->nid; seq++) | |
1046 | cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); | |
1047 | ||
24f7ac3d | 1048 | if (codec->fixup_id == CS8409_BULLSEYE) { |
8c70461b LT |
1049 | for (; seq_bullseye->nid; seq_bullseye++) |
1050 | cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff); | |
24f7ac3d | 1051 | } |
8c70461b | 1052 | |
6581a045 SB |
1053 | switch (codec->fixup_id) { |
1054 | case CS8409_CYBORG: | |
1055 | case CS8409_WARLOCK_MLK_DUAL_MIC: | |
1056 | /* DMIC1_MO=00b, DMIC1/2_SR=1 */ | |
8a772453 | 1057 | cs8409_vendor_coef_set(codec, CS8409_DMIC_CFG, 0x0003); |
6581a045 | 1058 | break; |
22bb8226 SB |
1059 | case CS8409_ODIN: |
1060 | /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=0 */ | |
1061 | cs8409_vendor_coef_set(codec, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc00); | |
1062 | break; | |
6581a045 SB |
1063 | default: |
1064 | break; | |
1065 | } | |
8c70461b | 1066 | |
24f7ac3d | 1067 | cs42l42_resume(cs42l42); |
8c70461b LT |
1068 | |
1069 | /* Enable Unsolicited Response */ | |
1070 | cs8409_enable_ur(codec, 1); | |
1071 | } | |
1072 | ||
8c70461b LT |
1073 | static const struct hda_codec_ops cs8409_cs42l42_patch_ops = { |
1074 | .build_controls = cs8409_build_controls, | |
1075 | .build_pcms = snd_hda_gen_build_pcms, | |
636eb9d2 | 1076 | .init = cs8409_init, |
647d50a0 | 1077 | .free = cs8409_free, |
24f7ac3d | 1078 | .unsol_event = cs8409_cs42l42_jack_unsol_event, |
636eb9d2 | 1079 | .suspend = cs8409_cs42l42_suspend, |
8c70461b LT |
1080 | }; |
1081 | ||
1082 | static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, | |
1083 | unsigned int *res) | |
1084 | { | |
1085 | struct hda_codec *codec = container_of(dev, struct hda_codec, core); | |
1086 | struct cs8409_spec *spec = codec->spec; | |
24f7ac3d | 1087 | struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; |
8c70461b LT |
1088 | |
1089 | unsigned int nid = ((cmd >> 20) & 0x07f); | |
1090 | unsigned int verb = ((cmd >> 8) & 0x0fff); | |
1091 | ||
1092 | /* CS8409 pins have no AC_PINSENSE_PRESENCE | |
1093 | * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34 | |
1094 | * and return correct pin sense values for read_pin_sense() call from | |
1095 | * hda_jack based on CS42L42 jack detect status. | |
1096 | */ | |
1097 | switch (nid) { | |
1098 | case CS8409_CS42L42_HP_PIN_NID: | |
1099 | if (verb == AC_VERB_GET_PIN_SENSE) { | |
24f7ac3d | 1100 | *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; |
8c70461b LT |
1101 | return 0; |
1102 | } | |
1103 | break; | |
8c70461b LT |
1104 | case CS8409_CS42L42_AMIC_PIN_NID: |
1105 | if (verb == AC_VERB_GET_PIN_SENSE) { | |
24f7ac3d | 1106 | *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; |
8c70461b LT |
1107 | return 0; |
1108 | } | |
1109 | break; | |
8c70461b LT |
1110 | default: |
1111 | break; | |
1112 | } | |
1113 | ||
1114 | return spec->exec_verb(dev, cmd, flags, res); | |
1115 | } | |
1116 | ||
9e7647b5 | 1117 | void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action) |
8c70461b LT |
1118 | { |
1119 | struct cs8409_spec *spec = codec->spec; | |
8c70461b LT |
1120 | |
1121 | switch (action) { | |
1122 | case HDA_FIXUP_ACT_PRE_PROBE: | |
1123 | snd_hda_add_verbs(codec, cs8409_cs42l42_init_verbs); | |
1124 | /* verb exec op override */ | |
1125 | spec->exec_verb = codec->core.exec_verb; | |
1126 | codec->core.exec_verb = cs8409_cs42l42_exec_verb; | |
1127 | ||
24f7ac3d LT |
1128 | spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l42_codec; |
1129 | spec->num_scodecs = 1; | |
1130 | spec->scodecs[CS8409_CODEC0]->codec = codec; | |
8c70461b LT |
1131 | codec->patch_ops = cs8409_cs42l42_patch_ops; |
1132 | ||
1133 | spec->gen.suppress_auto_mute = 1; | |
1134 | spec->gen.no_primary_hp = 1; | |
1135 | spec->gen.suppress_vmaster = 1; | |
1136 | ||
f129f26f SB |
1137 | spec->speaker_pdn_gpio = 0; |
1138 | ||
8c70461b | 1139 | /* GPIO 5 out, 3,4 in */ |
24f7ac3d | 1140 | spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio; |
8c70461b LT |
1141 | spec->gpio_data = 0; |
1142 | spec->gpio_mask = 0x03f; | |
1143 | ||
8c70461b LT |
1144 | /* Basic initial sequence for specific hw configuration */ |
1145 | snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs); | |
1146 | ||
636eb9d2 LT |
1147 | cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID); |
1148 | cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID); | |
1149 | ||
f129f26f SB |
1150 | spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020; |
1151 | ||
24f7ac3d | 1152 | switch (codec->fixup_id) { |
f129f26f SB |
1153 | case CS8409_CYBORG: |
1154 | spec->scodecs[CS8409_CODEC0]->full_scale_vol = | |
1155 | CS42L42_FULL_SCALE_VOL_MINUS6DB; | |
1156 | spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN; | |
1157 | break; | |
22bb8226 | 1158 | case CS8409_ODIN: |
f129f26f SB |
1159 | spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB; |
1160 | spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN; | |
1161 | break; | |
6581a045 SB |
1162 | case CS8409_WARLOCK_MLK: |
1163 | case CS8409_WARLOCK_MLK_DUAL_MIC: | |
6581a045 | 1164 | spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB; |
6e7cf670 | 1165 | spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN; |
6581a045 | 1166 | break; |
24f7ac3d | 1167 | default: |
342b6b61 SB |
1168 | spec->scodecs[CS8409_CODEC0]->full_scale_vol = |
1169 | CS42L42_FULL_SCALE_VOL_MINUS6DB; | |
6e7cf670 | 1170 | spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN; |
24f7ac3d LT |
1171 | break; |
1172 | } | |
1173 | ||
f129f26f SB |
1174 | if (spec->speaker_pdn_gpio > 0) { |
1175 | spec->gpio_dir |= spec->speaker_pdn_gpio; | |
1176 | spec->gpio_data |= spec->speaker_pdn_gpio; | |
1177 | } | |
1178 | ||
8c70461b LT |
1179 | break; |
1180 | case HDA_FIXUP_ACT_PROBE: | |
fed0aaca SB |
1181 | /* Fix Sample Rate to 48kHz */ |
1182 | spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; | |
1183 | spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture; | |
7482ec71 SB |
1184 | /* add hooks */ |
1185 | spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook; | |
1186 | spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook; | |
22bb8226 SB |
1187 | if (codec->fixup_id != CS8409_ODIN) |
1188 | /* Set initial DMIC volume to -26 dB */ | |
1189 | snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID, | |
1190 | HDA_INPUT, 0, 0xff, 0x19); | |
b2a88774 LT |
1191 | snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume", |
1192 | &cs42l42_dac_volume_mixer); | |
1193 | snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", | |
1194 | &cs42l42_adc_volume_mixer); | |
f129f26f SB |
1195 | if (spec->speaker_pdn_gpio > 0) |
1196 | snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch", | |
1197 | &cs8409_spk_sw_ctrl); | |
134ae782 LT |
1198 | /* Disable Unsolicited Response during boot */ |
1199 | cs8409_enable_ur(codec, 0); | |
8c70461b LT |
1200 | snd_hda_codec_set_name(codec, "CS8409/CS42L42"); |
1201 | break; | |
1202 | case HDA_FIXUP_ACT_INIT: | |
1203 | cs8409_cs42l42_hw_init(codec); | |
424e531b SB |
1204 | spec->init_done = 1; |
1205 | if (spec->init_done && spec->build_ctrl_done | |
1206 | && !spec->scodecs[CS8409_CODEC0]->hp_jack_in) | |
1207 | cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]); | |
1208 | break; | |
8c70461b | 1209 | case HDA_FIXUP_ACT_BUILD: |
424e531b | 1210 | spec->build_ctrl_done = 1; |
8c70461b LT |
1211 | /* Run jack auto detect first time on boot |
1212 | * after controls have been added, to check if jack has | |
1213 | * been already plugged in. | |
1214 | * Run immediately after init. | |
1215 | */ | |
424e531b SB |
1216 | if (spec->init_done && spec->build_ctrl_done |
1217 | && !spec->scodecs[CS8409_CODEC0]->hp_jack_in) | |
1218 | cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]); | |
8c70461b LT |
1219 | break; |
1220 | default: | |
1221 | break; | |
1222 | } | |
1223 | } | |
1224 | ||
20e50772 LT |
1225 | /****************************************************************************** |
1226 | * Dolphin Specific Functions | |
1227 | * CS8409/ 2 X CS42L42 | |
1228 | ******************************************************************************/ | |
1229 | ||
1230 | /* | |
1231 | * In the case of CS8409 we do not have unsolicited events when | |
1232 | * hs mic and hp are connected. Companion codec CS42L42 will | |
1233 | * generate interrupt via irq_mask to notify jack events. We have to overwrite | |
1234 | * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers | |
1235 | * and then notify status via generic snd_hda_jack_unsol_event() call. | |
1236 | */ | |
1237 | static void dolphin_jack_unsol_event(struct hda_codec *codec, unsigned int res) | |
1238 | { | |
1239 | struct cs8409_spec *spec = codec->spec; | |
1240 | struct sub_codec *cs42l42; | |
1241 | struct hda_jack_tbl *jk; | |
1242 | ||
1243 | cs42l42 = spec->scodecs[CS8409_CODEC0]; | |
1244 | if (!cs42l42->suspended && (~res & cs42l42->irq_mask) && | |
1245 | cs42l42_jack_unsol_event(cs42l42)) { | |
1246 | jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_HP_PIN_NID, 0); | |
1247 | if (jk) | |
1248 | snd_hda_jack_unsol_event(codec, | |
1249 | (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & | |
1250 | AC_UNSOL_RES_TAG); | |
1251 | ||
1252 | jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_AMIC_PIN_NID, 0); | |
1253 | if (jk) | |
1254 | snd_hda_jack_unsol_event(codec, | |
1255 | (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & | |
1256 | AC_UNSOL_RES_TAG); | |
1257 | } | |
1258 | ||
1259 | cs42l42 = spec->scodecs[CS8409_CODEC1]; | |
1260 | if (!cs42l42->suspended && (~res & cs42l42->irq_mask) && | |
1261 | cs42l42_jack_unsol_event(cs42l42)) { | |
1262 | jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_LO_PIN_NID, 0); | |
1263 | if (jk) | |
1264 | snd_hda_jack_unsol_event(codec, | |
1265 | (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & | |
1266 | AC_UNSOL_RES_TAG); | |
1267 | } | |
1268 | } | |
1269 | ||
1270 | /* Vendor specific HW configuration | |
1271 | * PLL, ASP, I2C, SPI, GPIOs, DMIC etc... | |
1272 | */ | |
1273 | static void dolphin_hw_init(struct hda_codec *codec) | |
1274 | { | |
1275 | const struct cs8409_cir_param *seq = dolphin_hw_cfg; | |
1276 | struct cs8409_spec *spec = codec->spec; | |
1277 | struct sub_codec *cs42l42; | |
1278 | int i; | |
1279 | ||
1280 | if (spec->gpio_mask) { | |
1281 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, | |
1282 | spec->gpio_mask); | |
1283 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, | |
1284 | spec->gpio_dir); | |
1285 | snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, | |
1286 | spec->gpio_data); | |
1287 | } | |
1288 | ||
1289 | for (; seq->nid; seq++) | |
1290 | cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); | |
1291 | ||
1292 | for (i = 0; i < spec->num_scodecs; i++) { | |
1293 | cs42l42 = spec->scodecs[i]; | |
1294 | cs42l42_resume(cs42l42); | |
1295 | } | |
1296 | ||
1297 | /* Enable Unsolicited Response */ | |
1298 | cs8409_enable_ur(codec, 1); | |
1299 | } | |
1300 | ||
1301 | static const struct hda_codec_ops cs8409_dolphin_patch_ops = { | |
1302 | .build_controls = cs8409_build_controls, | |
1303 | .build_pcms = snd_hda_gen_build_pcms, | |
1304 | .init = cs8409_init, | |
1305 | .free = cs8409_free, | |
1306 | .unsol_event = dolphin_jack_unsol_event, | |
20e50772 | 1307 | .suspend = cs8409_cs42l42_suspend, |
20e50772 LT |
1308 | }; |
1309 | ||
1310 | static int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, | |
1311 | unsigned int *res) | |
1312 | { | |
1313 | struct hda_codec *codec = container_of(dev, struct hda_codec, core); | |
1314 | struct cs8409_spec *spec = codec->spec; | |
1315 | struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; | |
1316 | ||
1317 | unsigned int nid = ((cmd >> 20) & 0x07f); | |
1318 | unsigned int verb = ((cmd >> 8) & 0x0fff); | |
1319 | ||
1320 | /* CS8409 pins have no AC_PINSENSE_PRESENCE | |
1321 | * capabilities. We have to intercept calls for CS42L42 pins | |
1322 | * and return correct pin sense values for read_pin_sense() call from | |
1323 | * hda_jack based on CS42L42 jack detect status. | |
1324 | */ | |
1325 | switch (nid) { | |
1326 | case DOLPHIN_HP_PIN_NID: | |
1327 | case DOLPHIN_LO_PIN_NID: | |
1328 | if (nid == DOLPHIN_LO_PIN_NID) | |
1329 | cs42l42 = spec->scodecs[CS8409_CODEC1]; | |
1330 | if (verb == AC_VERB_GET_PIN_SENSE) { | |
1331 | *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; | |
1332 | return 0; | |
1333 | } | |
1334 | break; | |
1335 | case DOLPHIN_AMIC_PIN_NID: | |
1336 | if (verb == AC_VERB_GET_PIN_SENSE) { | |
1337 | *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; | |
1338 | return 0; | |
1339 | } | |
1340 | break; | |
1341 | default: | |
1342 | break; | |
1343 | } | |
1344 | ||
1345 | return spec->exec_verb(dev, cmd, flags, res); | |
1346 | } | |
1347 | ||
1348 | void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action) | |
1349 | { | |
1350 | struct cs8409_spec *spec = codec->spec; | |
1351 | struct snd_kcontrol_new *kctrl; | |
1352 | int i; | |
1353 | ||
1354 | switch (action) { | |
1355 | case HDA_FIXUP_ACT_PRE_PROBE: | |
1356 | snd_hda_add_verbs(codec, dolphin_init_verbs); | |
1357 | /* verb exec op override */ | |
1358 | spec->exec_verb = codec->core.exec_verb; | |
1359 | codec->core.exec_verb = dolphin_exec_verb; | |
1360 | ||
1361 | spec->scodecs[CS8409_CODEC0] = &dolphin_cs42l42_0; | |
1362 | spec->scodecs[CS8409_CODEC0]->codec = codec; | |
1363 | spec->scodecs[CS8409_CODEC1] = &dolphin_cs42l42_1; | |
1364 | spec->scodecs[CS8409_CODEC1]->codec = codec; | |
1365 | spec->num_scodecs = 2; | |
a2ed0a44 | 1366 | spec->gen.suppress_vmaster = 1; |
20e50772 LT |
1367 | |
1368 | codec->patch_ops = cs8409_dolphin_patch_ops; | |
1369 | ||
1370 | /* GPIO 1,5 out, 0,4 in */ | |
1371 | spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio | | |
1372 | spec->scodecs[CS8409_CODEC1]->reset_gpio; | |
1373 | spec->gpio_data = 0; | |
1374 | spec->gpio_mask = 0x03f; | |
1375 | ||
1376 | /* Basic initial sequence for specific hw configuration */ | |
1377 | snd_hda_sequence_write(codec, dolphin_init_verbs); | |
1378 | ||
1379 | snd_hda_jack_add_kctl(codec, DOLPHIN_LO_PIN_NID, "Line Out", true, | |
1380 | SND_JACK_HEADPHONE, NULL); | |
1381 | ||
94d508fa SB |
1382 | snd_hda_jack_add_kctl(codec, DOLPHIN_AMIC_PIN_NID, "Microphone", true, |
1383 | SND_JACK_MICROPHONE, NULL); | |
1384 | ||
20e50772 LT |
1385 | cs8409_fix_caps(codec, DOLPHIN_HP_PIN_NID); |
1386 | cs8409_fix_caps(codec, DOLPHIN_LO_PIN_NID); | |
1387 | cs8409_fix_caps(codec, DOLPHIN_AMIC_PIN_NID); | |
1388 | ||
342b6b61 SB |
1389 | spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB; |
1390 | spec->scodecs[CS8409_CODEC1]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB; | |
1391 | ||
20e50772 LT |
1392 | break; |
1393 | case HDA_FIXUP_ACT_PROBE: | |
fed0aaca SB |
1394 | /* Fix Sample Rate to 48kHz */ |
1395 | spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; | |
1396 | spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture; | |
7482ec71 SB |
1397 | /* add hooks */ |
1398 | spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook; | |
1399 | spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook; | |
20e50772 LT |
1400 | snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume", |
1401 | &cs42l42_dac_volume_mixer); | |
1402 | snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer); | |
1403 | kctrl = snd_hda_gen_add_kctl(&spec->gen, "Line Out Playback Volume", | |
1404 | &cs42l42_dac_volume_mixer); | |
1405 | /* Update Line Out kcontrol template */ | |
1406 | kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1, | |
1407 | HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE; | |
1408 | cs8409_enable_ur(codec, 0); | |
20e50772 LT |
1409 | snd_hda_codec_set_name(codec, "CS8409/CS42L42"); |
1410 | break; | |
1411 | case HDA_FIXUP_ACT_INIT: | |
1412 | dolphin_hw_init(codec); | |
424e531b SB |
1413 | spec->init_done = 1; |
1414 | if (spec->init_done && spec->build_ctrl_done) { | |
1415 | for (i = 0; i < spec->num_scodecs; i++) { | |
1416 | if (!spec->scodecs[i]->hp_jack_in) | |
1417 | cs42l42_run_jack_detect(spec->scodecs[i]); | |
1418 | } | |
1419 | } | |
1420 | break; | |
20e50772 | 1421 | case HDA_FIXUP_ACT_BUILD: |
424e531b | 1422 | spec->build_ctrl_done = 1; |
20e50772 LT |
1423 | /* Run jack auto detect first time on boot |
1424 | * after controls have been added, to check if jack has | |
1425 | * been already plugged in. | |
1426 | * Run immediately after init. | |
1427 | */ | |
424e531b SB |
1428 | if (spec->init_done && spec->build_ctrl_done) { |
1429 | for (i = 0; i < spec->num_scodecs; i++) { | |
1430 | if (!spec->scodecs[i]->hp_jack_in) | |
1431 | cs42l42_run_jack_detect(spec->scodecs[i]); | |
1432 | } | |
1433 | } | |
20e50772 LT |
1434 | break; |
1435 | default: | |
1436 | break; | |
1437 | } | |
1438 | } | |
1439 | ||
8c70461b LT |
1440 | static int patch_cs8409(struct hda_codec *codec) |
1441 | { | |
1442 | int err; | |
1443 | ||
1444 | if (!cs8409_alloc_spec(codec)) | |
1445 | return -ENOMEM; | |
1446 | ||
1447 | snd_hda_pick_fixup(codec, cs8409_models, cs8409_fixup_tbl, cs8409_fixups); | |
1448 | ||
1449 | codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id, | |
1450 | codec->bus->pci->subsystem_vendor, | |
1451 | codec->bus->pci->subsystem_device); | |
1452 | ||
1453 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); | |
1454 | ||
1455 | err = cs8409_parse_auto_config(codec); | |
1456 | if (err < 0) { | |
647d50a0 | 1457 | cs8409_free(codec); |
8c70461b LT |
1458 | return err; |
1459 | } | |
1460 | ||
1461 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); | |
1462 | return 0; | |
1463 | } | |
1464 | ||
1465 | static const struct hda_device_id snd_hda_id_cs8409[] = { | |
1466 | HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409), | |
1467 | {} /* terminator */ | |
1468 | }; | |
1469 | MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs8409); | |
1470 | ||
1471 | static struct hda_codec_driver cs8409_driver = { | |
1472 | .id = snd_hda_id_cs8409, | |
1473 | }; | |
1474 | module_hda_codec_driver(cs8409_driver); | |
1475 | ||
1476 | MODULE_LICENSE("GPL"); | |
1477 | MODULE_DESCRIPTION("Cirrus Logic HDA bridge"); |