diff options
author | Kevin Cernekee <cernekee@chromium.org> | 2015-05-03 20:00:18 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-05-04 07:46:25 -0400 |
commit | 3fd6e7d9a146e2e0b55f428d8d4d500ca86909f5 (patch) | |
tree | 0894269b5684ff0a324e465343c1451520a26332 | |
parent | ee5d4df7298336a4c40140a1ce179e11ed179b03 (diff) |
ASoC: tas571x: New driver for TI TAS571x power amplifiers
Introduce a new codec driver for the Texas Instruments
TAS5711/TAS5717/TAS5719 power amplifier chips. These chips are typically
used to take an I2S digital audio input and drive 10-20W into a pair of
speakers.
Signed-off-by: Kevin Cernekee <cernekee@chromium.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/codecs/Kconfig | 5 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/tas571x.c | 520 | ||||
-rw-r--r-- | sound/soc/codecs/tas571x.h | 33 |
4 files changed, 560 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 061c46587628..befff910d71a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -104,6 +104,7 @@ config SND_SOC_ALL_CODECS | |||
104 | select SND_SOC_STAC9766 if SND_SOC_AC97_BUS | 104 | select SND_SOC_STAC9766 if SND_SOC_AC97_BUS |
105 | select SND_SOC_TAS2552 if I2C | 105 | select SND_SOC_TAS2552 if I2C |
106 | select SND_SOC_TAS5086 if I2C | 106 | select SND_SOC_TAS5086 if I2C |
107 | select SND_SOC_TAS571X if I2C | ||
107 | select SND_SOC_TFA9879 if I2C | 108 | select SND_SOC_TFA9879 if I2C |
108 | select SND_SOC_TLV320AIC23_I2C if I2C | 109 | select SND_SOC_TLV320AIC23_I2C if I2C |
109 | select SND_SOC_TLV320AIC23_SPI if SPI_MASTER | 110 | select SND_SOC_TLV320AIC23_SPI if SPI_MASTER |
@@ -611,6 +612,10 @@ config SND_SOC_TAS5086 | |||
611 | tristate "Texas Instruments TAS5086 speaker amplifier" | 612 | tristate "Texas Instruments TAS5086 speaker amplifier" |
612 | depends on I2C | 613 | depends on I2C |
613 | 614 | ||
615 | config SND_SOC_TAS571X | ||
616 | tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers" | ||
617 | depends on I2C | ||
618 | |||
614 | config SND_SOC_TFA9879 | 619 | config SND_SOC_TFA9879 |
615 | tristate "NXP Semiconductors TFA9879 amplifier" | 620 | tristate "NXP Semiconductors TFA9879 amplifier" |
616 | depends on I2C | 621 | depends on I2C |
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index abe2d7edf65c..3dcf5ac85e89 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -106,6 +106,7 @@ snd-soc-sta350-objs := sta350.o | |||
106 | snd-soc-sta529-objs := sta529.o | 106 | snd-soc-sta529-objs := sta529.o |
107 | snd-soc-stac9766-objs := stac9766.o | 107 | snd-soc-stac9766-objs := stac9766.o |
108 | snd-soc-tas5086-objs := tas5086.o | 108 | snd-soc-tas5086-objs := tas5086.o |
109 | snd-soc-tas571x-objs := tas571x.o | ||
109 | snd-soc-tfa9879-objs := tfa9879.o | 110 | snd-soc-tfa9879-objs := tfa9879.o |
110 | snd-soc-tlv320aic23-objs := tlv320aic23.o | 111 | snd-soc-tlv320aic23-objs := tlv320aic23.o |
111 | snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o | 112 | snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o |
@@ -288,6 +289,7 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o | |||
288 | obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o | 289 | obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o |
289 | obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o | 290 | obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o |
290 | obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o | 291 | obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o |
292 | obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o | ||
291 | obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o | 293 | obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o |
292 | obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o | 294 | obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o |
293 | obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o | 295 | obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o |
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c new file mode 100644 index 000000000000..ffdf48397491 --- /dev/null +++ b/sound/soc/codecs/tas571x.c | |||
@@ -0,0 +1,520 @@ | |||
1 | /* | ||
2 | * TAS571x amplifier audio driver | ||
3 | * | ||
4 | * Copyright (C) 2015 Google, Inc. | ||
5 | * Copyright (c) 2013 Daniel Mack <zonque@gmail.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/gpio/consumer.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/of_device.h> | ||
22 | #include <linux/regmap.h> | ||
23 | #include <linux/regulator/consumer.h> | ||
24 | #include <linux/stddef.h> | ||
25 | #include <sound/pcm_params.h> | ||
26 | #include <sound/soc.h> | ||
27 | #include <sound/tlv.h> | ||
28 | |||
29 | #include "tas571x.h" | ||
30 | |||
31 | #define TAS571X_MAX_SUPPLIES 6 | ||
32 | |||
33 | struct tas571x_chip { | ||
34 | const char *const *supply_names; | ||
35 | int num_supply_names; | ||
36 | const struct snd_kcontrol_new *controls; | ||
37 | int num_controls; | ||
38 | const struct regmap_config *regmap_config; | ||
39 | int vol_reg_size; | ||
40 | }; | ||
41 | |||
42 | struct tas571x_private { | ||
43 | const struct tas571x_chip *chip; | ||
44 | struct regmap *regmap; | ||
45 | struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES]; | ||
46 | struct clk *mclk; | ||
47 | unsigned int format; | ||
48 | struct gpio_desc *reset_gpio; | ||
49 | struct gpio_desc *pdn_gpio; | ||
50 | struct snd_soc_codec_driver codec_driver; | ||
51 | }; | ||
52 | |||
53 | static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg) | ||
54 | { | ||
55 | switch (reg) { | ||
56 | case TAS571X_MVOL_REG: | ||
57 | case TAS571X_CH1_VOL_REG: | ||
58 | case TAS571X_CH2_VOL_REG: | ||
59 | return priv->chip->vol_reg_size; | ||
60 | default: | ||
61 | return 1; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | static int tas571x_reg_write(void *context, unsigned int reg, | ||
66 | unsigned int value) | ||
67 | { | ||
68 | struct i2c_client *client = context; | ||
69 | struct tas571x_private *priv = i2c_get_clientdata(client); | ||
70 | unsigned int i, size; | ||
71 | uint8_t buf[5]; | ||
72 | int ret; | ||
73 | |||
74 | size = tas571x_register_size(priv, reg); | ||
75 | buf[0] = reg; | ||
76 | |||
77 | for (i = size; i >= 1; --i) { | ||
78 | buf[i] = value; | ||
79 | value >>= 8; | ||
80 | } | ||
81 | |||
82 | ret = i2c_master_send(client, buf, size + 1); | ||
83 | if (ret == size + 1) | ||
84 | return 0; | ||
85 | else if (ret < 0) | ||
86 | return ret; | ||
87 | else | ||
88 | return -EIO; | ||
89 | } | ||
90 | |||
91 | static int tas571x_reg_read(void *context, unsigned int reg, | ||
92 | unsigned int *value) | ||
93 | { | ||
94 | struct i2c_client *client = context; | ||
95 | struct tas571x_private *priv = i2c_get_clientdata(client); | ||
96 | uint8_t send_buf, recv_buf[4]; | ||
97 | struct i2c_msg msgs[2]; | ||
98 | unsigned int size; | ||
99 | unsigned int i; | ||
100 | int ret; | ||
101 | |||
102 | size = tas571x_register_size(priv, reg); | ||
103 | send_buf = reg; | ||
104 | |||
105 | msgs[0].addr = client->addr; | ||
106 | msgs[0].len = sizeof(send_buf); | ||
107 | msgs[0].buf = &send_buf; | ||
108 | msgs[0].flags = 0; | ||
109 | |||
110 | msgs[1].addr = client->addr; | ||
111 | msgs[1].len = size; | ||
112 | msgs[1].buf = recv_buf; | ||
113 | msgs[1].flags = I2C_M_RD; | ||
114 | |||
115 | ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | ||
116 | if (ret < 0) | ||
117 | return ret; | ||
118 | else if (ret != ARRAY_SIZE(msgs)) | ||
119 | return -EIO; | ||
120 | |||
121 | *value = 0; | ||
122 | |||
123 | for (i = 0; i < size; i++) { | ||
124 | *value <<= 8; | ||
125 | *value |= recv_buf[i]; | ||
126 | } | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) | ||
132 | { | ||
133 | struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); | ||
134 | |||
135 | priv->format = format; | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int tas571x_hw_params(struct snd_pcm_substream *substream, | ||
141 | struct snd_pcm_hw_params *params, | ||
142 | struct snd_soc_dai *dai) | ||
143 | { | ||
144 | struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); | ||
145 | u32 val; | ||
146 | |||
147 | switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
148 | case SND_SOC_DAIFMT_RIGHT_J: | ||
149 | val = 0x00; | ||
150 | break; | ||
151 | case SND_SOC_DAIFMT_I2S: | ||
152 | val = 0x03; | ||
153 | break; | ||
154 | case SND_SOC_DAIFMT_LEFT_J: | ||
155 | val = 0x06; | ||
156 | break; | ||
157 | default: | ||
158 | return -EINVAL; | ||
159 | } | ||
160 | |||
161 | if (params_width(params) >= 24) | ||
162 | val += 2; | ||
163 | else if (params_width(params) >= 20) | ||
164 | val += 1; | ||
165 | |||
166 | return regmap_update_bits(priv->regmap, TAS571X_SDI_REG, | ||
167 | TAS571X_SDI_FMT_MASK, val); | ||
168 | } | ||
169 | |||
170 | static int tas571x_set_bias_level(struct snd_soc_codec *codec, | ||
171 | enum snd_soc_bias_level level) | ||
172 | { | ||
173 | struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec); | ||
174 | int ret; | ||
175 | |||
176 | switch (level) { | ||
177 | case SND_SOC_BIAS_ON: | ||
178 | break; | ||
179 | case SND_SOC_BIAS_PREPARE: | ||
180 | break; | ||
181 | case SND_SOC_BIAS_STANDBY: | ||
182 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { | ||
183 | if (!IS_ERR(priv->mclk)) { | ||
184 | ret = clk_prepare_enable(priv->mclk); | ||
185 | if (ret) { | ||
186 | dev_err(codec->dev, | ||
187 | "Failed to enable master clock: %d\n", | ||
188 | ret); | ||
189 | return ret; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | gpiod_set_value(priv->pdn_gpio, 0); | ||
194 | usleep_range(5000, 6000); | ||
195 | |||
196 | regcache_cache_only(priv->regmap, false); | ||
197 | ret = regcache_sync(priv->regmap); | ||
198 | if (ret) | ||
199 | return ret; | ||
200 | } | ||
201 | break; | ||
202 | case SND_SOC_BIAS_OFF: | ||
203 | regcache_cache_only(priv->regmap, true); | ||
204 | gpiod_set_value(priv->pdn_gpio, 1); | ||
205 | |||
206 | if (!IS_ERR(priv->mclk)) | ||
207 | clk_disable_unprepare(priv->mclk); | ||
208 | break; | ||
209 | } | ||
210 | |||
211 | codec->dapm.bias_level = level; | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | static const struct snd_soc_dai_ops tas571x_dai_ops = { | ||
216 | .set_fmt = tas571x_set_dai_fmt, | ||
217 | .hw_params = tas571x_hw_params, | ||
218 | }; | ||
219 | |||
220 | static const char *const tas5711_supply_names[] = { | ||
221 | "AVDD", | ||
222 | "DVDD", | ||
223 | "PVDD_A", | ||
224 | "PVDD_B", | ||
225 | "PVDD_C", | ||
226 | "PVDD_D", | ||
227 | }; | ||
228 | |||
229 | static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1); | ||
230 | |||
231 | static const struct snd_kcontrol_new tas5711_controls[] = { | ||
232 | SOC_SINGLE_TLV("Master Volume", | ||
233 | TAS571X_MVOL_REG, | ||
234 | 0, 0xff, 1, tas5711_volume_tlv), | ||
235 | SOC_DOUBLE_R_TLV("Speaker Volume", | ||
236 | TAS571X_CH1_VOL_REG, | ||
237 | TAS571X_CH2_VOL_REG, | ||
238 | 0, 0xff, 1, tas5711_volume_tlv), | ||
239 | SOC_DOUBLE("Speaker Switch", | ||
240 | TAS571X_SOFT_MUTE_REG, | ||
241 | TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, | ||
242 | 1, 1), | ||
243 | }; | ||
244 | |||
245 | static const struct reg_default tas5711_reg_defaults[] = { | ||
246 | { 0x04, 0x05 }, | ||
247 | { 0x05, 0x40 }, | ||
248 | { 0x06, 0x00 }, | ||
249 | { 0x07, 0xff }, | ||
250 | { 0x08, 0x30 }, | ||
251 | { 0x09, 0x30 }, | ||
252 | { 0x1b, 0x82 }, | ||
253 | }; | ||
254 | |||
255 | static const struct regmap_config tas5711_regmap_config = { | ||
256 | .reg_bits = 8, | ||
257 | .val_bits = 32, | ||
258 | .max_register = 0xff, | ||
259 | .reg_read = tas571x_reg_read, | ||
260 | .reg_write = tas571x_reg_write, | ||
261 | .reg_defaults = tas5711_reg_defaults, | ||
262 | .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), | ||
263 | .cache_type = REGCACHE_RBTREE, | ||
264 | }; | ||
265 | |||
266 | static const struct tas571x_chip tas5711_chip = { | ||
267 | .supply_names = tas5711_supply_names, | ||
268 | .num_supply_names = ARRAY_SIZE(tas5711_supply_names), | ||
269 | .controls = tas5711_controls, | ||
270 | .num_controls = ARRAY_SIZE(tas5711_controls), | ||
271 | .regmap_config = &tas5711_regmap_config, | ||
272 | .vol_reg_size = 1, | ||
273 | }; | ||
274 | |||
275 | static const char *const tas5717_supply_names[] = { | ||
276 | "AVDD", | ||
277 | "DVDD", | ||
278 | "HPVDD", | ||
279 | "PVDD_AB", | ||
280 | "PVDD_CD", | ||
281 | }; | ||
282 | |||
283 | static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0); | ||
284 | |||
285 | static const struct snd_kcontrol_new tas5717_controls[] = { | ||
286 | /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */ | ||
287 | SOC_SINGLE_TLV("Master Volume", | ||
288 | TAS571X_MVOL_REG, 1, 0x1ff, 1, | ||
289 | tas5717_volume_tlv), | ||
290 | SOC_DOUBLE_R_TLV("Speaker Volume", | ||
291 | TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG, | ||
292 | 1, 0x1ff, 1, tas5717_volume_tlv), | ||
293 | SOC_DOUBLE("Speaker Switch", | ||
294 | TAS571X_SOFT_MUTE_REG, | ||
295 | TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, | ||
296 | 1, 1), | ||
297 | }; | ||
298 | |||
299 | static const struct reg_default tas5717_reg_defaults[] = { | ||
300 | { 0x04, 0x05 }, | ||
301 | { 0x05, 0x40 }, | ||
302 | { 0x06, 0x00 }, | ||
303 | { 0x07, 0x03ff }, | ||
304 | { 0x08, 0x00c0 }, | ||
305 | { 0x09, 0x00c0 }, | ||
306 | { 0x1b, 0x82 }, | ||
307 | }; | ||
308 | |||
309 | static const struct regmap_config tas5717_regmap_config = { | ||
310 | .reg_bits = 8, | ||
311 | .val_bits = 32, | ||
312 | .max_register = 0xff, | ||
313 | .reg_read = tas571x_reg_read, | ||
314 | .reg_write = tas571x_reg_write, | ||
315 | .reg_defaults = tas5717_reg_defaults, | ||
316 | .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), | ||
317 | .cache_type = REGCACHE_RBTREE, | ||
318 | }; | ||
319 | |||
320 | /* This entry is reused for tas5719 as the software interface is identical. */ | ||
321 | static const struct tas571x_chip tas5717_chip = { | ||
322 | .supply_names = tas5717_supply_names, | ||
323 | .num_supply_names = ARRAY_SIZE(tas5717_supply_names), | ||
324 | .controls = tas5717_controls, | ||
325 | .num_controls = ARRAY_SIZE(tas5717_controls), | ||
326 | .regmap_config = &tas5717_regmap_config, | ||
327 | .vol_reg_size = 2, | ||
328 | }; | ||
329 | |||
330 | static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { | ||
331 | SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), | ||
332 | SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), | ||
333 | |||
334 | SND_SOC_DAPM_OUTPUT("OUT_A"), | ||
335 | SND_SOC_DAPM_OUTPUT("OUT_B"), | ||
336 | SND_SOC_DAPM_OUTPUT("OUT_C"), | ||
337 | SND_SOC_DAPM_OUTPUT("OUT_D"), | ||
338 | }; | ||
339 | |||
340 | static const struct snd_soc_dapm_route tas571x_dapm_routes[] = { | ||
341 | { "DACL", NULL, "Playback" }, | ||
342 | { "DACR", NULL, "Playback" }, | ||
343 | |||
344 | { "OUT_A", NULL, "DACL" }, | ||
345 | { "OUT_B", NULL, "DACL" }, | ||
346 | { "OUT_C", NULL, "DACR" }, | ||
347 | { "OUT_D", NULL, "DACR" }, | ||
348 | }; | ||
349 | |||
350 | static const struct snd_soc_codec_driver tas571x_codec = { | ||
351 | .set_bias_level = tas571x_set_bias_level, | ||
352 | .idle_bias_off = true, | ||
353 | |||
354 | .dapm_widgets = tas571x_dapm_widgets, | ||
355 | .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), | ||
356 | .dapm_routes = tas571x_dapm_routes, | ||
357 | .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), | ||
358 | }; | ||
359 | |||
360 | static struct snd_soc_dai_driver tas571x_dai = { | ||
361 | .name = "tas571x-hifi", | ||
362 | .playback = { | ||
363 | .stream_name = "Playback", | ||
364 | .channels_min = 2, | ||
365 | .channels_max = 2, | ||
366 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
367 | .formats = SNDRV_PCM_FMTBIT_S32_LE | | ||
368 | SNDRV_PCM_FMTBIT_S24_LE | | ||
369 | SNDRV_PCM_FMTBIT_S16_LE, | ||
370 | }, | ||
371 | .ops = &tas571x_dai_ops, | ||
372 | }; | ||
373 | |||
374 | static const struct of_device_id tas571x_of_match[]; | ||
375 | |||
376 | static int tas571x_i2c_probe(struct i2c_client *client, | ||
377 | const struct i2c_device_id *id) | ||
378 | { | ||
379 | struct tas571x_private *priv; | ||
380 | struct device *dev = &client->dev; | ||
381 | int i, ret; | ||
382 | |||
383 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
384 | if (!priv) | ||
385 | return -ENOMEM; | ||
386 | i2c_set_clientdata(client, priv); | ||
387 | |||
388 | if (dev->of_node) { | ||
389 | const struct of_device_id *of_id; | ||
390 | |||
391 | of_id = of_match_device(tas571x_of_match, dev); | ||
392 | if (of_id) | ||
393 | priv->chip = of_id->data; | ||
394 | } | ||
395 | |||
396 | if (!priv->chip) { | ||
397 | dev_err(dev, "Unknown device type\n"); | ||
398 | return -EINVAL; | ||
399 | } | ||
400 | |||
401 | priv->mclk = devm_clk_get(dev, "mclk"); | ||
402 | if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { | ||
403 | dev_err(dev, "Failed to request mclk: %ld\n", | ||
404 | PTR_ERR(priv->mclk)); | ||
405 | return PTR_ERR(priv->mclk); | ||
406 | } | ||
407 | |||
408 | BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES); | ||
409 | for (i = 0; i < priv->chip->num_supply_names; i++) | ||
410 | priv->supplies[i].supply = priv->chip->supply_names[i]; | ||
411 | |||
412 | ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names, | ||
413 | priv->supplies); | ||
414 | if (ret) { | ||
415 | dev_err(dev, "Failed to get supplies: %d\n", ret); | ||
416 | return ret; | ||
417 | } | ||
418 | ret = regulator_bulk_enable(priv->chip->num_supply_names, | ||
419 | priv->supplies); | ||
420 | if (ret) { | ||
421 | dev_err(dev, "Failed to enable supplies: %d\n", ret); | ||
422 | return ret; | ||
423 | } | ||
424 | |||
425 | priv->regmap = devm_regmap_init(dev, NULL, client, | ||
426 | priv->chip->regmap_config); | ||
427 | if (IS_ERR(priv->regmap)) | ||
428 | return PTR_ERR(priv->regmap); | ||
429 | |||
430 | priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW); | ||
431 | if (IS_ERR(priv->pdn_gpio)) { | ||
432 | dev_err(dev, "error requesting pdn_gpio: %ld\n", | ||
433 | PTR_ERR(priv->pdn_gpio)); | ||
434 | return PTR_ERR(priv->pdn_gpio); | ||
435 | } | ||
436 | |||
437 | priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", | ||
438 | GPIOD_OUT_HIGH); | ||
439 | if (IS_ERR(priv->reset_gpio)) { | ||
440 | dev_err(dev, "error requesting reset_gpio: %ld\n", | ||
441 | PTR_ERR(priv->reset_gpio)); | ||
442 | return PTR_ERR(priv->reset_gpio); | ||
443 | } else if (priv->reset_gpio) { | ||
444 | /* pulse the active low reset line for ~100us */ | ||
445 | usleep_range(100, 200); | ||
446 | gpiod_set_value(priv->reset_gpio, 0); | ||
447 | usleep_range(12000, 20000); | ||
448 | } | ||
449 | |||
450 | ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); | ||
451 | if (ret) | ||
452 | return ret; | ||
453 | |||
454 | ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG, | ||
455 | TAS571X_SYS_CTRL_2_SDN_MASK, 0); | ||
456 | if (ret) | ||
457 | return ret; | ||
458 | |||
459 | memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); | ||
460 | priv->codec_driver.controls = priv->chip->controls; | ||
461 | priv->codec_driver.num_controls = priv->chip->num_controls; | ||
462 | |||
463 | if (priv->chip->vol_reg_size == 2) { | ||
464 | /* | ||
465 | * The master volume defaults to 0x3ff (mute), but we ignore | ||
466 | * (zero) the LSB because the hardware step size is 0.125 dB | ||
467 | * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB. | ||
468 | */ | ||
469 | ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0); | ||
470 | if (ret) | ||
471 | return ret; | ||
472 | } | ||
473 | |||
474 | regcache_cache_only(priv->regmap, true); | ||
475 | gpiod_set_value(priv->pdn_gpio, 1); | ||
476 | |||
477 | return snd_soc_register_codec(&client->dev, &priv->codec_driver, | ||
478 | &tas571x_dai, 1); | ||
479 | } | ||
480 | |||
481 | static int tas571x_i2c_remove(struct i2c_client *client) | ||
482 | { | ||
483 | struct tas571x_private *priv = i2c_get_clientdata(client); | ||
484 | |||
485 | snd_soc_unregister_codec(&client->dev); | ||
486 | regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static const struct of_device_id tas571x_of_match[] = { | ||
492 | { .compatible = "ti,tas5711", .data = &tas5711_chip, }, | ||
493 | { .compatible = "ti,tas5717", .data = &tas5717_chip, }, | ||
494 | { .compatible = "ti,tas5719", .data = &tas5717_chip, }, | ||
495 | { } | ||
496 | }; | ||
497 | MODULE_DEVICE_TABLE(of, tas571x_of_match); | ||
498 | |||
499 | static const struct i2c_device_id tas571x_i2c_id[] = { | ||
500 | { "tas5711", 0 }, | ||
501 | { "tas5717", 0 }, | ||
502 | { "tas5719", 0 }, | ||
503 | { } | ||
504 | }; | ||
505 | MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); | ||
506 | |||
507 | static struct i2c_driver tas571x_i2c_driver = { | ||
508 | .driver = { | ||
509 | .name = "tas571x", | ||
510 | .of_match_table = of_match_ptr(tas571x_of_match), | ||
511 | }, | ||
512 | .probe = tas571x_i2c_probe, | ||
513 | .remove = tas571x_i2c_remove, | ||
514 | .id_table = tas571x_i2c_id, | ||
515 | }; | ||
516 | module_i2c_driver(tas571x_i2c_driver); | ||
517 | |||
518 | MODULE_DESCRIPTION("ASoC TAS571x driver"); | ||
519 | MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>"); | ||
520 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h new file mode 100644 index 000000000000..0aee471232cd --- /dev/null +++ b/sound/soc/codecs/tas571x.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * TAS571x amplifier audio driver | ||
3 | * | ||
4 | * Copyright (C) 2015 Google, Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #ifndef _TAS571X_H | ||
13 | #define _TAS571X_H | ||
14 | |||
15 | /* device registers */ | ||
16 | #define TAS571X_SDI_REG 0x04 | ||
17 | #define TAS571X_SDI_FMT_MASK 0x0f | ||
18 | |||
19 | #define TAS571X_SYS_CTRL_2_REG 0x05 | ||
20 | #define TAS571X_SYS_CTRL_2_SDN_MASK 0x40 | ||
21 | |||
22 | #define TAS571X_SOFT_MUTE_REG 0x06 | ||
23 | #define TAS571X_SOFT_MUTE_CH1_SHIFT 0 | ||
24 | #define TAS571X_SOFT_MUTE_CH2_SHIFT 1 | ||
25 | #define TAS571X_SOFT_MUTE_CH3_SHIFT 2 | ||
26 | |||
27 | #define TAS571X_MVOL_REG 0x07 | ||
28 | #define TAS571X_CH1_VOL_REG 0x08 | ||
29 | #define TAS571X_CH2_VOL_REG 0x09 | ||
30 | |||
31 | #define TAS571X_OSC_TRIM_REG 0x1b | ||
32 | |||
33 | #endif /* _TAS571X_H */ | ||