1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier |
4 | * |
5 | * Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/ |
6 | * Author: Andreas Dannenberg <dannenberg@ti.com> |
7 | * Andrew F. Davis <afd@ti.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/errno.h> |
12 | #include <linux/device.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/regmap.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/regulator/consumer.h> |
17 | #include <linux/delay.h> |
18 | #include <linux/gpio/consumer.h> |
19 | |
20 | #include <sound/pcm.h> |
21 | #include <sound/pcm_params.h> |
22 | #include <sound/soc.h> |
23 | #include <sound/soc-dapm.h> |
24 | #include <sound/tlv.h> |
25 | |
26 | #include "tas6424.h" |
27 | |
28 | /* Define how often to check (and clear) the fault status register (in ms) */ |
29 | #define TAS6424_FAULT_CHECK_INTERVAL 200 |
30 | |
31 | static const char * const tas6424_supply_names[] = { |
32 | "dvdd" , /* Digital power supply. Connect to 3.3-V supply. */ |
33 | "vbat" , /* Supply used for higher voltage analog circuits. */ |
34 | "pvdd" , /* Class-D amp output FETs supply. */ |
35 | }; |
36 | #define TAS6424_NUM_SUPPLIES ARRAY_SIZE(tas6424_supply_names) |
37 | |
38 | struct tas6424_data { |
39 | struct device *dev; |
40 | struct regmap *regmap; |
41 | struct regulator_bulk_data supplies[TAS6424_NUM_SUPPLIES]; |
42 | struct delayed_work fault_check_work; |
43 | unsigned int last_cfault; |
44 | unsigned int last_fault1; |
45 | unsigned int last_fault2; |
46 | unsigned int last_warn; |
47 | struct gpio_desc *standby_gpio; |
48 | struct gpio_desc *mute_gpio; |
49 | }; |
50 | |
51 | /* |
52 | * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that |
53 | * setting the gain below -100 dB (register value <0x7) is effectively a MUTE |
54 | * as per device datasheet. |
55 | */ |
56 | static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0); |
57 | |
58 | static const struct snd_kcontrol_new tas6424_snd_controls[] = { |
59 | SOC_SINGLE_TLV("Speaker Driver CH1 Playback Volume" , |
60 | TAS6424_CH1_VOL_CTRL, 0, 0xff, 0, dac_tlv), |
61 | SOC_SINGLE_TLV("Speaker Driver CH2 Playback Volume" , |
62 | TAS6424_CH2_VOL_CTRL, 0, 0xff, 0, dac_tlv), |
63 | SOC_SINGLE_TLV("Speaker Driver CH3 Playback Volume" , |
64 | TAS6424_CH3_VOL_CTRL, 0, 0xff, 0, dac_tlv), |
65 | SOC_SINGLE_TLV("Speaker Driver CH4 Playback Volume" , |
66 | TAS6424_CH4_VOL_CTRL, 0, 0xff, 0, dac_tlv), |
67 | SOC_SINGLE_STROBE("Auto Diagnostics Switch" , TAS6424_DC_DIAG_CTRL1, |
68 | TAS6424_LDGBYPASS_SHIFT, 1), |
69 | }; |
70 | |
71 | static int tas6424_dac_event(struct snd_soc_dapm_widget *w, |
72 | struct snd_kcontrol *kcontrol, int event) |
73 | { |
74 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
75 | struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(c: component); |
76 | |
77 | dev_dbg(component->dev, "%s() event=0x%0x\n" , __func__, event); |
78 | |
79 | if (event & SND_SOC_DAPM_POST_PMU) { |
80 | /* Observe codec shutdown-to-active time */ |
81 | msleep(msecs: 12); |
82 | |
83 | /* Turn on TAS6424 periodic fault checking/handling */ |
84 | tas6424->last_fault1 = 0; |
85 | tas6424->last_fault2 = 0; |
86 | tas6424->last_warn = 0; |
87 | schedule_delayed_work(dwork: &tas6424->fault_check_work, |
88 | delay: msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL)); |
89 | } else if (event & SND_SOC_DAPM_PRE_PMD) { |
90 | /* Disable TAS6424 periodic fault checking/handling */ |
91 | cancel_delayed_work_sync(dwork: &tas6424->fault_check_work); |
92 | } |
93 | |
94 | return 0; |
95 | } |
96 | |
97 | static const struct snd_soc_dapm_widget tas6424_dapm_widgets[] = { |
98 | SND_SOC_DAPM_AIF_IN("DAC IN" , "Playback" , 0, SND_SOC_NOPM, 0, 0), |
99 | SND_SOC_DAPM_DAC_E("DAC" , NULL, SND_SOC_NOPM, 0, 0, tas6424_dac_event, |
100 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
101 | SND_SOC_DAPM_OUTPUT("OUT" ) |
102 | }; |
103 | |
104 | static const struct snd_soc_dapm_route tas6424_audio_map[] = { |
105 | { "DAC" , NULL, "DAC IN" }, |
106 | { "OUT" , NULL, "DAC" }, |
107 | }; |
108 | |
109 | static int tas6424_hw_params(struct snd_pcm_substream *substream, |
110 | struct snd_pcm_hw_params *params, |
111 | struct snd_soc_dai *dai) |
112 | { |
113 | struct snd_soc_component *component = dai->component; |
114 | unsigned int rate = params_rate(p: params); |
115 | unsigned int width = params_width(p: params); |
116 | u8 sap_ctrl = 0; |
117 | |
118 | dev_dbg(component->dev, "%s() rate=%u width=%u\n" , __func__, rate, width); |
119 | |
120 | switch (rate) { |
121 | case 44100: |
122 | sap_ctrl |= TAS6424_SAP_RATE_44100; |
123 | break; |
124 | case 48000: |
125 | sap_ctrl |= TAS6424_SAP_RATE_48000; |
126 | break; |
127 | case 96000: |
128 | sap_ctrl |= TAS6424_SAP_RATE_96000; |
129 | break; |
130 | default: |
131 | dev_err(component->dev, "unsupported sample rate: %u\n" , rate); |
132 | return -EINVAL; |
133 | } |
134 | |
135 | switch (width) { |
136 | case 16: |
137 | sap_ctrl |= TAS6424_SAP_TDM_SLOT_SZ_16; |
138 | break; |
139 | case 24: |
140 | break; |
141 | default: |
142 | dev_err(component->dev, "unsupported sample width: %u\n" , width); |
143 | return -EINVAL; |
144 | } |
145 | |
146 | snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, |
147 | TAS6424_SAP_RATE_MASK | |
148 | TAS6424_SAP_TDM_SLOT_SZ_16, |
149 | val: sap_ctrl); |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | static int tas6424_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
155 | { |
156 | struct snd_soc_component *component = dai->component; |
157 | u8 serial_format = 0; |
158 | |
159 | dev_dbg(component->dev, "%s() fmt=0x%0x\n" , __func__, fmt); |
160 | |
161 | /* clock masters */ |
162 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
163 | case SND_SOC_DAIFMT_CBC_CFC: |
164 | break; |
165 | default: |
166 | dev_err(component->dev, "Invalid DAI clocking\n" ); |
167 | return -EINVAL; |
168 | } |
169 | |
170 | /* signal polarity */ |
171 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
172 | case SND_SOC_DAIFMT_NB_NF: |
173 | break; |
174 | default: |
175 | dev_err(component->dev, "Invalid DAI clock signal polarity\n" ); |
176 | return -EINVAL; |
177 | } |
178 | |
179 | /* interface format */ |
180 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
181 | case SND_SOC_DAIFMT_I2S: |
182 | serial_format |= TAS6424_SAP_I2S; |
183 | break; |
184 | case SND_SOC_DAIFMT_DSP_A: |
185 | serial_format |= TAS6424_SAP_DSP; |
186 | break; |
187 | case SND_SOC_DAIFMT_DSP_B: |
188 | /* |
189 | * We can use the fact that the TAS6424 does not care about the |
190 | * LRCLK duty cycle during TDM to receive DSP_B formatted data |
191 | * in LEFTJ mode (no delaying of the 1st data bit). |
192 | */ |
193 | serial_format |= TAS6424_SAP_LEFTJ; |
194 | break; |
195 | case SND_SOC_DAIFMT_LEFT_J: |
196 | serial_format |= TAS6424_SAP_LEFTJ; |
197 | break; |
198 | default: |
199 | dev_err(component->dev, "Invalid DAI interface format\n" ); |
200 | return -EINVAL; |
201 | } |
202 | |
203 | snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, |
204 | TAS6424_SAP_FMT_MASK, val: serial_format); |
205 | |
206 | return 0; |
207 | } |
208 | |
209 | static int tas6424_set_dai_tdm_slot(struct snd_soc_dai *dai, |
210 | unsigned int tx_mask, unsigned int rx_mask, |
211 | int slots, int slot_width) |
212 | { |
213 | struct snd_soc_component *component = dai->component; |
214 | unsigned int first_slot, last_slot; |
215 | bool sap_tdm_slot_last; |
216 | |
217 | dev_dbg(component->dev, "%s() tx_mask=%d rx_mask=%d\n" , __func__, |
218 | tx_mask, rx_mask); |
219 | |
220 | if (!tx_mask || !rx_mask) |
221 | return 0; /* nothing needed to disable TDM mode */ |
222 | |
223 | /* |
224 | * Determine the first slot and last slot that is being requested so |
225 | * we'll be able to more easily enforce certain constraints as the |
226 | * TAS6424's TDM interface is not fully configurable. |
227 | */ |
228 | first_slot = __ffs(tx_mask); |
229 | last_slot = __fls(word: rx_mask); |
230 | |
231 | if (last_slot - first_slot != 4) { |
232 | dev_err(component->dev, "tdm mask must cover 4 contiguous slots\n" ); |
233 | return -EINVAL; |
234 | } |
235 | |
236 | switch (first_slot) { |
237 | case 0: |
238 | sap_tdm_slot_last = false; |
239 | break; |
240 | case 4: |
241 | sap_tdm_slot_last = true; |
242 | break; |
243 | default: |
244 | dev_err(component->dev, "tdm mask must start at slot 0 or 4\n" ); |
245 | return -EINVAL; |
246 | } |
247 | |
248 | snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, TAS6424_SAP_TDM_SLOT_LAST, |
249 | val: sap_tdm_slot_last ? TAS6424_SAP_TDM_SLOT_LAST : 0); |
250 | |
251 | return 0; |
252 | } |
253 | |
254 | static int tas6424_mute(struct snd_soc_dai *dai, int mute, int direction) |
255 | { |
256 | struct snd_soc_component *component = dai->component; |
257 | struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(c: component); |
258 | unsigned int val; |
259 | |
260 | dev_dbg(component->dev, "%s() mute=%d\n" , __func__, mute); |
261 | |
262 | if (tas6424->mute_gpio) { |
263 | gpiod_set_value_cansleep(desc: tas6424->mute_gpio, value: mute); |
264 | return 0; |
265 | } |
266 | |
267 | if (mute) |
268 | val = TAS6424_ALL_STATE_MUTE; |
269 | else |
270 | val = TAS6424_ALL_STATE_PLAY; |
271 | |
272 | snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, val); |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | static int tas6424_power_off(struct snd_soc_component *component) |
278 | { |
279 | struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(c: component); |
280 | int ret; |
281 | |
282 | snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, TAS6424_ALL_STATE_HIZ); |
283 | |
284 | regcache_cache_only(map: tas6424->regmap, enable: true); |
285 | regcache_mark_dirty(map: tas6424->regmap); |
286 | |
287 | ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), |
288 | consumers: tas6424->supplies); |
289 | if (ret < 0) { |
290 | dev_err(component->dev, "failed to disable supplies: %d\n" , ret); |
291 | return ret; |
292 | } |
293 | |
294 | return 0; |
295 | } |
296 | |
297 | static int tas6424_power_on(struct snd_soc_component *component) |
298 | { |
299 | struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(c: component); |
300 | int ret; |
301 | u8 chan_states; |
302 | int no_auto_diags = 0; |
303 | unsigned int reg_val; |
304 | |
305 | if (!regmap_read(map: tas6424->regmap, TAS6424_DC_DIAG_CTRL1, val: ®_val)) |
306 | no_auto_diags = reg_val & TAS6424_LDGBYPASS_MASK; |
307 | |
308 | ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies), |
309 | consumers: tas6424->supplies); |
310 | if (ret < 0) { |
311 | dev_err(component->dev, "failed to enable supplies: %d\n" , ret); |
312 | return ret; |
313 | } |
314 | |
315 | regcache_cache_only(map: tas6424->regmap, enable: false); |
316 | |
317 | ret = regcache_sync(map: tas6424->regmap); |
318 | if (ret < 0) { |
319 | dev_err(component->dev, "failed to sync regcache: %d\n" , ret); |
320 | return ret; |
321 | } |
322 | |
323 | if (tas6424->mute_gpio) { |
324 | gpiod_set_value_cansleep(desc: tas6424->mute_gpio, value: 0); |
325 | /* |
326 | * channels are muted via the mute pin. Don't also mute |
327 | * them via the registers so that subsequent register |
328 | * access is not necessary to un-mute the channels |
329 | */ |
330 | chan_states = TAS6424_ALL_STATE_PLAY; |
331 | } else { |
332 | chan_states = TAS6424_ALL_STATE_MUTE; |
333 | } |
334 | snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, val: chan_states); |
335 | |
336 | /* any time we come out of HIZ, the output channels automatically run DC |
337 | * load diagnostics if autodiagnotics are enabled. wait here until this |
338 | * completes. |
339 | */ |
340 | if (!no_auto_diags) |
341 | msleep(msecs: 230); |
342 | |
343 | return 0; |
344 | } |
345 | |
346 | static int tas6424_set_bias_level(struct snd_soc_component *component, |
347 | enum snd_soc_bias_level level) |
348 | { |
349 | dev_dbg(component->dev, "%s() level=%d\n" , __func__, level); |
350 | |
351 | switch (level) { |
352 | case SND_SOC_BIAS_ON: |
353 | case SND_SOC_BIAS_PREPARE: |
354 | break; |
355 | case SND_SOC_BIAS_STANDBY: |
356 | if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) |
357 | tas6424_power_on(component); |
358 | break; |
359 | case SND_SOC_BIAS_OFF: |
360 | tas6424_power_off(component); |
361 | break; |
362 | } |
363 | |
364 | return 0; |
365 | } |
366 | |
367 | static struct snd_soc_component_driver soc_codec_dev_tas6424 = { |
368 | .set_bias_level = tas6424_set_bias_level, |
369 | .controls = tas6424_snd_controls, |
370 | .num_controls = ARRAY_SIZE(tas6424_snd_controls), |
371 | .dapm_widgets = tas6424_dapm_widgets, |
372 | .num_dapm_widgets = ARRAY_SIZE(tas6424_dapm_widgets), |
373 | .dapm_routes = tas6424_audio_map, |
374 | .num_dapm_routes = ARRAY_SIZE(tas6424_audio_map), |
375 | .use_pmdown_time = 1, |
376 | .endianness = 1, |
377 | }; |
378 | |
379 | static const struct snd_soc_dai_ops tas6424_speaker_dai_ops = { |
380 | .hw_params = tas6424_hw_params, |
381 | .set_fmt = tas6424_set_dai_fmt, |
382 | .set_tdm_slot = tas6424_set_dai_tdm_slot, |
383 | .mute_stream = tas6424_mute, |
384 | .no_capture_mute = 1, |
385 | }; |
386 | |
387 | static struct snd_soc_dai_driver tas6424_dai[] = { |
388 | { |
389 | .name = "tas6424-amplifier" , |
390 | .playback = { |
391 | .stream_name = "Playback" , |
392 | .channels_min = 1, |
393 | .channels_max = 4, |
394 | .rates = TAS6424_RATES, |
395 | .formats = TAS6424_FORMATS, |
396 | }, |
397 | .ops = &tas6424_speaker_dai_ops, |
398 | }, |
399 | }; |
400 | |
401 | static void tas6424_fault_check_work(struct work_struct *work) |
402 | { |
403 | struct tas6424_data *tas6424 = container_of(work, struct tas6424_data, |
404 | fault_check_work.work); |
405 | struct device *dev = tas6424->dev; |
406 | unsigned int reg; |
407 | int ret; |
408 | |
409 | ret = regmap_read(map: tas6424->regmap, TAS6424_CHANNEL_FAULT, val: ®); |
410 | if (ret < 0) { |
411 | dev_err(dev, "failed to read CHANNEL_FAULT register: %d\n" , ret); |
412 | goto out; |
413 | } |
414 | |
415 | if (!reg) { |
416 | tas6424->last_cfault = reg; |
417 | goto check_global_fault1_reg; |
418 | } |
419 | |
420 | /* |
421 | * Only flag errors once for a given occurrence. This is needed as |
422 | * the TAS6424 will take time clearing the fault condition internally |
423 | * during which we don't want to bombard the system with the same |
424 | * error message over and over. |
425 | */ |
426 | if ((reg & TAS6424_FAULT_OC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH1)) |
427 | dev_crit(dev, "experienced a channel 1 overcurrent fault\n" ); |
428 | |
429 | if ((reg & TAS6424_FAULT_OC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH2)) |
430 | dev_crit(dev, "experienced a channel 2 overcurrent fault\n" ); |
431 | |
432 | if ((reg & TAS6424_FAULT_OC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH3)) |
433 | dev_crit(dev, "experienced a channel 3 overcurrent fault\n" ); |
434 | |
435 | if ((reg & TAS6424_FAULT_OC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH4)) |
436 | dev_crit(dev, "experienced a channel 4 overcurrent fault\n" ); |
437 | |
438 | if ((reg & TAS6424_FAULT_DC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH1)) |
439 | dev_crit(dev, "experienced a channel 1 DC fault\n" ); |
440 | |
441 | if ((reg & TAS6424_FAULT_DC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH2)) |
442 | dev_crit(dev, "experienced a channel 2 DC fault\n" ); |
443 | |
444 | if ((reg & TAS6424_FAULT_DC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH3)) |
445 | dev_crit(dev, "experienced a channel 3 DC fault\n" ); |
446 | |
447 | if ((reg & TAS6424_FAULT_DC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH4)) |
448 | dev_crit(dev, "experienced a channel 4 DC fault\n" ); |
449 | |
450 | /* Store current fault1 value so we can detect any changes next time */ |
451 | tas6424->last_cfault = reg; |
452 | |
453 | check_global_fault1_reg: |
454 | ret = regmap_read(map: tas6424->regmap, TAS6424_GLOB_FAULT1, val: ®); |
455 | if (ret < 0) { |
456 | dev_err(dev, "failed to read GLOB_FAULT1 register: %d\n" , ret); |
457 | goto out; |
458 | } |
459 | |
460 | /* |
461 | * Ignore any clock faults as there is no clean way to check for them. |
462 | * We would need to start checking for those faults *after* the SAIF |
463 | * stream has been setup, and stop checking *before* the stream is |
464 | * stopped to avoid any false-positives. However there are no |
465 | * appropriate hooks to monitor these events. |
466 | */ |
467 | reg &= TAS6424_FAULT_PVDD_OV | |
468 | TAS6424_FAULT_VBAT_OV | |
469 | TAS6424_FAULT_PVDD_UV | |
470 | TAS6424_FAULT_VBAT_UV; |
471 | |
472 | if (!reg) { |
473 | tas6424->last_fault1 = reg; |
474 | goto check_global_fault2_reg; |
475 | } |
476 | |
477 | if ((reg & TAS6424_FAULT_PVDD_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_OV)) |
478 | dev_crit(dev, "experienced a PVDD overvoltage fault\n" ); |
479 | |
480 | if ((reg & TAS6424_FAULT_VBAT_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_OV)) |
481 | dev_crit(dev, "experienced a VBAT overvoltage fault\n" ); |
482 | |
483 | if ((reg & TAS6424_FAULT_PVDD_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_UV)) |
484 | dev_crit(dev, "experienced a PVDD undervoltage fault\n" ); |
485 | |
486 | if ((reg & TAS6424_FAULT_VBAT_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_UV)) |
487 | dev_crit(dev, "experienced a VBAT undervoltage fault\n" ); |
488 | |
489 | /* Store current fault1 value so we can detect any changes next time */ |
490 | tas6424->last_fault1 = reg; |
491 | |
492 | check_global_fault2_reg: |
493 | ret = regmap_read(map: tas6424->regmap, TAS6424_GLOB_FAULT2, val: ®); |
494 | if (ret < 0) { |
495 | dev_err(dev, "failed to read GLOB_FAULT2 register: %d\n" , ret); |
496 | goto out; |
497 | } |
498 | |
499 | reg &= TAS6424_FAULT_OTSD | |
500 | TAS6424_FAULT_OTSD_CH1 | |
501 | TAS6424_FAULT_OTSD_CH2 | |
502 | TAS6424_FAULT_OTSD_CH3 | |
503 | TAS6424_FAULT_OTSD_CH4; |
504 | |
505 | if (!reg) { |
506 | tas6424->last_fault2 = reg; |
507 | goto check_warn_reg; |
508 | } |
509 | |
510 | if ((reg & TAS6424_FAULT_OTSD) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD)) |
511 | dev_crit(dev, "experienced a global overtemp shutdown\n" ); |
512 | |
513 | if ((reg & TAS6424_FAULT_OTSD_CH1) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH1)) |
514 | dev_crit(dev, "experienced an overtemp shutdown on CH1\n" ); |
515 | |
516 | if ((reg & TAS6424_FAULT_OTSD_CH2) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH2)) |
517 | dev_crit(dev, "experienced an overtemp shutdown on CH2\n" ); |
518 | |
519 | if ((reg & TAS6424_FAULT_OTSD_CH3) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH3)) |
520 | dev_crit(dev, "experienced an overtemp shutdown on CH3\n" ); |
521 | |
522 | if ((reg & TAS6424_FAULT_OTSD_CH4) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH4)) |
523 | dev_crit(dev, "experienced an overtemp shutdown on CH4\n" ); |
524 | |
525 | /* Store current fault2 value so we can detect any changes next time */ |
526 | tas6424->last_fault2 = reg; |
527 | |
528 | check_warn_reg: |
529 | ret = regmap_read(map: tas6424->regmap, TAS6424_WARN, val: ®); |
530 | if (ret < 0) { |
531 | dev_err(dev, "failed to read WARN register: %d\n" , ret); |
532 | goto out; |
533 | } |
534 | |
535 | reg &= TAS6424_WARN_VDD_UV | |
536 | TAS6424_WARN_VDD_POR | |
537 | TAS6424_WARN_VDD_OTW | |
538 | TAS6424_WARN_VDD_OTW_CH1 | |
539 | TAS6424_WARN_VDD_OTW_CH2 | |
540 | TAS6424_WARN_VDD_OTW_CH3 | |
541 | TAS6424_WARN_VDD_OTW_CH4; |
542 | |
543 | if (!reg) { |
544 | tas6424->last_warn = reg; |
545 | goto out; |
546 | } |
547 | |
548 | if ((reg & TAS6424_WARN_VDD_UV) && !(tas6424->last_warn & TAS6424_WARN_VDD_UV)) |
549 | dev_warn(dev, "experienced a VDD under voltage condition\n" ); |
550 | |
551 | if ((reg & TAS6424_WARN_VDD_POR) && !(tas6424->last_warn & TAS6424_WARN_VDD_POR)) |
552 | dev_warn(dev, "experienced a VDD POR condition\n" ); |
553 | |
554 | if ((reg & TAS6424_WARN_VDD_OTW) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW)) |
555 | dev_warn(dev, "experienced a global overtemp warning\n" ); |
556 | |
557 | if ((reg & TAS6424_WARN_VDD_OTW_CH1) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH1)) |
558 | dev_warn(dev, "experienced an overtemp warning on CH1\n" ); |
559 | |
560 | if ((reg & TAS6424_WARN_VDD_OTW_CH2) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH2)) |
561 | dev_warn(dev, "experienced an overtemp warning on CH2\n" ); |
562 | |
563 | if ((reg & TAS6424_WARN_VDD_OTW_CH3) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH3)) |
564 | dev_warn(dev, "experienced an overtemp warning on CH3\n" ); |
565 | |
566 | if ((reg & TAS6424_WARN_VDD_OTW_CH4) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH4)) |
567 | dev_warn(dev, "experienced an overtemp warning on CH4\n" ); |
568 | |
569 | /* Store current warn value so we can detect any changes next time */ |
570 | tas6424->last_warn = reg; |
571 | |
572 | /* Clear any warnings by toggling the CLEAR_FAULT control bit */ |
573 | ret = regmap_write_bits(map: tas6424->regmap, TAS6424_MISC_CTRL3, |
574 | TAS6424_CLEAR_FAULT, TAS6424_CLEAR_FAULT); |
575 | if (ret < 0) |
576 | dev_err(dev, "failed to write MISC_CTRL3 register: %d\n" , ret); |
577 | |
578 | ret = regmap_write_bits(map: tas6424->regmap, TAS6424_MISC_CTRL3, |
579 | TAS6424_CLEAR_FAULT, val: 0); |
580 | if (ret < 0) |
581 | dev_err(dev, "failed to write MISC_CTRL3 register: %d\n" , ret); |
582 | |
583 | out: |
584 | /* Schedule the next fault check at the specified interval */ |
585 | schedule_delayed_work(dwork: &tas6424->fault_check_work, |
586 | delay: msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL)); |
587 | } |
588 | |
589 | static const struct reg_default tas6424_reg_defaults[] = { |
590 | { TAS6424_MODE_CTRL, 0x00 }, |
591 | { TAS6424_MISC_CTRL1, 0x32 }, |
592 | { TAS6424_MISC_CTRL2, 0x62 }, |
593 | { TAS6424_SAP_CTRL, 0x04 }, |
594 | { TAS6424_CH_STATE_CTRL, 0x55 }, |
595 | { TAS6424_CH1_VOL_CTRL, 0xcf }, |
596 | { TAS6424_CH2_VOL_CTRL, 0xcf }, |
597 | { TAS6424_CH3_VOL_CTRL, 0xcf }, |
598 | { TAS6424_CH4_VOL_CTRL, 0xcf }, |
599 | { TAS6424_DC_DIAG_CTRL1, 0x00 }, |
600 | { TAS6424_DC_DIAG_CTRL2, 0x11 }, |
601 | { TAS6424_DC_DIAG_CTRL3, 0x11 }, |
602 | { TAS6424_PIN_CTRL, 0xff }, |
603 | { TAS6424_AC_DIAG_CTRL1, 0x00 }, |
604 | { TAS6424_MISC_CTRL3, 0x00 }, |
605 | { TAS6424_CLIP_CTRL, 0x01 }, |
606 | { TAS6424_CLIP_WINDOW, 0x14 }, |
607 | { TAS6424_CLIP_WARN, 0x00 }, |
608 | { TAS6424_CBC_STAT, 0x00 }, |
609 | { TAS6424_MISC_CTRL4, 0x40 }, |
610 | }; |
611 | |
612 | static bool tas6424_is_writable_reg(struct device *dev, unsigned int reg) |
613 | { |
614 | switch (reg) { |
615 | case TAS6424_MODE_CTRL: |
616 | case TAS6424_MISC_CTRL1: |
617 | case TAS6424_MISC_CTRL2: |
618 | case TAS6424_SAP_CTRL: |
619 | case TAS6424_CH_STATE_CTRL: |
620 | case TAS6424_CH1_VOL_CTRL: |
621 | case TAS6424_CH2_VOL_CTRL: |
622 | case TAS6424_CH3_VOL_CTRL: |
623 | case TAS6424_CH4_VOL_CTRL: |
624 | case TAS6424_DC_DIAG_CTRL1: |
625 | case TAS6424_DC_DIAG_CTRL2: |
626 | case TAS6424_DC_DIAG_CTRL3: |
627 | case TAS6424_PIN_CTRL: |
628 | case TAS6424_AC_DIAG_CTRL1: |
629 | case TAS6424_MISC_CTRL3: |
630 | case TAS6424_CLIP_CTRL: |
631 | case TAS6424_CLIP_WINDOW: |
632 | case TAS6424_CLIP_WARN: |
633 | case TAS6424_CBC_STAT: |
634 | case TAS6424_MISC_CTRL4: |
635 | return true; |
636 | default: |
637 | return false; |
638 | } |
639 | } |
640 | |
641 | static bool tas6424_is_volatile_reg(struct device *dev, unsigned int reg) |
642 | { |
643 | switch (reg) { |
644 | case TAS6424_DC_LOAD_DIAG_REP12: |
645 | case TAS6424_DC_LOAD_DIAG_REP34: |
646 | case TAS6424_DC_LOAD_DIAG_REPLO: |
647 | case TAS6424_CHANNEL_STATE: |
648 | case TAS6424_CHANNEL_FAULT: |
649 | case TAS6424_GLOB_FAULT1: |
650 | case TAS6424_GLOB_FAULT2: |
651 | case TAS6424_WARN: |
652 | case TAS6424_AC_LOAD_DIAG_REP1: |
653 | case TAS6424_AC_LOAD_DIAG_REP2: |
654 | case TAS6424_AC_LOAD_DIAG_REP3: |
655 | case TAS6424_AC_LOAD_DIAG_REP4: |
656 | return true; |
657 | default: |
658 | return false; |
659 | } |
660 | } |
661 | |
662 | static const struct regmap_config tas6424_regmap_config = { |
663 | .reg_bits = 8, |
664 | .val_bits = 8, |
665 | |
666 | .writeable_reg = tas6424_is_writable_reg, |
667 | .volatile_reg = tas6424_is_volatile_reg, |
668 | |
669 | .max_register = TAS6424_MAX, |
670 | .reg_defaults = tas6424_reg_defaults, |
671 | .num_reg_defaults = ARRAY_SIZE(tas6424_reg_defaults), |
672 | .cache_type = REGCACHE_RBTREE, |
673 | }; |
674 | |
675 | #if IS_ENABLED(CONFIG_OF) |
676 | static const struct of_device_id tas6424_of_ids[] = { |
677 | { .compatible = "ti,tas6424" , }, |
678 | { }, |
679 | }; |
680 | MODULE_DEVICE_TABLE(of, tas6424_of_ids); |
681 | #endif |
682 | |
683 | static int tas6424_i2c_probe(struct i2c_client *client) |
684 | { |
685 | struct device *dev = &client->dev; |
686 | struct tas6424_data *tas6424; |
687 | int ret; |
688 | int i; |
689 | |
690 | tas6424 = devm_kzalloc(dev, size: sizeof(*tas6424), GFP_KERNEL); |
691 | if (!tas6424) |
692 | return -ENOMEM; |
693 | dev_set_drvdata(dev, data: tas6424); |
694 | |
695 | tas6424->dev = dev; |
696 | |
697 | tas6424->regmap = devm_regmap_init_i2c(client, &tas6424_regmap_config); |
698 | if (IS_ERR(ptr: tas6424->regmap)) { |
699 | ret = PTR_ERR(ptr: tas6424->regmap); |
700 | dev_err(dev, "unable to allocate register map: %d\n" , ret); |
701 | return ret; |
702 | } |
703 | |
704 | /* |
705 | * Get control of the standby pin and set it LOW to take the codec |
706 | * out of the stand-by mode. |
707 | * Note: The actual pin polarity is taken care of in the GPIO lib |
708 | * according the polarity specified in the DTS. |
709 | */ |
710 | tas6424->standby_gpio = devm_gpiod_get_optional(dev, con_id: "standby" , |
711 | flags: GPIOD_OUT_LOW); |
712 | if (IS_ERR(ptr: tas6424->standby_gpio)) { |
713 | if (PTR_ERR(ptr: tas6424->standby_gpio) == -EPROBE_DEFER) |
714 | return -EPROBE_DEFER; |
715 | dev_info(dev, "failed to get standby GPIO: %ld\n" , |
716 | PTR_ERR(tas6424->standby_gpio)); |
717 | tas6424->standby_gpio = NULL; |
718 | } |
719 | |
720 | /* |
721 | * Get control of the mute pin and set it HIGH in order to start with |
722 | * all the output muted. |
723 | * Note: The actual pin polarity is taken care of in the GPIO lib |
724 | * according the polarity specified in the DTS. |
725 | */ |
726 | tas6424->mute_gpio = devm_gpiod_get_optional(dev, con_id: "mute" , |
727 | flags: GPIOD_OUT_HIGH); |
728 | if (IS_ERR(ptr: tas6424->mute_gpio)) { |
729 | if (PTR_ERR(ptr: tas6424->mute_gpio) == -EPROBE_DEFER) |
730 | return -EPROBE_DEFER; |
731 | dev_info(dev, "failed to get nmute GPIO: %ld\n" , |
732 | PTR_ERR(tas6424->mute_gpio)); |
733 | tas6424->mute_gpio = NULL; |
734 | } |
735 | |
736 | for (i = 0; i < ARRAY_SIZE(tas6424->supplies); i++) |
737 | tas6424->supplies[i].supply = tas6424_supply_names[i]; |
738 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(tas6424->supplies), |
739 | consumers: tas6424->supplies); |
740 | if (ret) { |
741 | dev_err(dev, "unable to request supplies: %d\n" , ret); |
742 | return ret; |
743 | } |
744 | |
745 | ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies), |
746 | consumers: tas6424->supplies); |
747 | if (ret) { |
748 | dev_err(dev, "unable to enable supplies: %d\n" , ret); |
749 | return ret; |
750 | } |
751 | |
752 | /* Reset device to establish well-defined startup state */ |
753 | ret = regmap_update_bits(map: tas6424->regmap, TAS6424_MODE_CTRL, |
754 | TAS6424_RESET, TAS6424_RESET); |
755 | if (ret) { |
756 | dev_err(dev, "unable to reset device: %d\n" , ret); |
757 | goto disable_regs; |
758 | } |
759 | |
760 | INIT_DELAYED_WORK(&tas6424->fault_check_work, tas6424_fault_check_work); |
761 | |
762 | ret = devm_snd_soc_register_component(dev, component_driver: &soc_codec_dev_tas6424, |
763 | dai_drv: tas6424_dai, ARRAY_SIZE(tas6424_dai)); |
764 | if (ret < 0) { |
765 | dev_err(dev, "unable to register codec: %d\n" , ret); |
766 | goto disable_regs; |
767 | } |
768 | |
769 | return 0; |
770 | |
771 | disable_regs: |
772 | regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), consumers: tas6424->supplies); |
773 | return ret; |
774 | } |
775 | |
776 | static void tas6424_i2c_remove(struct i2c_client *client) |
777 | { |
778 | struct device *dev = &client->dev; |
779 | struct tas6424_data *tas6424 = dev_get_drvdata(dev); |
780 | int ret; |
781 | |
782 | cancel_delayed_work_sync(dwork: &tas6424->fault_check_work); |
783 | |
784 | /* put the codec in stand-by */ |
785 | if (tas6424->standby_gpio) |
786 | gpiod_set_value_cansleep(desc: tas6424->standby_gpio, value: 1); |
787 | |
788 | ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), |
789 | consumers: tas6424->supplies); |
790 | if (ret < 0) |
791 | dev_err(dev, "unable to disable supplies: %d\n" , ret); |
792 | } |
793 | |
794 | static const struct i2c_device_id tas6424_i2c_ids[] = { |
795 | { "tas6424" , 0 }, |
796 | { } |
797 | }; |
798 | MODULE_DEVICE_TABLE(i2c, tas6424_i2c_ids); |
799 | |
800 | static struct i2c_driver tas6424_i2c_driver = { |
801 | .driver = { |
802 | .name = "tas6424" , |
803 | .of_match_table = of_match_ptr(tas6424_of_ids), |
804 | }, |
805 | .probe = tas6424_i2c_probe, |
806 | .remove = tas6424_i2c_remove, |
807 | .id_table = tas6424_i2c_ids, |
808 | }; |
809 | module_i2c_driver(tas6424_i2c_driver); |
810 | |
811 | MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>" ); |
812 | MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>" ); |
813 | MODULE_DESCRIPTION("TAS6424 Audio amplifier driver" ); |
814 | MODULE_LICENSE("GPL v2" ); |
815 | |