1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Driver for the Texas Instruments TAS2764 CODEC |
4 | // Copyright (C) 2020 Texas Instruments Inc. |
5 | |
6 | #include <linux/module.h> |
7 | #include <linux/moduleparam.h> |
8 | #include <linux/err.h> |
9 | #include <linux/init.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/pm.h> |
12 | #include <linux/i2c.h> |
13 | #include <linux/gpio.h> |
14 | #include <linux/gpio/consumer.h> |
15 | #include <linux/regulator/consumer.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/of.h> |
18 | #include <linux/of_gpio.h> |
19 | #include <linux/slab.h> |
20 | #include <sound/soc.h> |
21 | #include <sound/pcm.h> |
22 | #include <sound/pcm_params.h> |
23 | #include <sound/initval.h> |
24 | #include <sound/tlv.h> |
25 | |
26 | #include "tas2764.h" |
27 | |
28 | struct tas2764_priv { |
29 | struct snd_soc_component *component; |
30 | struct gpio_desc *reset_gpio; |
31 | struct gpio_desc *sdz_gpio; |
32 | struct regmap *regmap; |
33 | struct device *dev; |
34 | int irq; |
35 | |
36 | int v_sense_slot; |
37 | int i_sense_slot; |
38 | |
39 | bool dac_powered; |
40 | bool unmuted; |
41 | }; |
42 | |
43 | static const char *tas2764_int_ltch0_msgs[8] = { |
44 | "fault: over temperature" , /* INT_LTCH0 & BIT(0) */ |
45 | "fault: over current" , |
46 | "fault: bad TDM clock" , |
47 | "limiter active" , |
48 | "fault: PVDD below limiter inflection point" , |
49 | "fault: limiter max attenuation" , |
50 | "fault: BOP infinite hold" , |
51 | "fault: BOP mute" , /* INT_LTCH0 & BIT(7) */ |
52 | }; |
53 | |
54 | static const unsigned int tas2764_int_readout_regs[6] = { |
55 | TAS2764_INT_LTCH0, |
56 | TAS2764_INT_LTCH1, |
57 | TAS2764_INT_LTCH1_0, |
58 | TAS2764_INT_LTCH2, |
59 | TAS2764_INT_LTCH3, |
60 | TAS2764_INT_LTCH4, |
61 | }; |
62 | |
63 | static irqreturn_t tas2764_irq(int irq, void *data) |
64 | { |
65 | struct tas2764_priv *tas2764 = data; |
66 | u8 latched[6] = {0, 0, 0, 0, 0, 0}; |
67 | int ret = IRQ_NONE; |
68 | int i; |
69 | |
70 | for (i = 0; i < ARRAY_SIZE(latched); i++) |
71 | latched[i] = snd_soc_component_read(component: tas2764->component, |
72 | reg: tas2764_int_readout_regs[i]); |
73 | |
74 | for (i = 0; i < 8; i++) { |
75 | if (latched[0] & BIT(i)) { |
76 | dev_crit_ratelimited(tas2764->dev, "%s\n" , |
77 | tas2764_int_ltch0_msgs[i]); |
78 | ret = IRQ_HANDLED; |
79 | } |
80 | } |
81 | |
82 | if (latched[0]) { |
83 | dev_err_ratelimited(tas2764->dev, "other context to the fault: %02x,%02x,%02x,%02x,%02x" , |
84 | latched[1], latched[2], latched[3], latched[4], latched[5]); |
85 | snd_soc_component_update_bits(component: tas2764->component, |
86 | TAS2764_INT_CLK_CFG, |
87 | TAS2764_INT_CLK_CFG_IRQZ_CLR, |
88 | TAS2764_INT_CLK_CFG_IRQZ_CLR); |
89 | } |
90 | |
91 | return ret; |
92 | } |
93 | |
94 | static void tas2764_reset(struct tas2764_priv *tas2764) |
95 | { |
96 | if (tas2764->reset_gpio) { |
97 | gpiod_set_value_cansleep(desc: tas2764->reset_gpio, value: 0); |
98 | msleep(msecs: 20); |
99 | gpiod_set_value_cansleep(desc: tas2764->reset_gpio, value: 1); |
100 | usleep_range(min: 1000, max: 2000); |
101 | } |
102 | |
103 | snd_soc_component_write(component: tas2764->component, TAS2764_SW_RST, |
104 | TAS2764_RST); |
105 | usleep_range(min: 1000, max: 2000); |
106 | } |
107 | |
108 | static int tas2764_update_pwr_ctrl(struct tas2764_priv *tas2764) |
109 | { |
110 | struct snd_soc_component *component = tas2764->component; |
111 | unsigned int val; |
112 | int ret; |
113 | |
114 | if (tas2764->dac_powered) |
115 | val = tas2764->unmuted ? |
116 | TAS2764_PWR_CTRL_ACTIVE : TAS2764_PWR_CTRL_MUTE; |
117 | else |
118 | val = TAS2764_PWR_CTRL_SHUTDOWN; |
119 | |
120 | ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, |
121 | TAS2764_PWR_CTRL_MASK, val); |
122 | if (ret < 0) |
123 | return ret; |
124 | |
125 | return 0; |
126 | } |
127 | |
128 | #ifdef CONFIG_PM |
129 | static int tas2764_codec_suspend(struct snd_soc_component *component) |
130 | { |
131 | struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(c: component); |
132 | int ret; |
133 | |
134 | ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, |
135 | TAS2764_PWR_CTRL_MASK, |
136 | TAS2764_PWR_CTRL_SHUTDOWN); |
137 | |
138 | if (ret < 0) |
139 | return ret; |
140 | |
141 | if (tas2764->sdz_gpio) |
142 | gpiod_set_value_cansleep(desc: tas2764->sdz_gpio, value: 0); |
143 | |
144 | regcache_cache_only(map: tas2764->regmap, enable: true); |
145 | regcache_mark_dirty(map: tas2764->regmap); |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static int tas2764_codec_resume(struct snd_soc_component *component) |
151 | { |
152 | struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(c: component); |
153 | int ret; |
154 | |
155 | if (tas2764->sdz_gpio) { |
156 | gpiod_set_value_cansleep(desc: tas2764->sdz_gpio, value: 1); |
157 | usleep_range(min: 1000, max: 2000); |
158 | } |
159 | |
160 | ret = tas2764_update_pwr_ctrl(tas2764); |
161 | |
162 | if (ret < 0) |
163 | return ret; |
164 | |
165 | regcache_cache_only(map: tas2764->regmap, enable: false); |
166 | |
167 | return regcache_sync(map: tas2764->regmap); |
168 | } |
169 | #else |
170 | #define tas2764_codec_suspend NULL |
171 | #define tas2764_codec_resume NULL |
172 | #endif |
173 | |
174 | static const char * const tas2764_ASI1_src[] = { |
175 | "I2C offset" , "Left" , "Right" , "LeftRightDiv2" , |
176 | }; |
177 | |
178 | static SOC_ENUM_SINGLE_DECL( |
179 | tas2764_ASI1_src_enum, TAS2764_TDM_CFG2, TAS2764_TDM_CFG2_SCFG_SHIFT, |
180 | tas2764_ASI1_src); |
181 | |
182 | static const struct snd_kcontrol_new tas2764_asi1_mux = |
183 | SOC_DAPM_ENUM("ASI1 Source" , tas2764_ASI1_src_enum); |
184 | |
185 | static int tas2764_dac_event(struct snd_soc_dapm_widget *w, |
186 | struct snd_kcontrol *kcontrol, int event) |
187 | { |
188 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
189 | struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(c: component); |
190 | int ret; |
191 | |
192 | switch (event) { |
193 | case SND_SOC_DAPM_POST_PMU: |
194 | tas2764->dac_powered = true; |
195 | ret = tas2764_update_pwr_ctrl(tas2764); |
196 | break; |
197 | case SND_SOC_DAPM_PRE_PMD: |
198 | tas2764->dac_powered = false; |
199 | ret = tas2764_update_pwr_ctrl(tas2764); |
200 | break; |
201 | default: |
202 | dev_err(tas2764->dev, "Unsupported event\n" ); |
203 | return -EINVAL; |
204 | } |
205 | |
206 | if (ret < 0) |
207 | return ret; |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | static const struct snd_kcontrol_new isense_switch = |
213 | SOC_DAPM_SINGLE("Switch" , TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, 1, 1); |
214 | static const struct snd_kcontrol_new vsense_switch = |
215 | SOC_DAPM_SINGLE("Switch" , TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, 1, 1); |
216 | |
217 | static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = { |
218 | SND_SOC_DAPM_AIF_IN("ASI1" , "ASI1 Playback" , 0, SND_SOC_NOPM, 0, 0), |
219 | SND_SOC_DAPM_MUX("ASI1 Sel" , SND_SOC_NOPM, 0, 0, &tas2764_asi1_mux), |
220 | SND_SOC_DAPM_SWITCH("ISENSE" , TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, |
221 | 1, &isense_switch), |
222 | SND_SOC_DAPM_SWITCH("VSENSE" , TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, |
223 | 1, &vsense_switch), |
224 | SND_SOC_DAPM_DAC_E("DAC" , NULL, SND_SOC_NOPM, 0, 0, tas2764_dac_event, |
225 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
226 | SND_SOC_DAPM_OUTPUT("OUT" ), |
227 | SND_SOC_DAPM_SIGGEN("VMON" ), |
228 | SND_SOC_DAPM_SIGGEN("IMON" ) |
229 | }; |
230 | |
231 | static const struct snd_soc_dapm_route tas2764_audio_map[] = { |
232 | {"ASI1 Sel" , "I2C offset" , "ASI1" }, |
233 | {"ASI1 Sel" , "Left" , "ASI1" }, |
234 | {"ASI1 Sel" , "Right" , "ASI1" }, |
235 | {"ASI1 Sel" , "LeftRightDiv2" , "ASI1" }, |
236 | {"DAC" , NULL, "ASI1 Sel" }, |
237 | {"OUT" , NULL, "DAC" }, |
238 | {"ISENSE" , "Switch" , "IMON" }, |
239 | {"VSENSE" , "Switch" , "VMON" }, |
240 | }; |
241 | |
242 | static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction) |
243 | { |
244 | struct tas2764_priv *tas2764 = |
245 | snd_soc_component_get_drvdata(c: dai->component); |
246 | |
247 | tas2764->unmuted = !mute; |
248 | return tas2764_update_pwr_ctrl(tas2764); |
249 | } |
250 | |
251 | static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth) |
252 | { |
253 | struct snd_soc_component *component = tas2764->component; |
254 | int sense_en; |
255 | int val; |
256 | int ret; |
257 | |
258 | switch (bitwidth) { |
259 | case SNDRV_PCM_FORMAT_S16_LE: |
260 | ret = snd_soc_component_update_bits(component, |
261 | TAS2764_TDM_CFG2, |
262 | TAS2764_TDM_CFG2_RXW_MASK, |
263 | TAS2764_TDM_CFG2_RXW_16BITS); |
264 | break; |
265 | case SNDRV_PCM_FORMAT_S24_LE: |
266 | ret = snd_soc_component_update_bits(component, |
267 | TAS2764_TDM_CFG2, |
268 | TAS2764_TDM_CFG2_RXW_MASK, |
269 | TAS2764_TDM_CFG2_RXW_24BITS); |
270 | break; |
271 | case SNDRV_PCM_FORMAT_S32_LE: |
272 | ret = snd_soc_component_update_bits(component, |
273 | TAS2764_TDM_CFG2, |
274 | TAS2764_TDM_CFG2_RXW_MASK, |
275 | TAS2764_TDM_CFG2_RXW_32BITS); |
276 | break; |
277 | |
278 | default: |
279 | return -EINVAL; |
280 | } |
281 | |
282 | if (ret < 0) |
283 | return ret; |
284 | |
285 | val = snd_soc_component_read(component: tas2764->component, TAS2764_PWR_CTRL); |
286 | if (val < 0) |
287 | return val; |
288 | |
289 | if (val & (1 << TAS2764_VSENSE_POWER_EN)) |
290 | sense_en = 0; |
291 | else |
292 | sense_en = TAS2764_TDM_CFG5_VSNS_ENABLE; |
293 | |
294 | ret = snd_soc_component_update_bits(component: tas2764->component, TAS2764_TDM_CFG5, |
295 | TAS2764_TDM_CFG5_VSNS_ENABLE, |
296 | val: sense_en); |
297 | if (ret < 0) |
298 | return ret; |
299 | |
300 | if (val & (1 << TAS2764_ISENSE_POWER_EN)) |
301 | sense_en = 0; |
302 | else |
303 | sense_en = TAS2764_TDM_CFG6_ISNS_ENABLE; |
304 | |
305 | ret = snd_soc_component_update_bits(component: tas2764->component, TAS2764_TDM_CFG6, |
306 | TAS2764_TDM_CFG6_ISNS_ENABLE, |
307 | val: sense_en); |
308 | if (ret < 0) |
309 | return ret; |
310 | |
311 | return 0; |
312 | } |
313 | |
314 | static int tas2764_set_samplerate(struct tas2764_priv *tas2764, int samplerate) |
315 | { |
316 | struct snd_soc_component *component = tas2764->component; |
317 | int ramp_rate_val; |
318 | int ret; |
319 | |
320 | switch (samplerate) { |
321 | case 48000: |
322 | ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ | |
323 | TAS2764_TDM_CFG0_44_1_48KHZ; |
324 | break; |
325 | case 44100: |
326 | ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ | |
327 | TAS2764_TDM_CFG0_44_1_48KHZ; |
328 | break; |
329 | case 96000: |
330 | ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ | |
331 | TAS2764_TDM_CFG0_88_2_96KHZ; |
332 | break; |
333 | case 88200: |
334 | ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ | |
335 | TAS2764_TDM_CFG0_88_2_96KHZ; |
336 | break; |
337 | default: |
338 | return -EINVAL; |
339 | } |
340 | |
341 | ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0, |
342 | TAS2764_TDM_CFG0_SMP_MASK | |
343 | TAS2764_TDM_CFG0_MASK, |
344 | val: ramp_rate_val); |
345 | if (ret < 0) |
346 | return ret; |
347 | |
348 | return 0; |
349 | } |
350 | |
351 | static int tas2764_hw_params(struct snd_pcm_substream *substream, |
352 | struct snd_pcm_hw_params *params, |
353 | struct snd_soc_dai *dai) |
354 | { |
355 | struct snd_soc_component *component = dai->component; |
356 | struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(c: component); |
357 | int ret; |
358 | |
359 | ret = tas2764_set_bitwidth(tas2764, bitwidth: params_format(p: params)); |
360 | if (ret < 0) |
361 | return ret; |
362 | |
363 | return tas2764_set_samplerate(tas2764, samplerate: params_rate(p: params)); |
364 | } |
365 | |
366 | static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
367 | { |
368 | struct snd_soc_component *component = dai->component; |
369 | struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(c: component); |
370 | u8 tdm_rx_start_slot = 0, asi_cfg_0 = 0, asi_cfg_1 = 0; |
371 | int ret; |
372 | |
373 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
374 | case SND_SOC_DAIFMT_NB_IF: |
375 | asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START; |
376 | fallthrough; |
377 | case SND_SOC_DAIFMT_NB_NF: |
378 | asi_cfg_1 = TAS2764_TDM_CFG1_RX_RISING; |
379 | break; |
380 | case SND_SOC_DAIFMT_IB_IF: |
381 | asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START; |
382 | fallthrough; |
383 | case SND_SOC_DAIFMT_IB_NF: |
384 | asi_cfg_1 = TAS2764_TDM_CFG1_RX_FALLING; |
385 | break; |
386 | } |
387 | |
388 | ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1, |
389 | TAS2764_TDM_CFG1_RX_MASK, |
390 | val: asi_cfg_1); |
391 | if (ret < 0) |
392 | return ret; |
393 | |
394 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
395 | case SND_SOC_DAIFMT_I2S: |
396 | asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START; |
397 | fallthrough; |
398 | case SND_SOC_DAIFMT_DSP_A: |
399 | tdm_rx_start_slot = 1; |
400 | break; |
401 | case SND_SOC_DAIFMT_DSP_B: |
402 | case SND_SOC_DAIFMT_LEFT_J: |
403 | tdm_rx_start_slot = 0; |
404 | break; |
405 | default: |
406 | dev_err(tas2764->dev, |
407 | "DAI Format is not found, fmt=0x%x\n" , fmt); |
408 | return -EINVAL; |
409 | } |
410 | |
411 | ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0, |
412 | TAS2764_TDM_CFG0_FRAME_START, |
413 | val: asi_cfg_0); |
414 | if (ret < 0) |
415 | return ret; |
416 | |
417 | ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1, |
418 | TAS2764_TDM_CFG1_MASK, |
419 | val: (tdm_rx_start_slot << TAS2764_TDM_CFG1_51_SHIFT)); |
420 | if (ret < 0) |
421 | return ret; |
422 | |
423 | return 0; |
424 | } |
425 | |
426 | static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai, |
427 | unsigned int tx_mask, |
428 | unsigned int rx_mask, |
429 | int slots, int slot_width) |
430 | { |
431 | struct snd_soc_component *component = dai->component; |
432 | struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(c: component); |
433 | int left_slot, right_slot; |
434 | int slots_cfg; |
435 | int slot_size; |
436 | int ret; |
437 | |
438 | if (tx_mask == 0 || rx_mask != 0) |
439 | return -EINVAL; |
440 | |
441 | left_slot = __ffs(tx_mask); |
442 | tx_mask &= ~(1 << left_slot); |
443 | if (tx_mask == 0) { |
444 | right_slot = left_slot; |
445 | } else { |
446 | right_slot = __ffs(tx_mask); |
447 | tx_mask &= ~(1 << right_slot); |
448 | } |
449 | |
450 | if (tx_mask != 0 || left_slot >= slots || right_slot >= slots) |
451 | return -EINVAL; |
452 | |
453 | slots_cfg = (right_slot << TAS2764_TDM_CFG3_RXS_SHIFT) | left_slot; |
454 | |
455 | ret = snd_soc_component_write(component, TAS2764_TDM_CFG3, val: slots_cfg); |
456 | if (ret) |
457 | return ret; |
458 | |
459 | switch (slot_width) { |
460 | case 16: |
461 | slot_size = TAS2764_TDM_CFG2_RXS_16BITS; |
462 | break; |
463 | case 24: |
464 | slot_size = TAS2764_TDM_CFG2_RXS_24BITS; |
465 | break; |
466 | case 32: |
467 | slot_size = TAS2764_TDM_CFG2_RXS_32BITS; |
468 | break; |
469 | default: |
470 | return -EINVAL; |
471 | } |
472 | |
473 | ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2, |
474 | TAS2764_TDM_CFG2_RXS_MASK, |
475 | val: slot_size); |
476 | if (ret < 0) |
477 | return ret; |
478 | |
479 | ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG5, |
480 | TAS2764_TDM_CFG5_50_MASK, |
481 | val: tas2764->v_sense_slot); |
482 | if (ret < 0) |
483 | return ret; |
484 | |
485 | ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG6, |
486 | TAS2764_TDM_CFG6_50_MASK, |
487 | val: tas2764->i_sense_slot); |
488 | if (ret < 0) |
489 | return ret; |
490 | |
491 | return 0; |
492 | } |
493 | |
494 | static const struct snd_soc_dai_ops tas2764_dai_ops = { |
495 | .mute_stream = tas2764_mute, |
496 | .hw_params = tas2764_hw_params, |
497 | .set_fmt = tas2764_set_fmt, |
498 | .set_tdm_slot = tas2764_set_dai_tdm_slot, |
499 | .no_capture_mute = 1, |
500 | }; |
501 | |
502 | #define TAS2764_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ |
503 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) |
504 | |
505 | #define TAS2764_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ |
506 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200) |
507 | |
508 | static struct snd_soc_dai_driver tas2764_dai_driver[] = { |
509 | { |
510 | .name = "tas2764 ASI1" , |
511 | .id = 0, |
512 | .playback = { |
513 | .stream_name = "ASI1 Playback" , |
514 | .channels_min = 1, |
515 | .channels_max = 2, |
516 | .rates = TAS2764_RATES, |
517 | .formats = TAS2764_FORMATS, |
518 | }, |
519 | .capture = { |
520 | .stream_name = "ASI1 Capture" , |
521 | .channels_min = 0, |
522 | .channels_max = 2, |
523 | .rates = TAS2764_RATES, |
524 | .formats = TAS2764_FORMATS, |
525 | }, |
526 | .ops = &tas2764_dai_ops, |
527 | .symmetric_rate = 1, |
528 | }, |
529 | }; |
530 | |
531 | static int tas2764_codec_probe(struct snd_soc_component *component) |
532 | { |
533 | struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(c: component); |
534 | int ret; |
535 | |
536 | tas2764->component = component; |
537 | |
538 | if (tas2764->sdz_gpio) { |
539 | gpiod_set_value_cansleep(desc: tas2764->sdz_gpio, value: 1); |
540 | usleep_range(min: 1000, max: 2000); |
541 | } |
542 | |
543 | tas2764_reset(tas2764); |
544 | |
545 | if (tas2764->irq) { |
546 | ret = snd_soc_component_write(component: tas2764->component, TAS2764_INT_MASK0, val: 0xff); |
547 | if (ret < 0) |
548 | return ret; |
549 | |
550 | ret = snd_soc_component_write(component: tas2764->component, TAS2764_INT_MASK1, val: 0xff); |
551 | if (ret < 0) |
552 | return ret; |
553 | |
554 | ret = snd_soc_component_write(component: tas2764->component, TAS2764_INT_MASK2, val: 0xff); |
555 | if (ret < 0) |
556 | return ret; |
557 | |
558 | ret = snd_soc_component_write(component: tas2764->component, TAS2764_INT_MASK3, val: 0xff); |
559 | if (ret < 0) |
560 | return ret; |
561 | |
562 | ret = snd_soc_component_write(component: tas2764->component, TAS2764_INT_MASK4, val: 0xff); |
563 | if (ret < 0) |
564 | return ret; |
565 | |
566 | ret = devm_request_threaded_irq(dev: tas2764->dev, irq: tas2764->irq, NULL, thread_fn: tas2764_irq, |
567 | IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, |
568 | devname: "tas2764" , dev_id: tas2764); |
569 | if (ret) |
570 | dev_warn(tas2764->dev, "failed to request IRQ: %d\n" , ret); |
571 | } |
572 | |
573 | ret = snd_soc_component_update_bits(component: tas2764->component, TAS2764_TDM_CFG5, |
574 | TAS2764_TDM_CFG5_VSNS_ENABLE, val: 0); |
575 | if (ret < 0) |
576 | return ret; |
577 | |
578 | ret = snd_soc_component_update_bits(component: tas2764->component, TAS2764_TDM_CFG6, |
579 | TAS2764_TDM_CFG6_ISNS_ENABLE, val: 0); |
580 | if (ret < 0) |
581 | return ret; |
582 | |
583 | return 0; |
584 | } |
585 | |
586 | static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0); |
587 | static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10050, 50, 1); |
588 | |
589 | static const char * const tas2764_hpf_texts[] = { |
590 | "Disabled" , "2 Hz" , "50 Hz" , "100 Hz" , "200 Hz" , |
591 | "400 Hz" , "800 Hz" |
592 | }; |
593 | |
594 | static SOC_ENUM_SINGLE_DECL( |
595 | tas2764_hpf_enum, TAS2764_DC_BLK0, |
596 | TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT, tas2764_hpf_texts); |
597 | |
598 | static const struct snd_kcontrol_new tas2764_snd_controls[] = { |
599 | SOC_SINGLE_TLV("Speaker Volume" , TAS2764_DVC, 0, |
600 | TAS2764_DVC_MAX, 1, tas2764_playback_volume), |
601 | SOC_SINGLE_TLV("Amp Gain Volume" , TAS2764_CHNL_0, 1, 0x14, 0, |
602 | tas2764_digital_tlv), |
603 | SOC_ENUM("HPF Corner Frequency" , tas2764_hpf_enum), |
604 | }; |
605 | |
606 | static const struct snd_soc_component_driver soc_component_driver_tas2764 = { |
607 | .probe = tas2764_codec_probe, |
608 | .suspend = tas2764_codec_suspend, |
609 | .resume = tas2764_codec_resume, |
610 | .controls = tas2764_snd_controls, |
611 | .num_controls = ARRAY_SIZE(tas2764_snd_controls), |
612 | .dapm_widgets = tas2764_dapm_widgets, |
613 | .num_dapm_widgets = ARRAY_SIZE(tas2764_dapm_widgets), |
614 | .dapm_routes = tas2764_audio_map, |
615 | .num_dapm_routes = ARRAY_SIZE(tas2764_audio_map), |
616 | .idle_bias_on = 1, |
617 | .endianness = 1, |
618 | }; |
619 | |
620 | static const struct reg_default tas2764_reg_defaults[] = { |
621 | { TAS2764_PAGE, 0x00 }, |
622 | { TAS2764_SW_RST, 0x00 }, |
623 | { TAS2764_PWR_CTRL, 0x1a }, |
624 | { TAS2764_DVC, 0x00 }, |
625 | { TAS2764_CHNL_0, 0x28 }, |
626 | { TAS2764_TDM_CFG0, 0x09 }, |
627 | { TAS2764_TDM_CFG1, 0x02 }, |
628 | { TAS2764_TDM_CFG2, 0x0a }, |
629 | { TAS2764_TDM_CFG3, 0x10 }, |
630 | { TAS2764_TDM_CFG5, 0x42 }, |
631 | }; |
632 | |
633 | static const struct regmap_range_cfg tas2764_regmap_ranges[] = { |
634 | { |
635 | .range_min = 0, |
636 | .range_max = 1 * 128, |
637 | .selector_reg = TAS2764_PAGE, |
638 | .selector_mask = 0xff, |
639 | .selector_shift = 0, |
640 | .window_start = 0, |
641 | .window_len = 128, |
642 | }, |
643 | }; |
644 | |
645 | static bool tas2764_volatile_register(struct device *dev, unsigned int reg) |
646 | { |
647 | switch (reg) { |
648 | case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4: |
649 | case TAS2764_INT_CLK_CFG: |
650 | return true; |
651 | default: |
652 | return false; |
653 | } |
654 | } |
655 | |
656 | static const struct regmap_config tas2764_i2c_regmap = { |
657 | .reg_bits = 8, |
658 | .val_bits = 8, |
659 | .volatile_reg = tas2764_volatile_register, |
660 | .reg_defaults = tas2764_reg_defaults, |
661 | .num_reg_defaults = ARRAY_SIZE(tas2764_reg_defaults), |
662 | .cache_type = REGCACHE_RBTREE, |
663 | .ranges = tas2764_regmap_ranges, |
664 | .num_ranges = ARRAY_SIZE(tas2764_regmap_ranges), |
665 | .max_register = 1 * 128, |
666 | }; |
667 | |
668 | static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) |
669 | { |
670 | int ret = 0; |
671 | |
672 | tas2764->reset_gpio = devm_gpiod_get_optional(dev: tas2764->dev, con_id: "reset" , |
673 | flags: GPIOD_OUT_HIGH); |
674 | if (IS_ERR(ptr: tas2764->reset_gpio)) { |
675 | if (PTR_ERR(ptr: tas2764->reset_gpio) == -EPROBE_DEFER) { |
676 | tas2764->reset_gpio = NULL; |
677 | return -EPROBE_DEFER; |
678 | } |
679 | } |
680 | |
681 | tas2764->sdz_gpio = devm_gpiod_get_optional(dev, con_id: "shutdown" , flags: GPIOD_OUT_HIGH); |
682 | if (IS_ERR(ptr: tas2764->sdz_gpio)) { |
683 | if (PTR_ERR(ptr: tas2764->sdz_gpio) == -EPROBE_DEFER) |
684 | return -EPROBE_DEFER; |
685 | |
686 | tas2764->sdz_gpio = NULL; |
687 | } |
688 | |
689 | ret = fwnode_property_read_u32(fwnode: dev->fwnode, propname: "ti,imon-slot-no" , |
690 | val: &tas2764->i_sense_slot); |
691 | if (ret) |
692 | tas2764->i_sense_slot = 0; |
693 | |
694 | ret = fwnode_property_read_u32(fwnode: dev->fwnode, propname: "ti,vmon-slot-no" , |
695 | val: &tas2764->v_sense_slot); |
696 | if (ret) |
697 | tas2764->v_sense_slot = 2; |
698 | |
699 | return 0; |
700 | } |
701 | |
702 | static int tas2764_i2c_probe(struct i2c_client *client) |
703 | { |
704 | struct tas2764_priv *tas2764; |
705 | int result; |
706 | |
707 | tas2764 = devm_kzalloc(dev: &client->dev, size: sizeof(struct tas2764_priv), |
708 | GFP_KERNEL); |
709 | if (!tas2764) |
710 | return -ENOMEM; |
711 | |
712 | tas2764->dev = &client->dev; |
713 | tas2764->irq = client->irq; |
714 | i2c_set_clientdata(client, data: tas2764); |
715 | dev_set_drvdata(dev: &client->dev, data: tas2764); |
716 | |
717 | tas2764->regmap = devm_regmap_init_i2c(client, &tas2764_i2c_regmap); |
718 | if (IS_ERR(ptr: tas2764->regmap)) { |
719 | result = PTR_ERR(ptr: tas2764->regmap); |
720 | dev_err(&client->dev, "Failed to allocate register map: %d\n" , |
721 | result); |
722 | return result; |
723 | } |
724 | |
725 | if (client->dev.of_node) { |
726 | result = tas2764_parse_dt(dev: &client->dev, tas2764); |
727 | if (result) { |
728 | dev_err(tas2764->dev, "%s: Failed to parse devicetree\n" , |
729 | __func__); |
730 | return result; |
731 | } |
732 | } |
733 | |
734 | return devm_snd_soc_register_component(dev: tas2764->dev, |
735 | component_driver: &soc_component_driver_tas2764, |
736 | dai_drv: tas2764_dai_driver, |
737 | ARRAY_SIZE(tas2764_dai_driver)); |
738 | } |
739 | |
740 | static const struct i2c_device_id tas2764_i2c_id[] = { |
741 | { "tas2764" , 0}, |
742 | { } |
743 | }; |
744 | MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id); |
745 | |
746 | #if defined(CONFIG_OF) |
747 | static const struct of_device_id tas2764_of_match[] = { |
748 | { .compatible = "ti,tas2764" }, |
749 | {}, |
750 | }; |
751 | MODULE_DEVICE_TABLE(of, tas2764_of_match); |
752 | #endif |
753 | |
754 | static struct i2c_driver tas2764_i2c_driver = { |
755 | .driver = { |
756 | .name = "tas2764" , |
757 | .of_match_table = of_match_ptr(tas2764_of_match), |
758 | }, |
759 | .probe = tas2764_i2c_probe, |
760 | .id_table = tas2764_i2c_id, |
761 | }; |
762 | module_i2c_driver(tas2764_i2c_driver); |
763 | |
764 | MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>" ); |
765 | MODULE_DESCRIPTION("TAS2764 I2C Smart Amplifier driver" ); |
766 | MODULE_LICENSE("GPL v2" ); |
767 | |