diff options
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/Kconfig | 4 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/ssm2518.c | 9 | ||||
-rw-r--r-- | sound/soc/codecs/sta529.c | 3 | ||||
-rw-r--r-- | sound/soc/codecs/sti-sas.c | 628 |
5 files changed, 643 insertions, 3 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 6fd467cde101..0c9733ecd17f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -106,6 +106,7 @@ config SND_SOC_ALL_CODECS | |||
106 | select SND_SOC_STA350 if I2C | 106 | select SND_SOC_STA350 if I2C |
107 | select SND_SOC_STA529 if I2C | 107 | select SND_SOC_STA529 if I2C |
108 | select SND_SOC_STAC9766 if SND_SOC_AC97_BUS | 108 | select SND_SOC_STAC9766 if SND_SOC_AC97_BUS |
109 | select SND_SOC_STI_SAS | ||
109 | select SND_SOC_TAS2552 if I2C | 110 | select SND_SOC_TAS2552 if I2C |
110 | select SND_SOC_TAS5086 if I2C | 111 | select SND_SOC_TAS5086 if I2C |
111 | select SND_SOC_TAS571X if I2C | 112 | select SND_SOC_TAS571X if I2C |
@@ -631,6 +632,9 @@ config SND_SOC_STA529 | |||
631 | config SND_SOC_STAC9766 | 632 | config SND_SOC_STAC9766 |
632 | tristate | 633 | tristate |
633 | 634 | ||
635 | config SND_SOC_STI_SAS | ||
636 | tristate "codec Audio support for STI SAS codec" | ||
637 | |||
634 | config SND_SOC_TAS2552 | 638 | config SND_SOC_TAS2552 |
635 | tristate "Texas Instruments TAS2552 Mono Audio amplifier" | 639 | tristate "Texas Instruments TAS2552 Mono Audio amplifier" |
636 | depends on I2C | 640 | depends on I2C |
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f65bd7b0e97e..4a32077954ae 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -110,6 +110,7 @@ snd-soc-sta32x-objs := sta32x.o | |||
110 | snd-soc-sta350-objs := sta350.o | 110 | snd-soc-sta350-objs := sta350.o |
111 | snd-soc-sta529-objs := sta529.o | 111 | snd-soc-sta529-objs := sta529.o |
112 | snd-soc-stac9766-objs := stac9766.o | 112 | snd-soc-stac9766-objs := stac9766.o |
113 | snd-soc-sti-sas-objs := sti-sas.o | ||
113 | snd-soc-tas5086-objs := tas5086.o | 114 | snd-soc-tas5086-objs := tas5086.o |
114 | snd-soc-tas571x-objs := tas571x.o | 115 | snd-soc-tas571x-objs := tas571x.o |
115 | snd-soc-tfa9879-objs := tfa9879.o | 116 | snd-soc-tfa9879-objs := tfa9879.o |
@@ -297,6 +298,7 @@ obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o | |||
297 | obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o | 298 | obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o |
298 | obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o | 299 | obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o |
299 | obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o | 300 | obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o |
301 | obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o | ||
300 | obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o | 302 | obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o |
301 | obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o | 303 | obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o |
302 | obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o | 304 | obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o |
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 5d94d6c7ad33..ddb0203fc649 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c | |||
@@ -806,6 +806,14 @@ static int ssm2518_i2c_remove(struct i2c_client *client) | |||
806 | return 0; | 806 | return 0; |
807 | } | 807 | } |
808 | 808 | ||
809 | #ifdef CONFIG_OF | ||
810 | static const struct of_device_id ssm2518_dt_ids[] = { | ||
811 | { .compatible = "adi,ssm2518", }, | ||
812 | { } | ||
813 | }; | ||
814 | MODULE_DEVICE_TABLE(of, ssm2518_dt_ids); | ||
815 | #endif | ||
816 | |||
809 | static const struct i2c_device_id ssm2518_i2c_ids[] = { | 817 | static const struct i2c_device_id ssm2518_i2c_ids[] = { |
810 | { "ssm2518", 0 }, | 818 | { "ssm2518", 0 }, |
811 | { } | 819 | { } |
@@ -815,6 +823,7 @@ MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids); | |||
815 | static struct i2c_driver ssm2518_driver = { | 823 | static struct i2c_driver ssm2518_driver = { |
816 | .driver = { | 824 | .driver = { |
817 | .name = "ssm2518", | 825 | .name = "ssm2518", |
826 | .of_match_table = of_match_ptr(ssm2518_dt_ids), | ||
818 | }, | 827 | }, |
819 | .probe = ssm2518_i2c_probe, | 828 | .probe = ssm2518_i2c_probe, |
820 | .remove = ssm2518_i2c_remove, | 829 | .remove = ssm2518_i2c_remove, |
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 3430f444c1ae..2cdaca943a8c 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c | |||
@@ -339,9 +339,6 @@ static int sta529_i2c_probe(struct i2c_client *i2c, | |||
339 | struct sta529 *sta529; | 339 | struct sta529 *sta529; |
340 | int ret; | 340 | int ret; |
341 | 341 | ||
342 | if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
343 | return -EINVAL; | ||
344 | |||
345 | sta529 = devm_kzalloc(&i2c->dev, sizeof(struct sta529), GFP_KERNEL); | 342 | sta529 = devm_kzalloc(&i2c->dev, sizeof(struct sta529), GFP_KERNEL); |
346 | if (!sta529) | 343 | if (!sta529) |
347 | return -ENOMEM; | 344 | return -ENOMEM; |
diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c new file mode 100644 index 000000000000..160d61a66204 --- /dev/null +++ b/sound/soc/codecs/sti-sas.c | |||
@@ -0,0 +1,628 @@ | |||
1 | /* | ||
2 | * Copyright (C) STMicroelectronics SA 2015 | ||
3 | * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com> | ||
4 | * for STMicroelectronics. | ||
5 | * License terms: GNU General Public License (GPL), version 2 | ||
6 | */ | ||
7 | |||
8 | #include <linux/io.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/regmap.h> | ||
11 | #include <linux/reset.h> | ||
12 | #include <linux/mfd/syscon.h> | ||
13 | |||
14 | #include <sound/soc.h> | ||
15 | #include <sound/soc-dapm.h> | ||
16 | |||
17 | /* chipID supported */ | ||
18 | #define CHIPID_STIH416 0 | ||
19 | #define CHIPID_STIH407 1 | ||
20 | |||
21 | /* DAC definitions */ | ||
22 | |||
23 | /* stih416 DAC registers */ | ||
24 | /* sysconf 2517: Audio-DAC-Control */ | ||
25 | #define STIH416_AUDIO_DAC_CTRL 0x00000814 | ||
26 | /* sysconf 2519: Audio-Gue-Control */ | ||
27 | #define STIH416_AUDIO_GLUE_CTRL 0x0000081C | ||
28 | |||
29 | #define STIH416_DAC_NOT_STANDBY 0x3 | ||
30 | #define STIH416_DAC_SOFTMUTE 0x4 | ||
31 | #define STIH416_DAC_ANA_NOT_PWR 0x5 | ||
32 | #define STIH416_DAC_NOT_PNDBG 0x6 | ||
33 | |||
34 | #define STIH416_DAC_NOT_STANDBY_MASK BIT(STIH416_DAC_NOT_STANDBY) | ||
35 | #define STIH416_DAC_SOFTMUTE_MASK BIT(STIH416_DAC_SOFTMUTE) | ||
36 | #define STIH416_DAC_ANA_NOT_PWR_MASK BIT(STIH416_DAC_ANA_NOT_PWR) | ||
37 | #define STIH416_DAC_NOT_PNDBG_MASK BIT(STIH416_DAC_NOT_PNDBG) | ||
38 | |||
39 | /* stih407 DAC registers */ | ||
40 | /* sysconf 5041: Audio-Gue-Control */ | ||
41 | #define STIH407_AUDIO_GLUE_CTRL 0x000000A4 | ||
42 | /* sysconf 5042: Audio-DAC-Control */ | ||
43 | #define STIH407_AUDIO_DAC_CTRL 0x000000A8 | ||
44 | |||
45 | /* DAC definitions */ | ||
46 | #define STIH407_DAC_SOFTMUTE 0x0 | ||
47 | #define STIH407_DAC_STANDBY_ANA 0x1 | ||
48 | #define STIH407_DAC_STANDBY 0x2 | ||
49 | |||
50 | #define STIH407_DAC_SOFTMUTE_MASK BIT(STIH407_DAC_SOFTMUTE) | ||
51 | #define STIH407_DAC_STANDBY_ANA_MASK BIT(STIH407_DAC_STANDBY_ANA) | ||
52 | #define STIH407_DAC_STANDBY_MASK BIT(STIH407_DAC_STANDBY) | ||
53 | |||
54 | /* SPDIF definitions */ | ||
55 | #define SPDIF_BIPHASE_ENABLE 0x6 | ||
56 | #define SPDIF_BIPHASE_IDLE 0x7 | ||
57 | |||
58 | #define SPDIF_BIPHASE_ENABLE_MASK BIT(SPDIF_BIPHASE_ENABLE) | ||
59 | #define SPDIF_BIPHASE_IDLE_MASK BIT(SPDIF_BIPHASE_IDLE) | ||
60 | |||
61 | enum { | ||
62 | STI_SAS_DAI_SPDIF_OUT, | ||
63 | STI_SAS_DAI_ANALOG_OUT, | ||
64 | }; | ||
65 | |||
66 | static const struct reg_default stih416_sas_reg_defaults[] = { | ||
67 | { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, | ||
68 | { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, | ||
69 | }; | ||
70 | |||
71 | static const struct reg_default stih407_sas_reg_defaults[] = { | ||
72 | { STIH416_AUDIO_DAC_CTRL, 0x000000000 }, | ||
73 | { STIH416_AUDIO_GLUE_CTRL, 0x00000040 }, | ||
74 | }; | ||
75 | |||
76 | struct sti_dac_audio { | ||
77 | struct regmap *regmap; | ||
78 | struct regmap *virt_regmap; | ||
79 | struct regmap_field **field; | ||
80 | struct reset_control *rst; | ||
81 | int mclk; | ||
82 | }; | ||
83 | |||
84 | struct sti_spdif_audio { | ||
85 | struct regmap *regmap; | ||
86 | struct regmap_field **field; | ||
87 | int mclk; | ||
88 | }; | ||
89 | |||
90 | /* device data structure */ | ||
91 | struct sti_sas_dev_data { | ||
92 | const int chipid; /* IC version */ | ||
93 | const struct regmap_config *regmap; | ||
94 | const struct snd_soc_dai_ops *dac_ops; /* DAC function callbacks */ | ||
95 | const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */ | ||
96 | const int num_dapm_widgets; /* dapms declaration */ | ||
97 | const struct snd_soc_dapm_route *dapm_routes; /* route declaration */ | ||
98 | const int num_dapm_routes; /* route declaration */ | ||
99 | }; | ||
100 | |||
101 | /* driver data structure */ | ||
102 | struct sti_sas_data { | ||
103 | struct device *dev; | ||
104 | const struct sti_sas_dev_data *dev_data; | ||
105 | struct sti_dac_audio dac; | ||
106 | struct sti_spdif_audio spdif; | ||
107 | }; | ||
108 | |||
109 | /* Read a register from the sysconf reg bank */ | ||
110 | static int sti_sas_read_reg(void *context, unsigned int reg, | ||
111 | unsigned int *value) | ||
112 | { | ||
113 | struct sti_sas_data *drvdata = context; | ||
114 | int status; | ||
115 | u32 val; | ||
116 | |||
117 | status = regmap_read(drvdata->dac.regmap, reg, &val); | ||
118 | *value = (unsigned int)val; | ||
119 | |||
120 | return status; | ||
121 | } | ||
122 | |||
123 | /* Read a register from the sysconf reg bank */ | ||
124 | static int sti_sas_write_reg(void *context, unsigned int reg, | ||
125 | unsigned int value) | ||
126 | { | ||
127 | struct sti_sas_data *drvdata = context; | ||
128 | int status; | ||
129 | |||
130 | status = regmap_write(drvdata->dac.regmap, reg, value); | ||
131 | |||
132 | return status; | ||
133 | } | ||
134 | |||
135 | static int sti_sas_init_sas_registers(struct snd_soc_codec *codec, | ||
136 | struct sti_sas_data *data) | ||
137 | { | ||
138 | int ret; | ||
139 | /* | ||
140 | * DAC and SPDIF are activated by default | ||
141 | * put them in IDLE to save power | ||
142 | */ | ||
143 | |||
144 | /* Initialise bi-phase formatter to disabled */ | ||
145 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, | ||
146 | SPDIF_BIPHASE_ENABLE_MASK, 0); | ||
147 | |||
148 | if (!ret) | ||
149 | /* Initialise bi-phase formatter idle value to 0 */ | ||
150 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, | ||
151 | SPDIF_BIPHASE_IDLE_MASK, 0); | ||
152 | if (ret < 0) { | ||
153 | dev_err(codec->dev, "Failed to update SPDIF registers"); | ||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | /* Init DAC configuration */ | ||
158 | switch (data->dev_data->chipid) { | ||
159 | case CHIPID_STIH407: | ||
160 | /* init configuration */ | ||
161 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, | ||
162 | STIH407_DAC_STANDBY_MASK, | ||
163 | STIH407_DAC_STANDBY_MASK); | ||
164 | |||
165 | if (!ret) | ||
166 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, | ||
167 | STIH407_DAC_STANDBY_ANA_MASK, | ||
168 | STIH407_DAC_STANDBY_ANA_MASK); | ||
169 | if (!ret) | ||
170 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, | ||
171 | STIH407_DAC_SOFTMUTE_MASK, | ||
172 | STIH407_DAC_SOFTMUTE_MASK); | ||
173 | break; | ||
174 | case CHIPID_STIH416: | ||
175 | ret = snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, | ||
176 | STIH416_DAC_NOT_STANDBY_MASK, 0); | ||
177 | if (!ret) | ||
178 | ret = snd_soc_update_bits(codec, | ||
179 | STIH416_AUDIO_DAC_CTRL, | ||
180 | STIH416_DAC_ANA_NOT_PWR, 0); | ||
181 | if (!ret) | ||
182 | ret = snd_soc_update_bits(codec, | ||
183 | STIH416_AUDIO_DAC_CTRL, | ||
184 | STIH416_DAC_NOT_PNDBG_MASK, | ||
185 | 0); | ||
186 | if (!ret) | ||
187 | ret = snd_soc_update_bits(codec, | ||
188 | STIH416_AUDIO_DAC_CTRL, | ||
189 | STIH416_DAC_SOFTMUTE_MASK, | ||
190 | STIH416_DAC_SOFTMUTE_MASK); | ||
191 | break; | ||
192 | default: | ||
193 | return -EINVAL; | ||
194 | } | ||
195 | |||
196 | if (ret < 0) { | ||
197 | dev_err(codec->dev, "Failed to update DAC registers"); | ||
198 | return ret; | ||
199 | } | ||
200 | |||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * DAC | ||
206 | */ | ||
207 | static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | ||
208 | { | ||
209 | /* Sanity check only */ | ||
210 | if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { | ||
211 | dev_err(dai->codec->dev, | ||
212 | "%s: ERROR: Unsupporter master mask 0x%x\n", | ||
213 | __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); | ||
214 | return -EINVAL; | ||
215 | } | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int stih416_dac_probe(struct snd_soc_dai *dai) | ||
221 | { | ||
222 | struct snd_soc_codec *codec = dai->codec; | ||
223 | struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); | ||
224 | struct sti_dac_audio *dac = &drvdata->dac; | ||
225 | |||
226 | /* Get reset control */ | ||
227 | dac->rst = devm_reset_control_get(codec->dev, "dac_rst"); | ||
228 | if (IS_ERR(dac->rst)) { | ||
229 | dev_err(dai->codec->dev, | ||
230 | "%s: ERROR: DAC reset control not defined !\n", | ||
231 | __func__); | ||
232 | dac->rst = NULL; | ||
233 | return -EFAULT; | ||
234 | } | ||
235 | /* Put the DAC into reset */ | ||
236 | reset_control_assert(dac->rst); | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | static const struct snd_soc_dapm_widget stih416_sas_dapm_widgets[] = { | ||
242 | SND_SOC_DAPM_PGA("DAC bandgap", STIH416_AUDIO_DAC_CTRL, | ||
243 | STIH416_DAC_NOT_PNDBG_MASK, 0, NULL, 0), | ||
244 | SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH416_AUDIO_DAC_CTRL, | ||
245 | STIH416_DAC_ANA_NOT_PWR, 0, NULL, 0), | ||
246 | SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH416_AUDIO_DAC_CTRL, | ||
247 | STIH416_DAC_NOT_STANDBY, 0), | ||
248 | SND_SOC_DAPM_OUTPUT("DAC Output"), | ||
249 | }; | ||
250 | |||
251 | static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { | ||
252 | SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL, | ||
253 | STIH407_DAC_STANDBY_ANA, 1, NULL, 0), | ||
254 | SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH407_AUDIO_DAC_CTRL, | ||
255 | STIH407_DAC_STANDBY, 1), | ||
256 | SND_SOC_DAPM_OUTPUT("DAC Output"), | ||
257 | }; | ||
258 | |||
259 | static const struct snd_soc_dapm_route stih416_sas_route[] = { | ||
260 | {"DAC Output", NULL, "DAC bandgap"}, | ||
261 | {"DAC Output", NULL, "DAC standby ana"}, | ||
262 | {"DAC standby ana", NULL, "DAC standby"}, | ||
263 | }; | ||
264 | |||
265 | static const struct snd_soc_dapm_route stih407_sas_route[] = { | ||
266 | {"DAC Output", NULL, "DAC standby ana"}, | ||
267 | {"DAC standby ana", NULL, "DAC standby"}, | ||
268 | }; | ||
269 | |||
270 | static int stih416_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) | ||
271 | { | ||
272 | struct snd_soc_codec *codec = dai->codec; | ||
273 | |||
274 | if (mute) { | ||
275 | return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, | ||
276 | STIH416_DAC_SOFTMUTE_MASK, | ||
277 | STIH416_DAC_SOFTMUTE_MASK); | ||
278 | } else { | ||
279 | return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, | ||
280 | STIH416_DAC_SOFTMUTE_MASK, 0); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) | ||
285 | { | ||
286 | struct snd_soc_codec *codec = dai->codec; | ||
287 | |||
288 | if (mute) { | ||
289 | return snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, | ||
290 | STIH407_DAC_SOFTMUTE_MASK, | ||
291 | STIH407_DAC_SOFTMUTE_MASK); | ||
292 | } else { | ||
293 | return snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, | ||
294 | STIH407_DAC_SOFTMUTE_MASK, | ||
295 | 0); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * SPDIF | ||
301 | */ | ||
302 | static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai, | ||
303 | unsigned int fmt) | ||
304 | { | ||
305 | if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { | ||
306 | dev_err(dai->codec->dev, | ||
307 | "%s: ERROR: Unsupporter master mask 0x%x\n", | ||
308 | __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); | ||
309 | return -EINVAL; | ||
310 | } | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * sti_sas_spdif_trigger: | ||
317 | * Trigger function is used to ensure that BiPhase Formater is disabled | ||
318 | * before CPU dai is stopped. | ||
319 | * This is mandatory to avoid that BPF is stalled | ||
320 | */ | ||
321 | static int sti_sas_spdif_trigger(struct snd_pcm_substream *substream, int cmd, | ||
322 | struct snd_soc_dai *dai) | ||
323 | { | ||
324 | struct snd_soc_codec *codec = dai->codec; | ||
325 | |||
326 | switch (cmd) { | ||
327 | case SNDRV_PCM_TRIGGER_START: | ||
328 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
329 | return snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, | ||
330 | SPDIF_BIPHASE_ENABLE_MASK, | ||
331 | SPDIF_BIPHASE_ENABLE_MASK); | ||
332 | case SNDRV_PCM_TRIGGER_RESUME: | ||
333 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
334 | case SNDRV_PCM_TRIGGER_STOP: | ||
335 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
336 | return snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, | ||
337 | SPDIF_BIPHASE_ENABLE_MASK, | ||
338 | 0); | ||
339 | default: | ||
340 | return -EINVAL; | ||
341 | } | ||
342 | } | ||
343 | |||
344 | static bool sti_sas_volatile_register(struct device *dev, unsigned int reg) | ||
345 | { | ||
346 | if (reg == STIH407_AUDIO_GLUE_CTRL) | ||
347 | return true; | ||
348 | |||
349 | return false; | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * CODEC DAIS | ||
354 | */ | ||
355 | |||
356 | /* | ||
357 | * sti_sas_set_sysclk: | ||
358 | * get MCLK input frequency to check that MCLK-FS ratio is coherent | ||
359 | */ | ||
360 | static int sti_sas_set_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
361 | unsigned int freq, int dir) | ||
362 | { | ||
363 | struct snd_soc_codec *codec = dai->codec; | ||
364 | struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); | ||
365 | |||
366 | if (dir == SND_SOC_CLOCK_OUT) | ||
367 | return 0; | ||
368 | |||
369 | if (clk_id != 0) | ||
370 | return -EINVAL; | ||
371 | |||
372 | switch (dai->id) { | ||
373 | case STI_SAS_DAI_SPDIF_OUT: | ||
374 | drvdata->spdif.mclk = freq; | ||
375 | break; | ||
376 | |||
377 | case STI_SAS_DAI_ANALOG_OUT: | ||
378 | drvdata->dac.mclk = freq; | ||
379 | break; | ||
380 | } | ||
381 | |||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static int sti_sas_prepare(struct snd_pcm_substream *substream, | ||
386 | struct snd_soc_dai *dai) | ||
387 | { | ||
388 | struct snd_soc_codec *codec = dai->codec; | ||
389 | struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); | ||
390 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
391 | |||
392 | switch (dai->id) { | ||
393 | case STI_SAS_DAI_SPDIF_OUT: | ||
394 | if ((drvdata->spdif.mclk / runtime->rate) != 128) { | ||
395 | dev_err(codec->dev, "unexpected mclk-fs ratio"); | ||
396 | return -EINVAL; | ||
397 | } | ||
398 | break; | ||
399 | case STI_SAS_DAI_ANALOG_OUT: | ||
400 | if ((drvdata->dac.mclk / runtime->rate) != 256) { | ||
401 | dev_err(codec->dev, "unexpected mclk-fs ratio"); | ||
402 | return -EINVAL; | ||
403 | } | ||
404 | break; | ||
405 | } | ||
406 | |||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | static const struct snd_soc_dai_ops stih416_dac_ops = { | ||
411 | .set_fmt = sti_sas_dac_set_fmt, | ||
412 | .mute_stream = stih416_sas_dac_mute, | ||
413 | .prepare = sti_sas_prepare, | ||
414 | .set_sysclk = sti_sas_set_sysclk, | ||
415 | }; | ||
416 | |||
417 | static const struct snd_soc_dai_ops stih407_dac_ops = { | ||
418 | .set_fmt = sti_sas_dac_set_fmt, | ||
419 | .mute_stream = stih407_sas_dac_mute, | ||
420 | .prepare = sti_sas_prepare, | ||
421 | .set_sysclk = sti_sas_set_sysclk, | ||
422 | }; | ||
423 | |||
424 | static const struct regmap_config stih407_sas_regmap = { | ||
425 | .reg_bits = 32, | ||
426 | .val_bits = 32, | ||
427 | |||
428 | .max_register = STIH407_AUDIO_DAC_CTRL, | ||
429 | .reg_defaults = stih407_sas_reg_defaults, | ||
430 | .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults), | ||
431 | .volatile_reg = sti_sas_volatile_register, | ||
432 | .cache_type = REGCACHE_RBTREE, | ||
433 | .reg_read = sti_sas_read_reg, | ||
434 | .reg_write = sti_sas_write_reg, | ||
435 | }; | ||
436 | |||
437 | static const struct regmap_config stih416_sas_regmap = { | ||
438 | .reg_bits = 32, | ||
439 | .val_bits = 32, | ||
440 | |||
441 | .max_register = STIH416_AUDIO_DAC_CTRL, | ||
442 | .reg_defaults = stih416_sas_reg_defaults, | ||
443 | .num_reg_defaults = ARRAY_SIZE(stih416_sas_reg_defaults), | ||
444 | .volatile_reg = sti_sas_volatile_register, | ||
445 | .cache_type = REGCACHE_RBTREE, | ||
446 | .reg_read = sti_sas_read_reg, | ||
447 | .reg_write = sti_sas_write_reg, | ||
448 | }; | ||
449 | |||
450 | static const struct sti_sas_dev_data stih416_data = { | ||
451 | .chipid = CHIPID_STIH416, | ||
452 | .regmap = &stih416_sas_regmap, | ||
453 | .dac_ops = &stih416_dac_ops, | ||
454 | .dapm_widgets = stih416_sas_dapm_widgets, | ||
455 | .num_dapm_widgets = ARRAY_SIZE(stih416_sas_dapm_widgets), | ||
456 | .dapm_routes = stih416_sas_route, | ||
457 | .num_dapm_routes = ARRAY_SIZE(stih416_sas_route), | ||
458 | }; | ||
459 | |||
460 | static const struct sti_sas_dev_data stih407_data = { | ||
461 | .chipid = CHIPID_STIH407, | ||
462 | .regmap = &stih407_sas_regmap, | ||
463 | .dac_ops = &stih407_dac_ops, | ||
464 | .dapm_widgets = stih407_sas_dapm_widgets, | ||
465 | .num_dapm_widgets = ARRAY_SIZE(stih407_sas_dapm_widgets), | ||
466 | .dapm_routes = stih407_sas_route, | ||
467 | .num_dapm_routes = ARRAY_SIZE(stih407_sas_route), | ||
468 | }; | ||
469 | |||
470 | static struct snd_soc_dai_driver sti_sas_dai[] = { | ||
471 | { | ||
472 | .name = "sas-dai-spdif-out", | ||
473 | .id = STI_SAS_DAI_SPDIF_OUT, | ||
474 | .playback = { | ||
475 | .stream_name = "spdif_p", | ||
476 | .channels_min = 2, | ||
477 | .channels_max = 2, | ||
478 | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | | ||
479 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | | ||
480 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | | ||
481 | SNDRV_PCM_RATE_192000, | ||
482 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | ||
483 | SNDRV_PCM_FMTBIT_S32_LE, | ||
484 | }, | ||
485 | .ops = (struct snd_soc_dai_ops[]) { | ||
486 | { | ||
487 | .set_fmt = sti_sas_spdif_set_fmt, | ||
488 | .trigger = sti_sas_spdif_trigger, | ||
489 | .set_sysclk = sti_sas_set_sysclk, | ||
490 | .prepare = sti_sas_prepare, | ||
491 | } | ||
492 | }, | ||
493 | }, | ||
494 | { | ||
495 | .name = "sas-dai-dac", | ||
496 | .id = STI_SAS_DAI_ANALOG_OUT, | ||
497 | .playback = { | ||
498 | .stream_name = "dac_p", | ||
499 | .channels_min = 2, | ||
500 | .channels_max = 2, | ||
501 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
502 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | ||
503 | SNDRV_PCM_FMTBIT_S32_LE, | ||
504 | }, | ||
505 | }, | ||
506 | }; | ||
507 | |||
508 | #ifdef CONFIG_PM_SLEEP | ||
509 | static int sti_sas_resume(struct snd_soc_codec *codec) | ||
510 | { | ||
511 | struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); | ||
512 | |||
513 | return sti_sas_init_sas_registers(codec, drvdata); | ||
514 | } | ||
515 | #else | ||
516 | #define sti_sas_resume NULL | ||
517 | #endif | ||
518 | |||
519 | static int sti_sas_codec_probe(struct snd_soc_codec *codec) | ||
520 | { | ||
521 | struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); | ||
522 | int ret; | ||
523 | |||
524 | ret = sti_sas_init_sas_registers(codec, drvdata); | ||
525 | |||
526 | return ret; | ||
527 | } | ||
528 | |||
529 | static struct snd_soc_codec_driver sti_sas_driver = { | ||
530 | .probe = sti_sas_codec_probe, | ||
531 | .resume = sti_sas_resume, | ||
532 | }; | ||
533 | |||
534 | static const struct of_device_id sti_sas_dev_match[] = { | ||
535 | { | ||
536 | .compatible = "st,stih416-sas-codec", | ||
537 | .data = &stih416_data, | ||
538 | }, | ||
539 | { | ||
540 | .compatible = "st,stih407-sas-codec", | ||
541 | .data = &stih407_data, | ||
542 | }, | ||
543 | {}, | ||
544 | }; | ||
545 | |||
546 | static int sti_sas_driver_probe(struct platform_device *pdev) | ||
547 | { | ||
548 | struct device_node *pnode = pdev->dev.of_node; | ||
549 | struct sti_sas_data *drvdata; | ||
550 | const struct of_device_id *of_id; | ||
551 | |||
552 | /* Allocate device structure */ | ||
553 | drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_data), | ||
554 | GFP_KERNEL); | ||
555 | if (!drvdata) | ||
556 | return -ENOMEM; | ||
557 | |||
558 | /* Populate data structure depending on compatibility */ | ||
559 | of_id = of_match_node(sti_sas_dev_match, pnode); | ||
560 | if (!of_id->data) { | ||
561 | dev_err(&pdev->dev, "data associated to device is missing"); | ||
562 | return -EINVAL; | ||
563 | } | ||
564 | |||
565 | drvdata->dev_data = (struct sti_sas_dev_data *)of_id->data; | ||
566 | |||
567 | /* Initialise device structure */ | ||
568 | drvdata->dev = &pdev->dev; | ||
569 | |||
570 | /* Request the DAC & SPDIF registers memory region */ | ||
571 | drvdata->dac.virt_regmap = devm_regmap_init(&pdev->dev, NULL, drvdata, | ||
572 | drvdata->dev_data->regmap); | ||
573 | if (IS_ERR(drvdata->dac.virt_regmap)) { | ||
574 | dev_err(&pdev->dev, "audio registers not enabled\n"); | ||
575 | return PTR_ERR(drvdata->dac.virt_regmap); | ||
576 | } | ||
577 | |||
578 | /* Request the syscon region */ | ||
579 | drvdata->dac.regmap = | ||
580 | syscon_regmap_lookup_by_phandle(pnode, "st,syscfg"); | ||
581 | if (IS_ERR(drvdata->dac.regmap)) { | ||
582 | dev_err(&pdev->dev, "syscon registers not available\n"); | ||
583 | return PTR_ERR(drvdata->dac.regmap); | ||
584 | } | ||
585 | drvdata->spdif.regmap = drvdata->dac.regmap; | ||
586 | |||
587 | /* Set DAC dai probe */ | ||
588 | if (drvdata->dev_data->chipid == CHIPID_STIH416) | ||
589 | sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].probe = stih416_dac_probe; | ||
590 | |||
591 | sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops; | ||
592 | |||
593 | /* Set dapms*/ | ||
594 | sti_sas_driver.dapm_widgets = drvdata->dev_data->dapm_widgets; | ||
595 | sti_sas_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets; | ||
596 | |||
597 | sti_sas_driver.dapm_routes = drvdata->dev_data->dapm_routes; | ||
598 | sti_sas_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes; | ||
599 | |||
600 | /* Store context */ | ||
601 | dev_set_drvdata(&pdev->dev, drvdata); | ||
602 | |||
603 | return snd_soc_register_codec(&pdev->dev, &sti_sas_driver, | ||
604 | sti_sas_dai, | ||
605 | ARRAY_SIZE(sti_sas_dai)); | ||
606 | } | ||
607 | |||
608 | static int sti_sas_driver_remove(struct platform_device *pdev) | ||
609 | { | ||
610 | snd_soc_unregister_codec(&pdev->dev); | ||
611 | |||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | static struct platform_driver sti_sas_platform_driver = { | ||
616 | .driver = { | ||
617 | .name = "sti-sas-codec", | ||
618 | .of_match_table = sti_sas_dev_match, | ||
619 | }, | ||
620 | .probe = sti_sas_driver_probe, | ||
621 | .remove = sti_sas_driver_remove, | ||
622 | }; | ||
623 | |||
624 | module_platform_driver(sti_sas_platform_driver); | ||
625 | |||
626 | MODULE_DESCRIPTION("audio codec for STMicroelectronics sti platforms"); | ||
627 | MODULE_AUTHOR("Arnaud.pouliquen@st.com"); | ||
628 | MODULE_LICENSE("GPL v2"); | ||