1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver |
4 | * |
5 | * Copyright (c) 2015 Dialog Semiconductor Ltd. |
6 | * |
7 | * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/clk.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/property.h> |
15 | #include <linux/pm_wakeirq.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/delay.h> |
18 | #include <linux/workqueue.h> |
19 | #include <sound/soc.h> |
20 | #include <sound/jack.h> |
21 | #include <sound/da7219.h> |
22 | |
23 | #include "da7219.h" |
24 | #include "da7219-aad.h" |
25 | |
26 | |
27 | /* |
28 | * Detection control |
29 | */ |
30 | |
31 | void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack) |
32 | { |
33 | struct da7219_priv *da7219 = snd_soc_component_get_drvdata(c: component); |
34 | |
35 | da7219->aad->jack = jack; |
36 | da7219->aad->jack_inserted = false; |
37 | |
38 | /* Send an initial empty report */ |
39 | snd_soc_jack_report(jack, status: 0, DA7219_AAD_REPORT_ALL_MASK); |
40 | |
41 | /* Enable/Disable jack detection */ |
42 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, |
43 | DA7219_ACCDET_EN_MASK, |
44 | val: (jack ? DA7219_ACCDET_EN_MASK : 0)); |
45 | } |
46 | |
47 | /* |
48 | * Button/HPTest work |
49 | */ |
50 | |
51 | static void da7219_aad_btn_det_work(struct work_struct *work) |
52 | { |
53 | struct da7219_aad_priv *da7219_aad = |
54 | container_of(work, struct da7219_aad_priv, btn_det_work); |
55 | struct snd_soc_component *component = da7219_aad->component; |
56 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
57 | struct da7219_priv *da7219 = snd_soc_component_get_drvdata(c: component); |
58 | u8 statusa, micbias_ctrl; |
59 | bool micbias_up = false; |
60 | int retries = 0; |
61 | |
62 | /* Drive headphones/lineout */ |
63 | snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, |
64 | DA7219_HP_L_AMP_OE_MASK, |
65 | DA7219_HP_L_AMP_OE_MASK); |
66 | snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, |
67 | DA7219_HP_R_AMP_OE_MASK, |
68 | DA7219_HP_R_AMP_OE_MASK); |
69 | |
70 | /* Make sure mic bias is up */ |
71 | snd_soc_dapm_force_enable_pin(dapm, pin: "Mic Bias" ); |
72 | snd_soc_dapm_sync(dapm); |
73 | |
74 | do { |
75 | statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A); |
76 | if (statusa & DA7219_MICBIAS_UP_STS_MASK) |
77 | micbias_up = true; |
78 | else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES) |
79 | msleep(DA7219_AAD_MICBIAS_CHK_DELAY); |
80 | } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES)); |
81 | |
82 | if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES) |
83 | dev_warn(component->dev, "Mic bias status check timed out" ); |
84 | |
85 | da7219->micbias_on_event = true; |
86 | |
87 | /* |
88 | * Mic bias pulse required to enable mic, must be done before enabling |
89 | * button detection to prevent erroneous button readings. |
90 | */ |
91 | if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) { |
92 | /* Pulse higher level voltage */ |
93 | micbias_ctrl = snd_soc_component_read(component, DA7219_MICBIAS_CTRL); |
94 | snd_soc_component_update_bits(component, DA7219_MICBIAS_CTRL, |
95 | DA7219_MICBIAS1_LEVEL_MASK, |
96 | val: da7219_aad->micbias_pulse_lvl); |
97 | msleep(msecs: da7219_aad->micbias_pulse_time); |
98 | snd_soc_component_write(component, DA7219_MICBIAS_CTRL, val: micbias_ctrl); |
99 | |
100 | } |
101 | |
102 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, |
103 | DA7219_BUTTON_CONFIG_MASK, |
104 | val: da7219_aad->btn_cfg); |
105 | } |
106 | |
107 | static void da7219_aad_hptest_work(struct work_struct *work) |
108 | { |
109 | struct da7219_aad_priv *da7219_aad = |
110 | container_of(work, struct da7219_aad_priv, hptest_work); |
111 | struct snd_soc_component *component = da7219_aad->component; |
112 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
113 | struct da7219_priv *da7219 = snd_soc_component_get_drvdata(c: component); |
114 | |
115 | __le16 tonegen_freq_hptest; |
116 | u8 pll_srm_sts, pll_ctrl, gain_ramp_ctrl, accdet_cfg8; |
117 | int report = 0, ret; |
118 | |
119 | /* Lock DAPM, Kcontrols affected by this test and the PLL */ |
120 | snd_soc_dapm_mutex_lock(dapm); |
121 | mutex_lock(&da7219->ctrl_lock); |
122 | mutex_lock(&da7219->pll_lock); |
123 | |
124 | /* Ensure MCLK is available for HP test procedure */ |
125 | if (da7219->mclk) { |
126 | ret = clk_prepare_enable(clk: da7219->mclk); |
127 | if (ret) { |
128 | dev_err(component->dev, "Failed to enable mclk - %d\n" , ret); |
129 | mutex_unlock(lock: &da7219->pll_lock); |
130 | mutex_unlock(lock: &da7219->ctrl_lock); |
131 | snd_soc_dapm_mutex_unlock(dapm); |
132 | return; |
133 | } |
134 | } |
135 | |
136 | /* |
137 | * If MCLK not present, then we're using the internal oscillator and |
138 | * require different frequency settings to achieve the same result. |
139 | * |
140 | * If MCLK is present, but PLL is not enabled then we enable it here to |
141 | * ensure a consistent detection procedure. |
142 | */ |
143 | pll_srm_sts = snd_soc_component_read(component, DA7219_PLL_SRM_STS); |
144 | if (pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) { |
145 | tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ); |
146 | |
147 | pll_ctrl = snd_soc_component_read(component, DA7219_PLL_CTRL); |
148 | if ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS) |
149 | da7219_set_pll(component, source: DA7219_SYSCLK_PLL, |
150 | DA7219_PLL_FREQ_OUT_98304); |
151 | } else { |
152 | tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC); |
153 | } |
154 | |
155 | /* Ensure gain ramping at fastest rate */ |
156 | gain_ramp_ctrl = snd_soc_component_read(component, DA7219_GAIN_RAMP_CTRL); |
157 | snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_X8); |
158 | |
159 | /* Bypass cache so it saves current settings */ |
160 | regcache_cache_bypass(map: da7219->regmap, enable: true); |
161 | |
162 | /* Make sure Tone Generator is disabled */ |
163 | snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, val: 0); |
164 | |
165 | /* Enable HPTest block, 1KOhms check */ |
166 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8, |
167 | DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK, |
168 | DA7219_HPTEST_EN_MASK | |
169 | DA7219_HPTEST_RES_SEL_1KOHMS); |
170 | |
171 | /* Set gains to 0db */ |
172 | snd_soc_component_write(component, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB); |
173 | snd_soc_component_write(component, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB); |
174 | snd_soc_component_write(component, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB); |
175 | snd_soc_component_write(component, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB); |
176 | |
177 | /* Disable DAC filters, EQs and soft mute */ |
178 | snd_soc_component_update_bits(component, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK, |
179 | val: 0); |
180 | snd_soc_component_update_bits(component, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK, |
181 | val: 0); |
182 | snd_soc_component_update_bits(component, DA7219_DAC_FILTERS5, |
183 | DA7219_DAC_SOFTMUTE_EN_MASK, val: 0); |
184 | |
185 | /* Enable HP left & right paths */ |
186 | snd_soc_component_update_bits(component, DA7219_CP_CTRL, DA7219_CP_EN_MASK, |
187 | DA7219_CP_EN_MASK); |
188 | snd_soc_component_update_bits(component, DA7219_DIG_ROUTING_DAC, |
189 | DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK, |
190 | DA7219_DAC_L_SRC_TONEGEN | |
191 | DA7219_DAC_R_SRC_TONEGEN); |
192 | snd_soc_component_update_bits(component, DA7219_DAC_L_CTRL, |
193 | DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK, |
194 | DA7219_DAC_L_EN_MASK); |
195 | snd_soc_component_update_bits(component, DA7219_DAC_R_CTRL, |
196 | DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK, |
197 | DA7219_DAC_R_EN_MASK); |
198 | snd_soc_component_update_bits(component, DA7219_MIXOUT_L_SELECT, |
199 | DA7219_MIXOUT_L_MIX_SELECT_MASK, |
200 | DA7219_MIXOUT_L_MIX_SELECT_MASK); |
201 | snd_soc_component_update_bits(component, DA7219_MIXOUT_R_SELECT, |
202 | DA7219_MIXOUT_R_MIX_SELECT_MASK, |
203 | DA7219_MIXOUT_R_MIX_SELECT_MASK); |
204 | snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1L, |
205 | DA7219_OUTFILT_ST_1L_SRC_MASK, |
206 | DA7219_DMIX_ST_SRC_OUTFILT1L); |
207 | snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1R, |
208 | DA7219_OUTFILT_ST_1R_SRC_MASK, |
209 | DA7219_DMIX_ST_SRC_OUTFILT1R); |
210 | snd_soc_component_update_bits(component, DA7219_MIXOUT_L_CTRL, |
211 | DA7219_MIXOUT_L_AMP_EN_MASK, |
212 | DA7219_MIXOUT_L_AMP_EN_MASK); |
213 | snd_soc_component_update_bits(component, DA7219_MIXOUT_R_CTRL, |
214 | DA7219_MIXOUT_R_AMP_EN_MASK, |
215 | DA7219_MIXOUT_R_AMP_EN_MASK); |
216 | snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, |
217 | DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK, |
218 | DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK); |
219 | snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, |
220 | DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK, |
221 | DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK); |
222 | msleep(DA7219_SETTLING_DELAY); |
223 | snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, |
224 | DA7219_HP_L_AMP_MUTE_EN_MASK | |
225 | DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, val: 0); |
226 | snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, |
227 | DA7219_HP_R_AMP_MUTE_EN_MASK | |
228 | DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, val: 0); |
229 | |
230 | /* |
231 | * If we're running from the internal oscillator then give audio paths |
232 | * time to settle before running test. |
233 | */ |
234 | if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK)) |
235 | msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY); |
236 | |
237 | /* Configure & start Tone Generator */ |
238 | snd_soc_component_write(component, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK); |
239 | regmap_raw_write(map: da7219->regmap, DA7219_TONE_GEN_FREQ1_L, |
240 | val: &tonegen_freq_hptest, val_len: sizeof(tonegen_freq_hptest)); |
241 | snd_soc_component_update_bits(component, DA7219_TONE_GEN_CFG2, |
242 | DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK, |
243 | DA7219_SWG_SEL_SRAMP | |
244 | DA7219_TONE_GEN_GAIN_MINUS_15DB); |
245 | snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK); |
246 | |
247 | msleep(DA7219_AAD_HPTEST_PERIOD); |
248 | |
249 | /* Grab comparator reading */ |
250 | accdet_cfg8 = snd_soc_component_read(component, DA7219_ACCDET_CONFIG_8); |
251 | if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK) |
252 | report |= SND_JACK_HEADPHONE; |
253 | else |
254 | report |= SND_JACK_LINEOUT; |
255 | |
256 | /* Stop tone generator */ |
257 | snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, val: 0); |
258 | |
259 | msleep(DA7219_AAD_HPTEST_PERIOD); |
260 | |
261 | /* Restore original settings from cache */ |
262 | regcache_mark_dirty(map: da7219->regmap); |
263 | regcache_sync_region(map: da7219->regmap, DA7219_HP_L_CTRL, |
264 | DA7219_HP_R_CTRL); |
265 | msleep(DA7219_SETTLING_DELAY); |
266 | regcache_sync_region(map: da7219->regmap, DA7219_MIXOUT_L_CTRL, |
267 | DA7219_MIXOUT_R_CTRL); |
268 | regcache_sync_region(map: da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L, |
269 | DA7219_DROUTING_ST_OUTFILT_1R); |
270 | regcache_sync_region(map: da7219->regmap, DA7219_MIXOUT_L_SELECT, |
271 | DA7219_MIXOUT_R_SELECT); |
272 | regcache_sync_region(map: da7219->regmap, DA7219_DAC_L_CTRL, |
273 | DA7219_DAC_R_CTRL); |
274 | regcache_sync_region(map: da7219->regmap, DA7219_DIG_ROUTING_DAC, |
275 | DA7219_DIG_ROUTING_DAC); |
276 | regcache_sync_region(map: da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL); |
277 | regcache_sync_region(map: da7219->regmap, DA7219_DAC_FILTERS5, |
278 | DA7219_DAC_FILTERS5); |
279 | regcache_sync_region(map: da7219->regmap, DA7219_DAC_FILTERS4, |
280 | DA7219_DAC_FILTERS1); |
281 | regcache_sync_region(map: da7219->regmap, DA7219_HP_L_GAIN, |
282 | DA7219_HP_R_GAIN); |
283 | regcache_sync_region(map: da7219->regmap, DA7219_DAC_L_GAIN, |
284 | DA7219_DAC_R_GAIN); |
285 | regcache_sync_region(map: da7219->regmap, DA7219_TONE_GEN_ON_PER, |
286 | DA7219_TONE_GEN_ON_PER); |
287 | regcache_sync_region(map: da7219->regmap, DA7219_TONE_GEN_FREQ1_L, |
288 | DA7219_TONE_GEN_FREQ1_U); |
289 | regcache_sync_region(map: da7219->regmap, DA7219_TONE_GEN_CFG1, |
290 | DA7219_TONE_GEN_CFG2); |
291 | |
292 | regcache_cache_bypass(map: da7219->regmap, enable: false); |
293 | |
294 | /* Disable HPTest block */ |
295 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8, |
296 | DA7219_HPTEST_EN_MASK, val: 0); |
297 | |
298 | /* |
299 | * If we're running from the internal oscillator then give audio paths |
300 | * time to settle before allowing headphones to be driven as required. |
301 | */ |
302 | if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK)) |
303 | msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY); |
304 | |
305 | /* Restore gain ramping rate */ |
306 | snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, val: gain_ramp_ctrl); |
307 | |
308 | /* Drive Headphones/lineout */ |
309 | snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK, |
310 | DA7219_HP_L_AMP_OE_MASK); |
311 | snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK, |
312 | DA7219_HP_R_AMP_OE_MASK); |
313 | |
314 | /* Restore PLL to previous configuration, if re-configured */ |
315 | if ((pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) && |
316 | ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS)) |
317 | da7219_set_pll(component, source: DA7219_SYSCLK_MCLK, fout: 0); |
318 | |
319 | /* Remove MCLK, if previously enabled */ |
320 | if (da7219->mclk) |
321 | clk_disable_unprepare(clk: da7219->mclk); |
322 | |
323 | mutex_unlock(lock: &da7219->pll_lock); |
324 | mutex_unlock(lock: &da7219->ctrl_lock); |
325 | snd_soc_dapm_mutex_unlock(dapm); |
326 | |
327 | /* |
328 | * Only send report if jack hasn't been removed during process, |
329 | * otherwise it's invalid and we drop it. |
330 | */ |
331 | if (da7219_aad->jack_inserted) |
332 | snd_soc_jack_report(jack: da7219_aad->jack, status: report, |
333 | mask: SND_JACK_HEADSET | SND_JACK_LINEOUT); |
334 | } |
335 | |
336 | static void da7219_aad_jack_det_work(struct work_struct *work) |
337 | { |
338 | struct da7219_aad_priv *da7219_aad = |
339 | container_of(work, struct da7219_aad_priv, jack_det_work.work); |
340 | struct snd_soc_component *component = da7219_aad->component; |
341 | |
342 | /* Enable ground switch */ |
343 | snd_soc_component_update_bits(component, reg: 0xFB, mask: 0x01, val: 0x01); |
344 | } |
345 | |
346 | /* |
347 | * IRQ |
348 | */ |
349 | |
350 | static irqreturn_t da7219_aad_irq_thread(int irq, void *data) |
351 | { |
352 | struct da7219_aad_priv *da7219_aad = data; |
353 | struct snd_soc_component *component = da7219_aad->component; |
354 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
355 | struct da7219_priv *da7219 = snd_soc_component_get_drvdata(c: component); |
356 | u8 events[DA7219_AAD_IRQ_REG_MAX]; |
357 | u8 statusa; |
358 | int i, ret, report = 0, mask = 0; |
359 | |
360 | /* Read current IRQ events */ |
361 | ret = regmap_bulk_read(map: da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A, |
362 | val: events, val_count: DA7219_AAD_IRQ_REG_MAX); |
363 | if (ret) { |
364 | dev_warn_ratelimited(component->dev, "Failed to read IRQ events: %d\n" , ret); |
365 | return IRQ_NONE; |
366 | } |
367 | |
368 | if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B]) |
369 | return IRQ_NONE; |
370 | |
371 | /* Read status register for jack insertion & type status */ |
372 | statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A); |
373 | |
374 | if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_INSERTED_MASK) { |
375 | u8 srm_st; |
376 | int delay = 0; |
377 | |
378 | srm_st = snd_soc_component_read(component, |
379 | DA7219_PLL_SRM_STS) & DA7219_PLL_SRM_STS_MCLK; |
380 | delay = (da7219_aad->gnd_switch_delay * ((srm_st == 0x0) ? 2 : 1) - 2); |
381 | queue_delayed_work(wq: da7219_aad->aad_wq, |
382 | dwork: &da7219_aad->jack_det_work, |
383 | delay: msecs_to_jiffies(m: delay)); |
384 | } |
385 | |
386 | /* Clear events */ |
387 | regmap_bulk_write(map: da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A, |
388 | val: events, val_count: DA7219_AAD_IRQ_REG_MAX); |
389 | |
390 | dev_dbg(component->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n" , |
391 | events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B], |
392 | statusa); |
393 | |
394 | if (statusa & DA7219_JACK_INSERTION_STS_MASK) { |
395 | /* Jack Insertion */ |
396 | if (events[DA7219_AAD_IRQ_REG_A] & |
397 | DA7219_E_JACK_INSERTED_MASK) { |
398 | report |= SND_JACK_MECHANICAL; |
399 | mask |= SND_JACK_MECHANICAL; |
400 | da7219_aad->jack_inserted = true; |
401 | } |
402 | |
403 | /* Jack type detection */ |
404 | if (events[DA7219_AAD_IRQ_REG_A] & |
405 | DA7219_E_JACK_DETECT_COMPLETE_MASK) { |
406 | /* |
407 | * If 4-pole, then enable button detection, else perform |
408 | * HP impedance test to determine output type to report. |
409 | * |
410 | * We schedule work here as the tasks themselves can |
411 | * take time to complete, and in particular for hptest |
412 | * we want to be able to check if the jack was removed |
413 | * during the procedure as this will invalidate the |
414 | * result. By doing this as work, the IRQ thread can |
415 | * handle a removal, and we can check at the end of |
416 | * hptest if we have a valid result or not. |
417 | */ |
418 | |
419 | cancel_delayed_work_sync(dwork: &da7219_aad->jack_det_work); |
420 | /* Disable ground switch */ |
421 | snd_soc_component_update_bits(component, reg: 0xFB, mask: 0x01, val: 0x00); |
422 | |
423 | if (statusa & DA7219_JACK_TYPE_STS_MASK) { |
424 | report |= SND_JACK_HEADSET; |
425 | mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT; |
426 | queue_work(wq: da7219_aad->aad_wq, work: &da7219_aad->btn_det_work); |
427 | } else { |
428 | queue_work(wq: da7219_aad->aad_wq, work: &da7219_aad->hptest_work); |
429 | } |
430 | } |
431 | |
432 | /* Button support for 4-pole jack */ |
433 | if (statusa & DA7219_JACK_TYPE_STS_MASK) { |
434 | for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) { |
435 | /* Button Press */ |
436 | if (events[DA7219_AAD_IRQ_REG_B] & |
437 | (DA7219_E_BUTTON_A_PRESSED_MASK << i)) { |
438 | report |= SND_JACK_BTN_0 >> i; |
439 | mask |= SND_JACK_BTN_0 >> i; |
440 | } |
441 | } |
442 | snd_soc_jack_report(jack: da7219_aad->jack, status: report, mask); |
443 | |
444 | for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) { |
445 | /* Button Release */ |
446 | if (events[DA7219_AAD_IRQ_REG_B] & |
447 | (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) { |
448 | report &= ~(SND_JACK_BTN_0 >> i); |
449 | mask |= SND_JACK_BTN_0 >> i; |
450 | } |
451 | } |
452 | } |
453 | } else { |
454 | /* Jack removal */ |
455 | if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) { |
456 | report = 0; |
457 | mask |= DA7219_AAD_REPORT_ALL_MASK; |
458 | da7219_aad->jack_inserted = false; |
459 | |
460 | /* Cancel any pending work */ |
461 | cancel_delayed_work_sync(dwork: &da7219_aad->jack_det_work); |
462 | cancel_work_sync(work: &da7219_aad->btn_det_work); |
463 | cancel_work_sync(work: &da7219_aad->hptest_work); |
464 | |
465 | /* Un-drive headphones/lineout */ |
466 | snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, |
467 | DA7219_HP_R_AMP_OE_MASK, val: 0); |
468 | snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, |
469 | DA7219_HP_L_AMP_OE_MASK, val: 0); |
470 | |
471 | /* Ensure button detection disabled */ |
472 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, |
473 | DA7219_BUTTON_CONFIG_MASK, val: 0); |
474 | |
475 | da7219->micbias_on_event = false; |
476 | |
477 | /* Disable mic bias */ |
478 | snd_soc_dapm_disable_pin(dapm, pin: "Mic Bias" ); |
479 | snd_soc_dapm_sync(dapm); |
480 | |
481 | /* Disable ground switch */ |
482 | snd_soc_component_update_bits(component, reg: 0xFB, mask: 0x01, val: 0x00); |
483 | } |
484 | } |
485 | |
486 | snd_soc_jack_report(jack: da7219_aad->jack, status: report, mask); |
487 | |
488 | return IRQ_HANDLED; |
489 | } |
490 | |
491 | /* |
492 | * DT/ACPI to pdata conversion |
493 | */ |
494 | |
495 | static enum da7219_aad_micbias_pulse_lvl |
496 | da7219_aad_fw_micbias_pulse_lvl(struct device *dev, u32 val) |
497 | { |
498 | switch (val) { |
499 | case 2800: |
500 | return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V; |
501 | case 2900: |
502 | return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V; |
503 | default: |
504 | dev_warn(dev, "Invalid micbias pulse level" ); |
505 | return DA7219_AAD_MICBIAS_PULSE_LVL_OFF; |
506 | } |
507 | } |
508 | |
509 | static enum da7219_aad_btn_cfg |
510 | da7219_aad_fw_btn_cfg(struct device *dev, u32 val) |
511 | { |
512 | switch (val) { |
513 | case 2: |
514 | return DA7219_AAD_BTN_CFG_2MS; |
515 | case 5: |
516 | return DA7219_AAD_BTN_CFG_5MS; |
517 | case 10: |
518 | return DA7219_AAD_BTN_CFG_10MS; |
519 | case 50: |
520 | return DA7219_AAD_BTN_CFG_50MS; |
521 | case 100: |
522 | return DA7219_AAD_BTN_CFG_100MS; |
523 | case 200: |
524 | return DA7219_AAD_BTN_CFG_200MS; |
525 | case 500: |
526 | return DA7219_AAD_BTN_CFG_500MS; |
527 | default: |
528 | dev_warn(dev, "Invalid button config" ); |
529 | return DA7219_AAD_BTN_CFG_10MS; |
530 | } |
531 | } |
532 | |
533 | static enum da7219_aad_mic_det_thr |
534 | da7219_aad_fw_mic_det_thr(struct device *dev, u32 val) |
535 | { |
536 | switch (val) { |
537 | case 200: |
538 | return DA7219_AAD_MIC_DET_THR_200_OHMS; |
539 | case 500: |
540 | return DA7219_AAD_MIC_DET_THR_500_OHMS; |
541 | case 750: |
542 | return DA7219_AAD_MIC_DET_THR_750_OHMS; |
543 | case 1000: |
544 | return DA7219_AAD_MIC_DET_THR_1000_OHMS; |
545 | default: |
546 | dev_warn(dev, "Invalid mic detect threshold" ); |
547 | return DA7219_AAD_MIC_DET_THR_500_OHMS; |
548 | } |
549 | } |
550 | |
551 | static enum da7219_aad_jack_ins_deb |
552 | da7219_aad_fw_jack_ins_deb(struct device *dev, u32 val) |
553 | { |
554 | switch (val) { |
555 | case 5: |
556 | return DA7219_AAD_JACK_INS_DEB_5MS; |
557 | case 10: |
558 | return DA7219_AAD_JACK_INS_DEB_10MS; |
559 | case 20: |
560 | return DA7219_AAD_JACK_INS_DEB_20MS; |
561 | case 50: |
562 | return DA7219_AAD_JACK_INS_DEB_50MS; |
563 | case 100: |
564 | return DA7219_AAD_JACK_INS_DEB_100MS; |
565 | case 200: |
566 | return DA7219_AAD_JACK_INS_DEB_200MS; |
567 | case 500: |
568 | return DA7219_AAD_JACK_INS_DEB_500MS; |
569 | case 1000: |
570 | return DA7219_AAD_JACK_INS_DEB_1S; |
571 | default: |
572 | dev_warn(dev, "Invalid jack insert debounce" ); |
573 | return DA7219_AAD_JACK_INS_DEB_20MS; |
574 | } |
575 | } |
576 | |
577 | static enum da7219_aad_jack_ins_det_pty |
578 | da7219_aad_fw_jack_ins_det_pty(struct device *dev, const char *str) |
579 | { |
580 | if (!strcmp(str, "low" )) { |
581 | return DA7219_AAD_JACK_INS_DET_PTY_LOW; |
582 | } else if (!strcmp(str, "high" )) { |
583 | return DA7219_AAD_JACK_INS_DET_PTY_HIGH; |
584 | } else { |
585 | dev_warn(dev, "Invalid jack insertion detection polarity" ); |
586 | return DA7219_AAD_JACK_INS_DET_PTY_LOW; |
587 | } |
588 | } |
589 | |
590 | static enum da7219_aad_jack_det_rate |
591 | da7219_aad_fw_jack_det_rate(struct device *dev, const char *str) |
592 | { |
593 | if (!strcmp(str, "32_64" )) { |
594 | return DA7219_AAD_JACK_DET_RATE_32_64MS; |
595 | } else if (!strcmp(str, "64_128" )) { |
596 | return DA7219_AAD_JACK_DET_RATE_64_128MS; |
597 | } else if (!strcmp(str, "128_256" )) { |
598 | return DA7219_AAD_JACK_DET_RATE_128_256MS; |
599 | } else if (!strcmp(str, "256_512" )) { |
600 | return DA7219_AAD_JACK_DET_RATE_256_512MS; |
601 | } else { |
602 | dev_warn(dev, "Invalid jack detect rate" ); |
603 | return DA7219_AAD_JACK_DET_RATE_256_512MS; |
604 | } |
605 | } |
606 | |
607 | static enum da7219_aad_jack_rem_deb |
608 | da7219_aad_fw_jack_rem_deb(struct device *dev, u32 val) |
609 | { |
610 | switch (val) { |
611 | case 1: |
612 | return DA7219_AAD_JACK_REM_DEB_1MS; |
613 | case 5: |
614 | return DA7219_AAD_JACK_REM_DEB_5MS; |
615 | case 10: |
616 | return DA7219_AAD_JACK_REM_DEB_10MS; |
617 | case 20: |
618 | return DA7219_AAD_JACK_REM_DEB_20MS; |
619 | default: |
620 | dev_warn(dev, "Invalid jack removal debounce" ); |
621 | return DA7219_AAD_JACK_REM_DEB_1MS; |
622 | } |
623 | } |
624 | |
625 | static enum da7219_aad_btn_avg |
626 | da7219_aad_fw_btn_avg(struct device *dev, u32 val) |
627 | { |
628 | switch (val) { |
629 | case 1: |
630 | return DA7219_AAD_BTN_AVG_1; |
631 | case 2: |
632 | return DA7219_AAD_BTN_AVG_2; |
633 | case 4: |
634 | return DA7219_AAD_BTN_AVG_4; |
635 | case 8: |
636 | return DA7219_AAD_BTN_AVG_8; |
637 | default: |
638 | dev_warn(dev, "Invalid button average value" ); |
639 | return DA7219_AAD_BTN_AVG_2; |
640 | } |
641 | } |
642 | |
643 | static enum da7219_aad_adc_1bit_rpt |
644 | da7219_aad_fw_adc_1bit_rpt(struct device *dev, u32 val) |
645 | { |
646 | switch (val) { |
647 | case 1: |
648 | return DA7219_AAD_ADC_1BIT_RPT_1; |
649 | case 2: |
650 | return DA7219_AAD_ADC_1BIT_RPT_2; |
651 | case 4: |
652 | return DA7219_AAD_ADC_1BIT_RPT_4; |
653 | case 8: |
654 | return DA7219_AAD_ADC_1BIT_RPT_8; |
655 | default: |
656 | dev_warn(dev, "Invalid ADC 1-bit repeat value" ); |
657 | return DA7219_AAD_ADC_1BIT_RPT_1; |
658 | } |
659 | } |
660 | |
661 | static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev) |
662 | { |
663 | struct i2c_client *i2c = to_i2c_client(dev); |
664 | struct fwnode_handle *aad_np; |
665 | struct da7219_aad_pdata *aad_pdata; |
666 | const char *fw_str; |
667 | u32 fw_val32; |
668 | |
669 | aad_np = device_get_named_child_node(dev, childname: "da7219_aad" ); |
670 | if (!aad_np) |
671 | return NULL; |
672 | |
673 | aad_pdata = devm_kzalloc(dev, size: sizeof(*aad_pdata), GFP_KERNEL); |
674 | if (!aad_pdata) |
675 | return NULL; |
676 | |
677 | aad_pdata->irq = i2c->irq; |
678 | |
679 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,micbias-pulse-lvl" , |
680 | val: &fw_val32) >= 0) |
681 | aad_pdata->micbias_pulse_lvl = |
682 | da7219_aad_fw_micbias_pulse_lvl(dev, val: fw_val32); |
683 | else |
684 | aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF; |
685 | |
686 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,micbias-pulse-time" , |
687 | val: &fw_val32) >= 0) |
688 | aad_pdata->micbias_pulse_time = fw_val32; |
689 | |
690 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,btn-cfg" , val: &fw_val32) >= 0) |
691 | aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(dev, val: fw_val32); |
692 | else |
693 | aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS; |
694 | |
695 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,mic-det-thr" , val: &fw_val32) >= 0) |
696 | aad_pdata->mic_det_thr = |
697 | da7219_aad_fw_mic_det_thr(dev, val: fw_val32); |
698 | else |
699 | aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_200_OHMS; |
700 | |
701 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,jack-ins-deb" , val: &fw_val32) >= 0) |
702 | aad_pdata->jack_ins_deb = |
703 | da7219_aad_fw_jack_ins_deb(dev, val: fw_val32); |
704 | else |
705 | aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS; |
706 | |
707 | if (!fwnode_property_read_string(fwnode: aad_np, propname: "dlg,jack-ins-det-pty" , val: &fw_str)) |
708 | aad_pdata->jack_ins_det_pty = |
709 | da7219_aad_fw_jack_ins_det_pty(dev, str: fw_str); |
710 | else |
711 | aad_pdata->jack_ins_det_pty = DA7219_AAD_JACK_INS_DET_PTY_LOW; |
712 | |
713 | if (!fwnode_property_read_string(fwnode: aad_np, propname: "dlg,jack-det-rate" , val: &fw_str)) |
714 | aad_pdata->jack_det_rate = |
715 | da7219_aad_fw_jack_det_rate(dev, str: fw_str); |
716 | else |
717 | aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS; |
718 | |
719 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,jack-rem-deb" , val: &fw_val32) >= 0) |
720 | aad_pdata->jack_rem_deb = |
721 | da7219_aad_fw_jack_rem_deb(dev, val: fw_val32); |
722 | else |
723 | aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS; |
724 | |
725 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,a-d-btn-thr" , val: &fw_val32) >= 0) |
726 | aad_pdata->a_d_btn_thr = (u8) fw_val32; |
727 | else |
728 | aad_pdata->a_d_btn_thr = 0xA; |
729 | |
730 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,d-b-btn-thr" , val: &fw_val32) >= 0) |
731 | aad_pdata->d_b_btn_thr = (u8) fw_val32; |
732 | else |
733 | aad_pdata->d_b_btn_thr = 0x16; |
734 | |
735 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,b-c-btn-thr" , val: &fw_val32) >= 0) |
736 | aad_pdata->b_c_btn_thr = (u8) fw_val32; |
737 | else |
738 | aad_pdata->b_c_btn_thr = 0x21; |
739 | |
740 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,c-mic-btn-thr" , val: &fw_val32) >= 0) |
741 | aad_pdata->c_mic_btn_thr = (u8) fw_val32; |
742 | else |
743 | aad_pdata->c_mic_btn_thr = 0x3E; |
744 | |
745 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,btn-avg" , val: &fw_val32) >= 0) |
746 | aad_pdata->btn_avg = da7219_aad_fw_btn_avg(dev, val: fw_val32); |
747 | else |
748 | aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2; |
749 | |
750 | if (fwnode_property_read_u32(fwnode: aad_np, propname: "dlg,adc-1bit-rpt" , val: &fw_val32) >= 0) |
751 | aad_pdata->adc_1bit_rpt = |
752 | da7219_aad_fw_adc_1bit_rpt(dev, val: fw_val32); |
753 | else |
754 | aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1; |
755 | |
756 | return aad_pdata; |
757 | } |
758 | |
759 | static void da7219_aad_handle_pdata(struct snd_soc_component *component) |
760 | { |
761 | struct da7219_priv *da7219 = snd_soc_component_get_drvdata(c: component); |
762 | struct da7219_aad_priv *da7219_aad = da7219->aad; |
763 | struct da7219_pdata *pdata = da7219->pdata; |
764 | |
765 | if ((pdata) && (pdata->aad_pdata)) { |
766 | struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata; |
767 | u8 cfg, mask; |
768 | |
769 | da7219_aad->irq = aad_pdata->irq; |
770 | |
771 | switch (aad_pdata->micbias_pulse_lvl) { |
772 | case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V: |
773 | case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V: |
774 | da7219_aad->micbias_pulse_lvl = |
775 | (aad_pdata->micbias_pulse_lvl << |
776 | DA7219_MICBIAS1_LEVEL_SHIFT); |
777 | break; |
778 | default: |
779 | break; |
780 | } |
781 | |
782 | da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time; |
783 | |
784 | switch (aad_pdata->btn_cfg) { |
785 | case DA7219_AAD_BTN_CFG_2MS: |
786 | case DA7219_AAD_BTN_CFG_5MS: |
787 | case DA7219_AAD_BTN_CFG_10MS: |
788 | case DA7219_AAD_BTN_CFG_50MS: |
789 | case DA7219_AAD_BTN_CFG_100MS: |
790 | case DA7219_AAD_BTN_CFG_200MS: |
791 | case DA7219_AAD_BTN_CFG_500MS: |
792 | da7219_aad->btn_cfg = (aad_pdata->btn_cfg << |
793 | DA7219_BUTTON_CONFIG_SHIFT); |
794 | } |
795 | |
796 | cfg = 0; |
797 | mask = 0; |
798 | switch (aad_pdata->mic_det_thr) { |
799 | case DA7219_AAD_MIC_DET_THR_200_OHMS: |
800 | case DA7219_AAD_MIC_DET_THR_500_OHMS: |
801 | case DA7219_AAD_MIC_DET_THR_750_OHMS: |
802 | case DA7219_AAD_MIC_DET_THR_1000_OHMS: |
803 | cfg |= (aad_pdata->mic_det_thr << |
804 | DA7219_MIC_DET_THRESH_SHIFT); |
805 | mask |= DA7219_MIC_DET_THRESH_MASK; |
806 | } |
807 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, mask, val: cfg); |
808 | |
809 | cfg = 0; |
810 | mask = 0; |
811 | switch (aad_pdata->jack_ins_deb) { |
812 | case DA7219_AAD_JACK_INS_DEB_5MS: |
813 | case DA7219_AAD_JACK_INS_DEB_10MS: |
814 | case DA7219_AAD_JACK_INS_DEB_20MS: |
815 | case DA7219_AAD_JACK_INS_DEB_50MS: |
816 | case DA7219_AAD_JACK_INS_DEB_100MS: |
817 | case DA7219_AAD_JACK_INS_DEB_200MS: |
818 | case DA7219_AAD_JACK_INS_DEB_500MS: |
819 | case DA7219_AAD_JACK_INS_DEB_1S: |
820 | cfg |= (aad_pdata->jack_ins_deb << |
821 | DA7219_JACKDET_DEBOUNCE_SHIFT); |
822 | mask |= DA7219_JACKDET_DEBOUNCE_MASK; |
823 | } |
824 | switch (aad_pdata->jack_det_rate) { |
825 | case DA7219_AAD_JACK_DET_RATE_32_64MS: |
826 | case DA7219_AAD_JACK_DET_RATE_64_128MS: |
827 | case DA7219_AAD_JACK_DET_RATE_128_256MS: |
828 | case DA7219_AAD_JACK_DET_RATE_256_512MS: |
829 | cfg |= (aad_pdata->jack_det_rate << |
830 | DA7219_JACK_DETECT_RATE_SHIFT); |
831 | mask |= DA7219_JACK_DETECT_RATE_MASK; |
832 | } |
833 | switch (aad_pdata->jack_rem_deb) { |
834 | case DA7219_AAD_JACK_REM_DEB_1MS: |
835 | case DA7219_AAD_JACK_REM_DEB_5MS: |
836 | case DA7219_AAD_JACK_REM_DEB_10MS: |
837 | case DA7219_AAD_JACK_REM_DEB_20MS: |
838 | cfg |= (aad_pdata->jack_rem_deb << |
839 | DA7219_JACKDET_REM_DEB_SHIFT); |
840 | mask |= DA7219_JACKDET_REM_DEB_MASK; |
841 | } |
842 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_2, mask, val: cfg); |
843 | |
844 | snd_soc_component_write(component, DA7219_ACCDET_CONFIG_3, |
845 | val: aad_pdata->a_d_btn_thr); |
846 | snd_soc_component_write(component, DA7219_ACCDET_CONFIG_4, |
847 | val: aad_pdata->d_b_btn_thr); |
848 | snd_soc_component_write(component, DA7219_ACCDET_CONFIG_5, |
849 | val: aad_pdata->b_c_btn_thr); |
850 | snd_soc_component_write(component, DA7219_ACCDET_CONFIG_6, |
851 | val: aad_pdata->c_mic_btn_thr); |
852 | |
853 | cfg = 0; |
854 | mask = 0; |
855 | switch (aad_pdata->btn_avg) { |
856 | case DA7219_AAD_BTN_AVG_1: |
857 | case DA7219_AAD_BTN_AVG_2: |
858 | case DA7219_AAD_BTN_AVG_4: |
859 | case DA7219_AAD_BTN_AVG_8: |
860 | cfg |= (aad_pdata->btn_avg << |
861 | DA7219_BUTTON_AVERAGE_SHIFT); |
862 | mask |= DA7219_BUTTON_AVERAGE_MASK; |
863 | } |
864 | switch (aad_pdata->adc_1bit_rpt) { |
865 | case DA7219_AAD_ADC_1BIT_RPT_1: |
866 | case DA7219_AAD_ADC_1BIT_RPT_2: |
867 | case DA7219_AAD_ADC_1BIT_RPT_4: |
868 | case DA7219_AAD_ADC_1BIT_RPT_8: |
869 | cfg |= (aad_pdata->adc_1bit_rpt << |
870 | DA7219_ADC_1_BIT_REPEAT_SHIFT); |
871 | mask |= DA7219_ADC_1_BIT_REPEAT_MASK; |
872 | } |
873 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_7, mask, val: cfg); |
874 | |
875 | switch (aad_pdata->jack_ins_det_pty) { |
876 | case DA7219_AAD_JACK_INS_DET_PTY_LOW: |
877 | snd_soc_component_write(component, reg: 0xF0, val: 0x8B); |
878 | snd_soc_component_write(component, reg: 0x75, val: 0x80); |
879 | snd_soc_component_write(component, reg: 0xF0, val: 0x00); |
880 | break; |
881 | case DA7219_AAD_JACK_INS_DET_PTY_HIGH: |
882 | snd_soc_component_write(component, reg: 0xF0, val: 0x8B); |
883 | snd_soc_component_write(component, reg: 0x75, val: 0x00); |
884 | snd_soc_component_write(component, reg: 0xF0, val: 0x00); |
885 | break; |
886 | default: |
887 | break; |
888 | } |
889 | } |
890 | } |
891 | |
892 | static void da7219_aad_handle_gnd_switch_time(struct snd_soc_component *component) |
893 | { |
894 | struct da7219_priv *da7219 = snd_soc_component_get_drvdata(c: component); |
895 | struct da7219_aad_priv *da7219_aad = da7219->aad; |
896 | u8 jack_det; |
897 | |
898 | jack_det = snd_soc_component_read(component, DA7219_ACCDET_CONFIG_2) |
899 | & DA7219_JACK_DETECT_RATE_MASK; |
900 | switch (jack_det) { |
901 | case 0x00: |
902 | da7219_aad->gnd_switch_delay = 32; |
903 | break; |
904 | case 0x10: |
905 | da7219_aad->gnd_switch_delay = 64; |
906 | break; |
907 | case 0x20: |
908 | da7219_aad->gnd_switch_delay = 128; |
909 | break; |
910 | case 0x30: |
911 | da7219_aad->gnd_switch_delay = 256; |
912 | break; |
913 | default: |
914 | da7219_aad->gnd_switch_delay = 32; |
915 | break; |
916 | } |
917 | } |
918 | |
919 | /* |
920 | * Suspend/Resume |
921 | */ |
922 | |
923 | void da7219_aad_suspend(struct snd_soc_component *component) |
924 | { |
925 | struct da7219_priv *da7219 = snd_soc_component_get_drvdata(c: component); |
926 | struct da7219_aad_priv *da7219_aad = da7219->aad; |
927 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
928 | u8 micbias_ctrl; |
929 | |
930 | disable_irq(irq: da7219_aad->irq); |
931 | |
932 | if (da7219_aad->jack) { |
933 | /* Disable jack detection during suspend */ |
934 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, |
935 | DA7219_ACCDET_EN_MASK, val: 0); |
936 | cancel_delayed_work_sync(dwork: &da7219_aad->jack_det_work); |
937 | /* Disable ground switch */ |
938 | snd_soc_component_update_bits(component, reg: 0xFB, mask: 0x01, val: 0x00); |
939 | |
940 | /* |
941 | * If we have a 4-pole jack inserted, then micbias will be |
942 | * enabled. We can disable micbias here, and keep a note to |
943 | * re-enable it on resume. If jack removal occurred during |
944 | * suspend then this will be dealt with through the IRQ handler. |
945 | */ |
946 | if (da7219_aad->jack_inserted) { |
947 | micbias_ctrl = snd_soc_component_read(component, DA7219_MICBIAS_CTRL); |
948 | if (micbias_ctrl & DA7219_MICBIAS1_EN_MASK) { |
949 | snd_soc_dapm_disable_pin(dapm, pin: "Mic Bias" ); |
950 | snd_soc_dapm_sync(dapm); |
951 | da7219_aad->micbias_resume_enable = true; |
952 | } |
953 | } |
954 | } |
955 | } |
956 | |
957 | void da7219_aad_resume(struct snd_soc_component *component) |
958 | { |
959 | struct da7219_priv *da7219 = snd_soc_component_get_drvdata(c: component); |
960 | struct da7219_aad_priv *da7219_aad = da7219->aad; |
961 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
962 | |
963 | if (da7219_aad->jack) { |
964 | /* Re-enable micbias if previously enabled for 4-pole jack */ |
965 | if (da7219_aad->jack_inserted && |
966 | da7219_aad->micbias_resume_enable) { |
967 | snd_soc_dapm_force_enable_pin(dapm, pin: "Mic Bias" ); |
968 | snd_soc_dapm_sync(dapm); |
969 | da7219_aad->micbias_resume_enable = false; |
970 | } |
971 | |
972 | /* Re-enable jack detection */ |
973 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, |
974 | DA7219_ACCDET_EN_MASK, |
975 | DA7219_ACCDET_EN_MASK); |
976 | } |
977 | |
978 | enable_irq(irq: da7219_aad->irq); |
979 | } |
980 | |
981 | |
982 | /* |
983 | * Init/Exit |
984 | */ |
985 | |
986 | int da7219_aad_init(struct snd_soc_component *component) |
987 | { |
988 | struct da7219_priv *da7219 = snd_soc_component_get_drvdata(c: component); |
989 | struct da7219_aad_priv *da7219_aad = da7219->aad; |
990 | u8 mask[DA7219_AAD_IRQ_REG_MAX]; |
991 | int ret; |
992 | |
993 | da7219_aad->component = component; |
994 | |
995 | /* Handle any DT/ACPI/platform data */ |
996 | da7219_aad_handle_pdata(component); |
997 | |
998 | /* Disable button detection */ |
999 | snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, |
1000 | DA7219_BUTTON_CONFIG_MASK, val: 0); |
1001 | |
1002 | da7219_aad_handle_gnd_switch_time(component); |
1003 | |
1004 | da7219_aad->aad_wq = create_singlethread_workqueue("da7219-aad" ); |
1005 | if (!da7219_aad->aad_wq) { |
1006 | dev_err(component->dev, "Failed to create aad workqueue\n" ); |
1007 | return -ENOMEM; |
1008 | } |
1009 | |
1010 | INIT_DELAYED_WORK(&da7219_aad->jack_det_work, da7219_aad_jack_det_work); |
1011 | INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work); |
1012 | INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work); |
1013 | |
1014 | ret = request_threaded_irq(irq: da7219_aad->irq, NULL, |
1015 | thread_fn: da7219_aad_irq_thread, |
1016 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, |
1017 | name: "da7219-aad" , dev: da7219_aad); |
1018 | if (ret) { |
1019 | dev_err(component->dev, "Failed to request IRQ: %d\n" , ret); |
1020 | return ret; |
1021 | } |
1022 | |
1023 | /* Unmask AAD IRQs */ |
1024 | memset(mask, 0, DA7219_AAD_IRQ_REG_MAX); |
1025 | regmap_bulk_write(map: da7219->regmap, DA7219_ACCDET_IRQ_MASK_A, |
1026 | val: &mask, val_count: DA7219_AAD_IRQ_REG_MAX); |
1027 | |
1028 | return 0; |
1029 | } |
1030 | |
1031 | void da7219_aad_exit(struct snd_soc_component *component) |
1032 | { |
1033 | struct da7219_priv *da7219 = snd_soc_component_get_drvdata(c: component); |
1034 | struct da7219_aad_priv *da7219_aad = da7219->aad; |
1035 | u8 mask[DA7219_AAD_IRQ_REG_MAX]; |
1036 | |
1037 | /* Mask off AAD IRQs */ |
1038 | memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX); |
1039 | regmap_bulk_write(map: da7219->regmap, DA7219_ACCDET_IRQ_MASK_A, |
1040 | val: mask, val_count: DA7219_AAD_IRQ_REG_MAX); |
1041 | |
1042 | free_irq(da7219_aad->irq, da7219_aad); |
1043 | |
1044 | cancel_delayed_work_sync(dwork: &da7219_aad->jack_det_work); |
1045 | cancel_work_sync(work: &da7219_aad->btn_det_work); |
1046 | cancel_work_sync(work: &da7219_aad->hptest_work); |
1047 | destroy_workqueue(wq: da7219_aad->aad_wq); |
1048 | } |
1049 | |
1050 | /* |
1051 | * AAD related I2C probe handling |
1052 | */ |
1053 | |
1054 | int da7219_aad_probe(struct i2c_client *i2c) |
1055 | { |
1056 | struct da7219_priv *da7219 = i2c_get_clientdata(client: i2c); |
1057 | struct device *dev = &i2c->dev; |
1058 | struct da7219_aad_priv *da7219_aad; |
1059 | |
1060 | da7219_aad = devm_kzalloc(dev, size: sizeof(*da7219_aad), GFP_KERNEL); |
1061 | if (!da7219_aad) |
1062 | return -ENOMEM; |
1063 | |
1064 | da7219->aad = da7219_aad; |
1065 | |
1066 | /* Retrieve any DT/ACPI/platform data */ |
1067 | if (da7219->pdata && !da7219->pdata->aad_pdata) |
1068 | da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(dev); |
1069 | |
1070 | return 0; |
1071 | } |
1072 | |
1073 | MODULE_DESCRIPTION("ASoC DA7219 AAD Driver" ); |
1074 | MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>" ); |
1075 | MODULE_AUTHOR("David Rau <David.Rau.opensource@dm.renesas.com>" ); |
1076 | MODULE_LICENSE("GPL" ); |
1077 | |