aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
authorAndreas Dannenberg <dannenberg@ti.com>2016-04-26 18:15:57 -0400
committerMark Brown <broonie@kernel.org>2016-04-27 11:48:53 -0400
commitbd023ada36a66fe3d2fde619926b49b9a0428133 (patch)
treec6962ecae15c001ad5ae73b7aedea16327e1e888 /sound/soc
parent4e2f17be8f63a6ad9ebd4cdfce627e3dd25d80bb (diff)
ASoC: add support for TAS5720 digital amplifier
The Texas Instruments TAS5720L/M device is a high-efficiency mono Class-D audio power amplifier optimized for high transient power capability to use the dynamic power headroom of small loudspeakers. Its digital time division multiplexed (TDM) interface enables up to 16 devices to share the same bus. Signed-off-by: Andreas Dannenberg <dannenberg@ti.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/codecs/Kconfig8
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/tas5720.c620
-rw-r--r--sound/soc/codecs/tas5720.h90
4 files changed, 720 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index c011f076d58b..06d298b79b9b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -124,6 +124,7 @@ config SND_SOC_ALL_CODECS
124 select SND_SOC_TAS2552 if I2C 124 select SND_SOC_TAS2552 if I2C
125 select SND_SOC_TAS5086 if I2C 125 select SND_SOC_TAS5086 if I2C
126 select SND_SOC_TAS571X if I2C 126 select SND_SOC_TAS571X if I2C
127 select SND_SOC_TAS5720 if I2C
127 select SND_SOC_TFA9879 if I2C 128 select SND_SOC_TFA9879 if I2C
128 select SND_SOC_TLV320AIC23_I2C if I2C 129 select SND_SOC_TLV320AIC23_I2C if I2C
129 select SND_SOC_TLV320AIC23_SPI if SPI_MASTER 130 select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
@@ -740,6 +741,13 @@ config SND_SOC_TAS571X
740 tristate "Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 power amplifiers" 741 tristate "Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 power amplifiers"
741 depends on I2C 742 depends on I2C
742 743
744config SND_SOC_TAS5720
745 tristate "Texas Instruments TAS5720 Mono Audio amplifier"
746 depends on I2C
747 help
748 Enable support for Texas Instruments TAS5720L/M high-efficiency mono
749 Class-D audio power amplifiers.
750
743config SND_SOC_TFA9879 751config SND_SOC_TFA9879
744 tristate "NXP Semiconductors TFA9879 amplifier" 752 tristate "NXP Semiconductors TFA9879 amplifier"
745 depends on I2C 753 depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 185a712a7fe7..83d352ede6fd 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -129,6 +129,7 @@ snd-soc-stac9766-objs := stac9766.o
129snd-soc-sti-sas-objs := sti-sas.o 129snd-soc-sti-sas-objs := sti-sas.o
130snd-soc-tas5086-objs := tas5086.o 130snd-soc-tas5086-objs := tas5086.o
131snd-soc-tas571x-objs := tas571x.o 131snd-soc-tas571x-objs := tas571x.o
132snd-soc-tas5720-objs := tas5720.o
132snd-soc-tfa9879-objs := tfa9879.o 133snd-soc-tfa9879-objs := tfa9879.o
133snd-soc-tlv320aic23-objs := tlv320aic23.o 134snd-soc-tlv320aic23-objs := tlv320aic23.o
134snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o 135snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
@@ -335,6 +336,7 @@ obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
335obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o 336obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
336obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o 337obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
337obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o 338obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
339obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
338obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o 340obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
339obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o 341obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
340obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o 342obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
new file mode 100644
index 000000000000..f54fb46b77c2
--- /dev/null
+++ b/sound/soc/codecs/tas5720.c
@@ -0,0 +1,620 @@
1/*
2 * tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier
3 *
4 * Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com
5 *
6 * Author: Andreas Dannenberg <dannenberg@ti.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 */
17
18#include <linux/module.h>
19#include <linux/errno.h>
20#include <linux/device.h>
21#include <linux/i2c.h>
22#include <linux/pm_runtime.h>
23#include <linux/regmap.h>
24#include <linux/slab.h>
25#include <linux/regulator/consumer.h>
26#include <linux/delay.h>
27
28#include <sound/pcm.h>
29#include <sound/pcm_params.h>
30#include <sound/soc.h>
31#include <sound/soc-dapm.h>
32#include <sound/tlv.h>
33
34#include "tas5720.h"
35
36/* Define how often to check (and clear) the fault status register (in ms) */
37#define TAS5720_FAULT_CHECK_INTERVAL 200
38
39static const char * const tas5720_supply_names[] = {
40 "dvdd", /* Digital power supply. Connect to 3.3-V supply. */
41 "pvdd", /* Class-D amp and analog power supply (connected). */
42};
43
44#define TAS5720_NUM_SUPPLIES ARRAY_SIZE(tas5720_supply_names)
45
46struct tas5720_data {
47 struct snd_soc_codec *codec;
48 struct regmap *regmap;
49 struct i2c_client *tas5720_client;
50 struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES];
51 struct delayed_work fault_check_work;
52 unsigned int last_fault;
53};
54
55static int tas5720_hw_params(struct snd_pcm_substream *substream,
56 struct snd_pcm_hw_params *params,
57 struct snd_soc_dai *dai)
58{
59 struct snd_soc_codec *codec = dai->codec;
60 unsigned int rate = params_rate(params);
61 bool ssz_ds;
62 int ret;
63
64 switch (rate) {
65 case 44100:
66 case 48000:
67 ssz_ds = false;
68 break;
69 case 88200:
70 case 96000:
71 ssz_ds = true;
72 break;
73 default:
74 dev_err(codec->dev, "unsupported sample rate: %u\n", rate);
75 return -EINVAL;
76 }
77
78 ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG,
79 TAS5720_SSZ_DS, ssz_ds);
80 if (ret < 0) {
81 dev_err(codec->dev, "error setting sample rate: %d\n", ret);
82 return ret;
83 }
84
85 return 0;
86}
87
88static int tas5720_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
89{
90 struct snd_soc_codec *codec = dai->codec;
91 u8 serial_format;
92 int ret;
93
94 if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
95 dev_vdbg(codec->dev, "DAI Format master is not found\n");
96 return -EINVAL;
97 }
98
99 switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
100 SND_SOC_DAIFMT_INV_MASK)) {
101 case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
102 /* 1st data bit occur one BCLK cycle after the frame sync */
103 serial_format = TAS5720_SAIF_I2S;
104 break;
105 case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF):
106 /*
107 * Note that although the TAS5720 does not have a dedicated DSP
108 * mode it doesn't care about the LRCLK duty cycle during TDM
109 * operation. Therefore we can use the device's I2S mode with
110 * its delaying of the 1st data bit to receive DSP_A formatted
111 * data. See device datasheet for additional details.
112 */
113 serial_format = TAS5720_SAIF_I2S;
114 break;
115 case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF):
116 /*
117 * Similar to DSP_A, we can use the fact that the TAS5720 does
118 * not care about the LRCLK duty cycle during TDM to receive
119 * DSP_B formatted data in LEFTJ mode (no delaying of the 1st
120 * data bit).
121 */
122 serial_format = TAS5720_SAIF_LEFTJ;
123 break;
124 case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
125 /* No delay after the frame sync */
126 serial_format = TAS5720_SAIF_LEFTJ;
127 break;
128 default:
129 dev_vdbg(codec->dev, "DAI Format is not found\n");
130 return -EINVAL;
131 }
132
133 ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG,
134 TAS5720_SAIF_FORMAT_MASK,
135 serial_format);
136 if (ret < 0) {
137 dev_err(codec->dev, "error setting SAIF format: %d\n", ret);
138 return ret;
139 }
140
141 return 0;
142}
143
144static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai,
145 unsigned int tx_mask, unsigned int rx_mask,
146 int slots, int slot_width)
147{
148 struct snd_soc_codec *codec = dai->codec;
149 unsigned int first_slot;
150 int ret;
151
152 if (!tx_mask) {
153 dev_err(codec->dev, "tx masks must not be 0\n");
154 return -EINVAL;
155 }
156
157 /*
158 * Determine the first slot that is being requested. We will only
159 * use the first slot that is found since the TAS5720 is a mono
160 * amplifier.
161 */
162 first_slot = __ffs(tx_mask);
163
164 if (first_slot > 7) {
165 dev_err(codec->dev, "slot selection out of bounds (%u)\n",
166 first_slot);
167 return -EINVAL;
168 }
169
170 /* Enable manual TDM slot selection (instead of I2C ID based) */
171 ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG,
172 TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC);
173 if (ret < 0)
174 goto error_snd_soc_update_bits;
175
176 /* Configure the TDM slot to process audio from */
177 ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
178 TAS5720_TDM_SLOT_SEL_MASK, first_slot);
179 if (ret < 0)
180 goto error_snd_soc_update_bits;
181
182 return 0;
183
184error_snd_soc_update_bits:
185 dev_err(codec->dev, "error configuring TDM mode: %d\n", ret);
186 return ret;
187}
188
189static int tas5720_mute(struct snd_soc_dai *dai, int mute)
190{
191 struct snd_soc_codec *codec = dai->codec;
192 int ret;
193
194 ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
195 TAS5720_MUTE, mute ? TAS5720_MUTE : 0);
196 if (ret < 0) {
197 dev_err(codec->dev, "error (un-)muting device: %d\n", ret);
198 return ret;
199 }
200
201 return 0;
202}
203
204static void tas5720_fault_check_work(struct work_struct *work)
205{
206 struct tas5720_data *tas5720 = container_of(work, struct tas5720_data,
207 fault_check_work.work);
208 struct device *dev = tas5720->codec->dev;
209 unsigned int curr_fault;
210 int ret;
211
212 ret = regmap_read(tas5720->regmap, TAS5720_FAULT_REG, &curr_fault);
213 if (ret < 0) {
214 dev_err(dev, "failed to read FAULT register: %d\n", ret);
215 goto out;
216 }
217
218 /* Check/handle all errors except SAIF clock errors */
219 curr_fault &= TAS5720_OCE | TAS5720_DCE | TAS5720_OTE;
220
221 /*
222 * Only flag errors once for a given occurrence. This is needed as
223 * the TAS5720 will take time clearing the fault condition internally
224 * during which we don't want to bombard the system with the same
225 * error message over and over.
226 */
227 if ((curr_fault & TAS5720_OCE) && !(tas5720->last_fault & TAS5720_OCE))
228 dev_crit(dev, "experienced an over current hardware fault\n");
229
230 if ((curr_fault & TAS5720_DCE) && !(tas5720->last_fault & TAS5720_DCE))
231 dev_crit(dev, "experienced a DC detection fault\n");
232
233 if ((curr_fault & TAS5720_OTE) && !(tas5720->last_fault & TAS5720_OTE))
234 dev_crit(dev, "experienced an over temperature fault\n");
235
236 /* Store current fault value so we can detect any changes next time */
237 tas5720->last_fault = curr_fault;
238
239 if (!curr_fault)
240 goto out;
241
242 /*
243 * Periodically toggle SDZ (shutdown bit) H->L->H to clear any latching
244 * faults as long as a fault condition persists. Always going through
245 * the full sequence no matter the first return value to minimizes
246 * chances for the device to end up in shutdown mode.
247 */
248 ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,
249 TAS5720_SDZ, 0);
250 if (ret < 0)
251 dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);
252
253 ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,
254 TAS5720_SDZ, TAS5720_SDZ);
255 if (ret < 0)
256 dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);
257
258out:
259 /* Schedule the next fault check at the specified interval */
260 schedule_delayed_work(&tas5720->fault_check_work,
261 msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));
262}
263
264static int tas5720_codec_probe(struct snd_soc_codec *codec)
265{
266 struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
267 unsigned int device_id;
268 int ret;
269
270 tas5720->codec = codec;
271
272 ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),
273 tas5720->supplies);
274 if (ret != 0) {
275 dev_err(codec->dev, "failed to enable supplies: %d\n", ret);
276 return ret;
277 }
278
279 ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id);
280 if (ret < 0) {
281 dev_err(codec->dev, "failed to read device ID register: %d\n",
282 ret);
283 goto probe_fail;
284 }
285
286 if (device_id != TAS5720_DEVICE_ID) {
287 dev_err(codec->dev, "wrong device ID. expected: %u read: %u\n",
288 TAS5720_DEVICE_ID, device_id);
289 ret = -ENODEV;
290 goto probe_fail;
291 }
292
293 /* Set device to mute */
294 ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
295 TAS5720_MUTE, TAS5720_MUTE);
296 if (ret < 0)
297 goto error_snd_soc_update_bits;
298
299 /*
300 * Enter shutdown mode - our default when not playing audio - to
301 * minimize current consumption. On the TAS5720 there is no real down
302 * side doing so as all device registers are preserved and the wakeup
303 * of the codec is rather quick which we do using a dapm widget.
304 */
305 ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG,
306 TAS5720_SDZ, 0);
307 if (ret < 0)
308 goto error_snd_soc_update_bits;
309
310 INIT_DELAYED_WORK(&tas5720->fault_check_work, tas5720_fault_check_work);
311
312 return 0;
313
314error_snd_soc_update_bits:
315 dev_err(codec->dev, "error configuring device registers: %d\n", ret);
316
317probe_fail:
318 regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
319 tas5720->supplies);
320 return ret;
321}
322
323static int tas5720_codec_remove(struct snd_soc_codec *codec)
324{
325 struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
326 int ret;
327
328 cancel_delayed_work_sync(&tas5720->fault_check_work);
329
330 ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
331 tas5720->supplies);
332 if (ret < 0)
333 dev_err(codec->dev, "failed to disable supplies: %d\n", ret);
334
335 return ret;
336};
337
338static int tas5720_dac_event(struct snd_soc_dapm_widget *w,
339 struct snd_kcontrol *kcontrol, int event)
340{
341 struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
342 struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
343 int ret;
344
345 if (event & SND_SOC_DAPM_POST_PMU) {
346 /* Take TAS5720 out of shutdown mode */
347 ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG,
348 TAS5720_SDZ, TAS5720_SDZ);
349 if (ret < 0) {
350 dev_err(codec->dev, "error waking codec: %d\n", ret);
351 return ret;
352 }
353
354 /*
355 * Observe codec shutdown-to-active time. The datasheet only
356 * lists a nominal value however just use-it as-is without
357 * additional padding to minimize the delay introduced in
358 * starting to play audio (actually there is other setup done
359 * by the ASoC framework that will provide additional delays,
360 * so we should always be safe).
361 */
362 msleep(25);
363
364 /* Turn on TAS5720 periodic fault checking/handling */
365 tas5720->last_fault = 0;
366 schedule_delayed_work(&tas5720->fault_check_work,
367 msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));
368 } else if (event & SND_SOC_DAPM_PRE_PMD) {
369 /* Disable TAS5720 periodic fault checking/handling */
370 cancel_delayed_work_sync(&tas5720->fault_check_work);
371
372 /* Place TAS5720 in shutdown mode to minimize current draw */
373 ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG,
374 TAS5720_SDZ, 0);
375 if (ret < 0) {
376 dev_err(codec->dev, "error shutting down codec: %d\n",
377 ret);
378 return ret;
379 }
380 }
381
382 return 0;
383}
384
385#ifdef CONFIG_PM
386static int tas5720_suspend(struct snd_soc_codec *codec)
387{
388 struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
389 int ret;
390
391 regcache_cache_only(tas5720->regmap, true);
392 regcache_mark_dirty(tas5720->regmap);
393
394 ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
395 tas5720->supplies);
396 if (ret < 0)
397 dev_err(codec->dev, "failed to disable supplies: %d\n", ret);
398
399 return ret;
400}
401
402static int tas5720_resume(struct snd_soc_codec *codec)
403{
404 struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
405 int ret;
406
407 ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),
408 tas5720->supplies);
409 if (ret < 0) {
410 dev_err(codec->dev, "failed to enable supplies: %d\n", ret);
411 return ret;
412 }
413
414 regcache_cache_only(tas5720->regmap, false);
415
416 ret = regcache_sync(tas5720->regmap);
417 if (ret < 0) {
418 dev_err(codec->dev, "failed to sync regcache: %d\n", ret);
419 return ret;
420 }
421
422 return 0;
423}
424#else
425#define tas5720_suspend NULL
426#define tas5720_resume NULL
427#endif
428
429static bool tas5720_is_volatile_reg(struct device *dev, unsigned int reg)
430{
431 switch (reg) {
432 case TAS5720_DEVICE_ID_REG:
433 case TAS5720_FAULT_REG:
434 return true;
435 default:
436 return false;
437 }
438}
439
440static const struct regmap_config tas5720_regmap_config = {
441 .reg_bits = 8,
442 .val_bits = 8,
443
444 .max_register = TAS5720_MAX_REG,
445 .cache_type = REGCACHE_RBTREE,
446 .volatile_reg = tas5720_is_volatile_reg,
447};
448
449/*
450 * DAC analog gain. There are four discrete values to select from, ranging
451 * from 19.2 dB to 26.3dB.
452 */
453static const DECLARE_TLV_DB_RANGE(dac_analog_tlv,
454 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),
455 0x1, 0x1, TLV_DB_SCALE_ITEM(2070, 0, 0),
456 0x2, 0x2, TLV_DB_SCALE_ITEM(2350, 0, 0),
457 0x3, 0x3, TLV_DB_SCALE_ITEM(2630, 0, 0),
458);
459
460/*
461 * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that
462 * setting the gain below -100 dB (register value <0x7) is effectively a MUTE
463 * as per device datasheet.
464 */
465static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0);
466
467static const struct snd_kcontrol_new tas5720_snd_controls[] = {
468 SOC_SINGLE_TLV("Speaker Driver Playback Volume",
469 TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, dac_tlv),
470 SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
471 TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
472};
473
474static const struct snd_soc_dapm_widget tas5720_dapm_widgets[] = {
475 SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
476 SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas5720_dac_event,
477 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
478 SND_SOC_DAPM_OUTPUT("OUT")
479};
480
481static const struct snd_soc_dapm_route tas5720_audio_map[] = {
482 { "DAC", NULL, "DAC IN" },
483 { "OUT", NULL, "DAC" },
484};
485
486static struct snd_soc_codec_driver soc_codec_dev_tas5720 = {
487 .probe = tas5720_codec_probe,
488 .remove = tas5720_codec_remove,
489 .suspend = tas5720_suspend,
490 .resume = tas5720_resume,
491
492 .controls = tas5720_snd_controls,
493 .num_controls = ARRAY_SIZE(tas5720_snd_controls),
494 .dapm_widgets = tas5720_dapm_widgets,
495 .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),
496 .dapm_routes = tas5720_audio_map,
497 .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),
498};
499
500/* PCM rates supported by the TAS5720 driver */
501#define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
502 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
503
504/* Formats supported by TAS5720 driver */
505#define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\
506 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
507
508static struct snd_soc_dai_ops tas5720_speaker_dai_ops = {
509 .hw_params = tas5720_hw_params,
510 .set_fmt = tas5720_set_dai_fmt,
511 .set_tdm_slot = tas5720_set_dai_tdm_slot,
512 .digital_mute = tas5720_mute,
513};
514
515/*
516 * TAS5720 DAI structure
517 *
518 * Note that were are advertising .playback.channels_max = 2 despite this being
519 * a mono amplifier. The reason for that is that some serial ports such as TI's
520 * McASP module have a minimum number of channels (2) that they can output.
521 * Advertising more channels than we have will allow us to interface with such
522 * a serial port without really any negative side effects as the TAS5720 will
523 * simply ignore any extra channel(s) asides from the one channel that is
524 * configured to be played back.
525 */
526static struct snd_soc_dai_driver tas5720_dai[] = {
527 {
528 .name = "tas5720-amplifier",
529 .playback = {
530 .stream_name = "Playback",
531 .channels_min = 1,
532 .channels_max = 2,
533 .rates = TAS5720_RATES,
534 .formats = TAS5720_FORMATS,
535 },
536 .ops = &tas5720_speaker_dai_ops,
537 },
538};
539
540static int tas5720_probe(struct i2c_client *client,
541 const struct i2c_device_id *id)
542{
543 struct device *dev = &client->dev;
544 struct tas5720_data *data;
545 int ret;
546 int i;
547
548 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
549 if (!data)
550 return -ENOMEM;
551
552 data->tas5720_client = client;
553 data->regmap = devm_regmap_init_i2c(client, &tas5720_regmap_config);
554 if (IS_ERR(data->regmap)) {
555 ret = PTR_ERR(data->regmap);
556 dev_err(dev, "failed to allocate register map: %d\n", ret);
557 return ret;
558 }
559
560 for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
561 data->supplies[i].supply = tas5720_supply_names[i];
562
563 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
564 data->supplies);
565 if (ret != 0) {
566 dev_err(dev, "failed to request supplies: %d\n", ret);
567 return ret;
568 }
569
570 dev_set_drvdata(dev, data);
571
572 ret = snd_soc_register_codec(&client->dev,
573 &soc_codec_dev_tas5720,
574 tas5720_dai, ARRAY_SIZE(tas5720_dai));
575 if (ret < 0) {
576 dev_err(dev, "failed to register codec: %d\n", ret);
577 return ret;
578 }
579
580 return 0;
581}
582
583static int tas5720_remove(struct i2c_client *client)
584{
585 struct device *dev = &client->dev;
586
587 snd_soc_unregister_codec(dev);
588
589 return 0;
590}
591
592static const struct i2c_device_id tas5720_id[] = {
593 { "tas5720", 0 },
594 { }
595};
596MODULE_DEVICE_TABLE(i2c, tas5720_id);
597
598#if IS_ENABLED(CONFIG_OF)
599static const struct of_device_id tas5720_of_match[] = {
600 { .compatible = "ti,tas5720", },
601 { },
602};
603MODULE_DEVICE_TABLE(of, tas5720_of_match);
604#endif
605
606static struct i2c_driver tas5720_i2c_driver = {
607 .driver = {
608 .name = "tas5720",
609 .of_match_table = of_match_ptr(tas5720_of_match),
610 },
611 .probe = tas5720_probe,
612 .remove = tas5720_remove,
613 .id_table = tas5720_id,
614};
615
616module_i2c_driver(tas5720_i2c_driver);
617
618MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
619MODULE_DESCRIPTION("TAS5720 Audio amplifier driver");
620MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas5720.h b/sound/soc/codecs/tas5720.h
new file mode 100644
index 000000000000..3d077c779b12
--- /dev/null
+++ b/sound/soc/codecs/tas5720.h
@@ -0,0 +1,90 @@
1/*
2 * tas5720.h - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier
3 *
4 * Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com
5 *
6 * Author: Andreas Dannenberg <dannenberg@ti.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 */
17
18#ifndef __TAS5720_H__
19#define __TAS5720_H__
20
21/* Register Address Map */
22#define TAS5720_DEVICE_ID_REG 0x00
23#define TAS5720_POWER_CTRL_REG 0x01
24#define TAS5720_DIGITAL_CTRL1_REG 0x02
25#define TAS5720_DIGITAL_CTRL2_REG 0x03
26#define TAS5720_VOLUME_CTRL_REG 0x04
27#define TAS5720_ANALOG_CTRL_REG 0x06
28#define TAS5720_FAULT_REG 0x08
29#define TAS5720_DIGITAL_CLIP2_REG 0x10
30#define TAS5720_DIGITAL_CLIP1_REG 0x11
31#define TAS5720_MAX_REG TAS5720_DIGITAL_CLIP1_REG
32
33/* TAS5720_DEVICE_ID_REG */
34#define TAS5720_DEVICE_ID 0x01
35
36/* TAS5720_POWER_CTRL_REG */
37#define TAS5720_DIG_CLIP_MASK GENMASK(7, 2)
38#define TAS5720_SLEEP BIT(1)
39#define TAS5720_SDZ BIT(0)
40
41/* TAS5720_DIGITAL_CTRL1_REG */
42#define TAS5720_HPF_BYPASS BIT(7)
43#define TAS5720_TDM_CFG_SRC BIT(6)
44#define TAS5720_SSZ_DS BIT(3)
45#define TAS5720_SAIF_RIGHTJ_24BIT (0x0)
46#define TAS5720_SAIF_RIGHTJ_20BIT (0x1)
47#define TAS5720_SAIF_RIGHTJ_18BIT (0x2)
48#define TAS5720_SAIF_RIGHTJ_16BIT (0x3)
49#define TAS5720_SAIF_I2S (0x4)
50#define TAS5720_SAIF_LEFTJ (0x5)
51#define TAS5720_SAIF_FORMAT_MASK GENMASK(2, 0)
52
53/* TAS5720_DIGITAL_CTRL2_REG */
54#define TAS5720_MUTE BIT(4)
55#define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0)
56
57/* TAS5720_ANALOG_CTRL_REG */
58#define TAS5720_PWM_RATE_6_3_FSYNC (0x0 << 4)
59#define TAS5720_PWM_RATE_8_4_FSYNC (0x1 << 4)
60#define TAS5720_PWM_RATE_10_5_FSYNC (0x2 << 4)
61#define TAS5720_PWM_RATE_12_6_FSYNC (0x3 << 4)
62#define TAS5720_PWM_RATE_14_7_FSYNC (0x4 << 4)
63#define TAS5720_PWM_RATE_16_8_FSYNC (0x5 << 4)
64#define TAS5720_PWM_RATE_20_10_FSYNC (0x6 << 4)
65#define TAS5720_PWM_RATE_24_12_FSYNC (0x7 << 4)
66#define TAS5720_PWM_RATE_MASK GENMASK(6, 4)
67#define TAS5720_ANALOG_GAIN_19_2DBV (0x0 << 2)
68#define TAS5720_ANALOG_GAIN_20_7DBV (0x1 << 2)
69#define TAS5720_ANALOG_GAIN_23_5DBV (0x2 << 2)
70#define TAS5720_ANALOG_GAIN_26_3DBV (0x3 << 2)
71#define TAS5720_ANALOG_GAIN_MASK GENMASK(3, 2)
72#define TAS5720_ANALOG_GAIN_SHIFT (0x2)
73
74/* TAS5720_FAULT_REG */
75#define TAS5720_OC_THRESH_100PCT (0x0 << 4)
76#define TAS5720_OC_THRESH_75PCT (0x1 << 4)
77#define TAS5720_OC_THRESH_50PCT (0x2 << 4)
78#define TAS5720_OC_THRESH_25PCT (0x3 << 4)
79#define TAS5720_OC_THRESH_MASK GENMASK(5, 4)
80#define TAS5720_CLKE BIT(3)
81#define TAS5720_OCE BIT(2)
82#define TAS5720_DCE BIT(1)
83#define TAS5720_OTE BIT(0)
84#define TAS5720_FAULT_MASK GENMASK(3, 0)
85
86/* TAS5720_DIGITAL_CLIP1_REG */
87#define TAS5720_CLIP1_MASK GENMASK(7, 2)
88#define TAS5720_CLIP1_SHIFT (0x2)
89
90#endif /* __TAS5720_H__ */