1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // cs35l41.c -- CS35l41 ALSA SoC audio driver |
4 | // |
5 | // Copyright 2017-2021 Cirrus Logic, Inc. |
6 | // |
7 | // Author: David Rhodes <david.rhodes@cirrus.com> |
8 | |
9 | #include <linux/acpi.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/err.h> |
12 | #include <linux/init.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | #include <linux/moduleparam.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <linux/property.h> |
18 | #include <sound/initval.h> |
19 | #include <sound/pcm.h> |
20 | #include <sound/pcm_params.h> |
21 | #include <sound/soc.h> |
22 | #include <sound/soc-dapm.h> |
23 | #include <sound/tlv.h> |
24 | |
25 | #include "cs35l41.h" |
26 | |
27 | static const char * const cs35l41_supplies[CS35L41_NUM_SUPPLIES] = { |
28 | "VA" , |
29 | "VP" , |
30 | }; |
31 | |
32 | struct cs35l41_pll_sysclk_config { |
33 | int freq; |
34 | int clk_cfg; |
35 | }; |
36 | |
37 | static const struct cs35l41_pll_sysclk_config cs35l41_pll_sysclk[] = { |
38 | { 32768, 0x00 }, |
39 | { 8000, 0x01 }, |
40 | { 11025, 0x02 }, |
41 | { 12000, 0x03 }, |
42 | { 16000, 0x04 }, |
43 | { 22050, 0x05 }, |
44 | { 24000, 0x06 }, |
45 | { 32000, 0x07 }, |
46 | { 44100, 0x08 }, |
47 | { 48000, 0x09 }, |
48 | { 88200, 0x0A }, |
49 | { 96000, 0x0B }, |
50 | { 128000, 0x0C }, |
51 | { 176400, 0x0D }, |
52 | { 192000, 0x0E }, |
53 | { 256000, 0x0F }, |
54 | { 352800, 0x10 }, |
55 | { 384000, 0x11 }, |
56 | { 512000, 0x12 }, |
57 | { 705600, 0x13 }, |
58 | { 750000, 0x14 }, |
59 | { 768000, 0x15 }, |
60 | { 1000000, 0x16 }, |
61 | { 1024000, 0x17 }, |
62 | { 1200000, 0x18 }, |
63 | { 1411200, 0x19 }, |
64 | { 1500000, 0x1A }, |
65 | { 1536000, 0x1B }, |
66 | { 2000000, 0x1C }, |
67 | { 2048000, 0x1D }, |
68 | { 2400000, 0x1E }, |
69 | { 2822400, 0x1F }, |
70 | { 3000000, 0x20 }, |
71 | { 3072000, 0x21 }, |
72 | { 3200000, 0x22 }, |
73 | { 4000000, 0x23 }, |
74 | { 4096000, 0x24 }, |
75 | { 4800000, 0x25 }, |
76 | { 5644800, 0x26 }, |
77 | { 6000000, 0x27 }, |
78 | { 6144000, 0x28 }, |
79 | { 6250000, 0x29 }, |
80 | { 6400000, 0x2A }, |
81 | { 6500000, 0x2B }, |
82 | { 6750000, 0x2C }, |
83 | { 7526400, 0x2D }, |
84 | { 8000000, 0x2E }, |
85 | { 8192000, 0x2F }, |
86 | { 9600000, 0x30 }, |
87 | { 11289600, 0x31 }, |
88 | { 12000000, 0x32 }, |
89 | { 12288000, 0x33 }, |
90 | { 12500000, 0x34 }, |
91 | { 12800000, 0x35 }, |
92 | { 13000000, 0x36 }, |
93 | { 13500000, 0x37 }, |
94 | { 19200000, 0x38 }, |
95 | { 22579200, 0x39 }, |
96 | { 24000000, 0x3A }, |
97 | { 24576000, 0x3B }, |
98 | { 25000000, 0x3C }, |
99 | { 25600000, 0x3D }, |
100 | { 26000000, 0x3E }, |
101 | { 27000000, 0x3F }, |
102 | }; |
103 | |
104 | struct cs35l41_fs_mon_config { |
105 | int freq; |
106 | unsigned int fs1; |
107 | unsigned int fs2; |
108 | }; |
109 | |
110 | static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = { |
111 | { 32768, 2254, 3754 }, |
112 | { 8000, 9220, 15364 }, |
113 | { 11025, 6148, 10244 }, |
114 | { 12000, 6148, 10244 }, |
115 | { 16000, 4612, 7684 }, |
116 | { 22050, 3076, 5124 }, |
117 | { 24000, 3076, 5124 }, |
118 | { 32000, 2308, 3844 }, |
119 | { 44100, 1540, 2564 }, |
120 | { 48000, 1540, 2564 }, |
121 | { 88200, 772, 1284 }, |
122 | { 96000, 772, 1284 }, |
123 | { 128000, 580, 964 }, |
124 | { 176400, 388, 644 }, |
125 | { 192000, 388, 644 }, |
126 | { 256000, 292, 484 }, |
127 | { 352800, 196, 324 }, |
128 | { 384000, 196, 324 }, |
129 | { 512000, 148, 244 }, |
130 | { 705600, 100, 164 }, |
131 | { 750000, 100, 164 }, |
132 | { 768000, 100, 164 }, |
133 | { 1000000, 76, 124 }, |
134 | { 1024000, 76, 124 }, |
135 | { 1200000, 64, 104 }, |
136 | { 1411200, 52, 84 }, |
137 | { 1500000, 52, 84 }, |
138 | { 1536000, 52, 84 }, |
139 | { 2000000, 40, 64 }, |
140 | { 2048000, 40, 64 }, |
141 | { 2400000, 34, 54 }, |
142 | { 2822400, 28, 44 }, |
143 | { 3000000, 28, 44 }, |
144 | { 3072000, 28, 44 }, |
145 | { 3200000, 27, 42 }, |
146 | { 4000000, 22, 34 }, |
147 | { 4096000, 22, 34 }, |
148 | { 4800000, 19, 29 }, |
149 | { 5644800, 16, 24 }, |
150 | { 6000000, 16, 24 }, |
151 | { 6144000, 16, 24 }, |
152 | { 12288000, 0, 0 }, |
153 | }; |
154 | |
155 | static int cs35l41_get_fs_mon_config_index(int freq) |
156 | { |
157 | int i; |
158 | |
159 | for (i = 0; i < ARRAY_SIZE(cs35l41_fs_mon); i++) { |
160 | if (cs35l41_fs_mon[i].freq == freq) |
161 | return i; |
162 | } |
163 | |
164 | return -EINVAL; |
165 | } |
166 | |
167 | static const DECLARE_TLV_DB_RANGE(dig_vol_tlv, |
168 | 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), |
169 | 1, 913, TLV_DB_MINMAX_ITEM(-10200, 1200)); |
170 | static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 50, 100, 0); |
171 | |
172 | static const struct snd_kcontrol_new dre_ctrl = |
173 | SOC_DAPM_SINGLE("Switch" , CS35L41_PWR_CTRL3, 20, 1, 0); |
174 | |
175 | static const char * const cs35l41_pcm_sftramp_text[] = { |
176 | "Off" , ".5ms" , "1ms" , "2ms" , "4ms" , "8ms" , "15ms" , "30ms" |
177 | }; |
178 | |
179 | static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, |
180 | CS35L41_AMP_DIG_VOL_CTRL, 0, |
181 | cs35l41_pcm_sftramp_text); |
182 | |
183 | static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w, |
184 | struct snd_kcontrol *kcontrol, int event) |
185 | { |
186 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
187 | struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(c: component); |
188 | int ret; |
189 | |
190 | switch (event) { |
191 | case SND_SOC_DAPM_PRE_PMU: |
192 | if (cs35l41->dsp.cs_dsp.booted) |
193 | return 0; |
194 | |
195 | return wm_adsp_early_event(w, kcontrol, event); |
196 | case SND_SOC_DAPM_PRE_PMD: |
197 | if (cs35l41->dsp.preloaded) |
198 | return 0; |
199 | |
200 | if (cs35l41->dsp.cs_dsp.running) { |
201 | ret = wm_adsp_event(w, kcontrol, event); |
202 | if (ret) |
203 | return ret; |
204 | } |
205 | |
206 | return wm_adsp_early_event(w, kcontrol, event); |
207 | default: |
208 | return 0; |
209 | } |
210 | } |
211 | |
212 | static int cs35l41_dsp_audio_ev(struct snd_soc_dapm_widget *w, |
213 | struct snd_kcontrol *kcontrol, int event) |
214 | { |
215 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
216 | struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(c: component); |
217 | unsigned int fw_status; |
218 | int ret; |
219 | |
220 | switch (event) { |
221 | case SND_SOC_DAPM_POST_PMU: |
222 | if (!cs35l41->dsp.cs_dsp.running) |
223 | return wm_adsp_event(w, kcontrol, event); |
224 | |
225 | ret = regmap_read(map: cs35l41->regmap, CS35L41_DSP_MBOX_2, val: &fw_status); |
226 | if (ret < 0) { |
227 | dev_err(cs35l41->dev, |
228 | "Failed to read firmware status: %d\n" , ret); |
229 | return ret; |
230 | } |
231 | |
232 | switch (fw_status) { |
233 | case CSPL_MBOX_STS_RUNNING: |
234 | case CSPL_MBOX_STS_PAUSED: |
235 | break; |
236 | default: |
237 | dev_err(cs35l41->dev, "Firmware status is invalid: %u\n" , |
238 | fw_status); |
239 | return -EINVAL; |
240 | } |
241 | |
242 | return cs35l41_set_cspl_mbox_cmd(dev: cs35l41->dev, regmap: cs35l41->regmap, |
243 | cmd: CSPL_MBOX_CMD_RESUME); |
244 | case SND_SOC_DAPM_PRE_PMD: |
245 | return cs35l41_set_cspl_mbox_cmd(dev: cs35l41->dev, regmap: cs35l41->regmap, |
246 | cmd: CSPL_MBOX_CMD_PAUSE); |
247 | default: |
248 | return 0; |
249 | } |
250 | } |
251 | |
252 | static const char * const cs35l41_pcm_source_texts[] = {"ASP" , "DSP" }; |
253 | static const unsigned int cs35l41_pcm_source_values[] = {0x08, 0x32}; |
254 | static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum, |
255 | CS35L41_DAC_PCM1_SRC, |
256 | 0, CS35L41_ASP_SOURCE_MASK, |
257 | cs35l41_pcm_source_texts, |
258 | cs35l41_pcm_source_values); |
259 | |
260 | static const struct snd_kcontrol_new pcm_source_mux = |
261 | SOC_DAPM_ENUM("PCM Source" , cs35l41_pcm_source_enum); |
262 | |
263 | static const char * const cs35l41_tx_input_texts[] = { |
264 | "Zero" , "ASPRX1" , "ASPRX2" , "VMON" , "IMON" , |
265 | "VPMON" , "VBSTMON" , "DSPTX1" , "DSPTX2" |
266 | }; |
267 | |
268 | static const unsigned int cs35l41_tx_input_values[] = { |
269 | 0x00, CS35L41_INPUT_SRC_ASPRX1, CS35L41_INPUT_SRC_ASPRX2, |
270 | CS35L41_INPUT_SRC_VMON, CS35L41_INPUT_SRC_IMON, CS35L41_INPUT_SRC_VPMON, |
271 | CS35L41_INPUT_SRC_VBSTMON, CS35L41_INPUT_DSP_TX1, CS35L41_INPUT_DSP_TX2 |
272 | }; |
273 | |
274 | static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx1_enum, |
275 | CS35L41_ASP_TX1_SRC, |
276 | 0, CS35L41_ASP_SOURCE_MASK, |
277 | cs35l41_tx_input_texts, |
278 | cs35l41_tx_input_values); |
279 | |
280 | static const struct snd_kcontrol_new asp_tx1_mux = |
281 | SOC_DAPM_ENUM("ASPTX1 SRC" , cs35l41_asptx1_enum); |
282 | |
283 | static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx2_enum, |
284 | CS35L41_ASP_TX2_SRC, |
285 | 0, CS35L41_ASP_SOURCE_MASK, |
286 | cs35l41_tx_input_texts, |
287 | cs35l41_tx_input_values); |
288 | |
289 | static const struct snd_kcontrol_new asp_tx2_mux = |
290 | SOC_DAPM_ENUM("ASPTX2 SRC" , cs35l41_asptx2_enum); |
291 | |
292 | static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx3_enum, |
293 | CS35L41_ASP_TX3_SRC, |
294 | 0, CS35L41_ASP_SOURCE_MASK, |
295 | cs35l41_tx_input_texts, |
296 | cs35l41_tx_input_values); |
297 | |
298 | static const struct snd_kcontrol_new asp_tx3_mux = |
299 | SOC_DAPM_ENUM("ASPTX3 SRC" , cs35l41_asptx3_enum); |
300 | |
301 | static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum, |
302 | CS35L41_ASP_TX4_SRC, |
303 | 0, CS35L41_ASP_SOURCE_MASK, |
304 | cs35l41_tx_input_texts, |
305 | cs35l41_tx_input_values); |
306 | |
307 | static const struct snd_kcontrol_new asp_tx4_mux = |
308 | SOC_DAPM_ENUM("ASPTX4 SRC" , cs35l41_asptx4_enum); |
309 | |
310 | static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx1_enum, |
311 | CS35L41_DSP1_RX1_SRC, |
312 | 0, CS35L41_ASP_SOURCE_MASK, |
313 | cs35l41_tx_input_texts, |
314 | cs35l41_tx_input_values); |
315 | |
316 | static const struct snd_kcontrol_new dsp_rx1_mux = |
317 | SOC_DAPM_ENUM("DSPRX1 SRC" , cs35l41_dsprx1_enum); |
318 | |
319 | static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx2_enum, |
320 | CS35L41_DSP1_RX2_SRC, |
321 | 0, CS35L41_ASP_SOURCE_MASK, |
322 | cs35l41_tx_input_texts, |
323 | cs35l41_tx_input_values); |
324 | |
325 | static const struct snd_kcontrol_new dsp_rx2_mux = |
326 | SOC_DAPM_ENUM("DSPRX2 SRC" , cs35l41_dsprx2_enum); |
327 | |
328 | static const struct snd_kcontrol_new cs35l41_aud_controls[] = { |
329 | SOC_SINGLE_SX_TLV("Digital PCM Volume" , CS35L41_AMP_DIG_VOL_CTRL, |
330 | 3, 0x4CF, 0x391, dig_vol_tlv), |
331 | SOC_SINGLE_TLV("Analog PCM Volume" , CS35L41_AMP_GAIN_CTRL, 5, 0x14, 0, |
332 | amp_gain_tlv), |
333 | SOC_ENUM("PCM Soft Ramp" , pcm_sft_ramp), |
334 | SOC_SINGLE("HW Noise Gate Enable" , CS35L41_NG_CFG, 8, 63, 0), |
335 | SOC_SINGLE("HW Noise Gate Delay" , CS35L41_NG_CFG, 4, 7, 0), |
336 | SOC_SINGLE("HW Noise Gate Threshold" , CS35L41_NG_CFG, 0, 7, 0), |
337 | SOC_SINGLE("Aux Noise Gate CH1 Switch" , |
338 | CS35L41_MIXER_NGATE_CH1_CFG, 16, 1, 0), |
339 | SOC_SINGLE("Aux Noise Gate CH1 Entry Delay" , |
340 | CS35L41_MIXER_NGATE_CH1_CFG, 8, 15, 0), |
341 | SOC_SINGLE("Aux Noise Gate CH1 Threshold" , |
342 | CS35L41_MIXER_NGATE_CH1_CFG, 0, 7, 0), |
343 | SOC_SINGLE("Aux Noise Gate CH2 Entry Delay" , |
344 | CS35L41_MIXER_NGATE_CH2_CFG, 8, 15, 0), |
345 | SOC_SINGLE("Aux Noise Gate CH2 Switch" , |
346 | CS35L41_MIXER_NGATE_CH2_CFG, 16, 1, 0), |
347 | SOC_SINGLE("Aux Noise Gate CH2 Threshold" , |
348 | CS35L41_MIXER_NGATE_CH2_CFG, 0, 7, 0), |
349 | SOC_SINGLE("SCLK Force Switch" , CS35L41_SP_FORMAT, CS35L41_SCLK_FRC_SHIFT, 1, 0), |
350 | SOC_SINGLE("LRCLK Force Switch" , CS35L41_SP_FORMAT, CS35L41_LRCLK_FRC_SHIFT, 1, 0), |
351 | SOC_SINGLE("Invert Class D Switch" , CS35L41_AMP_DIG_VOL_CTRL, |
352 | CS35L41_AMP_INV_PCM_SHIFT, 1, 0), |
353 | SOC_SINGLE("Amp Gain ZC Switch" , CS35L41_AMP_GAIN_CTRL, |
354 | CS35L41_AMP_GAIN_ZC_SHIFT, 1, 0), |
355 | WM_ADSP2_PRELOAD_SWITCH("DSP1" , 1), |
356 | WM_ADSP_FW_CONTROL("DSP1" , 0), |
357 | }; |
358 | |
359 | static void cs35l41_boost_enable(struct cs35l41_private *cs35l41, unsigned int enable) |
360 | { |
361 | switch (cs35l41->hw_cfg.bst_type) { |
362 | case CS35L41_INT_BOOST: |
363 | case CS35L41_SHD_BOOST_ACTV: |
364 | enable = enable ? CS35L41_BST_EN_DEFAULT : CS35L41_BST_DIS_FET_OFF; |
365 | regmap_update_bits(map: cs35l41->regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK, |
366 | val: enable << CS35L41_BST_EN_SHIFT); |
367 | break; |
368 | default: |
369 | break; |
370 | } |
371 | } |
372 | |
373 | |
374 | static void cs35l41_error_release(struct cs35l41_private *cs35l41, unsigned int irq_err_bit, |
375 | unsigned int rel_err_bit) |
376 | { |
377 | regmap_write(map: cs35l41->regmap, CS35L41_IRQ1_STATUS1, val: irq_err_bit); |
378 | regmap_write(map: cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, val: 0); |
379 | regmap_update_bits(map: cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, mask: rel_err_bit, val: rel_err_bit); |
380 | regmap_update_bits(map: cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, mask: rel_err_bit, val: 0); |
381 | } |
382 | |
383 | static irqreturn_t cs35l41_irq(int irq, void *data) |
384 | { |
385 | struct cs35l41_private *cs35l41 = data; |
386 | unsigned int status[4] = { 0, 0, 0, 0 }; |
387 | unsigned int masks[4] = { 0, 0, 0, 0 }; |
388 | unsigned int i; |
389 | int ret; |
390 | |
391 | ret = pm_runtime_resume_and_get(dev: cs35l41->dev); |
392 | if (ret < 0) { |
393 | dev_err(cs35l41->dev, |
394 | "pm_runtime_resume_and_get failed in %s: %d\n" , |
395 | __func__, ret); |
396 | return IRQ_NONE; |
397 | } |
398 | |
399 | ret = IRQ_NONE; |
400 | |
401 | for (i = 0; i < ARRAY_SIZE(status); i++) { |
402 | regmap_read(map: cs35l41->regmap, |
403 | CS35L41_IRQ1_STATUS1 + (i * CS35L41_REGSTRIDE), |
404 | val: &status[i]); |
405 | regmap_read(map: cs35l41->regmap, |
406 | CS35L41_IRQ1_MASK1 + (i * CS35L41_REGSTRIDE), |
407 | val: &masks[i]); |
408 | } |
409 | |
410 | /* Check to see if unmasked bits are active */ |
411 | if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) && |
412 | !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) |
413 | goto done; |
414 | |
415 | if (status[3] & CS35L41_OTP_BOOT_DONE) { |
416 | regmap_update_bits(map: cs35l41->regmap, CS35L41_IRQ1_MASK4, |
417 | CS35L41_OTP_BOOT_DONE, CS35L41_OTP_BOOT_DONE); |
418 | } |
419 | |
420 | /* |
421 | * The following interrupts require a |
422 | * protection release cycle to get the |
423 | * speaker out of Safe-Mode. |
424 | */ |
425 | if (status[0] & CS35L41_AMP_SHORT_ERR) { |
426 | dev_crit_ratelimited(cs35l41->dev, "Amp short error\n" ); |
427 | cs35l41_error_release(cs35l41, CS35L41_AMP_SHORT_ERR, CS35L41_AMP_SHORT_ERR_RLS); |
428 | ret = IRQ_HANDLED; |
429 | } |
430 | |
431 | if (status[0] & CS35L41_TEMP_WARN) { |
432 | dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n" ); |
433 | cs35l41_error_release(cs35l41, CS35L41_TEMP_WARN, CS35L41_TEMP_WARN_ERR_RLS); |
434 | ret = IRQ_HANDLED; |
435 | } |
436 | |
437 | if (status[0] & CS35L41_TEMP_ERR) { |
438 | dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n" ); |
439 | cs35l41_error_release(cs35l41, CS35L41_TEMP_ERR, CS35L41_TEMP_ERR_RLS); |
440 | ret = IRQ_HANDLED; |
441 | } |
442 | |
443 | if (status[0] & CS35L41_BST_OVP_ERR) { |
444 | dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n" ); |
445 | cs35l41_boost_enable(cs35l41, enable: 0); |
446 | cs35l41_error_release(cs35l41, CS35L41_BST_OVP_ERR, CS35L41_BST_OVP_ERR_RLS); |
447 | cs35l41_boost_enable(cs35l41, enable: 1); |
448 | ret = IRQ_HANDLED; |
449 | } |
450 | |
451 | if (status[0] & CS35L41_BST_DCM_UVP_ERR) { |
452 | dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n" ); |
453 | cs35l41_boost_enable(cs35l41, enable: 0); |
454 | cs35l41_error_release(cs35l41, CS35L41_BST_DCM_UVP_ERR, CS35L41_BST_UVP_ERR_RLS); |
455 | cs35l41_boost_enable(cs35l41, enable: 1); |
456 | ret = IRQ_HANDLED; |
457 | } |
458 | |
459 | if (status[0] & CS35L41_BST_SHORT_ERR) { |
460 | dev_crit_ratelimited(cs35l41->dev, "LBST error: powering off!\n" ); |
461 | cs35l41_boost_enable(cs35l41, enable: 0); |
462 | cs35l41_error_release(cs35l41, CS35L41_BST_SHORT_ERR, CS35L41_BST_SHORT_ERR_RLS); |
463 | cs35l41_boost_enable(cs35l41, enable: 1); |
464 | ret = IRQ_HANDLED; |
465 | } |
466 | |
467 | if (status[2] & CS35L41_PLL_LOCK) { |
468 | regmap_write(map: cs35l41->regmap, CS35L41_IRQ1_STATUS3, CS35L41_PLL_LOCK); |
469 | |
470 | if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV || |
471 | cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS) { |
472 | ret = cs35l41_mdsync_up(regmap: cs35l41->regmap); |
473 | if (ret) |
474 | dev_err(cs35l41->dev, "MDSYNC-up failed: %d\n" , ret); |
475 | else |
476 | dev_dbg(cs35l41->dev, "MDSYNC-up done\n" ); |
477 | |
478 | dev_dbg(cs35l41->dev, "PUP-done status: %d\n" , |
479 | !!(status[0] & CS35L41_PUP_DONE_MASK)); |
480 | } |
481 | |
482 | ret = IRQ_HANDLED; |
483 | } |
484 | |
485 | done: |
486 | pm_runtime_mark_last_busy(dev: cs35l41->dev); |
487 | pm_runtime_put_autosuspend(dev: cs35l41->dev); |
488 | |
489 | return ret; |
490 | } |
491 | |
492 | static const struct reg_sequence cs35l41_pup_patch[] = { |
493 | { CS35L41_TEST_KEY_CTL, 0x00000055 }, |
494 | { CS35L41_TEST_KEY_CTL, 0x000000AA }, |
495 | { 0x00002084, 0x002F1AA0 }, |
496 | { CS35L41_TEST_KEY_CTL, 0x000000CC }, |
497 | { CS35L41_TEST_KEY_CTL, 0x00000033 }, |
498 | }; |
499 | |
500 | static const struct reg_sequence cs35l41_pdn_patch[] = { |
501 | { CS35L41_TEST_KEY_CTL, 0x00000055 }, |
502 | { CS35L41_TEST_KEY_CTL, 0x000000AA }, |
503 | { 0x00002084, 0x002F1AA3 }, |
504 | { CS35L41_TEST_KEY_CTL, 0x000000CC }, |
505 | { CS35L41_TEST_KEY_CTL, 0x00000033 }, |
506 | }; |
507 | |
508 | static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, |
509 | struct snd_kcontrol *kcontrol, int event) |
510 | { |
511 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
512 | struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(c: component); |
513 | int ret = 0; |
514 | |
515 | switch (event) { |
516 | case SND_SOC_DAPM_PRE_PMU: |
517 | regmap_multi_reg_write_bypassed(map: cs35l41->regmap, |
518 | regs: cs35l41_pup_patch, |
519 | ARRAY_SIZE(cs35l41_pup_patch)); |
520 | |
521 | ret = cs35l41_global_enable(dev: cs35l41->dev, regmap: cs35l41->regmap, b_type: cs35l41->hw_cfg.bst_type, |
522 | enable: 1, dsp: &cs35l41->dsp.cs_dsp); |
523 | break; |
524 | case SND_SOC_DAPM_POST_PMD: |
525 | ret = cs35l41_global_enable(dev: cs35l41->dev, regmap: cs35l41->regmap, b_type: cs35l41->hw_cfg.bst_type, |
526 | enable: 0, dsp: &cs35l41->dsp.cs_dsp); |
527 | |
528 | regmap_multi_reg_write_bypassed(map: cs35l41->regmap, |
529 | regs: cs35l41_pdn_patch, |
530 | ARRAY_SIZE(cs35l41_pdn_patch)); |
531 | break; |
532 | default: |
533 | dev_err(cs35l41->dev, "Invalid event = 0x%x\n" , event); |
534 | ret = -EINVAL; |
535 | } |
536 | |
537 | return ret; |
538 | } |
539 | |
540 | static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { |
541 | SND_SOC_DAPM_SPK("DSP1 Preload" , NULL), |
542 | SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader" , 100, SND_SOC_NOPM, 0, 0, |
543 | cs35l41_dsp_preload_ev, |
544 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), |
545 | SND_SOC_DAPM_OUT_DRV_E("DSP1" , SND_SOC_NOPM, 0, 0, NULL, 0, |
546 | cs35l41_dsp_audio_ev, |
547 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
548 | |
549 | SND_SOC_DAPM_OUTPUT("SPK" ), |
550 | |
551 | SND_SOC_DAPM_AIF_IN("ASPRX1" , NULL, 0, CS35L41_SP_ENABLES, 16, 0), |
552 | SND_SOC_DAPM_AIF_IN("ASPRX2" , NULL, 0, CS35L41_SP_ENABLES, 17, 0), |
553 | SND_SOC_DAPM_AIF_OUT("ASPTX1" , NULL, 0, CS35L41_SP_ENABLES, 0, 0), |
554 | SND_SOC_DAPM_AIF_OUT("ASPTX2" , NULL, 0, CS35L41_SP_ENABLES, 1, 0), |
555 | SND_SOC_DAPM_AIF_OUT("ASPTX3" , NULL, 0, CS35L41_SP_ENABLES, 2, 0), |
556 | SND_SOC_DAPM_AIF_OUT("ASPTX4" , NULL, 0, CS35L41_SP_ENABLES, 3, 0), |
557 | |
558 | SND_SOC_DAPM_SIGGEN("VSENSE" ), |
559 | SND_SOC_DAPM_SIGGEN("ISENSE" ), |
560 | SND_SOC_DAPM_SIGGEN("VP" ), |
561 | SND_SOC_DAPM_SIGGEN("VBST" ), |
562 | SND_SOC_DAPM_SIGGEN("TEMP" ), |
563 | |
564 | SND_SOC_DAPM_SUPPLY("VMON" , CS35L41_PWR_CTRL2, 12, 0, NULL, 0), |
565 | SND_SOC_DAPM_SUPPLY("IMON" , CS35L41_PWR_CTRL2, 13, 0, NULL, 0), |
566 | SND_SOC_DAPM_SUPPLY("VPMON" , CS35L41_PWR_CTRL2, 8, 0, NULL, 0), |
567 | SND_SOC_DAPM_SUPPLY("VBSTMON" , CS35L41_PWR_CTRL2, 9, 0, NULL, 0), |
568 | SND_SOC_DAPM_SUPPLY("TEMPMON" , CS35L41_PWR_CTRL2, 10, 0, NULL, 0), |
569 | |
570 | SND_SOC_DAPM_ADC("VMON ADC" , NULL, SND_SOC_NOPM, 0, 0), |
571 | SND_SOC_DAPM_ADC("IMON ADC" , NULL, SND_SOC_NOPM, 0, 0), |
572 | SND_SOC_DAPM_ADC("VPMON ADC" , NULL, SND_SOC_NOPM, 0, 0), |
573 | SND_SOC_DAPM_ADC("VBSTMON ADC" , NULL, SND_SOC_NOPM, 0, 0), |
574 | SND_SOC_DAPM_ADC("TEMPMON ADC" , NULL, SND_SOC_NOPM, 0, 0), |
575 | |
576 | SND_SOC_DAPM_ADC("CLASS H" , NULL, CS35L41_PWR_CTRL3, 4, 0), |
577 | |
578 | SND_SOC_DAPM_OUT_DRV_E("Main AMP" , CS35L41_PWR_CTRL2, 0, 0, NULL, 0, |
579 | cs35l41_main_amp_event, |
580 | SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), |
581 | |
582 | SND_SOC_DAPM_MUX("ASP TX1 Source" , SND_SOC_NOPM, 0, 0, &asp_tx1_mux), |
583 | SND_SOC_DAPM_MUX("ASP TX2 Source" , SND_SOC_NOPM, 0, 0, &asp_tx2_mux), |
584 | SND_SOC_DAPM_MUX("ASP TX3 Source" , SND_SOC_NOPM, 0, 0, &asp_tx3_mux), |
585 | SND_SOC_DAPM_MUX("ASP TX4 Source" , SND_SOC_NOPM, 0, 0, &asp_tx4_mux), |
586 | SND_SOC_DAPM_MUX("DSP RX1 Source" , SND_SOC_NOPM, 0, 0, &dsp_rx1_mux), |
587 | SND_SOC_DAPM_MUX("DSP RX2 Source" , SND_SOC_NOPM, 0, 0, &dsp_rx2_mux), |
588 | SND_SOC_DAPM_MUX("PCM Source" , SND_SOC_NOPM, 0, 0, &pcm_source_mux), |
589 | SND_SOC_DAPM_SWITCH("DRE" , SND_SOC_NOPM, 0, 0, &dre_ctrl), |
590 | }; |
591 | |
592 | static const struct snd_soc_dapm_route cs35l41_audio_map[] = { |
593 | {"DSP RX1 Source" , "ASPRX1" , "ASPRX1" }, |
594 | {"DSP RX1 Source" , "ASPRX2" , "ASPRX2" }, |
595 | {"DSP RX2 Source" , "ASPRX1" , "ASPRX1" }, |
596 | {"DSP RX2 Source" , "ASPRX2" , "ASPRX2" }, |
597 | |
598 | {"DSP1" , NULL, "DSP RX1 Source" }, |
599 | {"DSP1" , NULL, "DSP RX2 Source" }, |
600 | |
601 | {"ASP TX1 Source" , "VMON" , "VMON ADC" }, |
602 | {"ASP TX1 Source" , "IMON" , "IMON ADC" }, |
603 | {"ASP TX1 Source" , "VPMON" , "VPMON ADC" }, |
604 | {"ASP TX1 Source" , "VBSTMON" , "VBSTMON ADC" }, |
605 | {"ASP TX1 Source" , "DSPTX1" , "DSP1" }, |
606 | {"ASP TX1 Source" , "DSPTX2" , "DSP1" }, |
607 | {"ASP TX1 Source" , "ASPRX1" , "ASPRX1" }, |
608 | {"ASP TX1 Source" , "ASPRX2" , "ASPRX2" }, |
609 | {"ASP TX2 Source" , "VMON" , "VMON ADC" }, |
610 | {"ASP TX2 Source" , "IMON" , "IMON ADC" }, |
611 | {"ASP TX2 Source" , "VPMON" , "VPMON ADC" }, |
612 | {"ASP TX2 Source" , "VBSTMON" , "VBSTMON ADC" }, |
613 | {"ASP TX2 Source" , "DSPTX1" , "DSP1" }, |
614 | {"ASP TX2 Source" , "DSPTX2" , "DSP1" }, |
615 | {"ASP TX2 Source" , "ASPRX1" , "ASPRX1" }, |
616 | {"ASP TX2 Source" , "ASPRX2" , "ASPRX2" }, |
617 | {"ASP TX3 Source" , "VMON" , "VMON ADC" }, |
618 | {"ASP TX3 Source" , "IMON" , "IMON ADC" }, |
619 | {"ASP TX3 Source" , "VPMON" , "VPMON ADC" }, |
620 | {"ASP TX3 Source" , "VBSTMON" , "VBSTMON ADC" }, |
621 | {"ASP TX3 Source" , "DSPTX1" , "DSP1" }, |
622 | {"ASP TX3 Source" , "DSPTX2" , "DSP1" }, |
623 | {"ASP TX3 Source" , "ASPRX1" , "ASPRX1" }, |
624 | {"ASP TX3 Source" , "ASPRX2" , "ASPRX2" }, |
625 | {"ASP TX4 Source" , "VMON" , "VMON ADC" }, |
626 | {"ASP TX4 Source" , "IMON" , "IMON ADC" }, |
627 | {"ASP TX4 Source" , "VPMON" , "VPMON ADC" }, |
628 | {"ASP TX4 Source" , "VBSTMON" , "VBSTMON ADC" }, |
629 | {"ASP TX4 Source" , "DSPTX1" , "DSP1" }, |
630 | {"ASP TX4 Source" , "DSPTX2" , "DSP1" }, |
631 | {"ASP TX4 Source" , "ASPRX1" , "ASPRX1" }, |
632 | {"ASP TX4 Source" , "ASPRX2" , "ASPRX2" }, |
633 | {"ASPTX1" , NULL, "ASP TX1 Source" }, |
634 | {"ASPTX2" , NULL, "ASP TX2 Source" }, |
635 | {"ASPTX3" , NULL, "ASP TX3 Source" }, |
636 | {"ASPTX4" , NULL, "ASP TX4 Source" }, |
637 | {"AMP Capture" , NULL, "ASPTX1" }, |
638 | {"AMP Capture" , NULL, "ASPTX2" }, |
639 | {"AMP Capture" , NULL, "ASPTX3" }, |
640 | {"AMP Capture" , NULL, "ASPTX4" }, |
641 | |
642 | {"DSP1" , NULL, "VMON" }, |
643 | {"DSP1" , NULL, "IMON" }, |
644 | {"DSP1" , NULL, "VPMON" }, |
645 | {"DSP1" , NULL, "VBSTMON" }, |
646 | {"DSP1" , NULL, "TEMPMON" }, |
647 | |
648 | {"VMON ADC" , NULL, "VMON" }, |
649 | {"IMON ADC" , NULL, "IMON" }, |
650 | {"VPMON ADC" , NULL, "VPMON" }, |
651 | {"VBSTMON ADC" , NULL, "VBSTMON" }, |
652 | {"TEMPMON ADC" , NULL, "TEMPMON" }, |
653 | |
654 | {"VMON ADC" , NULL, "VSENSE" }, |
655 | {"IMON ADC" , NULL, "ISENSE" }, |
656 | {"VPMON ADC" , NULL, "VP" }, |
657 | {"VBSTMON ADC" , NULL, "VBST" }, |
658 | {"TEMPMON ADC" , NULL, "TEMP" }, |
659 | |
660 | {"DSP1 Preload" , NULL, "DSP1 Preloader" }, |
661 | {"DSP1" , NULL, "DSP1 Preloader" }, |
662 | |
663 | {"ASPRX1" , NULL, "AMP Playback" }, |
664 | {"ASPRX2" , NULL, "AMP Playback" }, |
665 | {"DRE" , "Switch" , "CLASS H" }, |
666 | {"Main AMP" , NULL, "CLASS H" }, |
667 | {"Main AMP" , NULL, "DRE" }, |
668 | {"SPK" , NULL, "Main AMP" }, |
669 | |
670 | {"PCM Source" , "ASP" , "ASPRX1" }, |
671 | {"PCM Source" , "DSP" , "DSP1" }, |
672 | {"CLASS H" , NULL, "PCM Source" }, |
673 | }; |
674 | |
675 | static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_n, |
676 | unsigned int *tx_slot, unsigned int rx_n, unsigned int *rx_slot) |
677 | { |
678 | struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(c: dai->component); |
679 | |
680 | return cs35l41_set_channels(dev: cs35l41->dev, reg: cs35l41->regmap, tx_num: tx_n, tx_slot, rx_num: rx_n, rx_slot); |
681 | } |
682 | |
683 | static int cs35l41_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
684 | { |
685 | struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(c: dai->component); |
686 | unsigned int daifmt = 0; |
687 | |
688 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
689 | case SND_SOC_DAIFMT_CBP_CFP: |
690 | daifmt |= CS35L41_SCLK_MSTR_MASK | CS35L41_LRCLK_MSTR_MASK; |
691 | break; |
692 | case SND_SOC_DAIFMT_CBC_CFC: |
693 | break; |
694 | default: |
695 | dev_warn(cs35l41->dev, "Mixed provider/consumer mode unsupported\n" ); |
696 | return -EINVAL; |
697 | } |
698 | |
699 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
700 | case SND_SOC_DAIFMT_DSP_A: |
701 | break; |
702 | case SND_SOC_DAIFMT_I2S: |
703 | daifmt |= 2 << CS35L41_ASP_FMT_SHIFT; |
704 | break; |
705 | default: |
706 | dev_warn(cs35l41->dev, "Invalid or unsupported DAI format\n" ); |
707 | return -EINVAL; |
708 | } |
709 | |
710 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
711 | case SND_SOC_DAIFMT_NB_IF: |
712 | daifmt |= CS35L41_LRCLK_INV_MASK; |
713 | break; |
714 | case SND_SOC_DAIFMT_IB_NF: |
715 | daifmt |= CS35L41_SCLK_INV_MASK; |
716 | break; |
717 | case SND_SOC_DAIFMT_IB_IF: |
718 | daifmt |= CS35L41_LRCLK_INV_MASK | CS35L41_SCLK_INV_MASK; |
719 | break; |
720 | case SND_SOC_DAIFMT_NB_NF: |
721 | break; |
722 | default: |
723 | dev_warn(cs35l41->dev, "Invalid DAI clock INV\n" ); |
724 | return -EINVAL; |
725 | } |
726 | |
727 | return regmap_update_bits(map: cs35l41->regmap, CS35L41_SP_FORMAT, |
728 | CS35L41_SCLK_MSTR_MASK | CS35L41_LRCLK_MSTR_MASK | |
729 | CS35L41_ASP_FMT_MASK | CS35L41_LRCLK_INV_MASK | |
730 | CS35L41_SCLK_INV_MASK, val: daifmt); |
731 | } |
732 | |
733 | struct cs35l41_global_fs_config { |
734 | int rate; |
735 | int fs_cfg; |
736 | }; |
737 | |
738 | static const struct cs35l41_global_fs_config cs35l41_fs_rates[] = { |
739 | { 12000, 0x01 }, |
740 | { 24000, 0x02 }, |
741 | { 48000, 0x03 }, |
742 | { 96000, 0x04 }, |
743 | { 192000, 0x05 }, |
744 | { 11025, 0x09 }, |
745 | { 22050, 0x0A }, |
746 | { 44100, 0x0B }, |
747 | { 88200, 0x0C }, |
748 | { 176400, 0x0D }, |
749 | { 8000, 0x11 }, |
750 | { 16000, 0x12 }, |
751 | { 32000, 0x13 }, |
752 | }; |
753 | |
754 | static int cs35l41_pcm_hw_params(struct snd_pcm_substream *substream, |
755 | struct snd_pcm_hw_params *params, |
756 | struct snd_soc_dai *dai) |
757 | { |
758 | struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(c: dai->component); |
759 | unsigned int rate = params_rate(p: params); |
760 | u8 asp_wl; |
761 | int i; |
762 | |
763 | for (i = 0; i < ARRAY_SIZE(cs35l41_fs_rates); i++) { |
764 | if (rate == cs35l41_fs_rates[i].rate) |
765 | break; |
766 | } |
767 | |
768 | if (i >= ARRAY_SIZE(cs35l41_fs_rates)) { |
769 | dev_err(cs35l41->dev, "Unsupported rate: %u\n" , rate); |
770 | return -EINVAL; |
771 | } |
772 | |
773 | asp_wl = params_width(p: params); |
774 | |
775 | if (i < ARRAY_SIZE(cs35l41_fs_rates)) |
776 | regmap_update_bits(map: cs35l41->regmap, CS35L41_GLOBAL_CLK_CTRL, |
777 | CS35L41_GLOBAL_FS_MASK, |
778 | val: cs35l41_fs_rates[i].fs_cfg << CS35L41_GLOBAL_FS_SHIFT); |
779 | |
780 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
781 | regmap_update_bits(map: cs35l41->regmap, CS35L41_SP_FORMAT, |
782 | CS35L41_ASP_WIDTH_RX_MASK, |
783 | val: asp_wl << CS35L41_ASP_WIDTH_RX_SHIFT); |
784 | regmap_update_bits(map: cs35l41->regmap, CS35L41_SP_RX_WL, |
785 | CS35L41_ASP_RX_WL_MASK, |
786 | val: asp_wl << CS35L41_ASP_RX_WL_SHIFT); |
787 | } else { |
788 | regmap_update_bits(map: cs35l41->regmap, CS35L41_SP_FORMAT, |
789 | CS35L41_ASP_WIDTH_TX_MASK, |
790 | val: asp_wl << CS35L41_ASP_WIDTH_TX_SHIFT); |
791 | regmap_update_bits(map: cs35l41->regmap, CS35L41_SP_TX_WL, |
792 | CS35L41_ASP_TX_WL_MASK, |
793 | val: asp_wl << CS35L41_ASP_TX_WL_SHIFT); |
794 | } |
795 | |
796 | return 0; |
797 | } |
798 | |
799 | static int cs35l41_get_clk_config(int freq) |
800 | { |
801 | int i; |
802 | |
803 | for (i = 0; i < ARRAY_SIZE(cs35l41_pll_sysclk); i++) { |
804 | if (cs35l41_pll_sysclk[i].freq == freq) |
805 | return cs35l41_pll_sysclk[i].clk_cfg; |
806 | } |
807 | |
808 | return -EINVAL; |
809 | } |
810 | |
811 | static const unsigned int cs35l41_src_rates[] = { |
812 | 8000, 12000, 11025, 16000, 22050, 24000, 32000, |
813 | 44100, 48000, 88200, 96000, 176400, 192000 |
814 | }; |
815 | |
816 | static const struct snd_pcm_hw_constraint_list cs35l41_constraints = { |
817 | .count = ARRAY_SIZE(cs35l41_src_rates), |
818 | .list = cs35l41_src_rates, |
819 | }; |
820 | |
821 | static int cs35l41_pcm_startup(struct snd_pcm_substream *substream, |
822 | struct snd_soc_dai *dai) |
823 | { |
824 | if (substream->runtime) |
825 | return snd_pcm_hw_constraint_list(runtime: substream->runtime, cond: 0, |
826 | SNDRV_PCM_HW_PARAM_RATE, |
827 | l: &cs35l41_constraints); |
828 | return 0; |
829 | } |
830 | |
831 | static int cs35l41_component_set_sysclk(struct snd_soc_component *component, |
832 | int clk_id, int source, |
833 | unsigned int freq, int dir) |
834 | { |
835 | struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(c: component); |
836 | int extclk_cfg, clksrc; |
837 | |
838 | switch (clk_id) { |
839 | case CS35L41_CLKID_SCLK: |
840 | clksrc = CS35L41_PLLSRC_SCLK; |
841 | break; |
842 | case CS35L41_CLKID_LRCLK: |
843 | clksrc = CS35L41_PLLSRC_LRCLK; |
844 | break; |
845 | case CS35L41_CLKID_MCLK: |
846 | clksrc = CS35L41_PLLSRC_MCLK; |
847 | break; |
848 | default: |
849 | dev_err(cs35l41->dev, "Invalid CLK Config\n" ); |
850 | return -EINVAL; |
851 | } |
852 | |
853 | extclk_cfg = cs35l41_get_clk_config(freq); |
854 | |
855 | if (extclk_cfg < 0) { |
856 | dev_err(cs35l41->dev, "Invalid CLK Config: %d, freq: %u\n" , |
857 | extclk_cfg, freq); |
858 | return -EINVAL; |
859 | } |
860 | |
861 | regmap_update_bits(map: cs35l41->regmap, CS35L41_PLL_CLK_CTRL, |
862 | CS35L41_PLL_OPENLOOP_MASK, |
863 | val: 1 << CS35L41_PLL_OPENLOOP_SHIFT); |
864 | regmap_update_bits(map: cs35l41->regmap, CS35L41_PLL_CLK_CTRL, |
865 | CS35L41_REFCLK_FREQ_MASK, |
866 | val: extclk_cfg << CS35L41_REFCLK_FREQ_SHIFT); |
867 | regmap_update_bits(map: cs35l41->regmap, CS35L41_PLL_CLK_CTRL, |
868 | CS35L41_PLL_CLK_EN_MASK, |
869 | val: 0 << CS35L41_PLL_CLK_EN_SHIFT); |
870 | regmap_update_bits(map: cs35l41->regmap, CS35L41_PLL_CLK_CTRL, |
871 | CS35L41_PLL_CLK_SEL_MASK, val: clksrc); |
872 | regmap_update_bits(map: cs35l41->regmap, CS35L41_PLL_CLK_CTRL, |
873 | CS35L41_PLL_OPENLOOP_MASK, |
874 | val: 0 << CS35L41_PLL_OPENLOOP_SHIFT); |
875 | regmap_update_bits(map: cs35l41->regmap, CS35L41_PLL_CLK_CTRL, |
876 | CS35L41_PLL_CLK_EN_MASK, |
877 | val: 1 << CS35L41_PLL_CLK_EN_SHIFT); |
878 | |
879 | return 0; |
880 | } |
881 | |
882 | static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai, |
883 | int clk_id, unsigned int freq, int dir) |
884 | { |
885 | struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(c: dai->component); |
886 | unsigned int fs1_val; |
887 | unsigned int fs2_val; |
888 | unsigned int val; |
889 | int fsindex; |
890 | |
891 | fsindex = cs35l41_get_fs_mon_config_index(freq); |
892 | if (fsindex < 0) { |
893 | dev_err(cs35l41->dev, "Invalid CLK Config freq: %u\n" , freq); |
894 | return -EINVAL; |
895 | } |
896 | |
897 | dev_dbg(cs35l41->dev, "Set DAI sysclk %d\n" , freq); |
898 | |
899 | if (freq <= 6144000) { |
900 | /* Use the lookup table */ |
901 | fs1_val = cs35l41_fs_mon[fsindex].fs1; |
902 | fs2_val = cs35l41_fs_mon[fsindex].fs2; |
903 | } else { |
904 | /* Use hard-coded values */ |
905 | fs1_val = 0x10; |
906 | fs2_val = 0x24; |
907 | } |
908 | |
909 | val = fs1_val; |
910 | val |= (fs2_val << CS35L41_FS2_WINDOW_SHIFT) & CS35L41_FS2_WINDOW_MASK; |
911 | regmap_write(map: cs35l41->regmap, CS35L41_TST_FS_MON0, val); |
912 | |
913 | return 0; |
914 | } |
915 | |
916 | static int cs35l41_set_pdata(struct cs35l41_private *cs35l41) |
917 | { |
918 | struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; |
919 | int ret; |
920 | |
921 | if (!hw_cfg->valid) |
922 | return -EINVAL; |
923 | |
924 | if (hw_cfg->bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) |
925 | return -EINVAL; |
926 | |
927 | /* Required */ |
928 | ret = cs35l41_init_boost(dev: cs35l41->dev, regmap: cs35l41->regmap, hw_cfg); |
929 | if (ret) |
930 | return ret; |
931 | |
932 | /* Optional */ |
933 | if (hw_cfg->dout_hiz <= CS35L41_ASP_DOUT_HIZ_MASK && hw_cfg->dout_hiz >= 0) |
934 | regmap_update_bits(map: cs35l41->regmap, CS35L41_SP_HIZ_CTRL, CS35L41_ASP_DOUT_HIZ_MASK, |
935 | val: hw_cfg->dout_hiz); |
936 | |
937 | return 0; |
938 | } |
939 | |
940 | static const struct snd_soc_dapm_route cs35l41_ext_bst_routes[] = { |
941 | {"Main AMP" , NULL, "VSPK" }, |
942 | }; |
943 | |
944 | static const struct snd_soc_dapm_widget cs35l41_ext_bst_widget[] = { |
945 | SND_SOC_DAPM_SUPPLY("VSPK" , CS35L41_GPIO1_CTRL1, CS35L41_GPIO_LVL_SHIFT, 0, NULL, 0), |
946 | }; |
947 | |
948 | static int cs35l41_component_probe(struct snd_soc_component *component) |
949 | { |
950 | struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(c: component); |
951 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
952 | int ret; |
953 | |
954 | if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) { |
955 | ret = snd_soc_dapm_new_controls(dapm, widget: cs35l41_ext_bst_widget, |
956 | ARRAY_SIZE(cs35l41_ext_bst_widget)); |
957 | if (ret) |
958 | return ret; |
959 | |
960 | ret = snd_soc_dapm_add_routes(dapm, route: cs35l41_ext_bst_routes, |
961 | ARRAY_SIZE(cs35l41_ext_bst_routes)); |
962 | if (ret) |
963 | return ret; |
964 | } |
965 | |
966 | return wm_adsp2_component_probe(dsp: &cs35l41->dsp, component); |
967 | } |
968 | |
969 | static void cs35l41_component_remove(struct snd_soc_component *component) |
970 | { |
971 | struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(c: component); |
972 | |
973 | wm_adsp2_component_remove(dsp: &cs35l41->dsp, component); |
974 | } |
975 | |
976 | static const struct snd_soc_dai_ops cs35l41_ops = { |
977 | .startup = cs35l41_pcm_startup, |
978 | .set_fmt = cs35l41_set_dai_fmt, |
979 | .hw_params = cs35l41_pcm_hw_params, |
980 | .set_sysclk = cs35l41_dai_set_sysclk, |
981 | .set_channel_map = cs35l41_set_channel_map, |
982 | }; |
983 | |
984 | static struct snd_soc_dai_driver cs35l41_dai[] = { |
985 | { |
986 | .name = "cs35l41-pcm" , |
987 | .id = 0, |
988 | .playback = { |
989 | .stream_name = "AMP Playback" , |
990 | .channels_min = 1, |
991 | .channels_max = 2, |
992 | .rates = SNDRV_PCM_RATE_KNOT, |
993 | .formats = CS35L41_RX_FORMATS, |
994 | }, |
995 | .capture = { |
996 | .stream_name = "AMP Capture" , |
997 | .channels_min = 1, |
998 | .channels_max = 4, |
999 | .rates = SNDRV_PCM_RATE_KNOT, |
1000 | .formats = CS35L41_TX_FORMATS, |
1001 | }, |
1002 | .ops = &cs35l41_ops, |
1003 | .symmetric_rate = 1, |
1004 | }, |
1005 | }; |
1006 | |
1007 | static const struct snd_soc_component_driver soc_component_dev_cs35l41 = { |
1008 | .name = "cs35l41-codec" , |
1009 | .probe = cs35l41_component_probe, |
1010 | .remove = cs35l41_component_remove, |
1011 | |
1012 | .dapm_widgets = cs35l41_dapm_widgets, |
1013 | .num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets), |
1014 | .dapm_routes = cs35l41_audio_map, |
1015 | .num_dapm_routes = ARRAY_SIZE(cs35l41_audio_map), |
1016 | |
1017 | .controls = cs35l41_aud_controls, |
1018 | .num_controls = ARRAY_SIZE(cs35l41_aud_controls), |
1019 | .set_sysclk = cs35l41_component_set_sysclk, |
1020 | |
1021 | .endianness = 1, |
1022 | }; |
1023 | |
1024 | static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cfg) |
1025 | { |
1026 | struct cs35l41_gpio_cfg *gpio1 = &hw_cfg->gpio1; |
1027 | struct cs35l41_gpio_cfg *gpio2 = &hw_cfg->gpio2; |
1028 | unsigned int val; |
1029 | int ret; |
1030 | |
1031 | /* Some ACPI systems received the Shared Boost feature before the upstream driver, |
1032 | * leaving those systems with deprecated _DSD properties. |
1033 | * To correctly configure those systems add shared-boost-active and shared-boost-passive |
1034 | * properties mapped to the correct value in boost-type. |
1035 | * These two are not DT properties and should not be used in new systems designs. |
1036 | */ |
1037 | if (device_property_read_bool(dev, propname: "cirrus,shared-boost-active" )) { |
1038 | hw_cfg->bst_type = CS35L41_SHD_BOOST_ACTV; |
1039 | } else if (device_property_read_bool(dev, propname: "cirrus,shared-boost-passive" )) { |
1040 | hw_cfg->bst_type = CS35L41_SHD_BOOST_PASS; |
1041 | } else { |
1042 | ret = device_property_read_u32(dev, propname: "cirrus,boost-type" , val: &val); |
1043 | if (ret >= 0) |
1044 | hw_cfg->bst_type = val; |
1045 | } |
1046 | |
1047 | ret = device_property_read_u32(dev, propname: "cirrus,boost-peak-milliamp" , val: &val); |
1048 | if (ret >= 0) |
1049 | hw_cfg->bst_ipk = val; |
1050 | else |
1051 | hw_cfg->bst_ipk = -1; |
1052 | |
1053 | ret = device_property_read_u32(dev, propname: "cirrus,boost-ind-nanohenry" , val: &val); |
1054 | if (ret >= 0) |
1055 | hw_cfg->bst_ind = val; |
1056 | else |
1057 | hw_cfg->bst_ind = -1; |
1058 | |
1059 | ret = device_property_read_u32(dev, propname: "cirrus,boost-cap-microfarad" , val: &val); |
1060 | if (ret >= 0) |
1061 | hw_cfg->bst_cap = val; |
1062 | else |
1063 | hw_cfg->bst_cap = -1; |
1064 | |
1065 | ret = device_property_read_u32(dev, propname: "cirrus,asp-sdout-hiz" , val: &val); |
1066 | if (ret >= 0) |
1067 | hw_cfg->dout_hiz = val; |
1068 | else |
1069 | hw_cfg->dout_hiz = -1; |
1070 | |
1071 | /* GPIO1 Pin Config */ |
1072 | gpio1->pol_inv = device_property_read_bool(dev, propname: "cirrus,gpio1-polarity-invert" ); |
1073 | gpio1->out_en = device_property_read_bool(dev, propname: "cirrus,gpio1-output-enable" ); |
1074 | ret = device_property_read_u32(dev, propname: "cirrus,gpio1-src-select" , val: &val); |
1075 | if (ret >= 0) { |
1076 | gpio1->func = val; |
1077 | gpio1->valid = true; |
1078 | } |
1079 | |
1080 | /* GPIO2 Pin Config */ |
1081 | gpio2->pol_inv = device_property_read_bool(dev, propname: "cirrus,gpio2-polarity-invert" ); |
1082 | gpio2->out_en = device_property_read_bool(dev, propname: "cirrus,gpio2-output-enable" ); |
1083 | ret = device_property_read_u32(dev, propname: "cirrus,gpio2-src-select" , val: &val); |
1084 | if (ret >= 0) { |
1085 | gpio2->func = val; |
1086 | gpio2->valid = true; |
1087 | } |
1088 | |
1089 | hw_cfg->valid = true; |
1090 | |
1091 | return 0; |
1092 | } |
1093 | |
1094 | static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) |
1095 | { |
1096 | struct wm_adsp *dsp; |
1097 | int ret; |
1098 | |
1099 | dsp = &cs35l41->dsp; |
1100 | dsp->part = "cs35l41" ; |
1101 | dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */ |
1102 | dsp->toggle_preload = true; |
1103 | |
1104 | cs35l41_configure_cs_dsp(dev: cs35l41->dev, reg: cs35l41->regmap, dsp: &dsp->cs_dsp); |
1105 | |
1106 | ret = cs35l41_write_fs_errata(dev: cs35l41->dev, regmap: cs35l41->regmap); |
1107 | if (ret < 0) |
1108 | return ret; |
1109 | |
1110 | ret = wm_halo_init(dsp); |
1111 | if (ret) { |
1112 | dev_err(cs35l41->dev, "wm_halo_init failed: %d\n" , ret); |
1113 | return ret; |
1114 | } |
1115 | |
1116 | ret = regmap_write(map: cs35l41->regmap, CS35L41_DSP1_RX5_SRC, |
1117 | CS35L41_INPUT_SRC_VPMON); |
1118 | if (ret < 0) { |
1119 | dev_err(cs35l41->dev, "Write INPUT_SRC_VPMON failed: %d\n" , ret); |
1120 | goto err_dsp; |
1121 | } |
1122 | ret = regmap_write(map: cs35l41->regmap, CS35L41_DSP1_RX6_SRC, |
1123 | CS35L41_INPUT_SRC_CLASSH); |
1124 | if (ret < 0) { |
1125 | dev_err(cs35l41->dev, "Write INPUT_SRC_CLASSH failed: %d\n" , ret); |
1126 | goto err_dsp; |
1127 | } |
1128 | ret = regmap_write(map: cs35l41->regmap, CS35L41_DSP1_RX7_SRC, |
1129 | CS35L41_INPUT_SRC_TEMPMON); |
1130 | if (ret < 0) { |
1131 | dev_err(cs35l41->dev, "Write INPUT_SRC_TEMPMON failed: %d\n" , ret); |
1132 | goto err_dsp; |
1133 | } |
1134 | ret = regmap_write(map: cs35l41->regmap, CS35L41_DSP1_RX8_SRC, |
1135 | CS35L41_INPUT_SRC_RSVD); |
1136 | if (ret < 0) { |
1137 | dev_err(cs35l41->dev, "Write INPUT_SRC_RSVD failed: %d\n" , ret); |
1138 | goto err_dsp; |
1139 | } |
1140 | |
1141 | return 0; |
1142 | |
1143 | err_dsp: |
1144 | wm_adsp2_remove(dsp); |
1145 | |
1146 | return ret; |
1147 | } |
1148 | |
1149 | static int cs35l41_acpi_get_name(struct cs35l41_private *cs35l41) |
1150 | { |
1151 | acpi_handle handle = ACPI_HANDLE(cs35l41->dev); |
1152 | const char *sub; |
1153 | |
1154 | /* If there is no ACPI_HANDLE, there is no ACPI for this system, return 0 */ |
1155 | if (!handle) |
1156 | return 0; |
1157 | |
1158 | sub = acpi_get_subsystem_id(handle); |
1159 | if (IS_ERR(ptr: sub)) { |
1160 | /* If bad ACPI, return 0 and fallback to legacy firmware path, otherwise fail */ |
1161 | if (PTR_ERR(ptr: sub) == -ENODATA) |
1162 | return 0; |
1163 | else |
1164 | return PTR_ERR(ptr: sub); |
1165 | } |
1166 | |
1167 | cs35l41->dsp.system_name = sub; |
1168 | dev_dbg(cs35l41->dev, "Subsystem ID: %s\n" , cs35l41->dsp.system_name); |
1169 | |
1170 | return 0; |
1171 | } |
1172 | |
1173 | int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg) |
1174 | { |
1175 | u32 regid, reg_revid, i, mtl_revid, int_status, chipid_match; |
1176 | int irq_pol = 0; |
1177 | int ret; |
1178 | |
1179 | if (hw_cfg) { |
1180 | cs35l41->hw_cfg = *hw_cfg; |
1181 | } else { |
1182 | ret = cs35l41_handle_pdata(dev: cs35l41->dev, hw_cfg: &cs35l41->hw_cfg); |
1183 | if (ret != 0) |
1184 | return ret; |
1185 | } |
1186 | |
1187 | for (i = 0; i < CS35L41_NUM_SUPPLIES; i++) |
1188 | cs35l41->supplies[i].supply = cs35l41_supplies[i]; |
1189 | |
1190 | ret = devm_regulator_bulk_get(dev: cs35l41->dev, CS35L41_NUM_SUPPLIES, |
1191 | consumers: cs35l41->supplies); |
1192 | if (ret != 0) |
1193 | return dev_err_probe(dev: cs35l41->dev, err: ret, |
1194 | fmt: "Failed to request core supplies\n" ); |
1195 | |
1196 | ret = regulator_bulk_enable(CS35L41_NUM_SUPPLIES, consumers: cs35l41->supplies); |
1197 | if (ret != 0) |
1198 | return dev_err_probe(dev: cs35l41->dev, err: ret, |
1199 | fmt: "Failed to enable core supplies\n" ); |
1200 | |
1201 | /* returning NULL can be an option if in stereo mode */ |
1202 | cs35l41->reset_gpio = devm_gpiod_get_optional(dev: cs35l41->dev, con_id: "reset" , |
1203 | flags: GPIOD_OUT_LOW); |
1204 | if (IS_ERR(ptr: cs35l41->reset_gpio)) { |
1205 | ret = PTR_ERR(ptr: cs35l41->reset_gpio); |
1206 | cs35l41->reset_gpio = NULL; |
1207 | if (ret == -EBUSY) { |
1208 | dev_info(cs35l41->dev, |
1209 | "Reset line busy, assuming shared reset\n" ); |
1210 | } else { |
1211 | dev_err_probe(dev: cs35l41->dev, err: ret, |
1212 | fmt: "Failed to get reset GPIO\n" ); |
1213 | goto err; |
1214 | } |
1215 | } |
1216 | if (cs35l41->reset_gpio) { |
1217 | /* satisfy minimum reset pulse width spec */ |
1218 | usleep_range(min: 2000, max: 2100); |
1219 | gpiod_set_value_cansleep(desc: cs35l41->reset_gpio, value: 1); |
1220 | } |
1221 | |
1222 | usleep_range(min: 2000, max: 2100); |
1223 | |
1224 | ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4, |
1225 | int_status, int_status & CS35L41_OTP_BOOT_DONE, |
1226 | 1000, 100000); |
1227 | if (ret) { |
1228 | dev_err_probe(dev: cs35l41->dev, err: ret, |
1229 | fmt: "Failed waiting for OTP_BOOT_DONE\n" ); |
1230 | goto err; |
1231 | } |
1232 | |
1233 | regmap_read(map: cs35l41->regmap, CS35L41_IRQ1_STATUS3, val: &int_status); |
1234 | if (int_status & CS35L41_OTP_BOOT_ERR) { |
1235 | dev_err(cs35l41->dev, "OTP Boot error\n" ); |
1236 | ret = -EINVAL; |
1237 | goto err; |
1238 | } |
1239 | |
1240 | ret = regmap_read(map: cs35l41->regmap, CS35L41_DEVID, val: ®id); |
1241 | if (ret < 0) { |
1242 | dev_err_probe(dev: cs35l41->dev, err: ret, fmt: "Get Device ID failed\n" ); |
1243 | goto err; |
1244 | } |
1245 | |
1246 | ret = regmap_read(map: cs35l41->regmap, CS35L41_REVID, val: ®_revid); |
1247 | if (ret < 0) { |
1248 | dev_err_probe(dev: cs35l41->dev, err: ret, fmt: "Get Revision ID failed\n" ); |
1249 | goto err; |
1250 | } |
1251 | |
1252 | mtl_revid = reg_revid & CS35L41_MTLREVID_MASK; |
1253 | |
1254 | /* CS35L41 will have even MTLREVID |
1255 | * CS35L41R will have odd MTLREVID |
1256 | */ |
1257 | chipid_match = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID; |
1258 | if (regid != chipid_match) { |
1259 | dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n" , |
1260 | regid, chipid_match); |
1261 | ret = -ENODEV; |
1262 | goto err; |
1263 | } |
1264 | |
1265 | cs35l41_test_key_unlock(dev: cs35l41->dev, regmap: cs35l41->regmap); |
1266 | |
1267 | ret = cs35l41_register_errata_patch(dev: cs35l41->dev, reg: cs35l41->regmap, reg_revid); |
1268 | if (ret) |
1269 | goto err; |
1270 | |
1271 | ret = cs35l41_otp_unpack(dev: cs35l41->dev, regmap: cs35l41->regmap); |
1272 | if (ret < 0) { |
1273 | dev_err_probe(dev: cs35l41->dev, err: ret, fmt: "OTP Unpack failed\n" ); |
1274 | goto err; |
1275 | } |
1276 | |
1277 | cs35l41_test_key_lock(dev: cs35l41->dev, regmap: cs35l41->regmap); |
1278 | |
1279 | irq_pol = cs35l41_gpio_config(regmap: cs35l41->regmap, hw_cfg: &cs35l41->hw_cfg); |
1280 | |
1281 | /* Set interrupt masks for critical errors */ |
1282 | regmap_write(map: cs35l41->regmap, CS35L41_IRQ1_MASK1, |
1283 | CS35L41_INT1_MASK_DEFAULT); |
1284 | if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS || |
1285 | cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV) |
1286 | regmap_update_bits(map: cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK, |
1287 | val: 0 << CS35L41_INT3_PLL_LOCK_SHIFT); |
1288 | |
1289 | ret = devm_request_threaded_irq(dev: cs35l41->dev, irq: cs35l41->irq, NULL, thread_fn: cs35l41_irq, |
1290 | IRQF_ONESHOT | IRQF_SHARED | irq_pol, |
1291 | devname: "cs35l41" , dev_id: cs35l41); |
1292 | if (ret != 0) { |
1293 | dev_err_probe(dev: cs35l41->dev, err: ret, fmt: "Failed to request IRQ\n" ); |
1294 | goto err; |
1295 | } |
1296 | |
1297 | ret = cs35l41_set_pdata(cs35l41); |
1298 | if (ret < 0) { |
1299 | dev_err_probe(dev: cs35l41->dev, err: ret, fmt: "Set pdata failed\n" ); |
1300 | goto err; |
1301 | } |
1302 | |
1303 | ret = cs35l41_acpi_get_name(cs35l41); |
1304 | if (ret < 0) |
1305 | goto err; |
1306 | |
1307 | ret = cs35l41_dsp_init(cs35l41); |
1308 | if (ret < 0) |
1309 | goto err; |
1310 | |
1311 | pm_runtime_set_autosuspend_delay(dev: cs35l41->dev, delay: 3000); |
1312 | pm_runtime_use_autosuspend(dev: cs35l41->dev); |
1313 | pm_runtime_mark_last_busy(dev: cs35l41->dev); |
1314 | pm_runtime_set_active(dev: cs35l41->dev); |
1315 | pm_runtime_get_noresume(dev: cs35l41->dev); |
1316 | pm_runtime_enable(dev: cs35l41->dev); |
1317 | |
1318 | ret = devm_snd_soc_register_component(dev: cs35l41->dev, |
1319 | component_driver: &soc_component_dev_cs35l41, |
1320 | dai_drv: cs35l41_dai, ARRAY_SIZE(cs35l41_dai)); |
1321 | if (ret < 0) { |
1322 | dev_err_probe(dev: cs35l41->dev, err: ret, fmt: "Register codec failed\n" ); |
1323 | goto err_pm; |
1324 | } |
1325 | |
1326 | pm_runtime_put_autosuspend(dev: cs35l41->dev); |
1327 | |
1328 | dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n" , |
1329 | regid, reg_revid); |
1330 | |
1331 | return 0; |
1332 | |
1333 | err_pm: |
1334 | pm_runtime_dont_use_autosuspend(dev: cs35l41->dev); |
1335 | pm_runtime_disable(dev: cs35l41->dev); |
1336 | pm_runtime_put_noidle(dev: cs35l41->dev); |
1337 | |
1338 | wm_adsp2_remove(dsp: &cs35l41->dsp); |
1339 | err: |
1340 | cs35l41_safe_reset(regmap: cs35l41->regmap, b_type: cs35l41->hw_cfg.bst_type); |
1341 | regulator_bulk_disable(CS35L41_NUM_SUPPLIES, consumers: cs35l41->supplies); |
1342 | gpiod_set_value_cansleep(desc: cs35l41->reset_gpio, value: 0); |
1343 | |
1344 | return ret; |
1345 | } |
1346 | EXPORT_SYMBOL_GPL(cs35l41_probe); |
1347 | |
1348 | void cs35l41_remove(struct cs35l41_private *cs35l41) |
1349 | { |
1350 | pm_runtime_get_sync(dev: cs35l41->dev); |
1351 | pm_runtime_dont_use_autosuspend(dev: cs35l41->dev); |
1352 | pm_runtime_disable(dev: cs35l41->dev); |
1353 | |
1354 | regmap_write(map: cs35l41->regmap, CS35L41_IRQ1_MASK1, val: 0xFFFFFFFF); |
1355 | if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS || |
1356 | cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV) |
1357 | regmap_update_bits(map: cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK, |
1358 | val: 1 << CS35L41_INT3_PLL_LOCK_SHIFT); |
1359 | kfree(objp: cs35l41->dsp.system_name); |
1360 | wm_adsp2_remove(dsp: &cs35l41->dsp); |
1361 | cs35l41_safe_reset(regmap: cs35l41->regmap, b_type: cs35l41->hw_cfg.bst_type); |
1362 | |
1363 | pm_runtime_put_noidle(dev: cs35l41->dev); |
1364 | |
1365 | regulator_bulk_disable(CS35L41_NUM_SUPPLIES, consumers: cs35l41->supplies); |
1366 | gpiod_set_value_cansleep(desc: cs35l41->reset_gpio, value: 0); |
1367 | } |
1368 | EXPORT_SYMBOL_GPL(cs35l41_remove); |
1369 | |
1370 | static int cs35l41_runtime_suspend(struct device *dev) |
1371 | { |
1372 | struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); |
1373 | |
1374 | dev_dbg(cs35l41->dev, "Runtime suspend\n" ); |
1375 | |
1376 | if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) |
1377 | return 0; |
1378 | |
1379 | cs35l41_enter_hibernate(dev, regmap: cs35l41->regmap, b_type: cs35l41->hw_cfg.bst_type); |
1380 | |
1381 | regcache_cache_only(map: cs35l41->regmap, enable: true); |
1382 | regcache_mark_dirty(map: cs35l41->regmap); |
1383 | |
1384 | return 0; |
1385 | } |
1386 | |
1387 | static int cs35l41_runtime_resume(struct device *dev) |
1388 | { |
1389 | struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); |
1390 | int ret; |
1391 | |
1392 | dev_dbg(cs35l41->dev, "Runtime resume\n" ); |
1393 | |
1394 | if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) |
1395 | return 0; |
1396 | |
1397 | regcache_cache_only(map: cs35l41->regmap, enable: false); |
1398 | |
1399 | ret = cs35l41_exit_hibernate(dev: cs35l41->dev, regmap: cs35l41->regmap); |
1400 | if (ret) |
1401 | return ret; |
1402 | |
1403 | /* Test key needs to be unlocked to allow the OTP settings to re-apply */ |
1404 | cs35l41_test_key_unlock(dev: cs35l41->dev, regmap: cs35l41->regmap); |
1405 | ret = regcache_sync(map: cs35l41->regmap); |
1406 | cs35l41_test_key_lock(dev: cs35l41->dev, regmap: cs35l41->regmap); |
1407 | if (ret) { |
1408 | dev_err(cs35l41->dev, "Failed to restore register cache: %d\n" , ret); |
1409 | return ret; |
1410 | } |
1411 | cs35l41_init_boost(dev: cs35l41->dev, regmap: cs35l41->regmap, hw_cfg: &cs35l41->hw_cfg); |
1412 | |
1413 | return 0; |
1414 | } |
1415 | |
1416 | static int cs35l41_sys_suspend(struct device *dev) |
1417 | { |
1418 | struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); |
1419 | |
1420 | dev_dbg(cs35l41->dev, "System suspend, disabling IRQ\n" ); |
1421 | disable_irq(irq: cs35l41->irq); |
1422 | |
1423 | return 0; |
1424 | } |
1425 | |
1426 | static int cs35l41_sys_suspend_noirq(struct device *dev) |
1427 | { |
1428 | struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); |
1429 | |
1430 | dev_dbg(cs35l41->dev, "Late system suspend, reenabling IRQ\n" ); |
1431 | enable_irq(irq: cs35l41->irq); |
1432 | |
1433 | return 0; |
1434 | } |
1435 | |
1436 | static int cs35l41_sys_resume_noirq(struct device *dev) |
1437 | { |
1438 | struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); |
1439 | |
1440 | dev_dbg(cs35l41->dev, "Early system resume, disabling IRQ\n" ); |
1441 | disable_irq(irq: cs35l41->irq); |
1442 | |
1443 | return 0; |
1444 | } |
1445 | |
1446 | static int cs35l41_sys_resume(struct device *dev) |
1447 | { |
1448 | struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); |
1449 | |
1450 | dev_dbg(cs35l41->dev, "System resume, reenabling IRQ\n" ); |
1451 | enable_irq(irq: cs35l41->irq); |
1452 | |
1453 | return 0; |
1454 | } |
1455 | |
1456 | EXPORT_GPL_DEV_PM_OPS(cs35l41_pm_ops) = { |
1457 | RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL) |
1458 | |
1459 | SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume) |
1460 | NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq) |
1461 | }; |
1462 | |
1463 | MODULE_DESCRIPTION("ASoC CS35L41 driver" ); |
1464 | MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>" ); |
1465 | MODULE_LICENSE("GPL" ); |
1466 | |