Commit | Line | Data |
---|---|---|
32a726b2 AP |
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, | |
601b9d9c AP |
230 | "%s: ERROR: DAC reset control not defined !\n", |
231 | __func__); | |
32a726b2 AP |
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 | ||
589bef32 | 241 | static const struct snd_soc_dapm_widget stih416_sas_dapm_widgets[] = { |
32a726b2 AP |
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 | ||
589bef32 | 251 | static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { |
32a726b2 AP |
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 | ||
589bef32 | 259 | static const struct snd_soc_dapm_route stih416_sas_route[] = { |
32a726b2 AP |
260 | {"DAC Output", NULL, "DAC bandgap"}, |
261 | {"DAC Output", NULL, "DAC standby ana"}, | |
262 | {"DAC standby ana", NULL, "DAC standby"}, | |
263 | }; | |
264 | ||
589bef32 | 265 | static const struct snd_soc_dapm_route stih407_sas_route[] = { |
32a726b2 AP |
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 | ||
589bef32 | 410 | static const struct snd_soc_dai_ops stih416_dac_ops = { |
32a726b2 AP |
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 | ||
589bef32 | 417 | static const struct snd_soc_dai_ops stih407_dac_ops = { |
32a726b2 AP |
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 | ||
589bef32 | 424 | static const struct regmap_config stih407_sas_regmap = { |
32a726b2 AP |
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 | ||
589bef32 | 437 | static const struct regmap_config stih416_sas_regmap = { |
32a726b2 AP |
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 | ||
589bef32 | 450 | static const struct sti_sas_dev_data stih416_data = { |
32a726b2 AP |
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 | ||
589bef32 | 460 | static const struct sti_sas_dev_data stih407_data = { |
32a726b2 AP |
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; | |
601b9d9c | 550 | const struct of_device_id *of_id; |
32a726b2 AP |
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 */ | |
601b9d9c AP |
559 | of_id = of_match_node(sti_sas_dev_match, pnode); |
560 | if (!of_id->data) { | |
32a726b2 AP |
561 | dev_err(&pdev->dev, "data associated to device is missing"); |
562 | return -EINVAL; | |
563 | } | |
564 | ||
601b9d9c | 565 | drvdata->dev_data = (struct sti_sas_dev_data *)of_id->data; |
32a726b2 AP |
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); | |
e27d9ee6 | 573 | if (IS_ERR(drvdata->dac.virt_regmap)) { |
32a726b2 | 574 | dev_err(&pdev->dev, "audio registers not enabled\n"); |
e27d9ee6 | 575 | return PTR_ERR(drvdata->dac.virt_regmap); |
32a726b2 AP |
576 | } |
577 | ||
578 | /* Request the syscon region */ | |
579 | drvdata->dac.regmap = | |
580 | syscon_regmap_lookup_by_phandle(pnode, "st,syscfg"); | |
e27d9ee6 | 581 | if (IS_ERR(drvdata->dac.regmap)) { |
32a726b2 | 582 | dev_err(&pdev->dev, "syscon registers not available\n"); |
e27d9ee6 | 583 | return PTR_ERR(drvdata->dac.regmap); |
32a726b2 AP |
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", | |
32a726b2 AP |
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"); |