1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // CS42L43 CODEC driver jack handling |
4 | // |
5 | // Copyright (C) 2022-2023 Cirrus Logic, Inc. and |
6 | // Cirrus Logic International Semiconductor Ltd. |
7 | |
8 | #include <linux/build_bug.h> |
9 | #include <linux/completion.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/errno.h> |
12 | #include <linux/irq.h> |
13 | #include <linux/jiffies.h> |
14 | #include <linux/mfd/cs42l43.h> |
15 | #include <linux/mfd/cs42l43-regs.h> |
16 | #include <linux/mutex.h> |
17 | #include <linux/pm_runtime.h> |
18 | #include <linux/property.h> |
19 | #include <linux/regmap.h> |
20 | #include <linux/time.h> |
21 | #include <linux/workqueue.h> |
22 | #include <sound/control.h> |
23 | #include <sound/jack.h> |
24 | #include <sound/pcm.h> |
25 | #include <sound/pcm_params.h> |
26 | #include <sound/soc-component.h> |
27 | #include <sound/soc-jack.h> |
28 | #include <sound/soc.h> |
29 | |
30 | #include "cs42l43.h" |
31 | |
32 | static const unsigned int cs42l43_accdet_us[] = { |
33 | 20, 100, 1000, 10000, 50000, 75000, 100000, 200000, |
34 | }; |
35 | |
36 | static const unsigned int cs42l43_accdet_db_ms[] = { |
37 | 0, 125, 250, 500, 750, 1000, 1250, 1500, |
38 | }; |
39 | |
40 | static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 }; |
41 | |
42 | static const unsigned int cs42l43_accdet_bias_sense[] = { |
43 | 14, 24, 43, 52, 61, 71, 90, 99, 0, |
44 | }; |
45 | |
46 | static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const prop, |
47 | unsigned int defval, unsigned int *val, |
48 | const unsigned int *values, const int nvalues) |
49 | { |
50 | struct cs42l43 *cs42l43 = priv->core; |
51 | int i, ret; |
52 | |
53 | ret = device_property_read_u32(dev: cs42l43->dev, propname: prop, val: &defval); |
54 | if (ret != -EINVAL && ret < 0) { |
55 | dev_err(priv->dev, "Property %s malformed: %d\n" , prop, ret); |
56 | return ret; |
57 | } |
58 | |
59 | if (val) |
60 | *val = defval; |
61 | |
62 | for (i = 0; i < nvalues; i++) |
63 | if (defval == values[i]) |
64 | return i; |
65 | |
66 | dev_err(priv->dev, "Invalid value for property %s: %d\n" , prop, defval); |
67 | return -EINVAL; |
68 | } |
69 | |
70 | int cs42l43_set_jack(struct snd_soc_component *component, |
71 | struct snd_soc_jack *jack, void *d) |
72 | { |
73 | struct cs42l43_codec *priv = snd_soc_component_get_drvdata(c: component); |
74 | struct cs42l43 *cs42l43 = priv->core; |
75 | /* This tip sense invert is always set, HW wants an inverted signal */ |
76 | unsigned int tip_deb = CS42L43_TIPSENSE_INV_MASK; |
77 | unsigned int hs2 = 0x2 << CS42L43_HSDET_MODE_SHIFT; |
78 | unsigned int autocontrol = 0, pdncntl = 0; |
79 | int ret; |
80 | |
81 | dev_dbg(priv->dev, "Configure accessory detect\n" ); |
82 | |
83 | ret = pm_runtime_resume_and_get(dev: priv->dev); |
84 | if (ret) { |
85 | dev_err(priv->dev, "Failed to resume for jack config: %d\n" , ret); |
86 | return ret; |
87 | } |
88 | |
89 | mutex_lock(&priv->jack_lock); |
90 | |
91 | priv->jack_hp = jack; |
92 | |
93 | if (!jack) |
94 | goto done; |
95 | |
96 | ret = device_property_count_u32(dev: cs42l43->dev, propname: "cirrus,buttons-ohms" ); |
97 | if (ret != -EINVAL) { |
98 | if (ret < 0) { |
99 | dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n" , |
100 | ret); |
101 | goto error; |
102 | } |
103 | |
104 | if (ret > CS42L43_N_BUTTONS) { |
105 | ret = -EINVAL; |
106 | dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n" ); |
107 | goto error; |
108 | } |
109 | |
110 | ret = device_property_read_u32_array(dev: cs42l43->dev, propname: "cirrus,buttons-ohms" , |
111 | val: priv->buttons, nval: ret); |
112 | if (ret < 0) { |
113 | dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n" , |
114 | ret); |
115 | goto error; |
116 | } |
117 | } else { |
118 | priv->buttons[0] = 70; |
119 | priv->buttons[1] = 185; |
120 | priv->buttons[2] = 355; |
121 | priv->buttons[3] = 735; |
122 | } |
123 | |
124 | ret = cs42l43_find_index(priv, prop: "cirrus,detect-us" , defval: 1000, val: &priv->detect_us, |
125 | values: cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); |
126 | if (ret < 0) |
127 | goto error; |
128 | |
129 | hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT; |
130 | |
131 | priv->bias_low = device_property_read_bool(dev: cs42l43->dev, propname: "cirrus,bias-low" ); |
132 | |
133 | ret = cs42l43_find_index(priv, prop: "cirrus,bias-ramp-ms" , defval: 170, |
134 | val: &priv->bias_ramp_ms, values: cs42l43_accdet_ramp_ms, |
135 | ARRAY_SIZE(cs42l43_accdet_ramp_ms)); |
136 | if (ret < 0) |
137 | goto error; |
138 | |
139 | hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT; |
140 | |
141 | ret = cs42l43_find_index(priv, prop: "cirrus,bias-sense-microamp" , defval: 14, |
142 | val: &priv->bias_sense_ua, values: cs42l43_accdet_bias_sense, |
143 | ARRAY_SIZE(cs42l43_accdet_bias_sense)); |
144 | if (ret < 0) |
145 | goto error; |
146 | |
147 | if (priv->bias_sense_ua) |
148 | autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT; |
149 | |
150 | if (!device_property_read_bool(dev: cs42l43->dev, propname: "cirrus,button-automute" )) |
151 | autocontrol |= CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK; |
152 | |
153 | ret = device_property_read_u32(dev: cs42l43->dev, propname: "cirrus,tip-debounce-ms" , |
154 | val: &priv->tip_debounce_ms); |
155 | if (ret < 0 && ret != -EINVAL) { |
156 | dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n" , ret); |
157 | goto error; |
158 | } |
159 | |
160 | /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */ |
161 | if (device_property_read_bool(dev: cs42l43->dev, propname: "cirrus,tip-invert" )) |
162 | autocontrol |= 0x1 << CS42L43_JACKDET_INV_SHIFT; |
163 | |
164 | if (device_property_read_bool(dev: cs42l43->dev, propname: "cirrus,tip-disable-pullup" )) |
165 | autocontrol |= 0x1 << CS42L43_JACKDET_MODE_SHIFT; |
166 | else |
167 | autocontrol |= 0x3 << CS42L43_JACKDET_MODE_SHIFT; |
168 | |
169 | ret = cs42l43_find_index(priv, prop: "cirrus,tip-fall-db-ms" , defval: 500, |
170 | NULL, values: cs42l43_accdet_db_ms, |
171 | ARRAY_SIZE(cs42l43_accdet_db_ms)); |
172 | if (ret < 0) |
173 | goto error; |
174 | |
175 | tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; |
176 | |
177 | ret = cs42l43_find_index(priv, prop: "cirrus,tip-rise-db-ms" , defval: 500, |
178 | NULL, values: cs42l43_accdet_db_ms, |
179 | ARRAY_SIZE(cs42l43_accdet_db_ms)); |
180 | if (ret < 0) |
181 | goto error; |
182 | |
183 | tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT; |
184 | |
185 | if (device_property_read_bool(dev: cs42l43->dev, propname: "cirrus,use-ring-sense" )) { |
186 | unsigned int ring_deb = 0; |
187 | |
188 | priv->use_ring_sense = true; |
189 | |
190 | /* HW wants an inverted signal, so invert the invert */ |
191 | if (!device_property_read_bool(dev: cs42l43->dev, propname: "cirrus,ring-invert" )) |
192 | ring_deb |= CS42L43_RINGSENSE_INV_MASK; |
193 | |
194 | if (!device_property_read_bool(dev: cs42l43->dev, |
195 | propname: "cirrus,ring-disable-pullup" )) |
196 | ring_deb |= CS42L43_RINGSENSE_PULLUP_PDNB_MASK; |
197 | |
198 | ret = cs42l43_find_index(priv, prop: "cirrus,ring-fall-db-ms" , defval: 500, |
199 | NULL, values: cs42l43_accdet_db_ms, |
200 | ARRAY_SIZE(cs42l43_accdet_db_ms)); |
201 | if (ret < 0) |
202 | goto error; |
203 | |
204 | ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT; |
205 | |
206 | ret = cs42l43_find_index(priv, prop: "cirrus,ring-rise-db-ms" , defval: 500, |
207 | NULL, values: cs42l43_accdet_db_ms, |
208 | ARRAY_SIZE(cs42l43_accdet_db_ms)); |
209 | if (ret < 0) |
210 | goto error; |
211 | |
212 | ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT; |
213 | pdncntl |= CS42L43_RING_SENSE_EN_MASK; |
214 | |
215 | regmap_update_bits(map: cs42l43->regmap, CS42L43_RINGSENSE_DEB_CTRL, |
216 | CS42L43_RINGSENSE_INV_MASK | |
217 | CS42L43_RINGSENSE_PULLUP_PDNB_MASK | |
218 | CS42L43_RINGSENSE_FALLING_DB_TIME_MASK | |
219 | CS42L43_RINGSENSE_RISING_DB_TIME_MASK, |
220 | val: ring_deb); |
221 | } |
222 | |
223 | regmap_update_bits(map: cs42l43->regmap, CS42L43_TIPSENSE_DEB_CTRL, |
224 | CS42L43_TIPSENSE_INV_MASK | |
225 | CS42L43_TIPSENSE_FALLING_DB_TIME_MASK | |
226 | CS42L43_TIPSENSE_RISING_DB_TIME_MASK, val: tip_deb); |
227 | regmap_update_bits(map: cs42l43->regmap, CS42L43_HS2, |
228 | CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK | |
229 | CS42L43_AUTO_HSDET_TIME_MASK, val: hs2); |
230 | |
231 | done: |
232 | ret = 0; |
233 | |
234 | regmap_update_bits(map: cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, |
235 | CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | |
236 | CS42L43_HSBIAS_SENSE_TRIP_MASK, val: autocontrol); |
237 | regmap_update_bits(map: cs42l43->regmap, CS42L43_PDNCNTL, |
238 | CS42L43_RING_SENSE_EN_MASK, val: pdncntl); |
239 | |
240 | dev_dbg(priv->dev, "Successfully configured accessory detect\n" ); |
241 | |
242 | error: |
243 | mutex_unlock(lock: &priv->jack_lock); |
244 | |
245 | pm_runtime_mark_last_busy(dev: priv->dev); |
246 | pm_runtime_put_autosuspend(dev: priv->dev); |
247 | |
248 | return ret; |
249 | } |
250 | |
251 | static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect) |
252 | { |
253 | struct cs42l43 *cs42l43 = priv->core; |
254 | unsigned int val = 0x3 << CS42L43_HSBIAS_MODE_SHIFT; |
255 | |
256 | dev_dbg(priv->dev, "Start headset bias\n" ); |
257 | |
258 | regmap_update_bits(map: cs42l43->regmap, CS42L43_HS2, |
259 | CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK); |
260 | |
261 | if (!type_detect) { |
262 | if (priv->bias_low) |
263 | val = 0x2 << CS42L43_HSBIAS_MODE_SHIFT; |
264 | |
265 | if (priv->bias_sense_ua) |
266 | regmap_update_bits(map: cs42l43->regmap, |
267 | CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, |
268 | CS42L43_HSBIAS_SENSE_EN_MASK | |
269 | CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, |
270 | CS42L43_HSBIAS_SENSE_EN_MASK | |
271 | CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); |
272 | } |
273 | |
274 | regmap_update_bits(map: cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, |
275 | CS42L43_HSBIAS_MODE_MASK, val); |
276 | |
277 | msleep(msecs: priv->bias_ramp_ms); |
278 | } |
279 | |
280 | static void cs42l43_stop_hs_bias(struct cs42l43_codec *priv) |
281 | { |
282 | struct cs42l43 *cs42l43 = priv->core; |
283 | |
284 | dev_dbg(priv->dev, "Stop headset bias\n" ); |
285 | |
286 | regmap_update_bits(map: cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, |
287 | CS42L43_HSBIAS_MODE_MASK, val: 0x1 << CS42L43_HSBIAS_MODE_SHIFT); |
288 | |
289 | regmap_update_bits(map: cs42l43->regmap, CS42L43_HS2, |
290 | CS42L43_HS_CLAMP_DISABLE_MASK, val: 0); |
291 | |
292 | if (priv->bias_sense_ua) { |
293 | regmap_update_bits(map: cs42l43->regmap, |
294 | CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, |
295 | CS42L43_HSBIAS_SENSE_EN_MASK | |
296 | CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, val: 0); |
297 | } |
298 | } |
299 | |
300 | irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data) |
301 | { |
302 | struct cs42l43_codec *priv = data; |
303 | |
304 | queue_delayed_work(wq: system_wq, dwork: &priv->bias_sense_timeout, |
305 | delay: msecs_to_jiffies(m: 1000)); |
306 | |
307 | return IRQ_HANDLED; |
308 | } |
309 | |
310 | #define CS42L43_JACK_PRESENT 0x3 |
311 | #define CS42L43_JACK_ABSENT 0x0 |
312 | |
313 | #define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT) |
314 | #define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE) |
315 | #define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET) |
316 | #define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT) |
317 | #define CS42L43_JACK_LINEIN (SND_JACK_MECHANICAL | SND_JACK_LINEIN) |
318 | #define CS42L43_JACK_EXTENSION (SND_JACK_MECHANICAL) |
319 | #define CS42L43_JACK_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | \ |
320 | SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5) |
321 | |
322 | static inline bool cs42l43_jack_present(struct cs42l43_codec *priv) |
323 | { |
324 | struct cs42l43 *cs42l43 = priv->core; |
325 | unsigned int sts = 0; |
326 | |
327 | regmap_read(map: cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, val: &sts); |
328 | |
329 | sts = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; |
330 | |
331 | return sts == CS42L43_JACK_PRESENT; |
332 | } |
333 | |
334 | static void cs42l43_start_button_detect(struct cs42l43_codec *priv) |
335 | { |
336 | struct cs42l43 *cs42l43 = priv->core; |
337 | unsigned int val = 0x3 << CS42L43_BUTTON_DETECT_MODE_SHIFT; |
338 | |
339 | dev_dbg(priv->dev, "Start button detect\n" ); |
340 | |
341 | priv->button_detect_running = true; |
342 | |
343 | if (priv->bias_low) |
344 | val = 0x1 << CS42L43_BUTTON_DETECT_MODE_SHIFT; |
345 | |
346 | regmap_update_bits(map: cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, |
347 | CS42L43_BUTTON_DETECT_MODE_MASK | |
348 | CS42L43_MIC_LVL_DET_DISABLE_MASK, val); |
349 | } |
350 | |
351 | static void cs42l43_stop_button_detect(struct cs42l43_codec *priv) |
352 | { |
353 | struct cs42l43 *cs42l43 = priv->core; |
354 | |
355 | dev_dbg(priv->dev, "Stop button detect\n" ); |
356 | |
357 | regmap_update_bits(map: cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, |
358 | CS42L43_BUTTON_DETECT_MODE_MASK | |
359 | CS42L43_MIC_LVL_DET_DISABLE_MASK, |
360 | CS42L43_MIC_LVL_DET_DISABLE_MASK); |
361 | |
362 | priv->button_detect_running = false; |
363 | } |
364 | |
365 | #define CS42L43_BUTTON_COMB_MAX 512 |
366 | #define CS42L43_BUTTON_ROUT 2210 |
367 | |
368 | void cs42l43_button_press_work(struct work_struct *work) |
369 | { |
370 | struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, |
371 | button_press_work.work); |
372 | struct cs42l43 *cs42l43 = priv->core; |
373 | unsigned int buttons = 0; |
374 | unsigned int val = 0; |
375 | int i, ret; |
376 | |
377 | ret = pm_runtime_resume_and_get(dev: priv->dev); |
378 | if (ret) { |
379 | dev_err(priv->dev, "Failed to resume for button press: %d\n" , ret); |
380 | return; |
381 | } |
382 | |
383 | mutex_lock(&priv->jack_lock); |
384 | |
385 | if (!priv->button_detect_running) { |
386 | dev_dbg(priv->dev, "Spurious button press IRQ\n" ); |
387 | goto error; |
388 | } |
389 | |
390 | regmap_read(map: cs42l43->regmap, CS42L43_DETECT_STATUS_1, val: &val); |
391 | |
392 | /* Bail if jack removed, the button is irrelevant and likely invalid */ |
393 | if (!cs42l43_jack_present(priv)) { |
394 | dev_dbg(priv->dev, "Button ignored due to removal\n" ); |
395 | goto error; |
396 | } |
397 | |
398 | if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) { |
399 | dev_dbg(priv->dev, "Button ignored due to bias sense\n" ); |
400 | goto error; |
401 | } |
402 | |
403 | val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT; |
404 | val = ((CS42L43_BUTTON_COMB_MAX << 20) / (val + 1)) - (1 << 20); |
405 | if (val) |
406 | val = (CS42L43_BUTTON_ROUT << 20) / val; |
407 | else |
408 | val = UINT_MAX; |
409 | |
410 | for (i = 0; i < CS42L43_N_BUTTONS; i++) { |
411 | if (val < priv->buttons[i]) { |
412 | buttons = SND_JACK_BTN_0 >> i; |
413 | dev_dbg(priv->dev, "Detected button %d at %d Ohms\n" , i, val); |
414 | break; |
415 | } |
416 | } |
417 | |
418 | if (!buttons) |
419 | dev_dbg(priv->dev, "Unrecognised button: %d Ohms\n" , val); |
420 | |
421 | snd_soc_jack_report(jack: priv->jack_hp, status: buttons, CS42L43_JACK_BUTTONS); |
422 | |
423 | error: |
424 | mutex_unlock(lock: &priv->jack_lock); |
425 | |
426 | pm_runtime_mark_last_busy(dev: priv->dev); |
427 | pm_runtime_put_autosuspend(dev: priv->dev); |
428 | } |
429 | |
430 | irqreturn_t cs42l43_button_press(int irq, void *data) |
431 | { |
432 | struct cs42l43_codec *priv = data; |
433 | |
434 | // Wait for 2 full cycles of comb filter to ensure good reading |
435 | queue_delayed_work(wq: system_wq, dwork: &priv->button_press_work, |
436 | delay: msecs_to_jiffies(m: 10)); |
437 | |
438 | return IRQ_HANDLED; |
439 | } |
440 | |
441 | void cs42l43_button_release_work(struct work_struct *work) |
442 | { |
443 | struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, |
444 | button_release_work); |
445 | int ret; |
446 | |
447 | ret = pm_runtime_resume_and_get(dev: priv->dev); |
448 | if (ret) { |
449 | dev_err(priv->dev, "Failed to resume for button release: %d\n" , ret); |
450 | return; |
451 | } |
452 | |
453 | mutex_lock(&priv->jack_lock); |
454 | |
455 | if (priv->button_detect_running) { |
456 | dev_dbg(priv->dev, "Button release IRQ\n" ); |
457 | |
458 | snd_soc_jack_report(jack: priv->jack_hp, status: 0, CS42L43_JACK_BUTTONS); |
459 | } else { |
460 | dev_dbg(priv->dev, "Spurious button release IRQ\n" ); |
461 | } |
462 | |
463 | mutex_unlock(lock: &priv->jack_lock); |
464 | |
465 | pm_runtime_mark_last_busy(dev: priv->dev); |
466 | pm_runtime_put_autosuspend(dev: priv->dev); |
467 | } |
468 | |
469 | irqreturn_t cs42l43_button_release(int irq, void *data) |
470 | { |
471 | struct cs42l43_codec *priv = data; |
472 | |
473 | queue_work(wq: system_wq, work: &priv->button_release_work); |
474 | |
475 | return IRQ_HANDLED; |
476 | } |
477 | |
478 | void cs42l43_bias_sense_timeout(struct work_struct *work) |
479 | { |
480 | struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, |
481 | bias_sense_timeout.work); |
482 | struct cs42l43 *cs42l43 = priv->core; |
483 | int ret; |
484 | |
485 | ret = pm_runtime_resume_and_get(dev: priv->dev); |
486 | if (ret) { |
487 | dev_err(priv->dev, "Failed to resume for bias sense: %d\n" , ret); |
488 | return; |
489 | } |
490 | |
491 | mutex_lock(&priv->jack_lock); |
492 | |
493 | if (cs42l43_jack_present(priv) && priv->button_detect_running) { |
494 | dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n" ); |
495 | |
496 | regmap_update_bits(map: cs42l43->regmap, |
497 | CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, |
498 | CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, val: 0); |
499 | regmap_update_bits(map: cs42l43->regmap, |
500 | CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, |
501 | CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, |
502 | CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); |
503 | } |
504 | |
505 | mutex_unlock(lock: &priv->jack_lock); |
506 | |
507 | pm_runtime_mark_last_busy(dev: priv->dev); |
508 | pm_runtime_put_autosuspend(dev: priv->dev); |
509 | } |
510 | |
511 | static void cs42l43_start_load_detect(struct cs42l43_codec *priv) |
512 | { |
513 | struct cs42l43 *cs42l43 = priv->core; |
514 | |
515 | dev_dbg(priv->dev, "Start load detect\n" ); |
516 | |
517 | snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component)); |
518 | |
519 | priv->load_detect_running = true; |
520 | |
521 | if (priv->hp_ena && !priv->hp_ilimited) { |
522 | unsigned long time_left; |
523 | |
524 | reinit_completion(x: &priv->hp_shutdown); |
525 | |
526 | regmap_update_bits(map: cs42l43->regmap, CS42L43_BLOCK_EN8, |
527 | CS42L43_HP_EN_MASK, val: 0); |
528 | |
529 | time_left = wait_for_completion_timeout(x: &priv->hp_shutdown, |
530 | timeout: msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS)); |
531 | if (!time_left) |
532 | dev_err(priv->dev, "Load detect HP power down timed out\n" ); |
533 | } |
534 | |
535 | regmap_update_bits(map: cs42l43->regmap, CS42L43_BLOCK_EN3, |
536 | CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, val: 0); |
537 | regmap_update_bits(map: cs42l43->regmap, CS42L43_DACCNFG2, CS42L43_HP_HPF_EN_MASK, val: 0); |
538 | regmap_update_bits(map: cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, |
539 | CS42L43_HSBIAS_MODE_MASK, val: 0); |
540 | regmap_update_bits(map: cs42l43->regmap, CS42L43_CTRL, |
541 | CS42L43_ADPTPWR_MODE_MASK, val: 0x4 << CS42L43_ADPTPWR_MODE_SHIFT); |
542 | regmap_update_bits(map: cs42l43->regmap, CS42L43_PGAVOL, |
543 | CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, val: 0x6); |
544 | regmap_update_bits(map: cs42l43->regmap, CS42L43_DACCNFG1, |
545 | CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, val: 0); |
546 | |
547 | regmap_update_bits(map: cs42l43->regmap, CS42L43_HS2, |
548 | CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK); |
549 | |
550 | regmap_update_bits(map: cs42l43->regmap, CS42L43_LOADDETENA, |
551 | CS42L43_HPLOAD_DET_EN_MASK, |
552 | CS42L43_HPLOAD_DET_EN_MASK); |
553 | |
554 | snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component)); |
555 | } |
556 | |
557 | static void cs42l43_stop_load_detect(struct cs42l43_codec *priv) |
558 | { |
559 | struct cs42l43 *cs42l43 = priv->core; |
560 | |
561 | dev_dbg(priv->dev, "Stop load detect\n" ); |
562 | |
563 | snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component)); |
564 | |
565 | regmap_update_bits(map: cs42l43->regmap, CS42L43_LOADDETENA, |
566 | CS42L43_HPLOAD_DET_EN_MASK, val: 0); |
567 | regmap_update_bits(map: cs42l43->regmap, CS42L43_HS2, |
568 | CS42L43_HS_CLAMP_DISABLE_MASK, val: 0); |
569 | regmap_update_bits(map: cs42l43->regmap, CS42L43_DACCNFG1, |
570 | CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, |
571 | CS42L43_HP_MSTR_VOL_CTRL_EN_MASK); |
572 | regmap_update_bits(map: cs42l43->regmap, CS42L43_PGAVOL, |
573 | CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, |
574 | val: 0x4 << CS42L43_HP_DIG_VOL_RAMP_SHIFT); |
575 | regmap_update_bits(map: cs42l43->regmap, CS42L43_CTRL, |
576 | CS42L43_ADPTPWR_MODE_MASK, val: 0x7 << CS42L43_ADPTPWR_MODE_SHIFT); |
577 | regmap_update_bits(map: cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, |
578 | CS42L43_HSBIAS_MODE_MASK, val: 0x1 << CS42L43_HSBIAS_MODE_SHIFT); |
579 | regmap_update_bits(map: cs42l43->regmap, CS42L43_DACCNFG2, |
580 | CS42L43_HP_HPF_EN_MASK, CS42L43_HP_HPF_EN_MASK); |
581 | |
582 | regmap_update_bits(map: cs42l43->regmap, CS42L43_BLOCK_EN3, |
583 | CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, |
584 | val: priv->adc_ena); |
585 | |
586 | if (priv->hp_ena && !priv->hp_ilimited) { |
587 | unsigned long time_left; |
588 | |
589 | reinit_completion(x: &priv->hp_startup); |
590 | |
591 | regmap_update_bits(map: cs42l43->regmap, CS42L43_BLOCK_EN8, |
592 | CS42L43_HP_EN_MASK, val: priv->hp_ena); |
593 | |
594 | time_left = wait_for_completion_timeout(x: &priv->hp_startup, |
595 | timeout: msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS)); |
596 | if (!time_left) |
597 | dev_err(priv->dev, "Load detect HP restore timed out\n" ); |
598 | } |
599 | |
600 | priv->load_detect_running = false; |
601 | |
602 | snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component)); |
603 | } |
604 | |
605 | static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic) |
606 | { |
607 | struct cs42l43 *cs42l43 = priv->core; |
608 | unsigned int val = 0; |
609 | unsigned long time_left; |
610 | |
611 | reinit_completion(x: &priv->load_detect); |
612 | |
613 | cs42l43_start_load_detect(priv); |
614 | time_left = wait_for_completion_timeout(x: &priv->load_detect, |
615 | timeout: msecs_to_jiffies(CS42L43_LOAD_TIMEOUT_MS)); |
616 | cs42l43_stop_load_detect(priv); |
617 | |
618 | if (!time_left) |
619 | return -ETIMEDOUT; |
620 | |
621 | regmap_read(map: cs42l43->regmap, CS42L43_LOADDETRESULTS, val: &val); |
622 | |
623 | dev_dbg(priv->dev, "Headphone load detect: 0x%x\n" , val); |
624 | |
625 | /* Bail if jack removed, the load is irrelevant and likely invalid */ |
626 | if (!cs42l43_jack_present(priv)) |
627 | return -ENODEV; |
628 | |
629 | if (mic) { |
630 | cs42l43_start_hs_bias(priv, type_detect: false); |
631 | cs42l43_start_button_detect(priv); |
632 | |
633 | return CS42L43_JACK_HEADSET; |
634 | } |
635 | |
636 | switch (val & CS42L43_AMP3_RES_DET_MASK) { |
637 | case 0x0: // low impedance |
638 | case 0x1: // high impedance |
639 | return CS42L43_JACK_HEADPHONE; |
640 | case 0x2: // lineout |
641 | case 0x3: // Open circuit |
642 | return CS42L43_JACK_LINEOUT; |
643 | default: |
644 | return -EINVAL; |
645 | } |
646 | } |
647 | |
648 | static int cs42l43_run_type_detect(struct cs42l43_codec *priv) |
649 | { |
650 | struct cs42l43 *cs42l43 = priv->core; |
651 | int timeout_ms = ((2 * priv->detect_us) / USEC_PER_MSEC) + 200; |
652 | unsigned int type = 0xff; |
653 | unsigned long time_left; |
654 | |
655 | reinit_completion(x: &priv->type_detect); |
656 | |
657 | cs42l43_start_hs_bias(priv, type_detect: true); |
658 | regmap_update_bits(map: cs42l43->regmap, CS42L43_HS2, |
659 | CS42L43_HSDET_MODE_MASK, val: 0x3 << CS42L43_HSDET_MODE_SHIFT); |
660 | |
661 | time_left = wait_for_completion_timeout(x: &priv->type_detect, |
662 | timeout: msecs_to_jiffies(m: timeout_ms)); |
663 | |
664 | regmap_update_bits(map: cs42l43->regmap, CS42L43_HS2, |
665 | CS42L43_HSDET_MODE_MASK, val: 0x2 << CS42L43_HSDET_MODE_SHIFT); |
666 | cs42l43_stop_hs_bias(priv); |
667 | |
668 | if (!time_left) |
669 | return -ETIMEDOUT; |
670 | |
671 | regmap_read(map: cs42l43->regmap, CS42L43_HS_STAT, val: &type); |
672 | |
673 | dev_dbg(priv->dev, "Type detect: 0x%x\n" , type); |
674 | |
675 | /* Bail if jack removed, the type is irrelevant and likely invalid */ |
676 | if (!cs42l43_jack_present(priv)) |
677 | return -ENODEV; |
678 | |
679 | switch (type & CS42L43_HSDET_TYPE_STS_MASK) { |
680 | case 0x0: // CTIA |
681 | case 0x1: // OMTP |
682 | return cs42l43_run_load_detect(priv, mic: true); |
683 | case 0x2: // 3-pole |
684 | return cs42l43_run_load_detect(priv, mic: false); |
685 | case 0x3: // Open-circuit |
686 | return CS42L43_JACK_EXTENSION; |
687 | default: |
688 | return -EINVAL; |
689 | } |
690 | } |
691 | |
692 | static void cs42l43_clear_jack(struct cs42l43_codec *priv) |
693 | { |
694 | struct cs42l43 *cs42l43 = priv->core; |
695 | |
696 | cs42l43_stop_button_detect(priv); |
697 | cs42l43_stop_hs_bias(priv); |
698 | |
699 | regmap_update_bits(map: cs42l43->regmap, CS42L43_ADC_B_CTRL1, |
700 | CS42L43_PGA_WIDESWING_MODE_EN_MASK, val: 0); |
701 | regmap_update_bits(map: cs42l43->regmap, CS42L43_ADC_B_CTRL2, |
702 | CS42L43_PGA_WIDESWING_MODE_EN_MASK, val: 0); |
703 | regmap_update_bits(map: cs42l43->regmap, CS42L43_STEREO_MIC_CTRL, |
704 | CS42L43_JACK_STEREO_CONFIG_MASK, val: 0); |
705 | regmap_update_bits(map: cs42l43->regmap, CS42L43_HS2, |
706 | CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK, |
707 | val: 0x2 << CS42L43_HSDET_MODE_SHIFT); |
708 | |
709 | snd_soc_jack_report(jack: priv->jack_hp, status: 0, mask: 0xFFFF); |
710 | } |
711 | |
712 | void cs42l43_tip_sense_work(struct work_struct *work) |
713 | { |
714 | struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, |
715 | tip_sense_work.work); |
716 | struct cs42l43 *cs42l43 = priv->core; |
717 | unsigned int sts = 0; |
718 | unsigned int tip, ring; |
719 | int ret, report; |
720 | |
721 | ret = pm_runtime_resume_and_get(dev: priv->dev); |
722 | if (ret) { |
723 | dev_err(priv->dev, "Failed to resume for tip work: %d\n" , ret); |
724 | return; |
725 | } |
726 | |
727 | mutex_lock(&priv->jack_lock); |
728 | |
729 | regmap_read(map: cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, val: &sts); |
730 | |
731 | dev_dbg(priv->dev, "Tip sense: 0x%x\n" , sts); |
732 | |
733 | tip = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; |
734 | ring = (sts >> CS42L43_RINGSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; |
735 | |
736 | if (tip == CS42L43_JACK_PRESENT) { |
737 | if (cs42l43->sdw && !priv->jack_present) { |
738 | priv->jack_present = true; |
739 | pm_runtime_get(dev: priv->dev); |
740 | } |
741 | |
742 | if (priv->use_ring_sense && ring == CS42L43_JACK_ABSENT) { |
743 | report = CS42L43_JACK_OPTICAL; |
744 | } else { |
745 | report = cs42l43_run_type_detect(priv); |
746 | if (report < 0) { |
747 | dev_err(priv->dev, "Jack detect failed: %d\n" , report); |
748 | goto error; |
749 | } |
750 | } |
751 | |
752 | snd_soc_jack_report(jack: priv->jack_hp, status: report, mask: report); |
753 | } else { |
754 | priv->jack_override = 0; |
755 | |
756 | cs42l43_clear_jack(priv); |
757 | |
758 | if (cs42l43->sdw && priv->jack_present) { |
759 | pm_runtime_put(dev: priv->dev); |
760 | priv->jack_present = false; |
761 | } |
762 | } |
763 | |
764 | error: |
765 | mutex_unlock(lock: &priv->jack_lock); |
766 | |
767 | pm_runtime_mark_last_busy(dev: priv->dev); |
768 | pm_runtime_put_autosuspend(dev: priv->dev); |
769 | } |
770 | |
771 | irqreturn_t cs42l43_tip_sense(int irq, void *data) |
772 | { |
773 | struct cs42l43_codec *priv = data; |
774 | |
775 | cancel_delayed_work(dwork: &priv->bias_sense_timeout); |
776 | cancel_delayed_work(dwork: &priv->tip_sense_work); |
777 | cancel_delayed_work(dwork: &priv->button_press_work); |
778 | cancel_work(work: &priv->button_release_work); |
779 | |
780 | queue_delayed_work(wq: system_long_wq, dwork: &priv->tip_sense_work, |
781 | delay: msecs_to_jiffies(m: priv->tip_debounce_ms)); |
782 | |
783 | return IRQ_HANDLED; |
784 | } |
785 | |
786 | enum cs42l43_raw_jack { |
787 | CS42L43_JACK_RAW_CTIA = 0, |
788 | CS42L43_JACK_RAW_OMTP, |
789 | CS42L43_JACK_RAW_HEADPHONE, |
790 | CS42L43_JACK_RAW_LINE_OUT, |
791 | CS42L43_JACK_RAW_LINE_IN, |
792 | CS42L43_JACK_RAW_MICROPHONE, |
793 | CS42L43_JACK_RAW_OPTICAL, |
794 | }; |
795 | |
796 | #define CS42L43_JACK_3_POLE_SWITCHES ((0x2 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | \ |
797 | CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | \ |
798 | CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | \ |
799 | CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | \ |
800 | CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | \ |
801 | CS42L43_HSGND_HS3_SEL_MASK | \ |
802 | CS42L43_HSGND_HS4_SEL_MASK) |
803 | |
804 | static const struct cs42l43_jack_override_mode { |
805 | unsigned int hsdet_mode; |
806 | unsigned int mic_ctrl; |
807 | unsigned int clamp_ctrl; |
808 | int report; |
809 | } cs42l43_jack_override_modes[] = { |
810 | [CS42L43_JACK_RAW_CTIA] = { |
811 | .hsdet_mode = CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | |
812 | CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | |
813 | CS42L43_HSBIAS_OUT_HS4_SEL_MASK | |
814 | CS42L43_HSGND_HS3_SEL_MASK, |
815 | .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, |
816 | .report = CS42L43_JACK_HEADSET, |
817 | }, |
818 | [CS42L43_JACK_RAW_OMTP] = { |
819 | .hsdet_mode = (0x1 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | |
820 | CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | |
821 | CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | |
822 | CS42L43_HSBIAS_OUT_HS3_SEL_MASK | |
823 | CS42L43_HSGND_HS4_SEL_MASK, |
824 | .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, |
825 | .report = CS42L43_JACK_HEADSET, |
826 | }, |
827 | [CS42L43_JACK_RAW_HEADPHONE] = { |
828 | .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, |
829 | .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, |
830 | .report = CS42L43_JACK_HEADPHONE, |
831 | }, |
832 | [CS42L43_JACK_RAW_LINE_OUT] = { |
833 | .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, |
834 | .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, |
835 | .report = CS42L43_JACK_LINEOUT, |
836 | }, |
837 | [CS42L43_JACK_RAW_LINE_IN] = { |
838 | .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, |
839 | .mic_ctrl = 0x2 << CS42L43_JACK_STEREO_CONFIG_SHIFT, |
840 | .report = CS42L43_JACK_LINEIN, |
841 | }, |
842 | [CS42L43_JACK_RAW_MICROPHONE] = { |
843 | .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, |
844 | .mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) | |
845 | CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK, |
846 | .report = CS42L43_JACK_LINEIN, |
847 | }, |
848 | [CS42L43_JACK_RAW_OPTICAL] = { |
849 | .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, |
850 | .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, |
851 | .report = CS42L43_JACK_OPTICAL, |
852 | }, |
853 | }; |
854 | |
855 | static const char * const cs42l43_jack_text[] = { |
856 | "None" , "CTIA" , "OMTP" , "Headphone" , "Line-Out" , |
857 | "Line-In" , "Microphone" , "Optical" , |
858 | }; |
859 | |
860 | static_assert(ARRAY_SIZE(cs42l43_jack_override_modes) == |
861 | ARRAY_SIZE(cs42l43_jack_text) - 1); |
862 | |
863 | SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text); |
864 | |
865 | int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
866 | { |
867 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
868 | struct cs42l43_codec *priv = snd_soc_component_get_drvdata(c: component); |
869 | |
870 | mutex_lock(&priv->jack_lock); |
871 | ucontrol->value.integer.value[0] = priv->jack_override; |
872 | mutex_unlock(lock: &priv->jack_lock); |
873 | |
874 | return 0; |
875 | } |
876 | |
877 | int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
878 | { |
879 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
880 | struct cs42l43_codec *priv = snd_soc_component_get_drvdata(c: component); |
881 | struct cs42l43 *cs42l43 = priv->core; |
882 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
883 | unsigned int override = ucontrol->value.integer.value[0]; |
884 | |
885 | if (override >= e->items) |
886 | return -EINVAL; |
887 | |
888 | mutex_lock(&priv->jack_lock); |
889 | |
890 | if (!cs42l43_jack_present(priv)) { |
891 | mutex_unlock(lock: &priv->jack_lock); |
892 | return -EBUSY; |
893 | } |
894 | |
895 | if (override == priv->jack_override) { |
896 | mutex_unlock(lock: &priv->jack_lock); |
897 | return 0; |
898 | } |
899 | |
900 | priv->jack_override = override; |
901 | |
902 | cs42l43_clear_jack(priv); |
903 | |
904 | if (!override) { |
905 | queue_delayed_work(wq: system_long_wq, dwork: &priv->tip_sense_work, delay: 0); |
906 | } else { |
907 | override--; |
908 | |
909 | regmap_update_bits(map: cs42l43->regmap, CS42L43_HS2, |
910 | CS42L43_HSDET_MODE_MASK | |
911 | CS42L43_HSDET_MANUAL_MODE_MASK | |
912 | CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | |
913 | CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | |
914 | CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | |
915 | CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | |
916 | CS42L43_HSBIAS_OUT_HS3_SEL_MASK | |
917 | CS42L43_HSBIAS_OUT_HS4_SEL_MASK | |
918 | CS42L43_HSGND_HS3_SEL_MASK | |
919 | CS42L43_HSGND_HS4_SEL_MASK, |
920 | val: cs42l43_jack_override_modes[override].hsdet_mode); |
921 | regmap_update_bits(map: cs42l43->regmap, CS42L43_STEREO_MIC_CTRL, |
922 | CS42L43_HS2_BIAS_EN_MASK | CS42L43_HS1_BIAS_EN_MASK | |
923 | CS42L43_JACK_STEREO_CONFIG_MASK, |
924 | val: cs42l43_jack_override_modes[override].mic_ctrl); |
925 | regmap_update_bits(map: cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL, |
926 | CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, |
927 | val: cs42l43_jack_override_modes[override].clamp_ctrl); |
928 | |
929 | switch (override) { |
930 | case CS42L43_JACK_RAW_CTIA: |
931 | case CS42L43_JACK_RAW_OMTP: |
932 | cs42l43_start_hs_bias(priv, type_detect: false); |
933 | cs42l43_start_button_detect(priv); |
934 | break; |
935 | case CS42L43_JACK_RAW_LINE_IN: |
936 | regmap_update_bits(map: cs42l43->regmap, CS42L43_ADC_B_CTRL1, |
937 | CS42L43_PGA_WIDESWING_MODE_EN_MASK, |
938 | CS42L43_PGA_WIDESWING_MODE_EN_MASK); |
939 | regmap_update_bits(map: cs42l43->regmap, CS42L43_ADC_B_CTRL2, |
940 | CS42L43_PGA_WIDESWING_MODE_EN_MASK, |
941 | CS42L43_PGA_WIDESWING_MODE_EN_MASK); |
942 | break; |
943 | case CS42L43_JACK_RAW_MICROPHONE: |
944 | cs42l43_start_hs_bias(priv, type_detect: false); |
945 | break; |
946 | default: |
947 | break; |
948 | } |
949 | |
950 | snd_soc_jack_report(jack: priv->jack_hp, |
951 | status: cs42l43_jack_override_modes[override].report, |
952 | mask: cs42l43_jack_override_modes[override].report); |
953 | } |
954 | |
955 | mutex_unlock(lock: &priv->jack_lock); |
956 | |
957 | return 1; |
958 | } |
959 | |