OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From 01c84e09a331a3b4c29c1cd5ce364c91577c7cea Mon Sep 17 00:00:00 2001 |
2 | From: Matthias Reichl <hias@horus.com> |
||
3 | Date: Sun, 22 Jan 2017 12:49:37 +0100 |
||
4 | Subject: [PATCH] ASoC: Add driver for Cirrus Logic Audio Card |
||
5 | |||
6 | Note: due to problems with deferred probing of regulators |
||
7 | the following softdep should be added to a modprobe.d file |
||
8 | |||
9 | softdep arizona-spi pre: arizona-ldo1 |
||
10 | |||
11 | Signed-off-by: Matthias Reichl <hias@horus.com> |
||
12 | --- |
||
13 | arch/arm/boot/dts/overlays/Makefile | 1 + |
||
14 | arch/arm/boot/dts/overlays/README | 6 + |
||
15 | .../dts/overlays/rpi-cirrus-wm5102-overlay.dts | 146 +++ |
||
16 | sound/soc/bcm/Kconfig | 9 + |
||
17 | sound/soc/bcm/Makefile | 2 + |
||
18 | sound/soc/bcm/rpi-cirrus.c | 1003 ++++++++++++++++++++ |
||
19 | 6 files changed, 1167 insertions(+) |
||
20 | create mode 100644 arch/arm/boot/dts/overlays/rpi-cirrus-wm5102-overlay.dts |
||
21 | create mode 100644 sound/soc/bcm/rpi-cirrus.c |
||
22 | |||
23 | --- a/arch/arm/boot/dts/overlays/Makefile |
||
24 | +++ b/arch/arm/boot/dts/overlays/Makefile |
||
25 | @@ -68,6 +68,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ |
||
26 | qca7000.dtbo \ |
||
27 | raspidac3.dtbo \ |
||
28 | rpi-backlight.dtbo \ |
||
29 | + rpi-cirrus-wm5102.dtbo \ |
||
30 | rpi-dac.dtbo \ |
||
31 | rpi-display.dtbo \ |
||
32 | rpi-ft5406.dtbo \ |
||
33 | --- a/arch/arm/boot/dts/overlays/README |
||
34 | +++ b/arch/arm/boot/dts/overlays/README |
||
35 | @@ -995,6 +995,12 @@ Load: dtoverlay=rpi-backlight |
||
36 | Params: <None> |
||
37 | |||
38 | |||
39 | +Name: rpi-cirrus-wm5102 |
||
40 | +Info: Configures the Cirrus Logic Audio Card |
||
41 | +Load: dtoverlay=rpi-cirrus-wm5102 |
||
42 | +Params: <None> |
||
43 | + |
||
44 | + |
||
45 | Name: rpi-dac |
||
46 | Info: Configures the RPi DAC audio card |
||
47 | Load: dtoverlay=rpi-dac |
||
48 | --- /dev/null |
||
49 | +++ b/arch/arm/boot/dts/overlays/rpi-cirrus-wm5102-overlay.dts |
||
50 | @@ -0,0 +1,146 @@ |
||
51 | +// Definitions for the Cirrus Logic Audio Card |
||
52 | +/dts-v1/; |
||
53 | +/plugin/; |
||
54 | +#include <dt-bindings/pinctrl/bcm2835.h> |
||
55 | +#include <dt-bindings/gpio/gpio.h> |
||
56 | +#include <dt-bindings/mfd/arizona.h> |
||
57 | + |
||
58 | +/ { |
||
59 | + compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; |
||
60 | + |
||
61 | + fragment@0 { |
||
62 | + target = <&i2s>; |
||
63 | + __overlay__ { |
||
64 | + status = "okay"; |
||
65 | + }; |
||
66 | + }; |
||
67 | + |
||
68 | + fragment@1 { |
||
69 | + target = <&gpio>; |
||
70 | + __overlay__ { |
||
71 | + wlf_pins: wlf_pins { |
||
72 | + brcm,pins = <17 22 27 8>; |
||
73 | + brcm,function = < |
||
74 | + BCM2835_FSEL_GPIO_OUT |
||
75 | + BCM2835_FSEL_GPIO_OUT |
||
76 | + BCM2835_FSEL_GPIO_IN |
||
77 | + BCM2835_FSEL_GPIO_OUT |
||
78 | + >; |
||
79 | + }; |
||
80 | + }; |
||
81 | + }; |
||
82 | + |
||
83 | + fragment@2 { |
||
84 | + target-path = "/"; |
||
85 | + __overlay__ { |
||
86 | + rpi_cirrus_reg_1v8: rpi_cirrus_reg_1v8 { |
||
87 | + compatible = "regulator-fixed"; |
||
88 | + regulator-name = "RPi-Cirrus 1v8"; |
||
89 | + regulator-min-microvolt = <1800000>; |
||
90 | + regulator-max-microvolt = <1800000>; |
||
91 | + regulator-always-on; |
||
92 | + }; |
||
93 | + }; |
||
94 | + }; |
||
95 | + |
||
96 | + fragment@3 { |
||
97 | + target = <&spi0>; |
||
98 | + __overlay__ { |
||
99 | + #address-cells = <1>; |
||
100 | + #size-cells = <0>; |
||
101 | + status = "okay"; |
||
102 | + |
||
103 | + spidev@0{ |
||
104 | + status = "disabled"; |
||
105 | + }; |
||
106 | + |
||
107 | + spidev@1{ |
||
108 | + status = "disabled"; |
||
109 | + }; |
||
110 | + |
||
111 | + wm5102@1{ |
||
112 | + compatible = "wlf,wm5102"; |
||
113 | + reg = <1>; |
||
114 | + |
||
115 | + spi-max-frequency = <500000>; |
||
116 | + |
||
117 | + interrupt-parent = <&gpio>; |
||
118 | + interrupts = <27 8>; |
||
119 | + interrupt-controller; |
||
120 | + #interrupt-cells = <2>; |
||
121 | + |
||
122 | + gpio-controller; |
||
123 | + #gpio-cells = <2>; |
||
124 | + |
||
125 | + LDOVDD-supply = <&rpi_cirrus_reg_1v8>; |
||
126 | + AVDD-supply = <&rpi_cirrus_reg_1v8>; |
||
127 | + DBVDD1-supply = <&rpi_cirrus_reg_1v8>; |
||
128 | + DBVDD2-supply = <&vdd_3v3_reg>; |
||
129 | + DBVDD3-supply = <&vdd_3v3_reg>; |
||
130 | + CPVDD-supply = <&rpi_cirrus_reg_1v8>; |
||
131 | + SPKVDDL-supply = <&vdd_5v0_reg>; |
||
132 | + SPKVDDR-supply = <&vdd_5v0_reg>; |
||
133 | + DCVDD-supply = <&arizona_ldo1>; |
||
134 | + |
||
135 | + wlf,reset = <&gpio 17 GPIO_ACTIVE_HIGH>; |
||
136 | + wlf,ldoena = <&gpio 22 GPIO_ACTIVE_HIGH>; |
||
137 | + wlf,gpio-defaults = < |
||
138 | + ARIZONA_GP_DEFAULT |
||
139 | + ARIZONA_GP_DEFAULT |
||
140 | + ARIZONA_GP_DEFAULT |
||
141 | + ARIZONA_GP_DEFAULT |
||
142 | + ARIZONA_GP_DEFAULT |
||
143 | + >; |
||
144 | + wlf,micd-configs = <0 1 0>; |
||
145 | + wlf,dmic-ref = < |
||
146 | + ARIZONA_DMIC_MICVDD |
||
147 | + ARIZONA_DMIC_MICBIAS2 |
||
148 | + ARIZONA_DMIC_MICVDD |
||
149 | + ARIZONA_DMIC_MICVDD |
||
150 | + >; |
||
151 | + wlf,inmode = < |
||
152 | + ARIZONA_INMODE_DIFF |
||
153 | + ARIZONA_INMODE_DMIC |
||
154 | + ARIZONA_INMODE_SE |
||
155 | + ARIZONA_INMODE_DIFF |
||
156 | + >; |
||
157 | + status = "okay"; |
||
158 | + |
||
159 | + arizona_ldo1: ldo1 { |
||
160 | + regulator-name = "LDO1"; |
||
161 | + // default constraints as in |
||
162 | + // arizona-ldo1.c |
||
163 | + regulator-min-microvolt = <1200000>; |
||
164 | + regulator-max-microvolt = <1800000>; |
||
165 | + }; |
||
166 | + }; |
||
167 | + }; |
||
168 | + }; |
||
169 | + |
||
170 | + fragment@4 { |
||
171 | + target = <&i2c1>; |
||
172 | + __overlay__ { |
||
173 | + status = "okay"; |
||
174 | + #address-cells = <1>; |
||
175 | + #size-cells = <0>; |
||
176 | + |
||
177 | + wm8804@3b { |
||
178 | + compatible = "wlf,wm8804"; |
||
179 | + reg = <0x3b>; |
||
180 | + status = "okay"; |
||
181 | + PVDD-supply = <&vdd_3v3_reg>; |
||
182 | + DVDD-supply = <&vdd_3v3_reg>; |
||
183 | + wlf,reset-gpio = <&gpio 8 GPIO_ACTIVE_HIGH>; |
||
184 | + }; |
||
185 | + }; |
||
186 | + }; |
||
187 | + |
||
188 | + fragment@5 { |
||
189 | + target = <&sound>; |
||
190 | + __overlay__ { |
||
191 | + compatible = "wlf,rpi-cirrus"; |
||
192 | + i2s-controller = <&i2s>; |
||
193 | + status = "okay"; |
||
194 | + }; |
||
195 | + }; |
||
196 | +}; |
||
197 | --- a/sound/soc/bcm/Kconfig |
||
198 | +++ b/sound/soc/bcm/Kconfig |
||
199 | @@ -45,6 +45,15 @@ config SND_BCM2708_SOC_HIFIBERRY_AMP |
||
200 | help |
||
201 | Say Y or M if you want to add support for the HifiBerry Amp amplifier board. |
||
202 | |||
203 | +config SND_BCM2708_SOC_RPI_CIRRUS |
||
204 | + tristate "Support for Cirrus Logic Audio Card" |
||
205 | + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S |
||
206 | + select SND_SOC_WM5102 |
||
207 | + select SND_SOC_WM8804 |
||
208 | + help |
||
209 | + Say Y or M if you want to add support for the Wolfson and |
||
210 | + Cirrus Logic audio cards. |
||
211 | + |
||
212 | config SND_BCM2708_SOC_RPI_DAC |
||
213 | tristate "Support for RPi-DAC" |
||
214 | depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S |
||
215 | --- a/sound/soc/bcm/Makefile |
||
216 | +++ b/sound/soc/bcm/Makefile |
||
217 | @@ -16,6 +16,7 @@ snd-soc-hifiberry-dacplus-objs := hifibe |
||
218 | snd-soc-hifiberry-digi-objs := hifiberry_digi.o |
||
219 | snd-soc-justboom-dac-objs := justboom-dac.o |
||
220 | snd-soc-justboom-digi-objs := justboom-digi.o |
||
221 | +snd-soc-rpi-cirrus-objs := rpi-cirrus.o |
||
222 | snd-soc-rpi-dac-objs := rpi-dac.o |
||
223 | snd-soc-rpi-proto-objs := rpi-proto.o |
||
224 | snd-soc-iqaudio-dac-objs := iqaudio-dac.o |
||
225 | @@ -34,6 +35,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_D |
||
226 | obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o |
||
227 | obj-$(CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC) += snd-soc-justboom-dac.o |
||
228 | obj-$(CONFIG_SND_BCM2708_SOC_JUSTBOOM_DIGI) += snd-soc-justboom-digi.o |
||
229 | +obj-$(CONFIG_SND_BCM2708_SOC_RPI_CIRRUS) += snd-soc-rpi-cirrus.o |
||
230 | obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o |
||
231 | obj-$(CONFIG_SND_BCM2708_SOC_RPI_PROTO) += snd-soc-rpi-proto.o |
||
232 | obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o |
||
233 | --- /dev/null |
||
234 | +++ b/sound/soc/bcm/rpi-cirrus.c |
||
235 | @@ -0,0 +1,1003 @@ |
||
236 | +/* |
||
237 | + * ASoC machine driver for Cirrus Logic Audio Card |
||
238 | + * (with WM5102 and WM8804 codecs) |
||
239 | + * |
||
240 | + * Copyright 2015-2017 Matthias Reichl <hias@horus.com> |
||
241 | + * |
||
242 | + * Based on rpi-cirrus-sound-pi driver (c) Wolfson / Cirrus Logic Inc. |
||
243 | + * |
||
244 | + * This program is free software; you can redistribute it and/or modify |
||
245 | + * it under the terms of the GNU General Public License version 2 as |
||
246 | + * published by the Free Software Foundation. |
||
247 | + */ |
||
248 | + |
||
249 | +#include <linux/module.h> |
||
250 | +#include <linux/mutex.h> |
||
251 | +#include <linux/slab.h> |
||
252 | +#include <linux/list.h> |
||
253 | +#include <linux/delay.h> |
||
254 | +#include <sound/pcm_params.h> |
||
255 | + |
||
256 | +#include <linux/mfd/arizona/registers.h> |
||
257 | + |
||
258 | +#include "../codecs/wm5102.h" |
||
259 | +#include "../codecs/wm8804.h" |
||
260 | + |
||
261 | +#define WM8804_CLKOUT_HZ 12000000 |
||
262 | + |
||
263 | +#define RPI_CIRRUS_DEFAULT_RATE 44100 |
||
264 | +#define WM5102_MAX_SYSCLK_1 49152000 /* max sysclk for 4K family */ |
||
265 | +#define WM5102_MAX_SYSCLK_2 45158400 /* max sysclk for 11.025K family */ |
||
266 | + |
||
267 | +static inline unsigned int calc_sysclk(unsigned int rate) |
||
268 | +{ |
||
269 | + return (rate % 4000) ? WM5102_MAX_SYSCLK_2 : WM5102_MAX_SYSCLK_1; |
||
270 | +} |
||
271 | + |
||
272 | +enum { |
||
273 | + DAI_WM5102 = 0, |
||
274 | + DAI_WM8804, |
||
275 | +}; |
||
276 | + |
||
277 | +struct rpi_cirrus_priv { |
||
278 | + /* mutex for synchronzing FLL1 access with DAPM */ |
||
279 | + struct mutex lock; |
||
280 | + unsigned int card_rate; |
||
281 | + int sync_path_enable; |
||
282 | + int fll1_freq; /* negative means RefClock in spdif rx case */ |
||
283 | + |
||
284 | + /* track hw params/free for substreams */ |
||
285 | + unsigned int params_set; |
||
286 | + unsigned int min_rate_idx, max_rate_idx; |
||
287 | + unsigned char iec958_status[4]; |
||
288 | +}; |
||
289 | + |
||
290 | +/* helper functions */ |
||
291 | +static inline struct snd_soc_pcm_runtime *get_wm5102_runtime( |
||
292 | + struct snd_soc_card *card) { |
||
293 | + return snd_soc_get_pcm_runtime(card, card->dai_link[DAI_WM5102].name); |
||
294 | +} |
||
295 | + |
||
296 | +static inline struct snd_soc_pcm_runtime *get_wm8804_runtime( |
||
297 | + struct snd_soc_card *card) { |
||
298 | + return snd_soc_get_pcm_runtime(card, card->dai_link[DAI_WM8804].name); |
||
299 | +} |
||
300 | + |
||
301 | + |
||
302 | +struct rate_info { |
||
303 | + unsigned int value; |
||
304 | + char *text; |
||
305 | +}; |
||
306 | + |
||
307 | +static struct rate_info min_rates[] = { |
||
308 | + { 0, "off"}, |
||
309 | + { 32000, "32kHz"}, |
||
310 | + { 44100, "44.1kHz"} |
||
311 | +}; |
||
312 | + |
||
313 | +#define NUM_MIN_RATES ARRAY_SIZE(min_rates) |
||
314 | + |
||
315 | +static int rpi_cirrus_min_rate_info(struct snd_kcontrol *kcontrol, |
||
316 | + struct snd_ctl_elem_info *uinfo) |
||
317 | +{ |
||
318 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; |
||
319 | + uinfo->count = 1; |
||
320 | + uinfo->value.enumerated.items = NUM_MIN_RATES; |
||
321 | + |
||
322 | + if (uinfo->value.enumerated.item >= NUM_MIN_RATES) |
||
323 | + uinfo->value.enumerated.item = NUM_MIN_RATES - 1; |
||
324 | + strcpy(uinfo->value.enumerated.name, |
||
325 | + min_rates[uinfo->value.enumerated.item].text); |
||
326 | + return 0; |
||
327 | +} |
||
328 | + |
||
329 | +static int rpi_cirrus_min_rate_get(struct snd_kcontrol *kcontrol, |
||
330 | + struct snd_ctl_elem_value *ucontrol) |
||
331 | +{ |
||
332 | + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); |
||
333 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
334 | + |
||
335 | + ucontrol->value.enumerated.item[0] = priv->min_rate_idx; |
||
336 | + return 0; |
||
337 | +} |
||
338 | + |
||
339 | +static int rpi_cirrus_min_rate_put(struct snd_kcontrol *kcontrol, |
||
340 | + struct snd_ctl_elem_value *ucontrol) |
||
341 | +{ |
||
342 | + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); |
||
343 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
344 | + int changed = 0; |
||
345 | + |
||
346 | + if (priv->min_rate_idx != ucontrol->value.enumerated.item[0]) { |
||
347 | + changed = 1; |
||
348 | + priv->min_rate_idx = ucontrol->value.enumerated.item[0]; |
||
349 | + } |
||
350 | + |
||
351 | + return changed; |
||
352 | +} |
||
353 | + |
||
354 | +static struct rate_info max_rates[] = { |
||
355 | + { 0, "off"}, |
||
356 | + { 48000, "48kHz"}, |
||
357 | + { 96000, "96kHz"} |
||
358 | +}; |
||
359 | + |
||
360 | +#define NUM_MAX_RATES ARRAY_SIZE(max_rates) |
||
361 | + |
||
362 | +static int rpi_cirrus_max_rate_info(struct snd_kcontrol *kcontrol, |
||
363 | + struct snd_ctl_elem_info *uinfo) |
||
364 | +{ |
||
365 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; |
||
366 | + uinfo->count = 1; |
||
367 | + uinfo->value.enumerated.items = NUM_MAX_RATES; |
||
368 | + if (uinfo->value.enumerated.item >= NUM_MAX_RATES) |
||
369 | + uinfo->value.enumerated.item = NUM_MAX_RATES - 1; |
||
370 | + strcpy(uinfo->value.enumerated.name, |
||
371 | + max_rates[uinfo->value.enumerated.item].text); |
||
372 | + return 0; |
||
373 | +} |
||
374 | + |
||
375 | +static int rpi_cirrus_max_rate_get(struct snd_kcontrol *kcontrol, |
||
376 | + struct snd_ctl_elem_value *ucontrol) |
||
377 | +{ |
||
378 | + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); |
||
379 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
380 | + |
||
381 | + ucontrol->value.enumerated.item[0] = priv->max_rate_idx; |
||
382 | + return 0; |
||
383 | +} |
||
384 | + |
||
385 | +static int rpi_cirrus_max_rate_put(struct snd_kcontrol *kcontrol, |
||
386 | + struct snd_ctl_elem_value *ucontrol) |
||
387 | +{ |
||
388 | + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); |
||
389 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
390 | + int changed = 0; |
||
391 | + |
||
392 | + if (priv->max_rate_idx != ucontrol->value.enumerated.item[0]) { |
||
393 | + changed = 1; |
||
394 | + priv->max_rate_idx = ucontrol->value.enumerated.item[0]; |
||
395 | + } |
||
396 | + |
||
397 | + return changed; |
||
398 | +} |
||
399 | + |
||
400 | +static int rpi_cirrus_spdif_info(struct snd_kcontrol *kcontrol, |
||
401 | + struct snd_ctl_elem_info *uinfo) |
||
402 | +{ |
||
403 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; |
||
404 | + uinfo->count = 1; |
||
405 | + return 0; |
||
406 | +} |
||
407 | + |
||
408 | +static int rpi_cirrus_spdif_playback_get(struct snd_kcontrol *kcontrol, |
||
409 | + struct snd_ctl_elem_value *ucontrol) |
||
410 | +{ |
||
411 | + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); |
||
412 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
413 | + int i; |
||
414 | + |
||
415 | + for (i = 0; i < 4; i++) |
||
416 | + ucontrol->value.iec958.status[i] = priv->iec958_status[i]; |
||
417 | + |
||
418 | + return 0; |
||
419 | +} |
||
420 | + |
||
421 | +static int rpi_cirrus_spdif_playback_put(struct snd_kcontrol *kcontrol, |
||
422 | + struct snd_ctl_elem_value *ucontrol) |
||
423 | +{ |
||
424 | + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); |
||
425 | + struct snd_soc_codec *wm8804_codec = get_wm8804_runtime(card)->codec; |
||
426 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
427 | + unsigned char *stat = priv->iec958_status; |
||
428 | + unsigned char *ctrl_stat = ucontrol->value.iec958.status; |
||
429 | + unsigned int mask; |
||
430 | + int i, changed = 0; |
||
431 | + |
||
432 | + for (i = 0; i < 4; i++) { |
||
433 | + mask = (i == 3) ? 0x3f : 0xff; |
||
434 | + if ((ctrl_stat[i] & mask) != (stat[i] & mask)) { |
||
435 | + changed = 1; |
||
436 | + stat[i] = ctrl_stat[i] & mask; |
||
437 | + snd_soc_update_bits(wm8804_codec, |
||
438 | + WM8804_SPDTX1 + i, mask, stat[i]); |
||
439 | + } |
||
440 | + } |
||
441 | + |
||
442 | + return changed; |
||
443 | +} |
||
444 | + |
||
445 | +static int rpi_cirrus_spdif_mask_get(struct snd_kcontrol *kcontrol, |
||
446 | + struct snd_ctl_elem_value *ucontrol) |
||
447 | +{ |
||
448 | + ucontrol->value.iec958.status[0] = 0xff; |
||
449 | + ucontrol->value.iec958.status[1] = 0xff; |
||
450 | + ucontrol->value.iec958.status[2] = 0xff; |
||
451 | + ucontrol->value.iec958.status[3] = 0x3f; |
||
452 | + |
||
453 | + return 0; |
||
454 | +} |
||
455 | + |
||
456 | +static int rpi_cirrus_spdif_capture_get(struct snd_kcontrol *kcontrol, |
||
457 | + struct snd_ctl_elem_value *ucontrol) |
||
458 | +{ |
||
459 | + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); |
||
460 | + struct snd_soc_codec *wm8804_codec = get_wm8804_runtime(card)->codec; |
||
461 | + unsigned int mask; |
||
462 | + int i; |
||
463 | + |
||
464 | + for (i = 0; i < 4; i++) { |
||
465 | + mask = (i == 3) ? 0x3f : 0xff; |
||
466 | + ucontrol->value.iec958.status[i] = |
||
467 | + snd_soc_read(wm8804_codec, WM8804_RXCHAN1 + i) & mask; |
||
468 | + } |
||
469 | + |
||
470 | + return 0; |
||
471 | +} |
||
472 | + |
||
473 | +#define SPDIF_FLAG_CTRL(desc, reg, bit, invert) \ |
||
474 | +{ \ |
||
475 | + .access = SNDRV_CTL_ELEM_ACCESS_READ \ |
||
476 | + | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ |
||
477 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ |
||
478 | + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) \ |
||
479 | + desc " Flag", \ |
||
480 | + .info = snd_ctl_boolean_mono_info, \ |
||
481 | + .get = rpi_cirrus_spdif_status_flag_get, \ |
||
482 | + .private_value = \ |
||
483 | + (bit) | ((reg) << 8) | ((invert) << 16) \ |
||
484 | +} |
||
485 | + |
||
486 | +static int rpi_cirrus_spdif_status_flag_get(struct snd_kcontrol *kcontrol, |
||
487 | + struct snd_ctl_elem_value *ucontrol) |
||
488 | +{ |
||
489 | + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); |
||
490 | + struct snd_soc_codec *wm8804_codec = get_wm8804_runtime(card)->codec; |
||
491 | + |
||
492 | + unsigned int bit = kcontrol->private_value & 0xff; |
||
493 | + unsigned int reg = (kcontrol->private_value >> 8) & 0xff; |
||
494 | + unsigned int invert = (kcontrol->private_value >> 16) & 0xff; |
||
495 | + |
||
496 | + bool flag = snd_soc_read(wm8804_codec, reg) & (1 << bit); |
||
497 | + |
||
498 | + ucontrol->value.integer.value[0] = invert ? !flag : flag; |
||
499 | + |
||
500 | + return 0; |
||
501 | +} |
||
502 | + |
||
503 | +static const char * const recovered_frequency_texts[] = { |
||
504 | + "176.4/192 kHz", |
||
505 | + "88.2/96 kHz", |
||
506 | + "44.1/48 kHz", |
||
507 | + "32 kHz" |
||
508 | +}; |
||
509 | + |
||
510 | +#define NUM_RECOVERED_FREQUENCIES \ |
||
511 | + ARRAY_SIZE(recovered_frequency_texts) |
||
512 | + |
||
513 | +static int rpi_cirrus_recovered_frequency_info(struct snd_kcontrol *kcontrol, |
||
514 | + struct snd_ctl_elem_info *uinfo) |
||
515 | +{ |
||
516 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; |
||
517 | + uinfo->count = 1; |
||
518 | + uinfo->value.enumerated.items = NUM_RECOVERED_FREQUENCIES; |
||
519 | + if (uinfo->value.enumerated.item >= NUM_RECOVERED_FREQUENCIES) |
||
520 | + uinfo->value.enumerated.item = NUM_RECOVERED_FREQUENCIES - 1; |
||
521 | + strcpy(uinfo->value.enumerated.name, |
||
522 | + recovered_frequency_texts[uinfo->value.enumerated.item]); |
||
523 | + return 0; |
||
524 | +} |
||
525 | + |
||
526 | +static int rpi_cirrus_recovered_frequency_get(struct snd_kcontrol *kcontrol, |
||
527 | + struct snd_ctl_elem_value *ucontrol) |
||
528 | +{ |
||
529 | + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); |
||
530 | + struct snd_soc_codec *wm8804_codec = get_wm8804_runtime(card)->codec; |
||
531 | + |
||
532 | + ucontrol->value.enumerated.item[0] = |
||
533 | + (snd_soc_read(wm8804_codec, WM8804_SPDSTAT) >> 4) & 0x03; |
||
534 | + return 0; |
||
535 | +} |
||
536 | + |
||
537 | +static const struct snd_kcontrol_new rpi_cirrus_controls[] = { |
||
538 | + { |
||
539 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
||
540 | + .name = "Min Sample Rate", |
||
541 | + .info = rpi_cirrus_min_rate_info, |
||
542 | + .get = rpi_cirrus_min_rate_get, |
||
543 | + .put = rpi_cirrus_min_rate_put, |
||
544 | + }, |
||
545 | + { |
||
546 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
||
547 | + .name = "Max Sample Rate", |
||
548 | + .info = rpi_cirrus_max_rate_info, |
||
549 | + .get = rpi_cirrus_max_rate_get, |
||
550 | + .put = rpi_cirrus_max_rate_put, |
||
551 | + }, |
||
552 | + { |
||
553 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
||
554 | + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), |
||
555 | + .info = rpi_cirrus_spdif_info, |
||
556 | + .get = rpi_cirrus_spdif_playback_get, |
||
557 | + .put = rpi_cirrus_spdif_playback_put, |
||
558 | + }, |
||
559 | + { |
||
560 | + .access = SNDRV_CTL_ELEM_ACCESS_READ |
||
561 | + | SNDRV_CTL_ELEM_ACCESS_VOLATILE, |
||
562 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
||
563 | + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), |
||
564 | + .info = rpi_cirrus_spdif_info, |
||
565 | + .get = rpi_cirrus_spdif_capture_get, |
||
566 | + }, |
||
567 | + { |
||
568 | + .access = SNDRV_CTL_ELEM_ACCESS_READ, |
||
569 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
||
570 | + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), |
||
571 | + .info = rpi_cirrus_spdif_info, |
||
572 | + .get = rpi_cirrus_spdif_mask_get, |
||
573 | + }, |
||
574 | + { |
||
575 | + .access = SNDRV_CTL_ELEM_ACCESS_READ |
||
576 | + | SNDRV_CTL_ELEM_ACCESS_VOLATILE, |
||
577 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
||
578 | + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) |
||
579 | + "Recovered Frequency", |
||
580 | + .info = rpi_cirrus_recovered_frequency_info, |
||
581 | + .get = rpi_cirrus_recovered_frequency_get, |
||
582 | + }, |
||
583 | + SPDIF_FLAG_CTRL("Audio", WM8804_SPDSTAT, 0, 1), |
||
584 | + SPDIF_FLAG_CTRL("Non-PCM", WM8804_SPDSTAT, 1, 0), |
||
585 | + SPDIF_FLAG_CTRL("Copyright", WM8804_SPDSTAT, 2, 1), |
||
586 | + SPDIF_FLAG_CTRL("De-Emphasis", WM8804_SPDSTAT, 3, 0), |
||
587 | + SPDIF_FLAG_CTRL("Lock", WM8804_SPDSTAT, 6, 1), |
||
588 | + SPDIF_FLAG_CTRL("Invalid", WM8804_INTSTAT, 1, 0), |
||
589 | + SPDIF_FLAG_CTRL("TransErr", WM8804_INTSTAT, 3, 0), |
||
590 | +}; |
||
591 | + |
||
592 | +static const char * const linein_micbias_texts[] = { |
||
593 | + "off", "on", |
||
594 | +}; |
||
595 | + |
||
596 | +static SOC_ENUM_SINGLE_VIRT_DECL(linein_micbias_enum, |
||
597 | + linein_micbias_texts); |
||
598 | + |
||
599 | +static const struct snd_kcontrol_new linein_micbias_mux = |
||
600 | + SOC_DAPM_ENUM("Route", linein_micbias_enum); |
||
601 | + |
||
602 | +static int rpi_cirrus_spdif_rx_enable_event(struct snd_soc_dapm_widget *w, |
||
603 | + struct snd_kcontrol *kcontrol, int event); |
||
604 | + |
||
605 | +const struct snd_soc_dapm_widget rpi_cirrus_dapm_widgets[] = { |
||
606 | + SND_SOC_DAPM_MIC("DMIC", NULL), |
||
607 | + SND_SOC_DAPM_MIC("Headset Mic", NULL), |
||
608 | + SND_SOC_DAPM_INPUT("Line Input"), |
||
609 | + SND_SOC_DAPM_MIC("Line Input with Micbias", NULL), |
||
610 | + SND_SOC_DAPM_MUX("Line Input Micbias", SND_SOC_NOPM, 0, 0, |
||
611 | + &linein_micbias_mux), |
||
612 | + SND_SOC_DAPM_INPUT("dummy SPDIF in"), |
||
613 | + SND_SOC_DAPM_PGA_E("dummy SPDIFRX", SND_SOC_NOPM, 0, 0, NULL, 0, |
||
614 | + rpi_cirrus_spdif_rx_enable_event, |
||
615 | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
||
616 | + SND_SOC_DAPM_INPUT("Dummy Input"), |
||
617 | + SND_SOC_DAPM_OUTPUT("Dummy Output"), |
||
618 | +}; |
||
619 | + |
||
620 | +const struct snd_soc_dapm_route rpi_cirrus_dapm_routes[] = { |
||
621 | + { "IN1L", NULL, "Headset Mic" }, |
||
622 | + { "IN1R", NULL, "Headset Mic" }, |
||
623 | + { "Headset Mic", NULL, "MICBIAS1" }, |
||
624 | + |
||
625 | + { "IN2L", NULL, "DMIC" }, |
||
626 | + { "IN2R", NULL, "DMIC" }, |
||
627 | + { "DMIC", NULL, "MICBIAS2" }, |
||
628 | + |
||
629 | + { "IN3L", NULL, "Line Input Micbias" }, |
||
630 | + { "IN3R", NULL, "Line Input Micbias" }, |
||
631 | + |
||
632 | + { "Line Input Micbias", "off", "Line Input" }, |
||
633 | + { "Line Input Micbias", "on", "Line Input with Micbias" }, |
||
634 | + |
||
635 | + /* Make sure MICVDD is enabled, otherwise we get noise */ |
||
636 | + { "Line Input", NULL, "MICVDD" }, |
||
637 | + { "Line Input with Micbias", NULL, "MICBIAS3" }, |
||
638 | + |
||
639 | + /* Dummy routes to check whether SPDIF RX is enabled or not */ |
||
640 | + {"dummy SPDIFRX", NULL, "dummy SPDIF in"}, |
||
641 | + {"AIFTX", NULL, "dummy SPDIFRX"}, |
||
642 | + |
||
643 | + /* |
||
644 | + * Dummy routes to keep wm5102 from staying off on |
||
645 | + * playback/capture if all mixers are off. |
||
646 | + */ |
||
647 | + { "Dummy Output", NULL, "AIF1RX1" }, |
||
648 | + { "Dummy Output", NULL, "AIF1RX2" }, |
||
649 | + { "AIF1TX1", NULL, "Dummy Input" }, |
||
650 | + { "AIF1TX2", NULL, "Dummy Input" }, |
||
651 | +}; |
||
652 | + |
||
653 | +static int rpi_cirrus_clear_flls(struct snd_soc_card *card, |
||
654 | + struct snd_soc_codec *wm5102_codec) { |
||
655 | + |
||
656 | + int ret1, ret2; |
||
657 | + |
||
658 | + ret1 = snd_soc_codec_set_pll(wm5102_codec, |
||
659 | + WM5102_FLL1, ARIZONA_FLL_SRC_NONE, 0, 0); |
||
660 | + ret2 = snd_soc_codec_set_pll(wm5102_codec, |
||
661 | + WM5102_FLL1_REFCLK, ARIZONA_FLL_SRC_NONE, 0, 0); |
||
662 | + |
||
663 | + if (ret1) { |
||
664 | + dev_warn(card->dev, |
||
665 | + "setting FLL1 to zero failed: %d\n", ret1); |
||
666 | + return ret1; |
||
667 | + } |
||
668 | + if (ret2) { |
||
669 | + dev_warn(card->dev, |
||
670 | + "setting FLL1_REFCLK to zero failed: %d\n", ret2); |
||
671 | + return ret2; |
||
672 | + } |
||
673 | + return 0; |
||
674 | +} |
||
675 | + |
||
676 | +static int rpi_cirrus_set_fll(struct snd_soc_card *card, |
||
677 | + struct snd_soc_codec *wm5102_codec, unsigned int clk_freq) |
||
678 | +{ |
||
679 | + int ret = snd_soc_codec_set_pll(wm5102_codec, |
||
680 | + WM5102_FLL1, |
||
681 | + ARIZONA_CLK_SRC_MCLK1, |
||
682 | + WM8804_CLKOUT_HZ, |
||
683 | + clk_freq); |
||
684 | + if (ret) |
||
685 | + dev_err(card->dev, "Failed to set FLL1 to %d: %d\n", |
||
686 | + clk_freq, ret); |
||
687 | + |
||
688 | + usleep_range(1000, 2000); |
||
689 | + return ret; |
||
690 | +} |
||
691 | + |
||
692 | +static int rpi_cirrus_set_fll_refclk(struct snd_soc_card *card, |
||
693 | + struct snd_soc_codec *wm5102_codec, |
||
694 | + unsigned int clk_freq, unsigned int aif2_freq) |
||
695 | +{ |
||
696 | + int ret = snd_soc_codec_set_pll(wm5102_codec, |
||
697 | + WM5102_FLL1_REFCLK, |
||
698 | + ARIZONA_CLK_SRC_MCLK1, |
||
699 | + WM8804_CLKOUT_HZ, |
||
700 | + clk_freq); |
||
701 | + if (ret) { |
||
702 | + dev_err(card->dev, |
||
703 | + "Failed to set FLL1_REFCLK to %d: %d\n", |
||
704 | + clk_freq, ret); |
||
705 | + return ret; |
||
706 | + } |
||
707 | + |
||
708 | + ret = snd_soc_codec_set_pll(wm5102_codec, |
||
709 | + WM5102_FLL1, |
||
710 | + ARIZONA_CLK_SRC_AIF2BCLK, |
||
711 | + aif2_freq, clk_freq); |
||
712 | + if (ret) |
||
713 | + dev_err(card->dev, |
||
714 | + "Failed to set FLL1 with Sync Clock %d to %d: %d\n", |
||
715 | + aif2_freq, clk_freq, ret); |
||
716 | + |
||
717 | + usleep_range(1000, 2000); |
||
718 | + return ret; |
||
719 | +} |
||
720 | + |
||
721 | +static int rpi_cirrus_spdif_rx_enable_event(struct snd_soc_dapm_widget *w, |
||
722 | + struct snd_kcontrol *kcontrol, int event) |
||
723 | +{ |
||
724 | + struct snd_soc_card *card = w->dapm->card; |
||
725 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
726 | + struct snd_soc_codec *wm5102_codec = get_wm5102_runtime(card)->codec; |
||
727 | + |
||
728 | + unsigned int clk_freq, aif2_freq; |
||
729 | + int ret = 0; |
||
730 | + |
||
731 | + switch (event) { |
||
732 | + case SND_SOC_DAPM_POST_PMU: |
||
733 | + mutex_lock(&priv->lock); |
||
734 | + |
||
735 | + /* Enable sync path in case of SPDIF capture use case */ |
||
736 | + |
||
737 | + clk_freq = calc_sysclk(priv->card_rate); |
||
738 | + aif2_freq = 64 * priv->card_rate; |
||
739 | + |
||
740 | + dev_dbg(card->dev, |
||
741 | + "spdif_rx: changing FLL1 to use Ref Clock clk: %d spdif: %d\n", |
||
742 | + clk_freq, aif2_freq); |
||
743 | + |
||
744 | + ret = rpi_cirrus_clear_flls(card, wm5102_codec); |
||
745 | + if (ret) { |
||
746 | + dev_err(card->dev, "spdif_rx: failed to clear FLLs\n"); |
||
747 | + goto out; |
||
748 | + } |
||
749 | + |
||
750 | + ret = rpi_cirrus_set_fll_refclk(card, wm5102_codec, |
||
751 | + clk_freq, aif2_freq); |
||
752 | + |
||
753 | + if (ret) { |
||
754 | + dev_err(card->dev, "spdif_rx: failed to set FLLs\n"); |
||
755 | + goto out; |
||
756 | + } |
||
757 | + |
||
758 | + /* set to negative to indicate we're doing spdif rx */ |
||
759 | + priv->fll1_freq = -clk_freq; |
||
760 | + priv->sync_path_enable = 1; |
||
761 | + break; |
||
762 | + |
||
763 | + case SND_SOC_DAPM_POST_PMD: |
||
764 | + mutex_lock(&priv->lock); |
||
765 | + priv->sync_path_enable = 0; |
||
766 | + break; |
||
767 | + |
||
768 | + default: |
||
769 | + return 0; |
||
770 | + } |
||
771 | + |
||
772 | +out: |
||
773 | + mutex_unlock(&priv->lock); |
||
774 | + return ret; |
||
775 | +} |
||
776 | + |
||
777 | +static int rpi_cirrus_set_bias_level(struct snd_soc_card *card, |
||
778 | + struct snd_soc_dapm_context *dapm, |
||
779 | + enum snd_soc_bias_level level) |
||
780 | +{ |
||
781 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
782 | + struct snd_soc_pcm_runtime *wm5102_runtime = get_wm5102_runtime(card); |
||
783 | + struct snd_soc_codec *wm5102_codec = wm5102_runtime->codec; |
||
784 | + |
||
785 | + int ret = 0; |
||
786 | + unsigned int clk_freq; |
||
787 | + |
||
788 | + if (dapm->dev != wm5102_runtime->codec_dai->dev) |
||
789 | + return 0; |
||
790 | + |
||
791 | + switch (level) { |
||
792 | + case SND_SOC_BIAS_PREPARE: |
||
793 | + if (dapm->bias_level == SND_SOC_BIAS_ON) |
||
794 | + break; |
||
795 | + |
||
796 | + mutex_lock(&priv->lock); |
||
797 | + |
||
798 | + if (!priv->sync_path_enable) { |
||
799 | + clk_freq = calc_sysclk(priv->card_rate); |
||
800 | + |
||
801 | + dev_dbg(card->dev, |
||
802 | + "set_bias: changing FLL1 from %d to %d\n", |
||
803 | + priv->fll1_freq, clk_freq); |
||
804 | + |
||
805 | + ret = rpi_cirrus_set_fll(card, wm5102_codec, clk_freq); |
||
806 | + if (ret) |
||
807 | + dev_err(card->dev, |
||
808 | + "set_bias: Failed to set FLL1\n"); |
||
809 | + else |
||
810 | + priv->fll1_freq = clk_freq; |
||
811 | + } |
||
812 | + mutex_unlock(&priv->lock); |
||
813 | + break; |
||
814 | + default: |
||
815 | + break; |
||
816 | + } |
||
817 | + |
||
818 | + return ret; |
||
819 | +} |
||
820 | + |
||
821 | +static int rpi_cirrus_set_bias_level_post(struct snd_soc_card *card, |
||
822 | + struct snd_soc_dapm_context *dapm, |
||
823 | + enum snd_soc_bias_level level) |
||
824 | +{ |
||
825 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
826 | + struct snd_soc_pcm_runtime *wm5102_runtime = get_wm5102_runtime(card); |
||
827 | + struct snd_soc_codec *wm5102_codec = wm5102_runtime->codec; |
||
828 | + |
||
829 | + if (dapm->dev != wm5102_runtime->codec_dai->dev) |
||
830 | + return 0; |
||
831 | + |
||
832 | + switch (level) { |
||
833 | + case SND_SOC_BIAS_STANDBY: |
||
834 | + mutex_lock(&priv->lock); |
||
835 | + |
||
836 | + dev_dbg(card->dev, |
||
837 | + "set_bias_post: changing FLL1 from %d to off\n", |
||
838 | + priv->fll1_freq); |
||
839 | + |
||
840 | + if (rpi_cirrus_clear_flls(card, wm5102_codec)) |
||
841 | + dev_err(card->dev, |
||
842 | + "set_bias_post: failed to clear FLLs\n"); |
||
843 | + else |
||
844 | + priv->fll1_freq = 0; |
||
845 | + |
||
846 | + mutex_unlock(&priv->lock); |
||
847 | + |
||
848 | + break; |
||
849 | + default: |
||
850 | + break; |
||
851 | + } |
||
852 | + |
||
853 | + return 0; |
||
854 | +} |
||
855 | + |
||
856 | +static int rpi_cirrus_set_wm8804_pll(struct snd_soc_card *card, |
||
857 | + struct snd_soc_dai *wm8804_dai, unsigned int rate) |
||
858 | +{ |
||
859 | + int ret; |
||
860 | + |
||
861 | + /* use 256fs */ |
||
862 | + unsigned int clk_freq = rate * 256; |
||
863 | + |
||
864 | + ret = snd_soc_dai_set_pll(wm8804_dai, 0, 0, |
||
865 | + WM8804_CLKOUT_HZ, clk_freq); |
||
866 | + if (ret) { |
||
867 | + dev_err(card->dev, |
||
868 | + "Failed to set WM8804 PLL to %d: %d\n", clk_freq, ret); |
||
869 | + return ret; |
||
870 | + } |
||
871 | + |
||
872 | + /* Set MCLK as PLL Output */ |
||
873 | + ret = snd_soc_dai_set_sysclk(wm8804_dai, |
||
874 | + WM8804_TX_CLKSRC_PLL, clk_freq, 0); |
||
875 | + if (ret) { |
||
876 | + dev_err(card->dev, |
||
877 | + "Failed to set MCLK as PLL Output: %d\n", ret); |
||
878 | + return ret; |
||
879 | + } |
||
880 | + |
||
881 | + return ret; |
||
882 | +} |
||
883 | + |
||
884 | +static int rpi_cirrus_startup(struct snd_pcm_substream *substream) |
||
885 | +{ |
||
886 | + struct snd_soc_pcm_runtime *rtd = substream->private_data; |
||
887 | + struct snd_soc_card *card = rtd->card; |
||
888 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
889 | + unsigned int min_rate = min_rates[priv->min_rate_idx].value; |
||
890 | + unsigned int max_rate = max_rates[priv->max_rate_idx].value; |
||
891 | + |
||
892 | + if (min_rate || max_rate) { |
||
893 | + if (max_rate == 0) |
||
894 | + max_rate = UINT_MAX; |
||
895 | + |
||
896 | + dev_dbg(card->dev, |
||
897 | + "startup: limiting rate to %u-%u\n", |
||
898 | + min_rate, max_rate); |
||
899 | + |
||
900 | + snd_pcm_hw_constraint_minmax(substream->runtime, |
||
901 | + SNDRV_PCM_HW_PARAM_RATE, min_rate, max_rate); |
||
902 | + } |
||
903 | + |
||
904 | + return 0; |
||
905 | +} |
||
906 | + |
||
907 | +static struct snd_soc_pcm_stream rpi_cirrus_dai_link2_params = { |
||
908 | + .formats = SNDRV_PCM_FMTBIT_S24_LE, |
||
909 | + .channels_min = 2, |
||
910 | + .channels_max = 2, |
||
911 | + .rate_min = RPI_CIRRUS_DEFAULT_RATE, |
||
912 | + .rate_max = RPI_CIRRUS_DEFAULT_RATE, |
||
913 | +}; |
||
914 | + |
||
915 | +static int rpi_cirrus_hw_params(struct snd_pcm_substream *substream, |
||
916 | + struct snd_pcm_hw_params *params) |
||
917 | +{ |
||
918 | + struct snd_soc_pcm_runtime *rtd = substream->private_data; |
||
919 | + struct snd_soc_card *card = rtd->card; |
||
920 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
921 | + struct snd_soc_dai *bcm_i2s_dai = rtd->cpu_dai; |
||
922 | + struct snd_soc_codec *wm5102_codec = rtd->codec; |
||
923 | + struct snd_soc_dai *wm8804_dai = get_wm8804_runtime(card)->codec_dai; |
||
924 | + |
||
925 | + int ret; |
||
926 | + |
||
927 | + unsigned int width = snd_pcm_format_physical_width( |
||
928 | + params_format(params)); |
||
929 | + unsigned int rate = params_rate(params); |
||
930 | + unsigned int clk_freq = calc_sysclk(rate); |
||
931 | + |
||
932 | + mutex_lock(&priv->lock); |
||
933 | + |
||
934 | + dev_dbg(card->dev, "hw_params: setting rate to %d\n", rate); |
||
935 | + |
||
936 | + ret = snd_soc_dai_set_bclk_ratio(bcm_i2s_dai, 2 * width); |
||
937 | + if (ret) { |
||
938 | + dev_err(card->dev, "set_bclk_ratio failed: %d\n", ret); |
||
939 | + goto out; |
||
940 | + } |
||
941 | + |
||
942 | + ret = snd_soc_dai_set_tdm_slot(rtd->codec_dai, 0x03, 0x03, 2, width); |
||
943 | + if (ret) { |
||
944 | + dev_err(card->dev, "set_tdm_slot failed: %d\n", ret); |
||
945 | + goto out; |
||
946 | + } |
||
947 | + |
||
948 | + /* WM8804 supports sample rates from 32k only */ |
||
949 | + if (rate >= 32000) { |
||
950 | + ret = rpi_cirrus_set_wm8804_pll(card, wm8804_dai, rate); |
||
951 | + if (ret) |
||
952 | + goto out; |
||
953 | + } |
||
954 | + |
||
955 | + ret = snd_soc_codec_set_sysclk(wm5102_codec, |
||
956 | + ARIZONA_CLK_SYSCLK, |
||
957 | + ARIZONA_CLK_SRC_FLL1, |
||
958 | + clk_freq, |
||
959 | + SND_SOC_CLOCK_IN); |
||
960 | + if (ret) { |
||
961 | + dev_err(card->dev, "Failed to set SYSCLK: %d\n", ret); |
||
962 | + goto out; |
||
963 | + } |
||
964 | + |
||
965 | + if ((priv->fll1_freq > 0) && (priv->fll1_freq != clk_freq)) { |
||
966 | + dev_dbg(card->dev, |
||
967 | + "hw_params: changing FLL1 from %d to %d\n", |
||
968 | + priv->fll1_freq, clk_freq); |
||
969 | + |
||
970 | + if (rpi_cirrus_clear_flls(card, wm5102_codec)) { |
||
971 | + dev_err(card->dev, "hw_params: failed to clear FLLs\n"); |
||
972 | + goto out; |
||
973 | + } |
||
974 | + |
||
975 | + if (rpi_cirrus_set_fll(card, wm5102_codec, clk_freq)) { |
||
976 | + dev_err(card->dev, "hw_params: failed to set FLL\n"); |
||
977 | + goto out; |
||
978 | + } |
||
979 | + |
||
980 | + priv->fll1_freq = clk_freq; |
||
981 | + } |
||
982 | + |
||
983 | + priv->card_rate = rate; |
||
984 | + rpi_cirrus_dai_link2_params.rate_min = rate; |
||
985 | + rpi_cirrus_dai_link2_params.rate_max = rate; |
||
986 | + |
||
987 | + priv->params_set |= 1 << substream->stream; |
||
988 | + |
||
989 | +out: |
||
990 | + mutex_unlock(&priv->lock); |
||
991 | + |
||
992 | + return ret; |
||
993 | +} |
||
994 | + |
||
995 | +static int rpi_cirrus_hw_free(struct snd_pcm_substream *substream) |
||
996 | +{ |
||
997 | + struct snd_soc_pcm_runtime *rtd = substream->private_data; |
||
998 | + struct snd_soc_card *card = rtd->card; |
||
999 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
1000 | + struct snd_soc_codec *wm5102_codec = rtd->codec; |
||
1001 | + int ret; |
||
1002 | + unsigned int old_params_set = priv->params_set; |
||
1003 | + |
||
1004 | + priv->params_set &= ~(1 << substream->stream); |
||
1005 | + |
||
1006 | + /* disable sysclk if this was the last open stream */ |
||
1007 | + if (priv->params_set == 0 && old_params_set) { |
||
1008 | + dev_dbg(card->dev, |
||
1009 | + "hw_free: Setting SYSCLK to Zero\n"); |
||
1010 | + |
||
1011 | + ret = snd_soc_codec_set_sysclk(wm5102_codec, |
||
1012 | + ARIZONA_CLK_SYSCLK, |
||
1013 | + ARIZONA_CLK_SRC_FLL1, |
||
1014 | + 0, |
||
1015 | + SND_SOC_CLOCK_IN); |
||
1016 | + if (ret) |
||
1017 | + dev_err(card->dev, |
||
1018 | + "hw_free: Failed to set SYSCLK to Zero: %d\n", |
||
1019 | + ret); |
||
1020 | + } |
||
1021 | + return 0; |
||
1022 | +} |
||
1023 | + |
||
1024 | +static int rpi_cirrus_init_wm5102(struct snd_soc_pcm_runtime *rtd) |
||
1025 | +{ |
||
1026 | + struct snd_soc_codec *codec = rtd->codec; |
||
1027 | + int ret; |
||
1028 | + |
||
1029 | + /* no 32kHz input, derive it from sysclk if needed */ |
||
1030 | + snd_soc_update_bits(codec, |
||
1031 | + ARIZONA_CLOCK_32K_1, ARIZONA_CLK_32K_SRC_MASK, 2); |
||
1032 | + |
||
1033 | + if (rpi_cirrus_clear_flls(rtd->card, codec)) |
||
1034 | + dev_warn(rtd->card->dev, |
||
1035 | + "init_wm5102: failed to clear FLLs\n"); |
||
1036 | + |
||
1037 | + ret = snd_soc_codec_set_sysclk(codec, |
||
1038 | + ARIZONA_CLK_SYSCLK, ARIZONA_CLK_SRC_FLL1, |
||
1039 | + 0, SND_SOC_CLOCK_IN); |
||
1040 | + if (ret) { |
||
1041 | + dev_err(rtd->card->dev, |
||
1042 | + "Failed to set SYSCLK to Zero: %d\n", ret); |
||
1043 | + return ret; |
||
1044 | + } |
||
1045 | + |
||
1046 | + return 0; |
||
1047 | +} |
||
1048 | + |
||
1049 | +static int rpi_cirrus_init_wm8804(struct snd_soc_pcm_runtime *rtd) |
||
1050 | +{ |
||
1051 | + struct snd_soc_codec *codec = rtd->codec; |
||
1052 | + struct snd_soc_dai *codec_dai = rtd->codec_dai; |
||
1053 | + struct snd_soc_card *card = rtd->card; |
||
1054 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
1055 | + unsigned int mask; |
||
1056 | + int i, ret; |
||
1057 | + |
||
1058 | + for (i = 0; i < 4; i++) { |
||
1059 | + mask = (i == 3) ? 0x3f : 0xff; |
||
1060 | + priv->iec958_status[i] = |
||
1061 | + snd_soc_read(codec, WM8804_SPDTX1 + i) & mask; |
||
1062 | + } |
||
1063 | + |
||
1064 | + /* Setup for 256fs */ |
||
1065 | + ret = snd_soc_dai_set_clkdiv(codec_dai, |
||
1066 | + WM8804_MCLK_DIV, WM8804_MCLKDIV_256FS); |
||
1067 | + if (ret) { |
||
1068 | + dev_err(card->dev, |
||
1069 | + "init_wm8804: Failed to set MCLK_DIV to 256fs: %d\n", |
||
1070 | + ret); |
||
1071 | + return ret; |
||
1072 | + } |
||
1073 | + |
||
1074 | + /* Output OSC on CLKOUT */ |
||
1075 | + ret = snd_soc_dai_set_sysclk(codec_dai, |
||
1076 | + WM8804_CLKOUT_SRC_OSCCLK, WM8804_CLKOUT_HZ, 0); |
||
1077 | + if (ret) |
||
1078 | + dev_err(card->dev, |
||
1079 | + "init_wm8804: Failed to set CLKOUT as OSC Frequency: %d\n", |
||
1080 | + ret); |
||
1081 | + |
||
1082 | + /* Init PLL with default samplerate */ |
||
1083 | + ret = rpi_cirrus_set_wm8804_pll(card, codec_dai, |
||
1084 | + RPI_CIRRUS_DEFAULT_RATE); |
||
1085 | + if (ret) |
||
1086 | + dev_err(card->dev, |
||
1087 | + "init_wm8804: Failed to setup PLL for %dHz: %d\n", |
||
1088 | + RPI_CIRRUS_DEFAULT_RATE, ret); |
||
1089 | + |
||
1090 | + return ret; |
||
1091 | +} |
||
1092 | + |
||
1093 | +static struct snd_soc_ops rpi_cirrus_ops = { |
||
1094 | + .startup = rpi_cirrus_startup, |
||
1095 | + .hw_params = rpi_cirrus_hw_params, |
||
1096 | + .hw_free = rpi_cirrus_hw_free, |
||
1097 | +}; |
||
1098 | + |
||
1099 | +static struct snd_soc_dai_link rpi_cirrus_dai[] = { |
||
1100 | + [DAI_WM5102] = { |
||
1101 | + .name = "WM5102", |
||
1102 | + .stream_name = "WM5102 AiFi", |
||
1103 | + .codec_dai_name = "wm5102-aif1", |
||
1104 | + .codec_name = "wm5102-codec", |
||
1105 | + .dai_fmt = SND_SOC_DAIFMT_I2S |
||
1106 | + | SND_SOC_DAIFMT_NB_NF |
||
1107 | + | SND_SOC_DAIFMT_CBM_CFM, |
||
1108 | + .ops = &rpi_cirrus_ops, |
||
1109 | + .init = rpi_cirrus_init_wm5102, |
||
1110 | + }, |
||
1111 | + [DAI_WM8804] = { |
||
1112 | + .name = "WM5102 SPDIF", |
||
1113 | + .stream_name = "SPDIF Tx/Rx", |
||
1114 | + .cpu_dai_name = "wm5102-aif2", |
||
1115 | + .codec_dai_name = "wm8804-spdif", |
||
1116 | + .codec_name = "wm8804.1-003b", |
||
1117 | + .dai_fmt = SND_SOC_DAIFMT_I2S |
||
1118 | + | SND_SOC_DAIFMT_NB_NF |
||
1119 | + | SND_SOC_DAIFMT_CBM_CFM, |
||
1120 | + .ignore_suspend = 1, |
||
1121 | + .params = &rpi_cirrus_dai_link2_params, |
||
1122 | + .init = rpi_cirrus_init_wm8804, |
||
1123 | + }, |
||
1124 | +}; |
||
1125 | + |
||
1126 | + |
||
1127 | +static int rpi_cirrus_late_probe(struct snd_soc_card *card) |
||
1128 | +{ |
||
1129 | + struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card); |
||
1130 | + struct snd_soc_pcm_runtime *wm5102_runtime = get_wm5102_runtime(card); |
||
1131 | + struct snd_soc_pcm_runtime *wm8804_runtime = get_wm8804_runtime(card); |
||
1132 | + int ret; |
||
1133 | + |
||
1134 | + dev_dbg(card->dev, "iec958_bits: %02x %02x %02x %02x\n", |
||
1135 | + priv->iec958_status[0], |
||
1136 | + priv->iec958_status[1], |
||
1137 | + priv->iec958_status[2], |
||
1138 | + priv->iec958_status[3]); |
||
1139 | + |
||
1140 | + ret = snd_soc_dai_set_sysclk( |
||
1141 | + wm5102_runtime->codec_dai, ARIZONA_CLK_SYSCLK, 0, 0); |
||
1142 | + if (ret) { |
||
1143 | + dev_err(card->dev, |
||
1144 | + "Failed to set WM5102 codec dai clk domain: %d\n", ret); |
||
1145 | + return ret; |
||
1146 | + } |
||
1147 | + |
||
1148 | + ret = snd_soc_dai_set_sysclk( |
||
1149 | + wm8804_runtime->cpu_dai, ARIZONA_CLK_SYSCLK, 0, 0); |
||
1150 | + if (ret) |
||
1151 | + dev_err(card->dev, |
||
1152 | + "Failed to set WM8804 codec dai clk domain: %d\n", ret); |
||
1153 | + |
||
1154 | + return ret; |
||
1155 | +} |
||
1156 | + |
||
1157 | +/* audio machine driver */ |
||
1158 | +static struct snd_soc_card rpi_cirrus_card = { |
||
1159 | + .name = "RPi-Cirrus", |
||
1160 | + .driver_name = "RPiCirrus", |
||
1161 | + .owner = THIS_MODULE, |
||
1162 | + .dai_link = rpi_cirrus_dai, |
||
1163 | + .num_links = ARRAY_SIZE(rpi_cirrus_dai), |
||
1164 | + .late_probe = rpi_cirrus_late_probe, |
||
1165 | + .controls = rpi_cirrus_controls, |
||
1166 | + .num_controls = ARRAY_SIZE(rpi_cirrus_controls), |
||
1167 | + .dapm_widgets = rpi_cirrus_dapm_widgets, |
||
1168 | + .num_dapm_widgets = ARRAY_SIZE(rpi_cirrus_dapm_widgets), |
||
1169 | + .dapm_routes = rpi_cirrus_dapm_routes, |
||
1170 | + .num_dapm_routes = ARRAY_SIZE(rpi_cirrus_dapm_routes), |
||
1171 | + .set_bias_level = rpi_cirrus_set_bias_level, |
||
1172 | + .set_bias_level_post = rpi_cirrus_set_bias_level_post, |
||
1173 | +}; |
||
1174 | + |
||
1175 | +static int rpi_cirrus_probe(struct platform_device *pdev) |
||
1176 | +{ |
||
1177 | + int ret = 0; |
||
1178 | + struct rpi_cirrus_priv *priv; |
||
1179 | + struct device_node *i2s_node; |
||
1180 | + |
||
1181 | + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); |
||
1182 | + if (!priv) |
||
1183 | + return -ENOMEM; |
||
1184 | + |
||
1185 | + priv->min_rate_idx = 1; /* min samplerate 32kHz */ |
||
1186 | + priv->card_rate = RPI_CIRRUS_DEFAULT_RATE; |
||
1187 | + |
||
1188 | + mutex_init(&priv->lock); |
||
1189 | + |
||
1190 | + snd_soc_card_set_drvdata(&rpi_cirrus_card, priv); |
||
1191 | + |
||
1192 | + if (!pdev->dev.of_node) |
||
1193 | + return -ENODEV; |
||
1194 | + |
||
1195 | + i2s_node = of_parse_phandle( |
||
1196 | + pdev->dev.of_node, "i2s-controller", 0); |
||
1197 | + if (!i2s_node) { |
||
1198 | + dev_err(&pdev->dev, "i2s-controller missing in DT\n"); |
||
1199 | + return -ENODEV; |
||
1200 | + } |
||
1201 | + |
||
1202 | + rpi_cirrus_dai[DAI_WM5102].cpu_of_node = i2s_node; |
||
1203 | + rpi_cirrus_dai[DAI_WM5102].platform_of_node = i2s_node; |
||
1204 | + |
||
1205 | + rpi_cirrus_card.dev = &pdev->dev; |
||
1206 | + |
||
1207 | + ret = devm_snd_soc_register_card(&pdev->dev, &rpi_cirrus_card); |
||
1208 | + if (ret) { |
||
1209 | + if (ret == -EPROBE_DEFER) |
||
1210 | + dev_dbg(&pdev->dev, |
||
1211 | + "register card requested probe deferral\n"); |
||
1212 | + else |
||
1213 | + dev_err(&pdev->dev, |
||
1214 | + "Failed to register card: %d\n", ret); |
||
1215 | + } |
||
1216 | + |
||
1217 | + return ret; |
||
1218 | +} |
||
1219 | + |
||
1220 | +static const struct of_device_id rpi_cirrus_of_match[] = { |
||
1221 | + { .compatible = "wlf,rpi-cirrus", }, |
||
1222 | + {}, |
||
1223 | +}; |
||
1224 | +MODULE_DEVICE_TABLE(of, rpi_cirrus_of_match); |
||
1225 | + |
||
1226 | +static struct platform_driver rpi_cirrus_driver = { |
||
1227 | + .driver = { |
||
1228 | + .name = "snd-rpi-cirrus", |
||
1229 | + .of_match_table = of_match_ptr(rpi_cirrus_of_match), |
||
1230 | + }, |
||
1231 | + .probe = rpi_cirrus_probe, |
||
1232 | +}; |
||
1233 | + |
||
1234 | +module_platform_driver(rpi_cirrus_driver); |
||
1235 | + |
||
1236 | +MODULE_AUTHOR("Matthias Reichl <hias@horus.com>"); |
||
1237 | +MODULE_DESCRIPTION("ASoC driver for Cirrus Logic Audio Card"); |
||
1238 | +MODULE_LICENSE("GPL"); |