aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@linaro.org>2014-03-12 19:04:08 -0400
committerMark Brown <broonie@linaro.org>2014-03-12 19:04:08 -0400
commit52db65f0a8da885d591d75186f68bc15de0323c7 (patch)
tree8155fe9d473da90d135acf4f54a0e1d156fed173
parent3b93f0598c30a4d14b72223023fa56315eab15a4 (diff)
parente97db9abf99280e6ff7d9b339dd2ca4846ce2eea (diff)
Merge remote-tracking branch 'asoc/topic/pcm512x' into asoc-next
-rw-r--r--Documentation/devicetree/bindings/sound/pcm512x.txt30
-rw-r--r--sound/soc/codecs/Kconfig17
-rw-r--r--sound/soc/codecs/Makefile6
-rw-r--r--sound/soc/codecs/pcm512x-i2c.c71
-rw-r--r--sound/soc/codecs/pcm512x-spi.c69
-rw-r--r--sound/soc/codecs/pcm512x.c589
-rw-r--r--sound/soc/codecs/pcm512x.h171
7 files changed, 953 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt
new file mode 100644
index 000000000000..faff75e64573
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/pcm512x.txt
@@ -0,0 +1,30 @@
1PCM512x audio CODECs
2
3These devices support both I2C and SPI (configured with pin strapping
4on the board).
5
6Required properties:
7
8 - compatible : One of "ti,pcm5121" or "ti,pcm5122"
9
10 - reg : the I2C address of the device for I2C, the chip select
11 number for SPI.
12
13 - AVDD-supply, DVDD-supply, and CPVDD-supply : power supplies for the
14 device, as covered in bindings/regulator/regulator.txt
15
16Optional properties:
17
18 - clocks : A clock specifier for the clock connected as SCLK. If this
19 is absent the device will be configured to clock from BCLK.
20
21Example:
22
23 pcm5122: pcm5122@4c {
24 compatible = "ti,pcm5122";
25 reg = <0x4c>;
26
27 AVDD-supply = <&reg_3v3_analog>;
28 DVDD-supply = <&reg_1v8>;
29 CPVDD-supply = <&reg_3v3>;
30 };
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 983d087aa92a..cebe3ceef4bd 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -59,6 +59,8 @@ config SND_SOC_ALL_CODECS
59 select SND_SOC_PCM1681 if I2C 59 select SND_SOC_PCM1681 if I2C
60 select SND_SOC_PCM1792A if SPI_MASTER 60 select SND_SOC_PCM1792A if SPI_MASTER
61 select SND_SOC_PCM3008 61 select SND_SOC_PCM3008
62 select SND_SOC_PCM512x_I2C if I2C
63 select SND_SOC_PCM512x_SPI if SPI_MASTER
62 select SND_SOC_RT5631 if I2C 64 select SND_SOC_RT5631 if I2C
63 select SND_SOC_RT5640 if I2C 65 select SND_SOC_RT5640 if I2C
64 select SND_SOC_SGTL5000 if I2C 66 select SND_SOC_SGTL5000 if I2C
@@ -313,6 +315,21 @@ config SND_SOC_PCM1792A
313config SND_SOC_PCM3008 315config SND_SOC_PCM3008
314 tristate 316 tristate
315 317
318config SND_SOC_PCM512x
319 tristate
320
321config SND_SOC_PCM512x_I2C
322 tristate "Texas Instruments PCM512x CODECs - I2C"
323 depends on I2C
324 select SND_SOC_PCM512x
325 select REGMAP_I2C
326
327config SND_SOC_PCM512x_SPI
328 tristate "Texas Instruments PCM512x CODECs - SPI"
329 depends on SPI_MASTER
330 select SND_SOC_PCM512x
331 select REGMAP_SPI
332
316config SND_SOC_RT5631 333config SND_SOC_RT5631
317 tristate 334 tristate
318 335
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index bc126764a44d..c1191c05c88e 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -46,6 +46,9 @@ snd-soc-hdmi-codec-objs := hdmi.o
46snd-soc-pcm1681-objs := pcm1681.o 46snd-soc-pcm1681-objs := pcm1681.o
47snd-soc-pcm1792a-codec-objs := pcm1792a.o 47snd-soc-pcm1792a-codec-objs := pcm1792a.o
48snd-soc-pcm3008-objs := pcm3008.o 48snd-soc-pcm3008-objs := pcm3008.o
49snd-soc-pcm512x-objs := pcm512x.o
50snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
51snd-soc-pcm512x-spi-objs := pcm512x-spi.o
49snd-soc-rt5631-objs := rt5631.o 52snd-soc-rt5631-objs := rt5631.o
50snd-soc-rt5640-objs := rt5640.o 53snd-soc-rt5640-objs := rt5640.o
51snd-soc-sgtl5000-objs := sgtl5000.o 54snd-soc-sgtl5000-objs := sgtl5000.o
@@ -179,6 +182,9 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
179obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o 182obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
180obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o 183obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
181obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o 184obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
185obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
186obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
187obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
182obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o 188obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
183obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o 189obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
184obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o 190obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c
new file mode 100644
index 000000000000..4d62230bd378
--- /dev/null
+++ b/sound/soc/codecs/pcm512x-i2c.c
@@ -0,0 +1,71 @@
1/*
2 * Driver for the PCM512x CODECs
3 *
4 * Author: Mark Brown <broonie@linaro.org>
5 * Copyright 2014 Linaro Ltd
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 */
16
17#include <linux/init.h>
18#include <linux/module.h>
19#include <linux/i2c.h>
20
21#include "pcm512x.h"
22
23static int pcm512x_i2c_probe(struct i2c_client *i2c,
24 const struct i2c_device_id *id)
25{
26 struct regmap *regmap;
27
28 regmap = devm_regmap_init_i2c(i2c, &pcm512x_regmap);
29 if (IS_ERR(regmap))
30 return PTR_ERR(regmap);
31
32 return pcm512x_probe(&i2c->dev, regmap);
33}
34
35static int pcm512x_i2c_remove(struct i2c_client *i2c)
36{
37 pcm512x_remove(&i2c->dev);
38 return 0;
39}
40
41static const struct i2c_device_id pcm512x_i2c_id[] = {
42 { "pcm5121", },
43 { "pcm5122", },
44 { }
45};
46MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
47
48static const struct of_device_id pcm512x_of_match[] = {
49 { .compatible = "ti,pcm5121", },
50 { .compatible = "ti,pcm5122", },
51 { }
52};
53MODULE_DEVICE_TABLE(of, pcm512x_of_match);
54
55static struct i2c_driver pcm512x_i2c_driver = {
56 .probe = pcm512x_i2c_probe,
57 .remove = pcm512x_i2c_remove,
58 .id_table = pcm512x_i2c_id,
59 .driver = {
60 .name = "pcm512x",
61 .owner = THIS_MODULE,
62 .of_match_table = pcm512x_of_match,
63 .pm = &pcm512x_pm_ops,
64 },
65};
66
67module_i2c_driver(pcm512x_i2c_driver);
68
69MODULE_DESCRIPTION("ASoC PCM512x codec driver - I2C");
70MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
71MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c
new file mode 100644
index 000000000000..f297058c0038
--- /dev/null
+++ b/sound/soc/codecs/pcm512x-spi.c
@@ -0,0 +1,69 @@
1/*
2 * Driver for the PCM512x CODECs
3 *
4 * Author: Mark Brown <broonie@linaro.org>
5 * Copyright 2014 Linaro Ltd
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 */
16
17#include <linux/init.h>
18#include <linux/module.h>
19#include <linux/spi/spi.h>
20
21#include "pcm512x.h"
22
23static int pcm512x_spi_probe(struct spi_device *spi)
24{
25 struct regmap *regmap;
26 int ret;
27
28 regmap = devm_regmap_init_spi(spi, &pcm512x_regmap);
29 if (IS_ERR(regmap)) {
30 ret = PTR_ERR(regmap);
31 return ret;
32 }
33
34 return pcm512x_probe(&spi->dev, regmap);
35}
36
37static int pcm512x_spi_remove(struct spi_device *spi)
38{
39 pcm512x_remove(&spi->dev);
40 return 0;
41}
42
43static const struct spi_device_id pcm512x_spi_id[] = {
44 { "pcm5121", },
45 { "pcm5122", },
46 { },
47};
48MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
49
50static const struct of_device_id pcm512x_of_match[] = {
51 { .compatible = "ti,pcm5121", },
52 { .compatible = "ti,pcm5122", },
53 { }
54};
55MODULE_DEVICE_TABLE(of, pcm512x_of_match);
56
57static struct spi_driver pcm512x_spi_driver = {
58 .probe = pcm512x_spi_probe,
59 .remove = pcm512x_spi_remove,
60 .id_table = pcm512x_spi_id,
61 .driver = {
62 .name = "pcm512x",
63 .owner = THIS_MODULE,
64 .of_match_table = pcm512x_of_match,
65 .pm = &pcm512x_pm_ops,
66 },
67};
68
69module_spi_driver(pcm512x_spi_driver);
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
new file mode 100644
index 000000000000..4b4c0c7bb918
--- /dev/null
+++ b/sound/soc/codecs/pcm512x.c
@@ -0,0 +1,589 @@
1/*
2 * Driver for the PCM512x CODECs
3 *
4 * Author: Mark Brown <broonie@linaro.org>
5 * Copyright 2014 Linaro Ltd
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 */
16
17
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/clk.h>
21#include <linux/pm_runtime.h>
22#include <linux/regmap.h>
23#include <linux/regulator/consumer.h>
24#include <sound/soc.h>
25#include <sound/soc-dapm.h>
26#include <sound/tlv.h>
27
28#include "pcm512x.h"
29
30#define PCM512x_NUM_SUPPLIES 3
31static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = {
32 "AVDD",
33 "DVDD",
34 "CPVDD",
35};
36
37struct pcm512x_priv {
38 struct regmap *regmap;
39 struct clk *sclk;
40 struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES];
41 struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES];
42};
43
44/*
45 * We can't use the same notifier block for more than one supply and
46 * there's no way I can see to get from a callback to the caller
47 * except container_of().
48 */
49#define PCM512x_REGULATOR_EVENT(n) \
50static int pcm512x_regulator_event_##n(struct notifier_block *nb, \
51 unsigned long event, void *data) \
52{ \
53 struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \
54 supply_nb[n]); \
55 if (event & REGULATOR_EVENT_DISABLE) { \
56 regcache_mark_dirty(pcm512x->regmap); \
57 regcache_cache_only(pcm512x->regmap, true); \
58 } \
59 return 0; \
60}
61
62PCM512x_REGULATOR_EVENT(0)
63PCM512x_REGULATOR_EVENT(1)
64PCM512x_REGULATOR_EVENT(2)
65
66static const struct reg_default pcm512x_reg_defaults[] = {
67 { PCM512x_RESET, 0x00 },
68 { PCM512x_POWER, 0x00 },
69 { PCM512x_MUTE, 0x00 },
70 { PCM512x_DSP, 0x00 },
71 { PCM512x_PLL_REF, 0x00 },
72 { PCM512x_DAC_ROUTING, 0x11 },
73 { PCM512x_DSP_PROGRAM, 0x01 },
74 { PCM512x_CLKDET, 0x00 },
75 { PCM512x_AUTO_MUTE, 0x00 },
76 { PCM512x_ERROR_DETECT, 0x00 },
77 { PCM512x_DIGITAL_VOLUME_1, 0x00 },
78 { PCM512x_DIGITAL_VOLUME_2, 0x30 },
79 { PCM512x_DIGITAL_VOLUME_3, 0x30 },
80 { PCM512x_DIGITAL_MUTE_1, 0x22 },
81 { PCM512x_DIGITAL_MUTE_2, 0x00 },
82 { PCM512x_DIGITAL_MUTE_3, 0x07 },
83 { PCM512x_OUTPUT_AMPLITUDE, 0x00 },
84 { PCM512x_ANALOG_GAIN_CTRL, 0x00 },
85 { PCM512x_UNDERVOLTAGE_PROT, 0x00 },
86 { PCM512x_ANALOG_MUTE_CTRL, 0x00 },
87 { PCM512x_ANALOG_GAIN_BOOST, 0x00 },
88 { PCM512x_VCOM_CTRL_1, 0x00 },
89 { PCM512x_VCOM_CTRL_2, 0x01 },
90};
91
92static bool pcm512x_readable(struct device *dev, unsigned int reg)
93{
94 switch (reg) {
95 case PCM512x_RESET:
96 case PCM512x_POWER:
97 case PCM512x_MUTE:
98 case PCM512x_PLL_EN:
99 case PCM512x_SPI_MISO_FUNCTION:
100 case PCM512x_DSP:
101 case PCM512x_GPIO_EN:
102 case PCM512x_BCLK_LRCLK_CFG:
103 case PCM512x_DSP_GPIO_INPUT:
104 case PCM512x_MASTER_MODE:
105 case PCM512x_PLL_REF:
106 case PCM512x_PLL_COEFF_0:
107 case PCM512x_PLL_COEFF_1:
108 case PCM512x_PLL_COEFF_2:
109 case PCM512x_PLL_COEFF_3:
110 case PCM512x_PLL_COEFF_4:
111 case PCM512x_DSP_CLKDIV:
112 case PCM512x_DAC_CLKDIV:
113 case PCM512x_NCP_CLKDIV:
114 case PCM512x_OSR_CLKDIV:
115 case PCM512x_MASTER_CLKDIV_1:
116 case PCM512x_MASTER_CLKDIV_2:
117 case PCM512x_FS_SPEED_MODE:
118 case PCM512x_IDAC_1:
119 case PCM512x_IDAC_2:
120 case PCM512x_ERROR_DETECT:
121 case PCM512x_I2S_1:
122 case PCM512x_I2S_2:
123 case PCM512x_DAC_ROUTING:
124 case PCM512x_DSP_PROGRAM:
125 case PCM512x_CLKDET:
126 case PCM512x_AUTO_MUTE:
127 case PCM512x_DIGITAL_VOLUME_1:
128 case PCM512x_DIGITAL_VOLUME_2:
129 case PCM512x_DIGITAL_VOLUME_3:
130 case PCM512x_DIGITAL_MUTE_1:
131 case PCM512x_DIGITAL_MUTE_2:
132 case PCM512x_DIGITAL_MUTE_3:
133 case PCM512x_GPIO_OUTPUT_1:
134 case PCM512x_GPIO_OUTPUT_2:
135 case PCM512x_GPIO_OUTPUT_3:
136 case PCM512x_GPIO_OUTPUT_4:
137 case PCM512x_GPIO_OUTPUT_5:
138 case PCM512x_GPIO_OUTPUT_6:
139 case PCM512x_GPIO_CONTROL_1:
140 case PCM512x_GPIO_CONTROL_2:
141 case PCM512x_OVERFLOW:
142 case PCM512x_RATE_DET_1:
143 case PCM512x_RATE_DET_2:
144 case PCM512x_RATE_DET_3:
145 case PCM512x_RATE_DET_4:
146 case PCM512x_ANALOG_MUTE_DET:
147 case PCM512x_GPIN:
148 case PCM512x_DIGITAL_MUTE_DET:
149 case PCM512x_OUTPUT_AMPLITUDE:
150 case PCM512x_ANALOG_GAIN_CTRL:
151 case PCM512x_UNDERVOLTAGE_PROT:
152 case PCM512x_ANALOG_MUTE_CTRL:
153 case PCM512x_ANALOG_GAIN_BOOST:
154 case PCM512x_VCOM_CTRL_1:
155 case PCM512x_VCOM_CTRL_2:
156 case PCM512x_CRAM_CTRL:
157 return true;
158 default:
159 /* There are 256 raw register addresses */
160 return reg < 0xff;
161 }
162}
163
164static bool pcm512x_volatile(struct device *dev, unsigned int reg)
165{
166 switch (reg) {
167 case PCM512x_PLL_EN:
168 case PCM512x_OVERFLOW:
169 case PCM512x_RATE_DET_1:
170 case PCM512x_RATE_DET_2:
171 case PCM512x_RATE_DET_3:
172 case PCM512x_RATE_DET_4:
173 case PCM512x_ANALOG_MUTE_DET:
174 case PCM512x_GPIN:
175 case PCM512x_DIGITAL_MUTE_DET:
176 case PCM512x_CRAM_CTRL:
177 return true;
178 default:
179 /* There are 256 raw register addresses */
180 return reg < 0xff;
181 }
182}
183
184static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1);
185static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0);
186static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0);
187
188static const char * const pcm512x_dsp_program_texts[] = {
189 "FIR interpolation with de-emphasis",
190 "Low latency IIR with de-emphasis",
191 "Fixed process flow",
192 "High attenuation with de-emphasis",
193 "Ringing-less low latency FIR",
194};
195
196static const unsigned int pcm512x_dsp_program_values[] = {
197 1,
198 2,
199 3,
200 5,
201 7,
202};
203
204static SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program,
205 PCM512x_DSP_PROGRAM, 0, 0x1f,
206 pcm512x_dsp_program_texts,
207 pcm512x_dsp_program_values);
208
209static const char * const pcm512x_clk_missing_text[] = {
210 "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s"
211};
212
213static const struct soc_enum pcm512x_clk_missing =
214 SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 8, pcm512x_clk_missing_text);
215
216static const char * const pcm512x_autom_text[] = {
217 "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s"
218};
219
220static const struct soc_enum pcm512x_autom_l =
221 SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 8,
222 pcm512x_autom_text);
223
224static const struct soc_enum pcm512x_autom_r =
225 SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 8,
226 pcm512x_autom_text);
227
228static const char * const pcm512x_ramp_rate_text[] = {
229 "1 sample/update", "2 samples/update", "4 samples/update",
230 "Immediate"
231};
232
233static const struct soc_enum pcm512x_vndf =
234 SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4,
235 pcm512x_ramp_rate_text);
236
237static const struct soc_enum pcm512x_vnuf =
238 SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4,
239 pcm512x_ramp_rate_text);
240
241static const struct soc_enum pcm512x_vedf =
242 SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4,
243 pcm512x_ramp_rate_text);
244
245static const char * const pcm512x_ramp_step_text[] = {
246 "4dB/step", "2dB/step", "1dB/step", "0.5dB/step"
247};
248
249static const struct soc_enum pcm512x_vnds =
250 SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4,
251 pcm512x_ramp_step_text);
252
253static const struct soc_enum pcm512x_vnus =
254 SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4,
255 pcm512x_ramp_step_text);
256
257static const struct soc_enum pcm512x_veds =
258 SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
259 pcm512x_ramp_step_text);
260
261static const struct snd_kcontrol_new pcm512x_controls[] = {
262SOC_DOUBLE_R_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2,
263 PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
264SOC_DOUBLE_TLV("Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
265 PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv),
266SOC_DOUBLE_TLV("Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
267 PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv),
268SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
269 PCM512x_RQMR_SHIFT, 1, 1),
270
271SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
272SOC_VALUE_ENUM("DSP Program", pcm512x_dsp_program),
273
274SOC_ENUM("Clock Missing Period", pcm512x_clk_missing),
275SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l),
276SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r),
277SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3,
278 PCM512x_ACTL_SHIFT, 1, 0),
279SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT,
280 PCM512x_AMLR_SHIFT, 1, 0),
281
282SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf),
283SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds),
284SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf),
285SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus),
286SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf),
287SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds),
288};
289
290static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = {
291SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
292SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
293
294SND_SOC_DAPM_OUTPUT("OUTL"),
295SND_SOC_DAPM_OUTPUT("OUTR"),
296};
297
298static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = {
299 { "DACL", NULL, "Playback" },
300 { "DACR", NULL, "Playback" },
301
302 { "OUTL", NULL, "DACL" },
303 { "OUTR", NULL, "DACR" },
304};
305
306static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
307 enum snd_soc_bias_level level)
308{
309 struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev);
310 int ret;
311
312 switch (level) {
313 case SND_SOC_BIAS_ON:
314 case SND_SOC_BIAS_PREPARE:
315 break;
316
317 case SND_SOC_BIAS_STANDBY:
318 ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
319 PCM512x_RQST, 0);
320 if (ret != 0) {
321 dev_err(codec->dev, "Failed to remove standby: %d\n",
322 ret);
323 return ret;
324 }
325 break;
326
327 case SND_SOC_BIAS_OFF:
328 ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
329 PCM512x_RQST, PCM512x_RQST);
330 if (ret != 0) {
331 dev_err(codec->dev, "Failed to request standby: %d\n",
332 ret);
333 return ret;
334 }
335 break;
336 }
337
338 codec->dapm.bias_level = level;
339
340 return 0;
341}
342
343static struct snd_soc_dai_driver pcm512x_dai = {
344 .name = "pcm512x-hifi",
345 .playback = {
346 .stream_name = "Playback",
347 .channels_min = 2,
348 .channels_max = 2,
349 .rates = SNDRV_PCM_RATE_8000_192000,
350 .formats = SNDRV_PCM_FMTBIT_S16_LE |
351 SNDRV_PCM_FMTBIT_S24_LE |
352 SNDRV_PCM_FMTBIT_S32_LE
353 },
354};
355
356static struct snd_soc_codec_driver pcm512x_codec_driver = {
357 .set_bias_level = pcm512x_set_bias_level,
358 .idle_bias_off = true,
359
360 .controls = pcm512x_controls,
361 .num_controls = ARRAY_SIZE(pcm512x_controls),
362 .dapm_widgets = pcm512x_dapm_widgets,
363 .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets),
364 .dapm_routes = pcm512x_dapm_routes,
365 .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes),
366};
367
368static const struct regmap_range_cfg pcm512x_range = {
369 .name = "Pages", .range_min = PCM512x_VIRT_BASE,
370 .range_max = PCM512x_MAX_REGISTER,
371 .selector_reg = PCM512x_PAGE,
372 .selector_mask = 0xff,
373 .window_start = 0, .window_len = 0x100,
374};
375
376const struct regmap_config pcm512x_regmap = {
377 .reg_bits = 8,
378 .val_bits = 8,
379
380 .readable_reg = pcm512x_readable,
381 .volatile_reg = pcm512x_volatile,
382
383 .ranges = &pcm512x_range,
384 .num_ranges = 1,
385
386 .max_register = PCM512x_MAX_REGISTER,
387 .reg_defaults = pcm512x_reg_defaults,
388 .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults),
389 .cache_type = REGCACHE_RBTREE,
390};
391EXPORT_SYMBOL_GPL(pcm512x_regmap);
392
393int pcm512x_probe(struct device *dev, struct regmap *regmap)
394{
395 struct pcm512x_priv *pcm512x;
396 int i, ret;
397
398 pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL);
399 if (!pcm512x)
400 return -ENOMEM;
401
402 dev_set_drvdata(dev, pcm512x);
403 pcm512x->regmap = regmap;
404
405 for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++)
406 pcm512x->supplies[i].supply = pcm512x_supply_names[i];
407
408 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies),
409 pcm512x->supplies);
410 if (ret != 0) {
411 dev_err(dev, "Failed to get supplies: %d\n", ret);
412 return ret;
413 }
414
415 pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0;
416 pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1;
417 pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
418
419 for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
420 ret = regulator_register_notifier(pcm512x->supplies[i].consumer,
421 &pcm512x->supply_nb[i]);
422 if (ret != 0) {
423 dev_err(dev,
424 "Failed to register regulator notifier: %d\n",
425 ret);
426 }
427 }
428
429 ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
430 pcm512x->supplies);
431 if (ret != 0) {
432 dev_err(dev, "Failed to enable supplies: %d\n", ret);
433 return ret;
434 }
435
436 /* Reset the device, verifying I/O in the process for I2C */
437 ret = regmap_write(regmap, PCM512x_RESET,
438 PCM512x_RSTM | PCM512x_RSTR);
439 if (ret != 0) {
440 dev_err(dev, "Failed to reset device: %d\n", ret);
441 goto err;
442 }
443
444 ret = regmap_write(regmap, PCM512x_RESET, 0);
445 if (ret != 0) {
446 dev_err(dev, "Failed to reset device: %d\n", ret);
447 goto err;
448 }
449
450 pcm512x->sclk = devm_clk_get(dev, NULL);
451 if (IS_ERR(pcm512x->sclk)) {
452 if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
453 return -EPROBE_DEFER;
454
455 dev_info(dev, "No SCLK, using BCLK: %ld\n",
456 PTR_ERR(pcm512x->sclk));
457
458 /* Disable reporting of missing SCLK as an error */
459 regmap_update_bits(regmap, PCM512x_ERROR_DETECT,
460 PCM512x_IDCH, PCM512x_IDCH);
461
462 /* Switch PLL input to BCLK */
463 regmap_update_bits(regmap, PCM512x_PLL_REF,
464 PCM512x_SREF, PCM512x_SREF);
465 } else {
466 ret = clk_prepare_enable(pcm512x->sclk);
467 if (ret != 0) {
468 dev_err(dev, "Failed to enable SCLK: %d\n", ret);
469 return ret;
470 }
471 }
472
473 /* Default to standby mode */
474 ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
475 PCM512x_RQST, PCM512x_RQST);
476 if (ret != 0) {
477 dev_err(dev, "Failed to request standby: %d\n",
478 ret);
479 goto err_clk;
480 }
481
482 pm_runtime_set_active(dev);
483 pm_runtime_enable(dev);
484 pm_runtime_idle(dev);
485
486 ret = snd_soc_register_codec(dev, &pcm512x_codec_driver,
487 &pcm512x_dai, 1);
488 if (ret != 0) {
489 dev_err(dev, "Failed to register CODEC: %d\n", ret);
490 goto err_pm;
491 }
492
493 return 0;
494
495err_pm:
496 pm_runtime_disable(dev);
497err_clk:
498 if (!IS_ERR(pcm512x->sclk))
499 clk_disable_unprepare(pcm512x->sclk);
500err:
501 regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
502 pcm512x->supplies);
503 return ret;
504}
505EXPORT_SYMBOL_GPL(pcm512x_probe);
506
507void pcm512x_remove(struct device *dev)
508{
509 struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
510
511 snd_soc_unregister_codec(dev);
512 pm_runtime_disable(dev);
513 if (!IS_ERR(pcm512x->sclk))
514 clk_disable_unprepare(pcm512x->sclk);
515 regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
516 pcm512x->supplies);
517}
518EXPORT_SYMBOL_GPL(pcm512x_remove);
519
520static int pcm512x_suspend(struct device *dev)
521{
522 struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
523 int ret;
524
525 ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
526 PCM512x_RQPD, PCM512x_RQPD);
527 if (ret != 0) {
528 dev_err(dev, "Failed to request power down: %d\n", ret);
529 return ret;
530 }
531
532 ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
533 pcm512x->supplies);
534 if (ret != 0) {
535 dev_err(dev, "Failed to disable supplies: %d\n", ret);
536 return ret;
537 }
538
539 if (!IS_ERR(pcm512x->sclk))
540 clk_disable_unprepare(pcm512x->sclk);
541
542 return 0;
543}
544
545static int pcm512x_resume(struct device *dev)
546{
547 struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
548 int ret;
549
550 if (!IS_ERR(pcm512x->sclk)) {
551 ret = clk_prepare_enable(pcm512x->sclk);
552 if (ret != 0) {
553 dev_err(dev, "Failed to enable SCLK: %d\n", ret);
554 return ret;
555 }
556 }
557
558 ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
559 pcm512x->supplies);
560 if (ret != 0) {
561 dev_err(dev, "Failed to enable supplies: %d\n", ret);
562 return ret;
563 }
564
565 regcache_cache_only(pcm512x->regmap, false);
566 ret = regcache_sync(pcm512x->regmap);
567 if (ret != 0) {
568 dev_err(dev, "Failed to sync cache: %d\n", ret);
569 return ret;
570 }
571
572 ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
573 PCM512x_RQPD, 0);
574 if (ret != 0) {
575 dev_err(dev, "Failed to remove power down: %d\n", ret);
576 return ret;
577 }
578
579 return 0;
580}
581
582const struct dev_pm_ops pcm512x_pm_ops = {
583 SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
584};
585EXPORT_SYMBOL_GPL(pcm512x_pm_ops);
586
587MODULE_DESCRIPTION("ASoC PCM512x codec driver");
588MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
589MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h
new file mode 100644
index 000000000000..6ee76aaca09a
--- /dev/null
+++ b/sound/soc/codecs/pcm512x.h
@@ -0,0 +1,171 @@
1/*
2 * Driver for the PCM512x CODECs
3 *
4 * Author: Mark Brown <broonie@linaro.org>
5 * Copyright 2014 Linaro Ltd
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 */
16
17#ifndef _SND_SOC_PCM512X
18#define _SND_SOC_PCM512X
19
20#include <linux/pm.h>
21#include <linux/regmap.h>
22
23#define PCM512x_VIRT_BASE 0x100
24#define PCM512x_PAGE_LEN 0x100
25#define PCM512x_PAGE_BASE(n) (PCM512x_VIRT_BASE + (PCM512x_PAGE_LEN * n))
26
27#define PCM512x_PAGE 0
28
29#define PCM512x_RESET (PCM512x_PAGE_BASE(0) + 1)
30#define PCM512x_POWER (PCM512x_PAGE_BASE(0) + 2)
31#define PCM512x_MUTE (PCM512x_PAGE_BASE(0) + 3)
32#define PCM512x_PLL_EN (PCM512x_PAGE_BASE(0) + 4)
33#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_BASE(0) + 6)
34#define PCM512x_DSP (PCM512x_PAGE_BASE(0) + 7)
35#define PCM512x_GPIO_EN (PCM512x_PAGE_BASE(0) + 8)
36#define PCM512x_BCLK_LRCLK_CFG (PCM512x_PAGE_BASE(0) + 9)
37#define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_BASE(0) + 10)
38#define PCM512x_MASTER_MODE (PCM512x_PAGE_BASE(0) + 12)
39#define PCM512x_PLL_REF (PCM512x_PAGE_BASE(0) + 13)
40#define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_BASE(0) + 20)
41#define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_BASE(0) + 21)
42#define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_BASE(0) + 22)
43#define PCM512x_PLL_COEFF_3 (PCM512x_PAGE_BASE(0) + 23)
44#define PCM512x_PLL_COEFF_4 (PCM512x_PAGE_BASE(0) + 24)
45#define PCM512x_DSP_CLKDIV (PCM512x_PAGE_BASE(0) + 27)
46#define PCM512x_DAC_CLKDIV (PCM512x_PAGE_BASE(0) + 28)
47#define PCM512x_NCP_CLKDIV (PCM512x_PAGE_BASE(0) + 29)
48#define PCM512x_OSR_CLKDIV (PCM512x_PAGE_BASE(0) + 30)
49#define PCM512x_MASTER_CLKDIV_1 (PCM512x_PAGE_BASE(0) + 32)
50#define PCM512x_MASTER_CLKDIV_2 (PCM512x_PAGE_BASE(0) + 33)
51#define PCM512x_FS_SPEED_MODE (PCM512x_PAGE_BASE(0) + 34)
52#define PCM512x_IDAC_1 (PCM512x_PAGE_BASE(0) + 35)
53#define PCM512x_IDAC_2 (PCM512x_PAGE_BASE(0) + 36)
54#define PCM512x_ERROR_DETECT (PCM512x_PAGE_BASE(0) + 37)
55#define PCM512x_I2S_1 (PCM512x_PAGE_BASE(0) + 40)
56#define PCM512x_I2S_2 (PCM512x_PAGE_BASE(0) + 41)
57#define PCM512x_DAC_ROUTING (PCM512x_PAGE_BASE(0) + 42)
58#define PCM512x_DSP_PROGRAM (PCM512x_PAGE_BASE(0) + 43)
59#define PCM512x_CLKDET (PCM512x_PAGE_BASE(0) + 44)
60#define PCM512x_AUTO_MUTE (PCM512x_PAGE_BASE(0) + 59)
61#define PCM512x_DIGITAL_VOLUME_1 (PCM512x_PAGE_BASE(0) + 60)
62#define PCM512x_DIGITAL_VOLUME_2 (PCM512x_PAGE_BASE(0) + 61)
63#define PCM512x_DIGITAL_VOLUME_3 (PCM512x_PAGE_BASE(0) + 62)
64#define PCM512x_DIGITAL_MUTE_1 (PCM512x_PAGE_BASE(0) + 63)
65#define PCM512x_DIGITAL_MUTE_2 (PCM512x_PAGE_BASE(0) + 64)
66#define PCM512x_DIGITAL_MUTE_3 (PCM512x_PAGE_BASE(0) + 65)
67#define PCM512x_GPIO_OUTPUT_1 (PCM512x_PAGE_BASE(0) + 80)
68#define PCM512x_GPIO_OUTPUT_2 (PCM512x_PAGE_BASE(0) + 81)
69#define PCM512x_GPIO_OUTPUT_3 (PCM512x_PAGE_BASE(0) + 82)
70#define PCM512x_GPIO_OUTPUT_4 (PCM512x_PAGE_BASE(0) + 83)
71#define PCM512x_GPIO_OUTPUT_5 (PCM512x_PAGE_BASE(0) + 84)
72#define PCM512x_GPIO_OUTPUT_6 (PCM512x_PAGE_BASE(0) + 85)
73#define PCM512x_GPIO_CONTROL_1 (PCM512x_PAGE_BASE(0) + 86)
74#define PCM512x_GPIO_CONTROL_2 (PCM512x_PAGE_BASE(0) + 87)
75#define PCM512x_OVERFLOW (PCM512x_PAGE_BASE(0) + 90)
76#define PCM512x_RATE_DET_1 (PCM512x_PAGE_BASE(0) + 91)
77#define PCM512x_RATE_DET_2 (PCM512x_PAGE_BASE(0) + 92)
78#define PCM512x_RATE_DET_3 (PCM512x_PAGE_BASE(0) + 93)
79#define PCM512x_RATE_DET_4 (PCM512x_PAGE_BASE(0) + 94)
80#define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_BASE(0) + 108)
81#define PCM512x_GPIN (PCM512x_PAGE_BASE(0) + 119)
82#define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_BASE(0) + 120)
83
84#define PCM512x_OUTPUT_AMPLITUDE (PCM512x_PAGE_BASE(1) + 1)
85#define PCM512x_ANALOG_GAIN_CTRL (PCM512x_PAGE_BASE(1) + 2)
86#define PCM512x_UNDERVOLTAGE_PROT (PCM512x_PAGE_BASE(1) + 5)
87#define PCM512x_ANALOG_MUTE_CTRL (PCM512x_PAGE_BASE(1) + 6)
88#define PCM512x_ANALOG_GAIN_BOOST (PCM512x_PAGE_BASE(1) + 7)
89#define PCM512x_VCOM_CTRL_1 (PCM512x_PAGE_BASE(1) + 8)
90#define PCM512x_VCOM_CTRL_2 (PCM512x_PAGE_BASE(1) + 9)
91
92#define PCM512x_CRAM_CTRL (PCM512x_PAGE_BASE(44) + 1)
93
94#define PCM512x_MAX_REGISTER (PCM512x_PAGE_BASE(44) + 1)
95
96/* Page 0, Register 1 - reset */
97#define PCM512x_RSTR (1 << 0)
98#define PCM512x_RSTM (1 << 4)
99
100/* Page 0, Register 2 - power */
101#define PCM512x_RQPD (1 << 0)
102#define PCM512x_RQPD_SHIFT 0
103#define PCM512x_RQST (1 << 4)
104#define PCM512x_RQST_SHIFT 4
105
106/* Page 0, Register 3 - mute */
107#define PCM512x_RQMR_SHIFT 0
108#define PCM512x_RQML_SHIFT 4
109
110/* Page 0, Register 4 - PLL */
111#define PCM512x_PLCE (1 << 0)
112#define PCM512x_RLCE_SHIFT 0
113#define PCM512x_PLCK (1 << 4)
114#define PCM512x_PLCK_SHIFT 4
115
116/* Page 0, Register 7 - DSP */
117#define PCM512x_SDSL (1 << 0)
118#define PCM512x_SDSL_SHIFT 0
119#define PCM512x_DEMP (1 << 4)
120#define PCM512x_DEMP_SHIFT 4
121
122/* Page 0, Register 13 - PLL reference */
123#define PCM512x_SREF (1 << 4)
124
125/* Page 0, Register 37 - Error detection */
126#define PCM512x_IPLK (1 << 0)
127#define PCM512x_DCAS (1 << 1)
128#define PCM512x_IDCM (1 << 2)
129#define PCM512x_IDCH (1 << 3)
130#define PCM512x_IDSK (1 << 4)
131#define PCM512x_IDBK (1 << 5)
132#define PCM512x_IDFS (1 << 6)
133
134/* Page 0, Register 42 - DAC routing */
135#define PCM512x_AUPR_SHIFT 0
136#define PCM512x_AUPL_SHIFT 4
137
138/* Page 0, Register 59 - auto mute */
139#define PCM512x_ATMR_SHIFT 0
140#define PCM512x_ATML_SHIFT 4
141
142/* Page 0, Register 63 - ramp rates */
143#define PCM512x_VNDF_SHIFT 6
144#define PCM512x_VNDS_SHIFT 4
145#define PCM512x_VNUF_SHIFT 2
146#define PCM512x_VNUS_SHIFT 0
147
148/* Page 0, Register 64 - emergency ramp rates */
149#define PCM512x_VEDF_SHIFT 6
150#define PCM512x_VEDS_SHIFT 4
151
152/* Page 0, Register 65 - Digital mute enables */
153#define PCM512x_ACTL_SHIFT 2
154#define PCM512x_AMLE_SHIFT 1
155#define PCM512x_AMLR_SHIFT 0
156
157/* Page 1, Register 2 - analog volume control */
158#define PCM512x_RAGN_SHIFT 0
159#define PCM512x_LAGN_SHIFT 4
160
161/* Page 1, Register 7 - analog boost control */
162#define PCM512x_AGBR_SHIFT 0
163#define PCM512x_AGBL_SHIFT 4
164
165extern const struct dev_pm_ops pcm512x_pm_ops;
166extern const struct regmap_config pcm512x_regmap;
167
168int pcm512x_probe(struct device *dev, struct regmap *regmap);
169void pcm512x_remove(struct device *dev);
170
171#endif