1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Driver for the Texas Instruments TAS2562 CODEC |
4 | // Copyright (C) 2019 Texas Instruments Inc. |
5 | |
6 | |
7 | #include <linux/module.h> |
8 | #include <linux/errno.h> |
9 | #include <linux/device.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/regmap.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/gpio/consumer.h> |
14 | #include <linux/regulator/consumer.h> |
15 | #include <linux/delay.h> |
16 | |
17 | #include <sound/pcm.h> |
18 | #include <sound/pcm_params.h> |
19 | #include <sound/soc.h> |
20 | #include <sound/soc-dapm.h> |
21 | #include <sound/tlv.h> |
22 | |
23 | #include "tas2562.h" |
24 | |
25 | #define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ |
26 | SNDRV_PCM_FORMAT_S32_LE) |
27 | |
28 | /* DVC equation involves floating point math |
29 | * round(10^(volume in dB/20)*2^30) |
30 | * so create a lookup table for 2dB step |
31 | */ |
32 | static const unsigned int float_vol_db_lookup[] = { |
33 | 0x00000d43, 0x000010b2, 0x00001505, 0x00001a67, 0x00002151, |
34 | 0x000029f1, 0x000034cd, 0x00004279, 0x000053af, 0x0000695b, |
35 | 0x0000695b, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a, |
36 | 0x0001a36e, 0x00021008, 0x000298c0, 0x000344df, 0x00041d8f, |
37 | 0x00052e5a, 0x000685c8, 0x00083621, 0x000a566d, 0x000d03a7, |
38 | 0x0010624d, 0x0014a050, 0x0019f786, 0x0020b0bc, 0x0029279d, |
39 | 0x0033cf8d, 0x004139d3, 0x00521d50, 0x00676044, 0x0082248a, |
40 | 0x00a3d70a, 0x00ce4328, 0x0103ab3d, 0x0146e75d, 0x019b8c27, |
41 | 0x02061b89, 0x028c423f, 0x03352529, 0x0409c2b0, 0x05156d68, |
42 | 0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, 0x143d1362, |
43 | 0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, 0x40000000 |
44 | }; |
45 | |
46 | struct tas2562_data { |
47 | struct snd_soc_component *component; |
48 | struct gpio_desc *sdz_gpio; |
49 | struct regmap *regmap; |
50 | struct device *dev; |
51 | struct i2c_client *client; |
52 | int v_sense_slot; |
53 | int i_sense_slot; |
54 | int volume_lvl; |
55 | int model_id; |
56 | bool dac_powered; |
57 | bool unmuted; |
58 | }; |
59 | |
60 | enum tas256x_model { |
61 | TAS2562, |
62 | TAS2564, |
63 | TAS2110, |
64 | }; |
65 | |
66 | static int tas2562_set_samplerate(struct tas2562_data *tas2562, int samplerate) |
67 | { |
68 | int samp_rate; |
69 | int ramp_rate; |
70 | |
71 | switch (samplerate) { |
72 | case 7350: |
73 | ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; |
74 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ; |
75 | break; |
76 | case 8000: |
77 | ramp_rate = 0; |
78 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ; |
79 | break; |
80 | case 14700: |
81 | ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; |
82 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ; |
83 | break; |
84 | case 16000: |
85 | ramp_rate = 0; |
86 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ; |
87 | break; |
88 | case 22050: |
89 | ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; |
90 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ; |
91 | break; |
92 | case 24000: |
93 | ramp_rate = 0; |
94 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ; |
95 | break; |
96 | case 29400: |
97 | ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; |
98 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ; |
99 | break; |
100 | case 32000: |
101 | ramp_rate = 0; |
102 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ; |
103 | break; |
104 | case 44100: |
105 | ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; |
106 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ; |
107 | break; |
108 | case 48000: |
109 | ramp_rate = 0; |
110 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ; |
111 | break; |
112 | case 88200: |
113 | ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; |
114 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ; |
115 | break; |
116 | case 96000: |
117 | ramp_rate = 0; |
118 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ; |
119 | break; |
120 | case 176400: |
121 | ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; |
122 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ; |
123 | break; |
124 | case 192000: |
125 | ramp_rate = 0; |
126 | samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ; |
127 | break; |
128 | default: |
129 | dev_info(tas2562->dev, "%s, unsupported sample rate, %d\n" , |
130 | __func__, samplerate); |
131 | return -EINVAL; |
132 | } |
133 | |
134 | snd_soc_component_update_bits(component: tas2562->component, TAS2562_TDM_CFG0, |
135 | TAS2562_TDM_CFG0_RAMPRATE_MASK, val: ramp_rate); |
136 | snd_soc_component_update_bits(component: tas2562->component, TAS2562_TDM_CFG0, |
137 | TAS2562_TDM_CFG0_SAMPRATE_MASK, val: samp_rate); |
138 | |
139 | return 0; |
140 | } |
141 | |
142 | static int tas2562_set_dai_tdm_slot(struct snd_soc_dai *dai, |
143 | unsigned int tx_mask, unsigned int rx_mask, |
144 | int slots, int slot_width) |
145 | { |
146 | struct snd_soc_component *component = dai->component; |
147 | struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(c: component); |
148 | int left_slot, right_slot; |
149 | int slots_cfg; |
150 | int ret; |
151 | |
152 | if (!tx_mask) { |
153 | dev_err(component->dev, "tx masks must not be 0\n" ); |
154 | return -EINVAL; |
155 | } |
156 | |
157 | if (slots == 1) { |
158 | if (tx_mask != 1) |
159 | return -EINVAL; |
160 | |
161 | left_slot = 0; |
162 | right_slot = 0; |
163 | } else { |
164 | left_slot = __ffs(tx_mask); |
165 | tx_mask &= ~(1 << left_slot); |
166 | if (tx_mask == 0) { |
167 | right_slot = left_slot; |
168 | } else { |
169 | right_slot = __ffs(tx_mask); |
170 | } |
171 | } |
172 | |
173 | slots_cfg = (right_slot << TAS2562_RIGHT_SLOT_SHIFT) | left_slot; |
174 | |
175 | ret = snd_soc_component_write(component, TAS2562_TDM_CFG3, val: slots_cfg); |
176 | if (ret < 0) |
177 | return ret; |
178 | |
179 | switch (slot_width) { |
180 | case 16: |
181 | ret = snd_soc_component_update_bits(component, |
182 | TAS2562_TDM_CFG2, |
183 | TAS2562_TDM_CFG2_RXLEN_MASK, |
184 | TAS2562_TDM_CFG2_RXLEN_16B); |
185 | break; |
186 | case 24: |
187 | ret = snd_soc_component_update_bits(component, |
188 | TAS2562_TDM_CFG2, |
189 | TAS2562_TDM_CFG2_RXLEN_MASK, |
190 | TAS2562_TDM_CFG2_RXLEN_24B); |
191 | break; |
192 | case 32: |
193 | ret = snd_soc_component_update_bits(component, |
194 | TAS2562_TDM_CFG2, |
195 | TAS2562_TDM_CFG2_RXLEN_MASK, |
196 | TAS2562_TDM_CFG2_RXLEN_32B); |
197 | break; |
198 | |
199 | case 0: |
200 | /* Do not change slot width */ |
201 | break; |
202 | default: |
203 | dev_err(tas2562->dev, "slot width not supported" ); |
204 | ret = -EINVAL; |
205 | } |
206 | |
207 | if (ret < 0) |
208 | return ret; |
209 | |
210 | ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG5, |
211 | TAS2562_TDM_CFG5_VSNS_SLOT_MASK, |
212 | val: tas2562->v_sense_slot); |
213 | if (ret < 0) |
214 | return ret; |
215 | |
216 | ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG6, |
217 | TAS2562_TDM_CFG6_ISNS_SLOT_MASK, |
218 | val: tas2562->i_sense_slot); |
219 | if (ret < 0) |
220 | return ret; |
221 | |
222 | return 0; |
223 | } |
224 | |
225 | static int tas2562_set_bitwidth(struct tas2562_data *tas2562, int bitwidth) |
226 | { |
227 | int ret; |
228 | int val; |
229 | int sense_en; |
230 | |
231 | switch (bitwidth) { |
232 | case SNDRV_PCM_FORMAT_S16_LE: |
233 | snd_soc_component_update_bits(component: tas2562->component, |
234 | TAS2562_TDM_CFG2, |
235 | TAS2562_TDM_CFG2_RXWLEN_MASK, |
236 | TAS2562_TDM_CFG2_RXWLEN_16B); |
237 | break; |
238 | case SNDRV_PCM_FORMAT_S24_LE: |
239 | snd_soc_component_update_bits(component: tas2562->component, |
240 | TAS2562_TDM_CFG2, |
241 | TAS2562_TDM_CFG2_RXWLEN_MASK, |
242 | TAS2562_TDM_CFG2_RXWLEN_24B); |
243 | break; |
244 | case SNDRV_PCM_FORMAT_S32_LE: |
245 | snd_soc_component_update_bits(component: tas2562->component, |
246 | TAS2562_TDM_CFG2, |
247 | TAS2562_TDM_CFG2_RXWLEN_MASK, |
248 | TAS2562_TDM_CFG2_RXWLEN_32B); |
249 | break; |
250 | |
251 | default: |
252 | dev_info(tas2562->dev, "Unsupported bitwidth format\n" ); |
253 | return -EINVAL; |
254 | } |
255 | |
256 | val = snd_soc_component_read(component: tas2562->component, TAS2562_PWR_CTRL); |
257 | if (val < 0) |
258 | return val; |
259 | |
260 | if (val & (1 << TAS2562_VSENSE_POWER_EN)) |
261 | sense_en = 0; |
262 | else |
263 | sense_en = TAS2562_TDM_CFG5_VSNS_EN; |
264 | |
265 | ret = snd_soc_component_update_bits(component: tas2562->component, TAS2562_TDM_CFG5, |
266 | TAS2562_TDM_CFG5_VSNS_EN, val: sense_en); |
267 | if (ret < 0) |
268 | return ret; |
269 | |
270 | if (val & (1 << TAS2562_ISENSE_POWER_EN)) |
271 | sense_en = 0; |
272 | else |
273 | sense_en = TAS2562_TDM_CFG6_ISNS_EN; |
274 | |
275 | ret = snd_soc_component_update_bits(component: tas2562->component, TAS2562_TDM_CFG6, |
276 | TAS2562_TDM_CFG6_ISNS_EN, val: sense_en); |
277 | if (ret < 0) |
278 | return ret; |
279 | |
280 | return 0; |
281 | } |
282 | |
283 | static int tas2562_hw_params(struct snd_pcm_substream *substream, |
284 | struct snd_pcm_hw_params *params, |
285 | struct snd_soc_dai *dai) |
286 | { |
287 | struct snd_soc_component *component = dai->component; |
288 | struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(c: component); |
289 | int ret; |
290 | |
291 | ret = tas2562_set_bitwidth(tas2562, bitwidth: params_format(p: params)); |
292 | if (ret) { |
293 | dev_err(tas2562->dev, "set bitwidth failed, %d\n" , ret); |
294 | return ret; |
295 | } |
296 | |
297 | ret = tas2562_set_samplerate(tas2562, samplerate: params_rate(p: params)); |
298 | if (ret) |
299 | dev_err(tas2562->dev, "set sample rate failed, %d\n" , ret); |
300 | |
301 | return ret; |
302 | } |
303 | |
304 | static int tas2562_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
305 | { |
306 | struct snd_soc_component *component = dai->component; |
307 | struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(c: component); |
308 | u8 asi_cfg_1 = 0; |
309 | u8 tdm_rx_start_slot = 0; |
310 | int ret; |
311 | |
312 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
313 | case SND_SOC_DAIFMT_NB_NF: |
314 | asi_cfg_1 = 0; |
315 | break; |
316 | case SND_SOC_DAIFMT_IB_NF: |
317 | asi_cfg_1 |= TAS2562_TDM_CFG1_RX_FALLING; |
318 | break; |
319 | default: |
320 | dev_err(tas2562->dev, "ASI format Inverse is not found\n" ); |
321 | return -EINVAL; |
322 | } |
323 | |
324 | ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1, |
325 | TAS2562_TDM_CFG1_RX_EDGE_MASK, |
326 | val: asi_cfg_1); |
327 | if (ret < 0) { |
328 | dev_err(tas2562->dev, "Failed to set RX edge\n" ); |
329 | return ret; |
330 | } |
331 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
332 | case SND_SOC_DAIFMT_LEFT_J: |
333 | case SND_SOC_DAIFMT_DSP_B: |
334 | tdm_rx_start_slot = 0; |
335 | break; |
336 | case SND_SOC_DAIFMT_I2S: |
337 | case SND_SOC_DAIFMT_DSP_A: |
338 | tdm_rx_start_slot = 1; |
339 | break; |
340 | default: |
341 | dev_err(tas2562->dev, |
342 | "DAI Format is not found, fmt=0x%x\n" , fmt); |
343 | return -EINVAL; |
344 | } |
345 | |
346 | ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1, |
347 | TAS2562_RX_OFF_MASK, val: (tdm_rx_start_slot << 1)); |
348 | if (ret < 0) |
349 | return ret; |
350 | |
351 | return 0; |
352 | } |
353 | |
354 | static int tas2562_update_pwr_ctrl(struct tas2562_data *tas2562) |
355 | { |
356 | struct snd_soc_component *component = tas2562->component; |
357 | unsigned int val; |
358 | int ret; |
359 | |
360 | if (tas2562->dac_powered) |
361 | val = tas2562->unmuted ? |
362 | TAS2562_ACTIVE : TAS2562_MUTE; |
363 | else |
364 | val = TAS2562_SHUTDOWN; |
365 | |
366 | ret = snd_soc_component_update_bits(component, TAS2562_PWR_CTRL, |
367 | TAS2562_MODE_MASK, val); |
368 | if (ret < 0) |
369 | return ret; |
370 | |
371 | return 0; |
372 | } |
373 | |
374 | static int tas2562_mute(struct snd_soc_dai *dai, int mute, int direction) |
375 | { |
376 | struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(c: dai->component); |
377 | |
378 | tas2562->unmuted = !mute; |
379 | return tas2562_update_pwr_ctrl(tas2562); |
380 | } |
381 | |
382 | static int tas2562_codec_probe(struct snd_soc_component *component) |
383 | { |
384 | struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(c: component); |
385 | |
386 | tas2562->component = component; |
387 | |
388 | if (tas2562->sdz_gpio) |
389 | gpiod_set_value_cansleep(desc: tas2562->sdz_gpio, value: 1); |
390 | |
391 | return 0; |
392 | } |
393 | |
394 | #ifdef CONFIG_PM |
395 | static int tas2562_suspend(struct snd_soc_component *component) |
396 | { |
397 | struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(c: component); |
398 | |
399 | regcache_cache_only(map: tas2562->regmap, enable: true); |
400 | regcache_mark_dirty(map: tas2562->regmap); |
401 | |
402 | if (tas2562->sdz_gpio) |
403 | gpiod_set_value_cansleep(desc: tas2562->sdz_gpio, value: 0); |
404 | |
405 | return 0; |
406 | } |
407 | |
408 | static int tas2562_resume(struct snd_soc_component *component) |
409 | { |
410 | struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(c: component); |
411 | |
412 | if (tas2562->sdz_gpio) |
413 | gpiod_set_value_cansleep(desc: tas2562->sdz_gpio, value: 1); |
414 | |
415 | regcache_cache_only(map: tas2562->regmap, enable: false); |
416 | |
417 | return regcache_sync(map: tas2562->regmap); |
418 | } |
419 | #else |
420 | #define tas2562_suspend NULL |
421 | #define tas2562_resume NULL |
422 | #endif |
423 | |
424 | static const char * const tas2562_ASI1_src[] = { |
425 | "I2C offset" , "Left" , "Right" , "LeftRightDiv2" , |
426 | }; |
427 | |
428 | static SOC_ENUM_SINGLE_DECL(tas2562_ASI1_src_enum, TAS2562_TDM_CFG2, 4, |
429 | tas2562_ASI1_src); |
430 | |
431 | static const struct snd_kcontrol_new tas2562_asi1_mux = |
432 | SOC_DAPM_ENUM("ASI1 Source" , tas2562_ASI1_src_enum); |
433 | |
434 | static int tas2562_dac_event(struct snd_soc_dapm_widget *w, |
435 | struct snd_kcontrol *kcontrol, int event) |
436 | { |
437 | struct snd_soc_component *component = |
438 | snd_soc_dapm_to_component(dapm: w->dapm); |
439 | struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(c: component); |
440 | int ret = 0; |
441 | |
442 | switch (event) { |
443 | case SND_SOC_DAPM_POST_PMU: |
444 | tas2562->dac_powered = true; |
445 | ret = tas2562_update_pwr_ctrl(tas2562); |
446 | break; |
447 | case SND_SOC_DAPM_PRE_PMD: |
448 | tas2562->dac_powered = false; |
449 | ret = tas2562_update_pwr_ctrl(tas2562); |
450 | break; |
451 | default: |
452 | dev_err(tas2562->dev, "Not supported evevt\n" ); |
453 | return -EINVAL; |
454 | } |
455 | |
456 | return ret; |
457 | } |
458 | |
459 | static int tas2562_volume_control_get(struct snd_kcontrol *kcontrol, |
460 | struct snd_ctl_elem_value *ucontrol) |
461 | { |
462 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
463 | struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(c: component); |
464 | |
465 | ucontrol->value.integer.value[0] = tas2562->volume_lvl; |
466 | return 0; |
467 | } |
468 | |
469 | static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, |
470 | struct snd_ctl_elem_value *ucontrol) |
471 | { |
472 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
473 | struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(c: component); |
474 | int ret; |
475 | u32 reg_val; |
476 | |
477 | reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2]; |
478 | ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, |
479 | val: (reg_val & 0xff)); |
480 | if (ret) |
481 | return ret; |
482 | ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, |
483 | val: ((reg_val >> 8) & 0xff)); |
484 | if (ret) |
485 | return ret; |
486 | ret = snd_soc_component_write(component, TAS2562_DVC_CFG2, |
487 | val: ((reg_val >> 16) & 0xff)); |
488 | if (ret) |
489 | return ret; |
490 | ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, |
491 | val: ((reg_val >> 24) & 0xff)); |
492 | if (ret) |
493 | return ret; |
494 | |
495 | tas2562->volume_lvl = ucontrol->value.integer.value[0]; |
496 | |
497 | return 0; |
498 | } |
499 | |
500 | /* Digital Volume Control. From 0 dB to -110 dB in 1 dB steps */ |
501 | static const DECLARE_TLV_DB_SCALE(dvc_tlv, -11000, 100, 0); |
502 | |
503 | static DECLARE_TLV_DB_SCALE(tas2562_dac_tlv, 850, 50, 0); |
504 | |
505 | static const struct snd_kcontrol_new isense_switch = |
506 | SOC_DAPM_SINGLE("Switch" , TAS2562_PWR_CTRL, TAS2562_ISENSE_POWER_EN, |
507 | 1, 1); |
508 | |
509 | static const struct snd_kcontrol_new vsense_switch = |
510 | SOC_DAPM_SINGLE("Switch" , TAS2562_PWR_CTRL, TAS2562_VSENSE_POWER_EN, |
511 | 1, 1); |
512 | |
513 | static const struct snd_kcontrol_new tas2562_snd_controls[] = { |
514 | SOC_SINGLE_TLV("Amp Gain Volume" , TAS2562_PB_CFG1, 1, 0x1c, 0, |
515 | tas2562_dac_tlv), |
516 | { |
517 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
518 | .name = "Digital Volume Control" , |
519 | .index = 0, |
520 | .tlv.p = dvc_tlv, |
521 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, |
522 | .info = snd_soc_info_volsw, |
523 | .get = tas2562_volume_control_get, |
524 | .put = tas2562_volume_control_put, |
525 | .private_value = SOC_SINGLE_VALUE(TAS2562_DVC_CFG1, 0, 110, 0, 0), |
526 | }, |
527 | }; |
528 | |
529 | static const struct snd_soc_dapm_widget tas2110_dapm_widgets[] = { |
530 | SND_SOC_DAPM_AIF_IN("ASI1" , "ASI1 Playback" , 0, SND_SOC_NOPM, 0, 0), |
531 | SND_SOC_DAPM_MUX("ASI1 Sel" , SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux), |
532 | SND_SOC_DAPM_DAC_E("DAC" , NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event, |
533 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
534 | SND_SOC_DAPM_OUTPUT("OUT" ), |
535 | }; |
536 | |
537 | static const struct snd_soc_dapm_route tas2110_audio_map[] = { |
538 | {"ASI1 Sel" , "I2C offset" , "ASI1" }, |
539 | {"ASI1 Sel" , "Left" , "ASI1" }, |
540 | {"ASI1 Sel" , "Right" , "ASI1" }, |
541 | {"ASI1 Sel" , "LeftRightDiv2" , "ASI1" }, |
542 | { "DAC" , NULL, "ASI1 Sel" }, |
543 | { "OUT" , NULL, "DAC" }, |
544 | }; |
545 | |
546 | static const struct snd_soc_component_driver soc_component_dev_tas2110 = { |
547 | .probe = tas2562_codec_probe, |
548 | .suspend = tas2562_suspend, |
549 | .resume = tas2562_resume, |
550 | .controls = tas2562_snd_controls, |
551 | .num_controls = ARRAY_SIZE(tas2562_snd_controls), |
552 | .dapm_widgets = tas2110_dapm_widgets, |
553 | .num_dapm_widgets = ARRAY_SIZE(tas2110_dapm_widgets), |
554 | .dapm_routes = tas2110_audio_map, |
555 | .num_dapm_routes = ARRAY_SIZE(tas2110_audio_map), |
556 | .idle_bias_on = 1, |
557 | .use_pmdown_time = 1, |
558 | .endianness = 1, |
559 | }; |
560 | |
561 | static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = { |
562 | SND_SOC_DAPM_AIF_IN("ASI1" , "ASI1 Playback" , 0, SND_SOC_NOPM, 0, 0), |
563 | SND_SOC_DAPM_MUX("ASI1 Sel" , SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux), |
564 | SND_SOC_DAPM_DAC_E("DAC" , NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event, |
565 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
566 | SND_SOC_DAPM_SWITCH("ISENSE" , TAS2562_PWR_CTRL, 3, 1, &isense_switch), |
567 | SND_SOC_DAPM_SWITCH("VSENSE" , TAS2562_PWR_CTRL, 2, 1, &vsense_switch), |
568 | SND_SOC_DAPM_SIGGEN("VMON" ), |
569 | SND_SOC_DAPM_SIGGEN("IMON" ), |
570 | SND_SOC_DAPM_OUTPUT("OUT" ), |
571 | }; |
572 | |
573 | static const struct snd_soc_dapm_route tas2562_audio_map[] = { |
574 | {"ASI1 Sel" , "I2C offset" , "ASI1" }, |
575 | {"ASI1 Sel" , "Left" , "ASI1" }, |
576 | {"ASI1 Sel" , "Right" , "ASI1" }, |
577 | {"ASI1 Sel" , "LeftRightDiv2" , "ASI1" }, |
578 | { "DAC" , NULL, "ASI1 Sel" }, |
579 | { "OUT" , NULL, "DAC" }, |
580 | {"ISENSE" , "Switch" , "IMON" }, |
581 | {"VSENSE" , "Switch" , "VMON" }, |
582 | }; |
583 | |
584 | static const struct snd_soc_component_driver soc_component_dev_tas2562 = { |
585 | .probe = tas2562_codec_probe, |
586 | .suspend = tas2562_suspend, |
587 | .resume = tas2562_resume, |
588 | .controls = tas2562_snd_controls, |
589 | .num_controls = ARRAY_SIZE(tas2562_snd_controls), |
590 | .dapm_widgets = tas2562_dapm_widgets, |
591 | .num_dapm_widgets = ARRAY_SIZE(tas2562_dapm_widgets), |
592 | .dapm_routes = tas2562_audio_map, |
593 | .num_dapm_routes = ARRAY_SIZE(tas2562_audio_map), |
594 | .idle_bias_on = 1, |
595 | .use_pmdown_time = 1, |
596 | .endianness = 1, |
597 | }; |
598 | |
599 | static const struct snd_soc_dai_ops tas2562_speaker_dai_ops = { |
600 | .hw_params = tas2562_hw_params, |
601 | .set_fmt = tas2562_set_dai_fmt, |
602 | .set_tdm_slot = tas2562_set_dai_tdm_slot, |
603 | .mute_stream = tas2562_mute, |
604 | .no_capture_mute = 1, |
605 | }; |
606 | |
607 | static struct snd_soc_dai_driver tas2562_dai[] = { |
608 | { |
609 | .name = "tas2562-amplifier" , |
610 | .id = 0, |
611 | .playback = { |
612 | .stream_name = "ASI1 Playback" , |
613 | .channels_min = 2, |
614 | .channels_max = 2, |
615 | .rates = SNDRV_PCM_RATE_8000_192000, |
616 | .formats = TAS2562_FORMATS, |
617 | }, |
618 | .capture = { |
619 | .stream_name = "ASI1 Capture" , |
620 | .channels_min = 0, |
621 | .channels_max = 2, |
622 | .rates = SNDRV_PCM_RATE_8000_192000, |
623 | .formats = TAS2562_FORMATS, |
624 | }, |
625 | .ops = &tas2562_speaker_dai_ops, |
626 | }, |
627 | }; |
628 | |
629 | static const struct regmap_range_cfg tas2562_ranges[] = { |
630 | { |
631 | .range_min = 0, |
632 | .range_max = 5 * 128, |
633 | .selector_reg = TAS2562_PAGE_CTRL, |
634 | .selector_mask = 0xff, |
635 | .selector_shift = 0, |
636 | .window_start = 0, |
637 | .window_len = 128, |
638 | }, |
639 | }; |
640 | |
641 | static const struct reg_default tas2562_reg_defaults[] = { |
642 | { TAS2562_PAGE_CTRL, 0x00 }, |
643 | { TAS2562_SW_RESET, 0x00 }, |
644 | { TAS2562_PWR_CTRL, 0x0e }, |
645 | { TAS2562_PB_CFG1, 0x20 }, |
646 | { TAS2562_TDM_CFG0, 0x09 }, |
647 | { TAS2562_TDM_CFG1, 0x02 }, |
648 | { TAS2562_DVC_CFG1, 0x40 }, |
649 | { TAS2562_DVC_CFG2, 0x40 }, |
650 | { TAS2562_DVC_CFG3, 0x00 }, |
651 | { TAS2562_DVC_CFG4, 0x00 }, |
652 | }; |
653 | |
654 | static const struct regmap_config tas2562_regmap_config = { |
655 | .reg_bits = 8, |
656 | .val_bits = 8, |
657 | |
658 | .max_register = 5 * 128, |
659 | .cache_type = REGCACHE_RBTREE, |
660 | .reg_defaults = tas2562_reg_defaults, |
661 | .num_reg_defaults = ARRAY_SIZE(tas2562_reg_defaults), |
662 | .ranges = tas2562_ranges, |
663 | .num_ranges = ARRAY_SIZE(tas2562_ranges), |
664 | }; |
665 | |
666 | static int tas2562_parse_dt(struct tas2562_data *tas2562) |
667 | { |
668 | struct device *dev = tas2562->dev; |
669 | int ret = 0; |
670 | |
671 | tas2562->sdz_gpio = devm_gpiod_get_optional(dev, con_id: "shutdown" , flags: GPIOD_OUT_HIGH); |
672 | if (IS_ERR(ptr: tas2562->sdz_gpio)) { |
673 | if (PTR_ERR(ptr: tas2562->sdz_gpio) == -EPROBE_DEFER) |
674 | return -EPROBE_DEFER; |
675 | |
676 | tas2562->sdz_gpio = NULL; |
677 | } |
678 | |
679 | /* |
680 | * The shut-down property is deprecated but needs to be checked for |
681 | * backwards compatibility. |
682 | */ |
683 | if (tas2562->sdz_gpio == NULL) { |
684 | tas2562->sdz_gpio = devm_gpiod_get_optional(dev, con_id: "shut-down" , |
685 | flags: GPIOD_OUT_HIGH); |
686 | if (IS_ERR(ptr: tas2562->sdz_gpio)) |
687 | if (PTR_ERR(ptr: tas2562->sdz_gpio) == -EPROBE_DEFER) |
688 | return -EPROBE_DEFER; |
689 | |
690 | tas2562->sdz_gpio = NULL; |
691 | } |
692 | |
693 | if (tas2562->model_id == TAS2110) |
694 | return ret; |
695 | |
696 | ret = fwnode_property_read_u32(fwnode: dev->fwnode, propname: "ti,imon-slot-no" , |
697 | val: &tas2562->i_sense_slot); |
698 | if (ret) { |
699 | dev_err(dev, "Property %s is missing setting default slot\n" , |
700 | "ti,imon-slot-no" ); |
701 | tas2562->i_sense_slot = 0; |
702 | } |
703 | |
704 | |
705 | ret = fwnode_property_read_u32(fwnode: dev->fwnode, propname: "ti,vmon-slot-no" , |
706 | val: &tas2562->v_sense_slot); |
707 | if (ret) { |
708 | dev_info(dev, "Property %s is missing setting default slot\n" , |
709 | "ti,vmon-slot-no" ); |
710 | tas2562->v_sense_slot = 2; |
711 | } |
712 | |
713 | if (tas2562->v_sense_slot < tas2562->i_sense_slot) { |
714 | dev_err(dev, "Vsense slot must be greater than Isense slot\n" ); |
715 | return -EINVAL; |
716 | } |
717 | |
718 | return ret; |
719 | } |
720 | |
721 | static const struct i2c_device_id tas2562_id[] = { |
722 | { "tas2562" , TAS2562 }, |
723 | { "tas2564" , TAS2564 }, |
724 | { "tas2110" , TAS2110 }, |
725 | { } |
726 | }; |
727 | MODULE_DEVICE_TABLE(i2c, tas2562_id); |
728 | |
729 | static int tas2562_probe(struct i2c_client *client) |
730 | { |
731 | struct device *dev = &client->dev; |
732 | struct tas2562_data *data; |
733 | int ret; |
734 | const struct i2c_device_id *id; |
735 | |
736 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
737 | if (!data) |
738 | return -ENOMEM; |
739 | |
740 | id = i2c_match_id(id: tas2562_id, client); |
741 | data->client = client; |
742 | data->dev = &client->dev; |
743 | data->model_id = id->driver_data; |
744 | |
745 | tas2562_parse_dt(tas2562: data); |
746 | |
747 | data->regmap = devm_regmap_init_i2c(client, &tas2562_regmap_config); |
748 | if (IS_ERR(ptr: data->regmap)) { |
749 | ret = PTR_ERR(ptr: data->regmap); |
750 | dev_err(dev, "failed to allocate register map: %d\n" , ret); |
751 | return ret; |
752 | } |
753 | |
754 | dev_set_drvdata(dev: &client->dev, data); |
755 | |
756 | if (data->model_id == TAS2110) |
757 | return devm_snd_soc_register_component(dev, |
758 | component_driver: &soc_component_dev_tas2110, |
759 | dai_drv: tas2562_dai, |
760 | ARRAY_SIZE(tas2562_dai)); |
761 | |
762 | return devm_snd_soc_register_component(dev, component_driver: &soc_component_dev_tas2562, |
763 | dai_drv: tas2562_dai, |
764 | ARRAY_SIZE(tas2562_dai)); |
765 | |
766 | } |
767 | |
768 | #ifdef CONFIG_OF |
769 | static const struct of_device_id tas2562_of_match[] = { |
770 | { .compatible = "ti,tas2562" , }, |
771 | { .compatible = "ti,tas2564" , }, |
772 | { .compatible = "ti,tas2110" , }, |
773 | { }, |
774 | }; |
775 | MODULE_DEVICE_TABLE(of, tas2562_of_match); |
776 | #endif |
777 | |
778 | static struct i2c_driver tas2562_i2c_driver = { |
779 | .driver = { |
780 | .name = "tas2562" , |
781 | .of_match_table = of_match_ptr(tas2562_of_match), |
782 | }, |
783 | .probe = tas2562_probe, |
784 | .id_table = tas2562_id, |
785 | }; |
786 | |
787 | module_i2c_driver(tas2562_i2c_driver); |
788 | |
789 | MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>" ); |
790 | MODULE_DESCRIPTION("TAS2562 Audio amplifier driver" ); |
791 | MODULE_LICENSE("GPL" ); |
792 | |