Commit | Line | Data |
---|---|---|
af873fce | 1 | // SPDX-License-Identifier: GPL-2.0-only |
32a726b2 AP |
2 | /* |
3 | * Copyright (C) STMicroelectronics SA 2015 | |
4 | * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com> | |
5 | * for STMicroelectronics. | |
32a726b2 AP |
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 | ||
32a726b2 AP |
17 | /* DAC definitions */ |
18 | ||
32a726b2 AP |
19 | /* stih407 DAC registers */ |
20 | /* sysconf 5041: Audio-Gue-Control */ | |
21 | #define STIH407_AUDIO_GLUE_CTRL 0x000000A4 | |
22 | /* sysconf 5042: Audio-DAC-Control */ | |
23 | #define STIH407_AUDIO_DAC_CTRL 0x000000A8 | |
24 | ||
25 | /* DAC definitions */ | |
26 | #define STIH407_DAC_SOFTMUTE 0x0 | |
27 | #define STIH407_DAC_STANDBY_ANA 0x1 | |
28 | #define STIH407_DAC_STANDBY 0x2 | |
29 | ||
30 | #define STIH407_DAC_SOFTMUTE_MASK BIT(STIH407_DAC_SOFTMUTE) | |
31 | #define STIH407_DAC_STANDBY_ANA_MASK BIT(STIH407_DAC_STANDBY_ANA) | |
32 | #define STIH407_DAC_STANDBY_MASK BIT(STIH407_DAC_STANDBY) | |
33 | ||
34 | /* SPDIF definitions */ | |
35 | #define SPDIF_BIPHASE_ENABLE 0x6 | |
36 | #define SPDIF_BIPHASE_IDLE 0x7 | |
37 | ||
38 | #define SPDIF_BIPHASE_ENABLE_MASK BIT(SPDIF_BIPHASE_ENABLE) | |
39 | #define SPDIF_BIPHASE_IDLE_MASK BIT(SPDIF_BIPHASE_IDLE) | |
40 | ||
41 | enum { | |
42 | STI_SAS_DAI_SPDIF_OUT, | |
43 | STI_SAS_DAI_ANALOG_OUT, | |
44 | }; | |
45 | ||
32a726b2 | 46 | static const struct reg_default stih407_sas_reg_defaults[] = { |
165a57a3 AP |
47 | { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, |
48 | { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, | |
32a726b2 AP |
49 | }; |
50 | ||
51 | struct sti_dac_audio { | |
52 | struct regmap *regmap; | |
53 | struct regmap *virt_regmap; | |
54 | struct regmap_field **field; | |
55 | struct reset_control *rst; | |
56 | int mclk; | |
57 | }; | |
58 | ||
59 | struct sti_spdif_audio { | |
60 | struct regmap *regmap; | |
61 | struct regmap_field **field; | |
62 | int mclk; | |
63 | }; | |
64 | ||
65 | /* device data structure */ | |
66 | struct sti_sas_dev_data { | |
32a726b2 AP |
67 | const struct regmap_config *regmap; |
68 | const struct snd_soc_dai_ops *dac_ops; /* DAC function callbacks */ | |
69 | const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */ | |
70 | const int num_dapm_widgets; /* dapms declaration */ | |
71 | const struct snd_soc_dapm_route *dapm_routes; /* route declaration */ | |
72 | const int num_dapm_routes; /* route declaration */ | |
73 | }; | |
74 | ||
75 | /* driver data structure */ | |
76 | struct sti_sas_data { | |
77 | struct device *dev; | |
78 | const struct sti_sas_dev_data *dev_data; | |
79 | struct sti_dac_audio dac; | |
80 | struct sti_spdif_audio spdif; | |
81 | }; | |
82 | ||
83 | /* Read a register from the sysconf reg bank */ | |
84 | static int sti_sas_read_reg(void *context, unsigned int reg, | |
85 | unsigned int *value) | |
86 | { | |
87 | struct sti_sas_data *drvdata = context; | |
88 | int status; | |
89 | u32 val; | |
90 | ||
91 | status = regmap_read(drvdata->dac.regmap, reg, &val); | |
92 | *value = (unsigned int)val; | |
93 | ||
94 | return status; | |
95 | } | |
96 | ||
97 | /* Read a register from the sysconf reg bank */ | |
98 | static int sti_sas_write_reg(void *context, unsigned int reg, | |
99 | unsigned int value) | |
100 | { | |
101 | struct sti_sas_data *drvdata = context; | |
102 | int status; | |
103 | ||
104 | status = regmap_write(drvdata->dac.regmap, reg, value); | |
105 | ||
106 | return status; | |
107 | } | |
108 | ||
049c1bfc | 109 | static int sti_sas_init_sas_registers(struct snd_soc_component *component, |
32a726b2 AP |
110 | struct sti_sas_data *data) |
111 | { | |
112 | int ret; | |
113 | /* | |
114 | * DAC and SPDIF are activated by default | |
115 | * put them in IDLE to save power | |
116 | */ | |
117 | ||
118 | /* Initialise bi-phase formatter to disabled */ | |
049c1bfc | 119 | ret = snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, |
32a726b2 AP |
120 | SPDIF_BIPHASE_ENABLE_MASK, 0); |
121 | ||
122 | if (!ret) | |
123 | /* Initialise bi-phase formatter idle value to 0 */ | |
049c1bfc | 124 | ret = snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, |
32a726b2 AP |
125 | SPDIF_BIPHASE_IDLE_MASK, 0); |
126 | if (ret < 0) { | |
049c1bfc | 127 | dev_err(component->dev, "Failed to update SPDIF registers\n"); |
32a726b2 AP |
128 | return ret; |
129 | } | |
130 | ||
131 | /* Init DAC configuration */ | |
165a57a3 | 132 | /* init configuration */ |
049c1bfc | 133 | ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, |
165a57a3 AP |
134 | STIH407_DAC_STANDBY_MASK, |
135 | STIH407_DAC_STANDBY_MASK); | |
136 | ||
137 | if (!ret) | |
049c1bfc | 138 | ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, |
165a57a3 AP |
139 | STIH407_DAC_STANDBY_ANA_MASK, |
140 | STIH407_DAC_STANDBY_ANA_MASK); | |
141 | if (!ret) | |
049c1bfc | 142 | ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, |
165a57a3 AP |
143 | STIH407_DAC_SOFTMUTE_MASK, |
144 | STIH407_DAC_SOFTMUTE_MASK); | |
32a726b2 AP |
145 | |
146 | if (ret < 0) { | |
049c1bfc | 147 | dev_err(component->dev, "Failed to update DAC registers\n"); |
32a726b2 AP |
148 | return ret; |
149 | } | |
150 | ||
151 | return ret; | |
152 | } | |
153 | ||
154 | /* | |
155 | * DAC | |
156 | */ | |
157 | static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |
158 | { | |
159 | /* Sanity check only */ | |
160 | if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { | |
049c1bfc | 161 | dev_err(dai->component->dev, |
32a726b2 AP |
162 | "%s: ERROR: Unsupporter master mask 0x%x\n", |
163 | __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); | |
164 | return -EINVAL; | |
165 | } | |
166 | ||
167 | return 0; | |
168 | } | |
169 | ||
589bef32 | 170 | static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { |
32a726b2 AP |
171 | SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL, |
172 | STIH407_DAC_STANDBY_ANA, 1, NULL, 0), | |
173 | SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH407_AUDIO_DAC_CTRL, | |
174 | STIH407_DAC_STANDBY, 1), | |
175 | SND_SOC_DAPM_OUTPUT("DAC Output"), | |
176 | }; | |
177 | ||
589bef32 | 178 | static const struct snd_soc_dapm_route stih407_sas_route[] = { |
32a726b2 AP |
179 | {"DAC Output", NULL, "DAC standby ana"}, |
180 | {"DAC standby ana", NULL, "DAC standby"}, | |
181 | }; | |
182 | ||
32a726b2 AP |
183 | |
184 | static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) | |
185 | { | |
049c1bfc | 186 | struct snd_soc_component *component = dai->component; |
32a726b2 AP |
187 | |
188 | if (mute) { | |
049c1bfc | 189 | return snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, |
32a726b2 AP |
190 | STIH407_DAC_SOFTMUTE_MASK, |
191 | STIH407_DAC_SOFTMUTE_MASK); | |
192 | } else { | |
049c1bfc | 193 | return snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, |
32a726b2 AP |
194 | STIH407_DAC_SOFTMUTE_MASK, |
195 | 0); | |
196 | } | |
197 | } | |
198 | ||
199 | /* | |
200 | * SPDIF | |
201 | */ | |
202 | static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai, | |
203 | unsigned int fmt) | |
204 | { | |
205 | if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { | |
049c1bfc | 206 | dev_err(dai->component->dev, |
32a726b2 AP |
207 | "%s: ERROR: Unsupporter master mask 0x%x\n", |
208 | __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); | |
209 | return -EINVAL; | |
210 | } | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | /* | |
216 | * sti_sas_spdif_trigger: | |
217 | * Trigger function is used to ensure that BiPhase Formater is disabled | |
218 | * before CPU dai is stopped. | |
219 | * This is mandatory to avoid that BPF is stalled | |
220 | */ | |
221 | static int sti_sas_spdif_trigger(struct snd_pcm_substream *substream, int cmd, | |
222 | struct snd_soc_dai *dai) | |
223 | { | |
049c1bfc | 224 | struct snd_soc_component *component = dai->component; |
32a726b2 AP |
225 | |
226 | switch (cmd) { | |
227 | case SNDRV_PCM_TRIGGER_START: | |
228 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
049c1bfc | 229 | return snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, |
32a726b2 AP |
230 | SPDIF_BIPHASE_ENABLE_MASK, |
231 | SPDIF_BIPHASE_ENABLE_MASK); | |
232 | case SNDRV_PCM_TRIGGER_RESUME: | |
233 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
234 | case SNDRV_PCM_TRIGGER_STOP: | |
235 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
049c1bfc | 236 | return snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, |
32a726b2 AP |
237 | SPDIF_BIPHASE_ENABLE_MASK, |
238 | 0); | |
239 | default: | |
240 | return -EINVAL; | |
241 | } | |
242 | } | |
243 | ||
244 | static bool sti_sas_volatile_register(struct device *dev, unsigned int reg) | |
245 | { | |
246 | if (reg == STIH407_AUDIO_GLUE_CTRL) | |
247 | return true; | |
248 | ||
249 | return false; | |
250 | } | |
251 | ||
252 | /* | |
253 | * CODEC DAIS | |
254 | */ | |
255 | ||
256 | /* | |
257 | * sti_sas_set_sysclk: | |
258 | * get MCLK input frequency to check that MCLK-FS ratio is coherent | |
259 | */ | |
260 | static int sti_sas_set_sysclk(struct snd_soc_dai *dai, int clk_id, | |
261 | unsigned int freq, int dir) | |
262 | { | |
049c1bfc KM |
263 | struct snd_soc_component *component = dai->component; |
264 | struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); | |
32a726b2 AP |
265 | |
266 | if (dir == SND_SOC_CLOCK_OUT) | |
267 | return 0; | |
268 | ||
269 | if (clk_id != 0) | |
270 | return -EINVAL; | |
271 | ||
272 | switch (dai->id) { | |
273 | case STI_SAS_DAI_SPDIF_OUT: | |
274 | drvdata->spdif.mclk = freq; | |
275 | break; | |
276 | ||
277 | case STI_SAS_DAI_ANALOG_OUT: | |
278 | drvdata->dac.mclk = freq; | |
279 | break; | |
280 | } | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
285 | static int sti_sas_prepare(struct snd_pcm_substream *substream, | |
286 | struct snd_soc_dai *dai) | |
287 | { | |
049c1bfc KM |
288 | struct snd_soc_component *component = dai->component; |
289 | struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); | |
32a726b2 AP |
290 | struct snd_pcm_runtime *runtime = substream->runtime; |
291 | ||
292 | switch (dai->id) { | |
293 | case STI_SAS_DAI_SPDIF_OUT: | |
294 | if ((drvdata->spdif.mclk / runtime->rate) != 128) { | |
049c1bfc | 295 | dev_err(component->dev, "unexpected mclk-fs ratio\n"); |
32a726b2 AP |
296 | return -EINVAL; |
297 | } | |
298 | break; | |
299 | case STI_SAS_DAI_ANALOG_OUT: | |
300 | if ((drvdata->dac.mclk / runtime->rate) != 256) { | |
049c1bfc | 301 | dev_err(component->dev, "unexpected mclk-fs ratio\n"); |
32a726b2 AP |
302 | return -EINVAL; |
303 | } | |
304 | break; | |
305 | } | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
589bef32 | 310 | static const struct snd_soc_dai_ops stih407_dac_ops = { |
32a726b2 AP |
311 | .set_fmt = sti_sas_dac_set_fmt, |
312 | .mute_stream = stih407_sas_dac_mute, | |
313 | .prepare = sti_sas_prepare, | |
314 | .set_sysclk = sti_sas_set_sysclk, | |
315 | }; | |
316 | ||
589bef32 | 317 | static const struct regmap_config stih407_sas_regmap = { |
32a726b2 AP |
318 | .reg_bits = 32, |
319 | .val_bits = 32, | |
7e235deb | 320 | .fast_io = true, |
32a726b2 AP |
321 | .max_register = STIH407_AUDIO_DAC_CTRL, |
322 | .reg_defaults = stih407_sas_reg_defaults, | |
323 | .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults), | |
324 | .volatile_reg = sti_sas_volatile_register, | |
325 | .cache_type = REGCACHE_RBTREE, | |
326 | .reg_read = sti_sas_read_reg, | |
327 | .reg_write = sti_sas_write_reg, | |
328 | }; | |
329 | ||
589bef32 | 330 | static const struct sti_sas_dev_data stih407_data = { |
32a726b2 AP |
331 | .regmap = &stih407_sas_regmap, |
332 | .dac_ops = &stih407_dac_ops, | |
333 | .dapm_widgets = stih407_sas_dapm_widgets, | |
334 | .num_dapm_widgets = ARRAY_SIZE(stih407_sas_dapm_widgets), | |
335 | .dapm_routes = stih407_sas_route, | |
336 | .num_dapm_routes = ARRAY_SIZE(stih407_sas_route), | |
337 | }; | |
338 | ||
339 | static struct snd_soc_dai_driver sti_sas_dai[] = { | |
340 | { | |
341 | .name = "sas-dai-spdif-out", | |
342 | .id = STI_SAS_DAI_SPDIF_OUT, | |
343 | .playback = { | |
344 | .stream_name = "spdif_p", | |
345 | .channels_min = 2, | |
346 | .channels_max = 2, | |
347 | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | | |
348 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | | |
349 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | | |
350 | SNDRV_PCM_RATE_192000, | |
351 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | |
352 | SNDRV_PCM_FMTBIT_S32_LE, | |
353 | }, | |
354 | .ops = (struct snd_soc_dai_ops[]) { | |
355 | { | |
356 | .set_fmt = sti_sas_spdif_set_fmt, | |
357 | .trigger = sti_sas_spdif_trigger, | |
358 | .set_sysclk = sti_sas_set_sysclk, | |
359 | .prepare = sti_sas_prepare, | |
360 | } | |
361 | }, | |
362 | }, | |
363 | { | |
364 | .name = "sas-dai-dac", | |
365 | .id = STI_SAS_DAI_ANALOG_OUT, | |
366 | .playback = { | |
367 | .stream_name = "dac_p", | |
368 | .channels_min = 2, | |
369 | .channels_max = 2, | |
370 | .rates = SNDRV_PCM_RATE_8000_48000, | |
371 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | |
372 | SNDRV_PCM_FMTBIT_S32_LE, | |
373 | }, | |
374 | }, | |
375 | }; | |
376 | ||
377 | #ifdef CONFIG_PM_SLEEP | |
049c1bfc | 378 | static int sti_sas_resume(struct snd_soc_component *component) |
32a726b2 | 379 | { |
049c1bfc | 380 | struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); |
32a726b2 | 381 | |
049c1bfc | 382 | return sti_sas_init_sas_registers(component, drvdata); |
32a726b2 AP |
383 | } |
384 | #else | |
385 | #define sti_sas_resume NULL | |
386 | #endif | |
387 | ||
049c1bfc | 388 | static int sti_sas_component_probe(struct snd_soc_component *component) |
32a726b2 | 389 | { |
049c1bfc | 390 | struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); |
32a726b2 AP |
391 | int ret; |
392 | ||
049c1bfc | 393 | ret = sti_sas_init_sas_registers(component, drvdata); |
32a726b2 AP |
394 | |
395 | return ret; | |
396 | } | |
397 | ||
049c1bfc KM |
398 | static struct snd_soc_component_driver sti_sas_driver = { |
399 | .probe = sti_sas_component_probe, | |
400 | .resume = sti_sas_resume, | |
401 | .idle_bias_on = 1, | |
402 | .use_pmdown_time = 1, | |
403 | .endianness = 1, | |
404 | .non_legacy_dai_naming = 1, | |
32a726b2 AP |
405 | }; |
406 | ||
407 | static const struct of_device_id sti_sas_dev_match[] = { | |
32a726b2 AP |
408 | { |
409 | .compatible = "st,stih407-sas-codec", | |
410 | .data = &stih407_data, | |
411 | }, | |
412 | {}, | |
413 | }; | |
414 | ||
415 | static int sti_sas_driver_probe(struct platform_device *pdev) | |
416 | { | |
417 | struct device_node *pnode = pdev->dev.of_node; | |
418 | struct sti_sas_data *drvdata; | |
601b9d9c | 419 | const struct of_device_id *of_id; |
32a726b2 AP |
420 | |
421 | /* Allocate device structure */ | |
422 | drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_data), | |
423 | GFP_KERNEL); | |
424 | if (!drvdata) | |
425 | return -ENOMEM; | |
426 | ||
427 | /* Populate data structure depending on compatibility */ | |
601b9d9c AP |
428 | of_id = of_match_node(sti_sas_dev_match, pnode); |
429 | if (!of_id->data) { | |
92591efa | 430 | dev_err(&pdev->dev, "data associated to device is missing\n"); |
32a726b2 AP |
431 | return -EINVAL; |
432 | } | |
433 | ||
601b9d9c | 434 | drvdata->dev_data = (struct sti_sas_dev_data *)of_id->data; |
32a726b2 AP |
435 | |
436 | /* Initialise device structure */ | |
437 | drvdata->dev = &pdev->dev; | |
438 | ||
439 | /* Request the DAC & SPDIF registers memory region */ | |
440 | drvdata->dac.virt_regmap = devm_regmap_init(&pdev->dev, NULL, drvdata, | |
441 | drvdata->dev_data->regmap); | |
e27d9ee6 | 442 | if (IS_ERR(drvdata->dac.virt_regmap)) { |
32a726b2 | 443 | dev_err(&pdev->dev, "audio registers not enabled\n"); |
e27d9ee6 | 444 | return PTR_ERR(drvdata->dac.virt_regmap); |
32a726b2 AP |
445 | } |
446 | ||
447 | /* Request the syscon region */ | |
448 | drvdata->dac.regmap = | |
449 | syscon_regmap_lookup_by_phandle(pnode, "st,syscfg"); | |
e27d9ee6 | 450 | if (IS_ERR(drvdata->dac.regmap)) { |
32a726b2 | 451 | dev_err(&pdev->dev, "syscon registers not available\n"); |
e27d9ee6 | 452 | return PTR_ERR(drvdata->dac.regmap); |
32a726b2 AP |
453 | } |
454 | drvdata->spdif.regmap = drvdata->dac.regmap; | |
455 | ||
32a726b2 AP |
456 | sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops; |
457 | ||
458 | /* Set dapms*/ | |
049c1bfc KM |
459 | sti_sas_driver.dapm_widgets = drvdata->dev_data->dapm_widgets; |
460 | sti_sas_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets; | |
32a726b2 | 461 | |
049c1bfc KM |
462 | sti_sas_driver.dapm_routes = drvdata->dev_data->dapm_routes; |
463 | sti_sas_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes; | |
32a726b2 AP |
464 | |
465 | /* Store context */ | |
466 | dev_set_drvdata(&pdev->dev, drvdata); | |
467 | ||
049c1bfc | 468 | return devm_snd_soc_register_component(&pdev->dev, &sti_sas_driver, |
32a726b2 AP |
469 | sti_sas_dai, |
470 | ARRAY_SIZE(sti_sas_dai)); | |
471 | } | |
472 | ||
32a726b2 AP |
473 | static struct platform_driver sti_sas_platform_driver = { |
474 | .driver = { | |
475 | .name = "sti-sas-codec", | |
32a726b2 AP |
476 | .of_match_table = sti_sas_dev_match, |
477 | }, | |
478 | .probe = sti_sas_driver_probe, | |
32a726b2 AP |
479 | }; |
480 | ||
481 | module_platform_driver(sti_sas_platform_driver); | |
482 | ||
483 | MODULE_DESCRIPTION("audio codec for STMicroelectronics sti platforms"); | |
484 | MODULE_AUTHOR("Arnaud.pouliquen@st.com"); | |
485 | MODULE_LICENSE("GPL v2"); |