diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/tas2552.txt | 26 | ||||
-rw-r--r-- | include/sound/tas2552-plat.h | 25 | ||||
-rw-r--r-- | sound/soc/codecs/Kconfig | 5 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/tas2552.c | 540 | ||||
-rw-r--r-- | sound/soc/codecs/tas2552.h | 129 |
6 files changed, 727 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt new file mode 100644 index 000000000000..55e2a0af5645 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tas2552.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | Texas Instruments - tas2552 Codec module | ||
2 | |||
3 | The tas2552 serial control bus communicates through I2C protocols | ||
4 | |||
5 | Required properties: | ||
6 | - compatible - One of: | ||
7 | "ti,tas2552" - TAS2552 | ||
8 | - reg - I2C slave address | ||
9 | - supply-*: Required supply regulators are: | ||
10 | "vbat" battery voltage | ||
11 | "iovdd" I/O Voltage | ||
12 | "avdd" Analog DAC Voltage | ||
13 | |||
14 | Optional properties: | ||
15 | - enable-gpio - gpio pin to enable/disable the device | ||
16 | |||
17 | Example: | ||
18 | |||
19 | tas2552: tas2552@41 { | ||
20 | compatible = "ti,tas2552"; | ||
21 | reg = <0x41>; | ||
22 | enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>; | ||
23 | }; | ||
24 | |||
25 | For more product information please see the link below: | ||
26 | http://www.ti.com/product/TAS2552 | ||
diff --git a/include/sound/tas2552-plat.h b/include/sound/tas2552-plat.h new file mode 100644 index 000000000000..65e7627ba38e --- /dev/null +++ b/include/sound/tas2552-plat.h | |||
@@ -0,0 +1,25 @@ | |||
1 | /* | ||
2 | * TAS2552 driver platform header | ||
3 | * | ||
4 | * Copyright (C) 2014 Texas Instruments Inc. | ||
5 | * | ||
6 | * Author: Dan Murphy <dmurphy@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 TAS2552_PLAT_H | ||
19 | #define TAS2552_PLAT_H | ||
20 | |||
21 | struct tas2552_platform_data { | ||
22 | int enable_gpio; | ||
23 | }; | ||
24 | |||
25 | #endif | ||
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cbfa1e18f651..480881747ec5 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -91,6 +91,7 @@ config SND_SOC_ALL_CODECS | |||
91 | select SND_SOC_STA350 if I2C | 91 | select SND_SOC_STA350 if I2C |
92 | select SND_SOC_STA529 if I2C | 92 | select SND_SOC_STA529 if I2C |
93 | select SND_SOC_STAC9766 if SND_SOC_AC97_BUS | 93 | select SND_SOC_STAC9766 if SND_SOC_AC97_BUS |
94 | select SND_SOC_TAS2552 if I2C | ||
94 | select SND_SOC_TAS5086 if I2C | 95 | select SND_SOC_TAS5086 if I2C |
95 | select SND_SOC_TLV320AIC23_I2C if I2C | 96 | select SND_SOC_TLV320AIC23_I2C if I2C |
96 | select SND_SOC_TLV320AIC23_SPI if SPI_MASTER | 97 | select SND_SOC_TLV320AIC23_SPI if SPI_MASTER |
@@ -513,6 +514,10 @@ config SND_SOC_STA529 | |||
513 | config SND_SOC_STAC9766 | 514 | config SND_SOC_STAC9766 |
514 | tristate | 515 | tristate |
515 | 516 | ||
517 | config SND_SOC_TAS2552 | ||
518 | tristate "Texas Instruments TAS2552 Mono Audio amplifier" | ||
519 | depends on I2C | ||
520 | |||
516 | config SND_SOC_TAS5086 | 521 | config SND_SOC_TAS5086 |
517 | tristate "Texas Instruments TAS5086 speaker amplifier" | 522 | tristate "Texas Instruments TAS5086 speaker amplifier" |
518 | depends on I2C | 523 | depends on I2C |
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index be3377b8d73f..d79de052ef8e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -160,6 +160,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o | |||
160 | # Amp | 160 | # Amp |
161 | snd-soc-max9877-objs := max9877.o | 161 | snd-soc-max9877-objs := max9877.o |
162 | snd-soc-tpa6130a2-objs := tpa6130a2.o | 162 | snd-soc-tpa6130a2-objs := tpa6130a2.o |
163 | snd-soc-tas2552-objs := tas2552.o | ||
163 | 164 | ||
164 | obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o | 165 | obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o |
165 | obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o | 166 | obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o |
@@ -251,6 +252,7 @@ obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o | |||
251 | obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o | 252 | obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o |
252 | obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o | 253 | obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o |
253 | obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o | 254 | obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o |
255 | obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o | ||
254 | obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o | 256 | obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o |
255 | obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o | 257 | obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o |
256 | obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o | 258 | obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o |
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c new file mode 100644 index 000000000000..f0760af5a21e --- /dev/null +++ b/sound/soc/codecs/tas2552.c | |||
@@ -0,0 +1,540 @@ | |||
1 | /* | ||
2 | * tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier | ||
3 | * | ||
4 | * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com | ||
5 | * | ||
6 | * Author: Dan Murphy <dmurphy@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/gpio.h> | ||
23 | #include <linux/of_gpio.h> | ||
24 | #include <linux/pm_runtime.h> | ||
25 | #include <linux/regmap.h> | ||
26 | #include <linux/slab.h> | ||
27 | |||
28 | #include <linux/gpio/consumer.h> | ||
29 | #include <linux/regulator/consumer.h> | ||
30 | |||
31 | #include <sound/pcm.h> | ||
32 | #include <sound/pcm_params.h> | ||
33 | #include <sound/soc.h> | ||
34 | #include <sound/soc-dapm.h> | ||
35 | #include <sound/tlv.h> | ||
36 | #include <sound/tas2552-plat.h> | ||
37 | |||
38 | #include "tas2552.h" | ||
39 | |||
40 | static struct reg_default tas2552_reg_defs[] = { | ||
41 | {TAS2552_CFG_1, 0x22}, | ||
42 | {TAS2552_CFG_3, 0x80}, | ||
43 | {TAS2552_DOUT, 0x00}, | ||
44 | {TAS2552_OUTPUT_DATA, 0xc0}, | ||
45 | {TAS2552_PDM_CFG, 0x01}, | ||
46 | {TAS2552_PGA_GAIN, 0x00}, | ||
47 | {TAS2552_BOOST_PT_CTRL, 0x0f}, | ||
48 | {TAS2552_RESERVED_0D, 0x00}, | ||
49 | {TAS2552_LIMIT_RATE_HYS, 0x08}, | ||
50 | {TAS2552_CFG_2, 0xef}, | ||
51 | {TAS2552_SER_CTRL_1, 0x00}, | ||
52 | {TAS2552_SER_CTRL_2, 0x00}, | ||
53 | {TAS2552_PLL_CTRL_1, 0x10}, | ||
54 | {TAS2552_PLL_CTRL_2, 0x00}, | ||
55 | {TAS2552_PLL_CTRL_3, 0x00}, | ||
56 | {TAS2552_BTIP, 0x8f}, | ||
57 | {TAS2552_BTS_CTRL, 0x80}, | ||
58 | {TAS2552_LIMIT_RELEASE, 0x04}, | ||
59 | {TAS2552_LIMIT_INT_COUNT, 0x00}, | ||
60 | {TAS2552_EDGE_RATE_CTRL, 0x40}, | ||
61 | {TAS2552_VBAT_DATA, 0x00}, | ||
62 | }; | ||
63 | |||
64 | #define TAS2552_NUM_SUPPLIES 3 | ||
65 | static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = { | ||
66 | "vbat", /* vbat voltage */ | ||
67 | "iovdd", /* I/O Voltage */ | ||
68 | "avdd", /* Analog DAC Voltage */ | ||
69 | }; | ||
70 | |||
71 | struct tas2552_data { | ||
72 | struct snd_soc_codec *codec; | ||
73 | struct regmap *regmap; | ||
74 | struct i2c_client *tas2552_client; | ||
75 | struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES]; | ||
76 | struct gpio_desc *enable_gpio; | ||
77 | unsigned char regs[TAS2552_VBAT_DATA]; | ||
78 | unsigned int mclk; | ||
79 | }; | ||
80 | |||
81 | static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown) | ||
82 | { | ||
83 | u8 cfg1_reg; | ||
84 | |||
85 | if (sw_shutdown) | ||
86 | cfg1_reg = 0; | ||
87 | else | ||
88 | cfg1_reg = TAS2552_SWS_MASK; | ||
89 | |||
90 | snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, | ||
91 | TAS2552_SWS_MASK, cfg1_reg); | ||
92 | } | ||
93 | |||
94 | static int tas2552_hw_params(struct snd_pcm_substream *substream, | ||
95 | struct snd_pcm_hw_params *params, | ||
96 | struct snd_soc_dai *dai) | ||
97 | { | ||
98 | struct snd_soc_codec *codec = dai->codec; | ||
99 | struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); | ||
100 | int sample_rate, pll_clk; | ||
101 | int d; | ||
102 | u8 p, j; | ||
103 | |||
104 | /* Turn on Class D amplifier */ | ||
105 | snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN_MASK, | ||
106 | TAS2552_CLASSD_EN); | ||
107 | |||
108 | if (!tas2552->mclk) | ||
109 | return -EINVAL; | ||
110 | |||
111 | snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); | ||
112 | |||
113 | if (tas2552->mclk == TAS2552_245MHZ_CLK || | ||
114 | tas2552->mclk == TAS2552_225MHZ_CLK) { | ||
115 | /* By pass the PLL configuration */ | ||
116 | snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, | ||
117 | TAS2552_PLL_BYPASS_MASK, | ||
118 | TAS2552_PLL_BYPASS); | ||
119 | } else { | ||
120 | /* Fill in the PLL control registers for J & D | ||
121 | * PLL_CLK = (.5 * freq * J.D) / 2^p | ||
122 | * Need to fill in J and D here based on incoming freq | ||
123 | */ | ||
124 | p = snd_soc_read(codec, TAS2552_PLL_CTRL_1); | ||
125 | p = (p >> 7); | ||
126 | sample_rate = params_rate(params); | ||
127 | |||
128 | if (sample_rate == 48000) | ||
129 | pll_clk = TAS2552_245MHZ_CLK; | ||
130 | else if (sample_rate == 44100) | ||
131 | pll_clk = TAS2552_225MHZ_CLK; | ||
132 | else { | ||
133 | dev_vdbg(codec->dev, "Substream sample rate is not found %i\n", | ||
134 | params_rate(params)); | ||
135 | return -EINVAL; | ||
136 | } | ||
137 | |||
138 | j = (pll_clk * 2 * (1 << p)) / tas2552->mclk; | ||
139 | d = (pll_clk * 2 * (1 << p)) % tas2552->mclk; | ||
140 | |||
141 | snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, | ||
142 | TAS2552_PLL_J_MASK, j); | ||
143 | snd_soc_write(codec, TAS2552_PLL_CTRL_2, | ||
144 | (d >> 7) & TAS2552_PLL_D_UPPER_MASK); | ||
145 | snd_soc_write(codec, TAS2552_PLL_CTRL_3, | ||
146 | d & TAS2552_PLL_D_LOWER_MASK); | ||
147 | |||
148 | } | ||
149 | |||
150 | snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, | ||
151 | TAS2552_PLL_ENABLE); | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) | ||
157 | { | ||
158 | struct snd_soc_codec *codec = dai->codec; | ||
159 | u8 serial_format; | ||
160 | u8 serial_control_mask; | ||
161 | |||
162 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
163 | case SND_SOC_DAIFMT_CBS_CFS: | ||
164 | serial_format = 0x00; | ||
165 | break; | ||
166 | case SND_SOC_DAIFMT_CBS_CFM: | ||
167 | serial_format = TAS2552_WORD_CLK_MASK; | ||
168 | break; | ||
169 | case SND_SOC_DAIFMT_CBM_CFS: | ||
170 | serial_format = TAS2552_BIT_CLK_MASK; | ||
171 | break; | ||
172 | case SND_SOC_DAIFMT_CBM_CFM: | ||
173 | serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK); | ||
174 | break; | ||
175 | default: | ||
176 | dev_vdbg(codec->dev, "DAI Format master is not found\n"); | ||
177 | return -EINVAL; | ||
178 | } | ||
179 | |||
180 | serial_control_mask = TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK; | ||
181 | |||
182 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
183 | case SND_SOC_DAIFMT_I2S: | ||
184 | serial_format &= TAS2552_DAIFMT_I2S_MASK; | ||
185 | break; | ||
186 | case SND_SOC_DAIFMT_DSP_A: | ||
187 | serial_format |= TAS2552_DAIFMT_DSP; | ||
188 | break; | ||
189 | case SND_SOC_DAIFMT_RIGHT_J: | ||
190 | serial_format |= TAS2552_DAIFMT_RIGHT_J; | ||
191 | break; | ||
192 | case SND_SOC_DAIFMT_LEFT_J: | ||
193 | serial_format |= TAS2552_DAIFMT_LEFT_J; | ||
194 | break; | ||
195 | default: | ||
196 | dev_vdbg(codec->dev, "DAI Format is not found\n"); | ||
197 | return -EINVAL; | ||
198 | } | ||
199 | |||
200 | if (fmt & SND_SOC_DAIFMT_FORMAT_MASK) | ||
201 | serial_control_mask |= TAS2552_DATA_FORMAT_MASK; | ||
202 | |||
203 | snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, serial_control_mask, | ||
204 | serial_format); | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
210 | unsigned int freq, int dir) | ||
211 | { | ||
212 | struct snd_soc_codec *codec = dai->codec; | ||
213 | struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); | ||
214 | |||
215 | tas2552->mclk = freq; | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int tas2552_mute(struct snd_soc_dai *dai, int mute) | ||
221 | { | ||
222 | u8 cfg1_reg; | ||
223 | struct snd_soc_codec *codec = dai->codec; | ||
224 | |||
225 | if (mute) | ||
226 | cfg1_reg = TAS2552_MUTE_MASK; | ||
227 | else | ||
228 | cfg1_reg = ~TAS2552_MUTE_MASK; | ||
229 | |||
230 | snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, cfg1_reg); | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | #ifdef CONFIG_PM_RUNTIME | ||
236 | static int tas2552_runtime_suspend(struct device *dev) | ||
237 | { | ||
238 | struct tas2552_data *tas2552 = dev_get_drvdata(dev); | ||
239 | |||
240 | tas2552_sw_shutdown(tas2552, 0); | ||
241 | |||
242 | if (tas2552->enable_gpio) | ||
243 | gpiod_set_value(tas2552->enable_gpio, 0); | ||
244 | |||
245 | regcache_cache_only(tas2552->regmap, true); | ||
246 | regcache_mark_dirty(tas2552->regmap); | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static int tas2552_runtime_resume(struct device *dev) | ||
252 | { | ||
253 | struct tas2552_data *tas2552 = dev_get_drvdata(dev); | ||
254 | |||
255 | if (tas2552->enable_gpio) | ||
256 | gpiod_set_value(tas2552->enable_gpio, 1); | ||
257 | |||
258 | tas2552_sw_shutdown(tas2552, 1); | ||
259 | |||
260 | regcache_cache_only(tas2552->regmap, false); | ||
261 | regcache_sync(tas2552->regmap); | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | #endif | ||
266 | |||
267 | static const struct dev_pm_ops tas2552_pm = { | ||
268 | SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume, | ||
269 | NULL) | ||
270 | }; | ||
271 | |||
272 | static void tas2552_shutdown(struct snd_pcm_substream *substream, | ||
273 | struct snd_soc_dai *dai) | ||
274 | { | ||
275 | struct snd_soc_codec *codec = dai->codec; | ||
276 | |||
277 | snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); | ||
278 | } | ||
279 | |||
280 | static struct snd_soc_dai_ops tas2552_speaker_dai_ops = { | ||
281 | .hw_params = tas2552_hw_params, | ||
282 | .set_sysclk = tas2552_set_dai_sysclk, | ||
283 | .set_fmt = tas2552_set_dai_fmt, | ||
284 | .shutdown = tas2552_shutdown, | ||
285 | .digital_mute = tas2552_mute, | ||
286 | }; | ||
287 | |||
288 | /* Formats supported by TAS2552 driver. */ | ||
289 | #define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | ||
290 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | ||
291 | |||
292 | /* TAS2552 dai structure. */ | ||
293 | static struct snd_soc_dai_driver tas2552_dai[] = { | ||
294 | { | ||
295 | .name = "tas2552-amplifier", | ||
296 | .playback = { | ||
297 | .stream_name = "Speaker", | ||
298 | .channels_min = 2, | ||
299 | .channels_max = 2, | ||
300 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
301 | .formats = TAS2552_FORMATS, | ||
302 | }, | ||
303 | .ops = &tas2552_speaker_dai_ops, | ||
304 | }, | ||
305 | }; | ||
306 | |||
307 | /* | ||
308 | * DAC digital volumes. From -7 to 24 dB in 1 dB steps | ||
309 | */ | ||
310 | static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24); | ||
311 | |||
312 | static const struct snd_kcontrol_new tas2552_snd_controls[] = { | ||
313 | SOC_SINGLE_TLV("Speaker Driver Playback Volume", | ||
314 | TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv), | ||
315 | }; | ||
316 | |||
317 | static const struct reg_default tas2552_init_regs[] = { | ||
318 | { TAS2552_RESERVED_0D, 0xc0 }, | ||
319 | }; | ||
320 | |||
321 | static int tas2552_codec_probe(struct snd_soc_codec *codec) | ||
322 | { | ||
323 | struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); | ||
324 | int ret; | ||
325 | |||
326 | tas2552->codec = codec; | ||
327 | |||
328 | ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies), | ||
329 | tas2552->supplies); | ||
330 | |||
331 | if (ret != 0) { | ||
332 | dev_err(codec->dev, "Failed to enable supplies: %d\n", | ||
333 | ret); | ||
334 | return ret; | ||
335 | } | ||
336 | |||
337 | if (tas2552->enable_gpio) | ||
338 | gpiod_set_value(tas2552->enable_gpio, 1); | ||
339 | |||
340 | ret = pm_runtime_get_sync(codec->dev); | ||
341 | if (ret < 0) { | ||
342 | dev_err(codec->dev, "Enabling device failed: %d\n", | ||
343 | ret); | ||
344 | goto probe_fail; | ||
345 | } | ||
346 | |||
347 | snd_soc_write(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK | | ||
348 | TAS2552_PLL_SRC_BCLK); | ||
349 | snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL | | ||
350 | TAS2552_DIN_SRC_SEL_AVG_L_R | TAS2552_88_96KHZ); | ||
351 | snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I); | ||
352 | snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8); | ||
353 | snd_soc_write(codec, TAS2552_PDM_CFG, TAS2552_PDM_BCLK_SEL); | ||
354 | snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 | | ||
355 | TAS2552_APT_THRESH_2_1_7); | ||
356 | |||
357 | ret = regmap_register_patch(tas2552->regmap, tas2552_init_regs, | ||
358 | ARRAY_SIZE(tas2552_init_regs)); | ||
359 | if (ret != 0) { | ||
360 | dev_err(codec->dev, "Failed to write init registers: %d\n", | ||
361 | ret); | ||
362 | goto patch_fail; | ||
363 | } | ||
364 | |||
365 | snd_soc_write(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN | | ||
366 | TAS2552_BOOST_EN | TAS2552_APT_EN | | ||
367 | TAS2552_LIM_EN); | ||
368 | return 0; | ||
369 | |||
370 | patch_fail: | ||
371 | pm_runtime_put(codec->dev); | ||
372 | probe_fail: | ||
373 | if (tas2552->enable_gpio) | ||
374 | gpiod_set_value(tas2552->enable_gpio, 0); | ||
375 | |||
376 | regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), | ||
377 | tas2552->supplies); | ||
378 | return -EIO; | ||
379 | } | ||
380 | |||
381 | static int tas2552_codec_remove(struct snd_soc_codec *codec) | ||
382 | { | ||
383 | struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); | ||
384 | |||
385 | if (tas2552->enable_gpio) | ||
386 | gpiod_set_value(tas2552->enable_gpio, 0); | ||
387 | |||
388 | return 0; | ||
389 | }; | ||
390 | |||
391 | #ifdef CONFIG_PM | ||
392 | static int tas2552_suspend(struct snd_soc_codec *codec) | ||
393 | { | ||
394 | struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); | ||
395 | int ret; | ||
396 | |||
397 | ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), | ||
398 | tas2552->supplies); | ||
399 | |||
400 | if (ret != 0) | ||
401 | dev_err(codec->dev, "Failed to disable supplies: %d\n", | ||
402 | ret); | ||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | static int tas2552_resume(struct snd_soc_codec *codec) | ||
407 | { | ||
408 | struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); | ||
409 | int ret; | ||
410 | |||
411 | ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies), | ||
412 | tas2552->supplies); | ||
413 | |||
414 | if (ret != 0) { | ||
415 | dev_err(codec->dev, "Failed to enable supplies: %d\n", | ||
416 | ret); | ||
417 | } | ||
418 | |||
419 | return 0; | ||
420 | } | ||
421 | #else | ||
422 | #define tas2552_suspend NULL | ||
423 | #define tas2552_resume NULL | ||
424 | #endif | ||
425 | |||
426 | static struct snd_soc_codec_driver soc_codec_dev_tas2552 = { | ||
427 | .probe = tas2552_codec_probe, | ||
428 | .remove = tas2552_codec_remove, | ||
429 | .suspend = tas2552_suspend, | ||
430 | .resume = tas2552_resume, | ||
431 | .controls = tas2552_snd_controls, | ||
432 | .num_controls = ARRAY_SIZE(tas2552_snd_controls), | ||
433 | }; | ||
434 | |||
435 | static const struct regmap_config tas2552_regmap_config = { | ||
436 | .reg_bits = 8, | ||
437 | .val_bits = 8, | ||
438 | |||
439 | .max_register = TAS2552_MAX_REG, | ||
440 | .reg_defaults = tas2552_reg_defs, | ||
441 | .num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs), | ||
442 | .cache_type = REGCACHE_RBTREE, | ||
443 | }; | ||
444 | |||
445 | static int tas2552_probe(struct i2c_client *client, | ||
446 | const struct i2c_device_id *id) | ||
447 | { | ||
448 | struct device *dev; | ||
449 | struct tas2552_data *data; | ||
450 | int ret; | ||
451 | int i; | ||
452 | |||
453 | dev = &client->dev; | ||
454 | data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); | ||
455 | if (data == NULL) | ||
456 | return -ENOMEM; | ||
457 | |||
458 | data->enable_gpio = devm_gpiod_get(dev, "enable"); | ||
459 | if (IS_ERR(data->enable_gpio)) { | ||
460 | ret = PTR_ERR(data->enable_gpio); | ||
461 | if (ret != -ENOENT && ret != -ENOSYS) | ||
462 | return ret; | ||
463 | |||
464 | data->enable_gpio = NULL; | ||
465 | } else { | ||
466 | gpiod_direction_output(data->enable_gpio, 0); | ||
467 | } | ||
468 | |||
469 | data->tas2552_client = client; | ||
470 | data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); | ||
471 | if (IS_ERR(data->regmap)) { | ||
472 | ret = PTR_ERR(data->regmap); | ||
473 | dev_err(&client->dev, "Failed to allocate register map: %d\n", | ||
474 | ret); | ||
475 | return ret; | ||
476 | } | ||
477 | |||
478 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) | ||
479 | data->supplies[i].supply = tas2552_supply_names[i]; | ||
480 | |||
481 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), | ||
482 | data->supplies); | ||
483 | if (ret != 0) | ||
484 | dev_err(dev, "Failed to request supplies: %d\n", ret); | ||
485 | |||
486 | pm_runtime_set_active(&client->dev); | ||
487 | pm_runtime_set_autosuspend_delay(&client->dev, 1000); | ||
488 | pm_runtime_use_autosuspend(&client->dev); | ||
489 | pm_runtime_enable(&client->dev); | ||
490 | pm_runtime_mark_last_busy(&client->dev); | ||
491 | pm_runtime_put_sync_autosuspend(&client->dev); | ||
492 | |||
493 | dev_set_drvdata(&client->dev, data); | ||
494 | |||
495 | ret = snd_soc_register_codec(&client->dev, | ||
496 | &soc_codec_dev_tas2552, | ||
497 | tas2552_dai, ARRAY_SIZE(tas2552_dai)); | ||
498 | if (ret < 0) | ||
499 | dev_err(&client->dev, "Failed to register codec: %d\n", ret); | ||
500 | |||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | static int tas2552_i2c_remove(struct i2c_client *client) | ||
505 | { | ||
506 | snd_soc_unregister_codec(&client->dev); | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | static const struct i2c_device_id tas2552_id[] = { | ||
511 | { "tas2552", 0 }, | ||
512 | { } | ||
513 | }; | ||
514 | MODULE_DEVICE_TABLE(i2c, tas2552_id); | ||
515 | |||
516 | #if IS_ENABLED(CONFIG_OF) | ||
517 | static const struct of_device_id tas2552_of_match[] = { | ||
518 | { .compatible = "ti,tas2552", }, | ||
519 | {}, | ||
520 | }; | ||
521 | MODULE_DEVICE_TABLE(of, tas2552_of_match); | ||
522 | #endif | ||
523 | |||
524 | static struct i2c_driver tas2552_i2c_driver = { | ||
525 | .driver = { | ||
526 | .name = "tas2552", | ||
527 | .owner = THIS_MODULE, | ||
528 | .of_match_table = of_match_ptr(tas2552_of_match), | ||
529 | .pm = &tas2552_pm, | ||
530 | }, | ||
531 | .probe = tas2552_probe, | ||
532 | .remove = tas2552_i2c_remove, | ||
533 | .id_table = tas2552_id, | ||
534 | }; | ||
535 | |||
536 | module_i2c_driver(tas2552_i2c_driver); | ||
537 | |||
538 | MODULE_AUTHOR("Dan Muprhy <dmurphy@ti.com>"); | ||
539 | MODULE_DESCRIPTION("TAS2552 Audio amplifier driver"); | ||
540 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h new file mode 100644 index 000000000000..6cea8f31bf88 --- /dev/null +++ b/sound/soc/codecs/tas2552.h | |||
@@ -0,0 +1,129 @@ | |||
1 | /* | ||
2 | * tas2552.h - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier | ||
3 | * | ||
4 | * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com | ||
5 | * | ||
6 | * Author: Dan Murphy <dmurphy@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 __TAS2552_H__ | ||
19 | #define __TAS2552_H__ | ||
20 | |||
21 | /* Register Address Map */ | ||
22 | #define TAS2552_DEVICE_STATUS 0x00 | ||
23 | #define TAS2552_CFG_1 0x01 | ||
24 | #define TAS2552_CFG_2 0x02 | ||
25 | #define TAS2552_CFG_3 0x03 | ||
26 | #define TAS2552_DOUT 0x04 | ||
27 | #define TAS2552_SER_CTRL_1 0x05 | ||
28 | #define TAS2552_SER_CTRL_2 0x06 | ||
29 | #define TAS2552_OUTPUT_DATA 0x07 | ||
30 | #define TAS2552_PLL_CTRL_1 0x08 | ||
31 | #define TAS2552_PLL_CTRL_2 0x09 | ||
32 | #define TAS2552_PLL_CTRL_3 0x0a | ||
33 | #define TAS2552_BTIP 0x0b | ||
34 | #define TAS2552_BTS_CTRL 0x0c | ||
35 | #define TAS2552_RESERVED_0D 0x0d | ||
36 | #define TAS2552_LIMIT_RATE_HYS 0x0e | ||
37 | #define TAS2552_LIMIT_RELEASE 0x0f | ||
38 | #define TAS2552_LIMIT_INT_COUNT 0x10 | ||
39 | #define TAS2552_PDM_CFG 0x11 | ||
40 | #define TAS2552_PGA_GAIN 0x12 | ||
41 | #define TAS2552_EDGE_RATE_CTRL 0x13 | ||
42 | #define TAS2552_BOOST_PT_CTRL 0x14 | ||
43 | #define TAS2552_VER_NUM 0x16 | ||
44 | #define TAS2552_VBAT_DATA 0x19 | ||
45 | #define TAS2552_MAX_REG 0x20 | ||
46 | |||
47 | /* CFG1 Register Masks */ | ||
48 | #define TAS2552_MUTE_MASK (1 << 2) | ||
49 | #define TAS2552_SWS_MASK (1 << 1) | ||
50 | #define TAS2552_WCLK_MASK 0x07 | ||
51 | #define TAS2552_CLASSD_EN_MASK (1 << 7) | ||
52 | |||
53 | /* CFG2 Register Masks */ | ||
54 | #define TAS2552_CLASSD_EN (1 << 7) | ||
55 | #define TAS2552_BOOST_EN (1 << 6) | ||
56 | #define TAS2552_APT_EN (1 << 5) | ||
57 | #define TAS2552_PLL_ENABLE (1 << 3) | ||
58 | #define TAS2552_LIM_EN (1 << 2) | ||
59 | #define TAS2552_IVSENSE_EN (1 << 1) | ||
60 | |||
61 | /* CFG3 Register Masks */ | ||
62 | #define TAS2552_WORD_CLK_MASK (1 << 7) | ||
63 | #define TAS2552_BIT_CLK_MASK (1 << 6) | ||
64 | #define TAS2552_DATA_FORMAT_MASK (0x11 << 2) | ||
65 | |||
66 | #define TAS2552_DAIFMT_I2S_MASK 0xf3 | ||
67 | #define TAS2552_DAIFMT_DSP (1 << 3) | ||
68 | #define TAS2552_DAIFMT_RIGHT_J (1 << 4) | ||
69 | #define TAS2552_DAIFMT_LEFT_J (0x11 << 3) | ||
70 | |||
71 | #define TAS2552_PLL_SRC_MCLK 0x00 | ||
72 | #define TAS2552_PLL_SRC_BCLK (1 << 3) | ||
73 | #define TAS2552_PLL_SRC_IVCLKIN (1 << 4) | ||
74 | #define TAS2552_PLL_SRC_1_8_FIXED (0x11 << 3) | ||
75 | |||
76 | #define TAS2552_DIN_SRC_SEL_MUTED 0x00 | ||
77 | #define TAS2552_DIN_SRC_SEL_LEFT (1 << 4) | ||
78 | #define TAS2552_DIN_SRC_SEL_RIGHT (1 << 5) | ||
79 | #define TAS2552_DIN_SRC_SEL_AVG_L_R (0x11 << 4) | ||
80 | |||
81 | #define TAS2552_PDM_IN_SEL (1 << 5) | ||
82 | #define TAS2552_I2S_OUT_SEL (1 << 6) | ||
83 | #define TAS2552_ANALOG_IN_SEL (1 << 7) | ||
84 | |||
85 | /* CFG3 WCLK Dividers */ | ||
86 | #define TAS2552_8KHZ 0x00 | ||
87 | #define TAS2552_11_12KHZ (1 << 1) | ||
88 | #define TAS2552_16KHZ (1 << 2) | ||
89 | #define TAS2552_22_24KHZ (1 << 3) | ||
90 | #define TAS2552_32KHZ (1 << 4) | ||
91 | #define TAS2552_44_48KHZ (1 << 5) | ||
92 | #define TAS2552_88_96KHZ (1 << 6) | ||
93 | #define TAS2552_176_192KHZ (1 << 7) | ||
94 | |||
95 | /* OUTPUT_DATA register */ | ||
96 | #define TAS2552_PDM_DATA_I 0x00 | ||
97 | #define TAS2552_PDM_DATA_V (1 << 6) | ||
98 | #define TAS2552_PDM_DATA_I_V (1 << 7) | ||
99 | #define TAS2552_PDM_DATA_V_I (0x11 << 6) | ||
100 | |||
101 | /* PDM CFG Register */ | ||
102 | #define TAS2552_PDM_DATA_ES_RISE 0x4 | ||
103 | |||
104 | #define TAS2552_PDM_PLL_CLK_SEL 0x00 | ||
105 | #define TAS2552_PDM_IV_CLK_SEL (1 << 1) | ||
106 | #define TAS2552_PDM_BCLK_SEL (1 << 2) | ||
107 | #define TAS2552_PDM_MCLK_SEL (1 << 3) | ||
108 | |||
109 | /* Boost pass-through register */ | ||
110 | #define TAS2552_APT_DELAY_50 0x00 | ||
111 | #define TAS2552_APT_DELAY_75 (1 << 1) | ||
112 | #define TAS2552_APT_DELAY_125 (1 << 2) | ||
113 | #define TAS2552_APT_DELAY_200 (1 << 3) | ||
114 | |||
115 | #define TAS2552_APT_THRESH_2_5 0x00 | ||
116 | #define TAS2552_APT_THRESH_1_7 (1 << 3) | ||
117 | #define TAS2552_APT_THRESH_1_4_1_1 (1 << 4) | ||
118 | #define TAS2552_APT_THRESH_2_1_7 (0x11 << 2) | ||
119 | |||
120 | /* PLL Control Register */ | ||
121 | #define TAS2552_245MHZ_CLK 24576000 | ||
122 | #define TAS2552_225MHZ_CLK 22579200 | ||
123 | #define TAS2552_PLL_J_MASK 0x7f | ||
124 | #define TAS2552_PLL_D_UPPER_MASK 0x3f | ||
125 | #define TAS2552_PLL_D_LOWER_MASK 0xff | ||
126 | #define TAS2552_PLL_BYPASS_MASK 0x80 | ||
127 | #define TAS2552_PLL_BYPASS 0x80 | ||
128 | |||
129 | #endif | ||