]> git.pld-linux.org Git - packages/kernel.git/blame - rpi-sound.patch
up to 5.19.4
[packages/kernel.git] / rpi-sound.patch
CommitLineData
cf66ed33
JP
1--- linux-5.9.orig/sound/soc/bcm/Kconfig 2020-10-11 23:15:50.000000000 +0200
2+++ linux-5.9/sound/soc/bcm/Kconfig 2020-12-14 01:08:25.450035831 +0100
3@@ -26,3 +26,22 @@
4 DSL/PON chips (bcm63158, bcm63178)
5
6 If you don't know what to do here, say N
7+
8+config SND_BCM2708_SOC_HIFIBERRY_DAC
9+ tristate "Support for HifiBerry DAC"
10+ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
11+ select SND_SOC_PCM5102A
12+ select SND_RPI_SIMPLE_SOUNDCARD
13+ help
14+ Say Y or M if you want to add support for HifiBerry DAC.
15+
16+config SND_RPI_SIMPLE_SOUNDCARD
17+ tristate "Support for Raspberry Pi simple soundcards"
18+ help
19+ Say Y or M if you want to add support Raspbery Pi simple soundcards
20+
21+config SND_RPI_WM8804_SOUNDCARD
22+ tristate "Support for Raspberry Pi generic WM8804 soundcards"
23+ help
24+ Say Y or M if you want to add support for the Raspberry Pi
25+ generic driver for WM8804 based soundcards.
26--- linux-5.9.orig/sound/soc/bcm/Makefile 2020-10-11 23:15:50.000000000 +0200
27+++ linux-5.9/sound/soc/bcm/Makefile 2020-12-14 01:05:51.274295380 +0100
28@@ -12,4 +12,10 @@
29 # BCM63XX Platform Support
30 snd-soc-63xx-objs := bcm63xx-i2s-whistler.o bcm63xx-pcm-whistler.o
31
32-obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o
33\ No newline at end of file
34+obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o
35+
36+snd-soc-rpi-simple-soundcard-objs := rpi-simple-soundcard.o
37+snd-soc-rpi-wm8804-soundcard-objs := rpi-wm8804-soundcard.o
38+
39+obj-$(CONFIG_SND_RPI_SIMPLE_SOUNDCARD) += snd-soc-rpi-simple-soundcard.o
40+obj-$(CONFIG_SND_RPI_WM8804_SOUNDCARD) += snd-soc-rpi-wm8804-soundcard.o
41--- linux-5.9.orig/sound/soc/bcm/rpi-wm8804-soundcard.c 1970-01-01 01:00:00.000000000 +0100
42+++ linux-5.9/sound/soc/bcm/rpi-wm8804-soundcard.c 2020-12-14 01:02:38.688758934 +0100
43@@ -0,0 +1,410 @@
44+// SPDX-License-Identifier: GPL-2.0
45+/*
46+ * rpi--wm8804.c -- ALSA SoC Raspberry Pi soundcard.
47+ *
48+ * Copyright (C) 2018 Raspberry Pi.
49+ *
50+ * Authors: Tim Gover <tim.gover@raspberrypi.org>
51+ *
52+ * Generic driver for Pi Hat WM8804 digi sounds cards
53+ *
54+ * Based upon code from:
55+ * justboom-digi.c
56+ * by Milan Neskovic <info@justboom.co>
57+ *
58+ * iqaudio_digi.c
59+ * by Daniel Matuschek <info@crazy-audio.com>
60+ *
61+ * allo-digione.c
62+ * by Baswaraj <jaikumar@cem-solutions.net>
63+ *
64+ * hifiberry-digi.c
65+ * Daniel Matuschek <info@crazy-audio.com>
66+ *
67+ * This program is free software; you can redistribute it and/or
68+ * modify it under the terms of the GNU General Public License
69+ * version 2 as published by the Free Software Foundation.
70+ *
71+ * This program is distributed in the hope that it will be useful, but
72+ * WITHOUT ANY WARRANTY; without even the implied warranty of
73+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74+ * General Public License for more details.
75+ */
76+
77+#include <linux/gpio/consumer.h>
78+#include <linux/platform_device.h>
79+#include <linux/module.h>
80+
81+#include <sound/core.h>
82+#include <sound/pcm.h>
83+#include <sound/pcm_params.h>
84+#include <sound/soc.h>
85+
86+#include "../codecs/wm8804.h"
87+
88+struct wm8804_clk_cfg {
89+ unsigned int sysclk_freq;
90+ unsigned int mclk_freq;
91+ unsigned int mclk_div;
92+};
93+
94+/* Parameters for generic functions */
95+struct snd_rpi_wm8804_drvdata {
96+ /* Required - pointer to the DAI structure */
97+ struct snd_soc_dai_link *dai;
98+ /* Required - snd_soc_card name */
99+ const char *card_name;
100+ /* Optional DT node names if card info is defined in DT */
101+ const char *card_name_dt;
102+ const char *dai_name_dt;
103+ const char *dai_stream_name_dt;
104+ /* Optional probe extension - called prior to register_card */
105+ int (*probe)(struct platform_device *pdev);
106+};
107+
108+static struct gpio_desc *snd_clk44gpio;
109+static struct gpio_desc *snd_clk48gpio;
110+static int wm8804_samplerate = 0;
111+
112+/* Forward declarations */
113+static struct snd_soc_dai_link snd_allo_digione_dai[];
114+static struct snd_soc_card snd_rpi_wm8804;
115+
116+
117+#define CLK_44EN_RATE 22579200UL
118+#define CLK_48EN_RATE 24576000UL
119+
120+static unsigned int snd_rpi_wm8804_enable_clock(unsigned int samplerate)
121+{
122+ switch (samplerate) {
123+ case 11025:
124+ case 22050:
125+ case 44100:
126+ case 88200:
127+ case 176400:
128+ gpiod_set_value_cansleep(snd_clk44gpio, 1);
129+ gpiod_set_value_cansleep(snd_clk48gpio, 0);
130+ return CLK_44EN_RATE;
131+ default:
132+ gpiod_set_value_cansleep(snd_clk48gpio, 1);
133+ gpiod_set_value_cansleep(snd_clk44gpio, 0);
134+ return CLK_48EN_RATE;
135+ }
136+}
137+
138+static void snd_rpi_wm8804_clk_cfg(unsigned int samplerate,
139+ struct wm8804_clk_cfg *clk_cfg)
140+{
141+ clk_cfg->sysclk_freq = 27000000;
142+
143+ if (samplerate <= 96000 ||
144+ snd_rpi_wm8804.dai_link == snd_allo_digione_dai) {
145+ clk_cfg->mclk_freq = samplerate * 256;
146+ clk_cfg->mclk_div = WM8804_MCLKDIV_256FS;
147+ } else {
148+ clk_cfg->mclk_freq = samplerate * 128;
149+ clk_cfg->mclk_div = WM8804_MCLKDIV_128FS;
150+ }
151+
152+ if (!(IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio)))
153+ clk_cfg->sysclk_freq = snd_rpi_wm8804_enable_clock(samplerate);
154+}
155+
156+static int snd_rpi_wm8804_hw_params(struct snd_pcm_substream *substream,
157+ struct snd_pcm_hw_params *params)
158+{
159+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
160+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
161+ struct snd_soc_component *component = codec_dai->component;
162+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
163+ int sampling_freq = 1;
164+ int ret;
165+ struct wm8804_clk_cfg clk_cfg;
166+ int samplerate = params_rate(params);
167+
168+ if (samplerate == wm8804_samplerate)
169+ return 0;
170+
171+ /* clear until all clocks are setup properly */
172+ wm8804_samplerate = 0;
173+
174+ snd_rpi_wm8804_clk_cfg(samplerate, &clk_cfg);
175+
176+ pr_debug("%s samplerate: %d mclk_freq: %u mclk_div: %u sysclk: %u\n",
177+ __func__, samplerate, clk_cfg.mclk_freq,
178+ clk_cfg.mclk_div, clk_cfg.sysclk_freq);
179+
180+ switch (samplerate) {
181+ case 32000:
182+ sampling_freq = 0x03;
183+ break;
184+ case 44100:
185+ sampling_freq = 0x00;
186+ break;
187+ case 48000:
188+ sampling_freq = 0x02;
189+ break;
190+ case 88200:
191+ sampling_freq = 0x08;
192+ break;
193+ case 96000:
194+ sampling_freq = 0x0a;
195+ break;
196+ case 176400:
197+ sampling_freq = 0x0c;
198+ break;
199+ case 192000:
200+ sampling_freq = 0x0e;
201+ break;
202+ default:
203+ dev_err(rtd->card->dev,
204+ "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n",
205+ samplerate);
206+ }
207+
208+ snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, clk_cfg.mclk_div);
209+ snd_soc_dai_set_pll(codec_dai, 0, 0,
210+ clk_cfg.sysclk_freq, clk_cfg.mclk_freq);
211+
212+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
213+ clk_cfg.sysclk_freq, SND_SOC_CLOCK_OUT);
214+ if (ret < 0) {
215+ dev_err(rtd->card->dev,
216+ "Failed to set WM8804 SYSCLK: %d\n", ret);
217+ return ret;
218+ }
219+
220+ wm8804_samplerate = samplerate;
221+
222+ /* set sampling frequency status bits */
223+ snd_soc_component_update_bits(component, WM8804_SPDTX4, 0x0f,
224+ sampling_freq);
225+
226+ return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
227+}
228+
229+static struct snd_soc_ops snd_rpi_wm8804_ops = {
230+ .hw_params = snd_rpi_wm8804_hw_params,
231+};
232+
233+SND_SOC_DAILINK_DEFS(justboom_digi,
234+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
235+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
236+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
237+
238+static struct snd_soc_dai_link snd_justboom_digi_dai[] = {
239+{
240+ .name = "JustBoom Digi",
241+ .stream_name = "JustBoom Digi HiFi",
242+ SND_SOC_DAILINK_REG(justboom_digi),
243+},
244+};
245+
246+static struct snd_rpi_wm8804_drvdata drvdata_justboom_digi = {
247+ .card_name = "snd_rpi_justboom_digi",
248+ .dai = snd_justboom_digi_dai,
249+};
250+
251+SND_SOC_DAILINK_DEFS(iqaudio_digi,
252+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
253+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
254+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
255+
256+static struct snd_soc_dai_link snd_iqaudio_digi_dai[] = {
257+{
258+ .name = "IQAudIO Digi",
259+ .stream_name = "IQAudIO Digi HiFi",
260+ SND_SOC_DAILINK_REG(iqaudio_digi),
261+},
262+};
263+
264+static struct snd_rpi_wm8804_drvdata drvdata_iqaudio_digi = {
265+ .card_name = "IQAudIODigi",
266+ .dai = snd_iqaudio_digi_dai,
267+ .card_name_dt = "wm8804-digi,card-name",
268+ .dai_name_dt = "wm8804-digi,dai-name",
269+ .dai_stream_name_dt = "wm8804-digi,dai-stream-name",
270+};
271+
272+static int snd_allo_digione_probe(struct platform_device *pdev)
273+{
274+ pr_debug("%s\n", __func__);
275+
276+ if (IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio)) {
277+ dev_err(&pdev->dev, "devm_gpiod_get() failed\n");
278+ return -EINVAL;
279+ }
280+ return 0;
281+}
282+
283+SND_SOC_DAILINK_DEFS(allo_digione,
284+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
285+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
286+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
287+
288+static struct snd_soc_dai_link snd_allo_digione_dai[] = {
289+{
290+ .name = "Allo DigiOne",
291+ .stream_name = "Allo DigiOne HiFi",
292+ SND_SOC_DAILINK_REG(allo_digione),
293+},
294+};
295+
296+static struct snd_rpi_wm8804_drvdata drvdata_allo_digione = {
297+ .card_name = "snd_allo_digione",
298+ .dai = snd_allo_digione_dai,
299+ .probe = snd_allo_digione_probe,
300+};
301+
302+SND_SOC_DAILINK_DEFS(hifiberry_digi,
303+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
304+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
305+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
306+
307+static struct snd_soc_dai_link snd_hifiberry_digi_dai[] = {
308+{
309+ .name = "HifiBerry Digi",
310+ .stream_name = "HifiBerry Digi HiFi",
311+ SND_SOC_DAILINK_REG(hifiberry_digi),
312+},
313+};
314+
315+static int snd_hifiberry_digi_probe(struct platform_device *pdev)
316+{
317+ pr_debug("%s\n", __func__);
318+
319+ if (IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio))
320+ return 0;
321+
322+ snd_hifiberry_digi_dai->name = "HiFiBerry Digi+ Pro";
323+ snd_hifiberry_digi_dai->stream_name = "HiFiBerry Digi+ Pro HiFi";
324+ return 0;
325+}
326+
327+static struct snd_rpi_wm8804_drvdata drvdata_hifiberry_digi = {
328+ .card_name = "snd_rpi_hifiberry_digi",
329+ .dai = snd_hifiberry_digi_dai,
330+ .probe = snd_hifiberry_digi_probe,
331+};
332+
333+static const struct of_device_id snd_rpi_wm8804_of_match[] = {
334+ { .compatible = "justboom,justboom-digi",
335+ .data = (void *) &drvdata_justboom_digi },
336+ { .compatible = "iqaudio,wm8804-digi",
337+ .data = (void *) &drvdata_iqaudio_digi },
338+ { .compatible = "allo,allo-digione",
339+ .data = (void *) &drvdata_allo_digione },
340+ { .compatible = "hifiberry,hifiberry-digi",
341+ .data = (void *) &drvdata_hifiberry_digi },
342+ {},
343+};
344+
345+static struct snd_soc_card snd_rpi_wm8804 = {
346+ .driver_name = "RPi-WM8804",
347+ .owner = THIS_MODULE,
348+ .dai_link = NULL,
349+ .num_links = 1,
350+};
351+
352+static int snd_rpi_wm8804_probe(struct platform_device *pdev)
353+{
354+ int ret = 0;
355+ const struct of_device_id *of_id;
356+
357+ snd_rpi_wm8804.dev = &pdev->dev;
358+ of_id = of_match_node(snd_rpi_wm8804_of_match, pdev->dev.of_node);
359+
360+ if (pdev->dev.of_node && of_id->data) {
361+ struct device_node *i2s_node;
362+ struct snd_rpi_wm8804_drvdata *drvdata =
363+ (struct snd_rpi_wm8804_drvdata *) of_id->data;
364+ struct snd_soc_dai_link *dai = drvdata->dai;
365+
366+ snd_soc_card_set_drvdata(&snd_rpi_wm8804, drvdata);
367+
368+ if (!dai->ops)
369+ dai->ops = &snd_rpi_wm8804_ops;
370+ if (!dai->codecs->dai_name)
371+ dai->codecs->dai_name = "wm8804-spdif";
372+ if (!dai->codecs->name)
373+ dai->codecs->name = "wm8804.1-003b";
374+ if (!dai->dai_fmt)
375+ dai->dai_fmt = SND_SOC_DAIFMT_I2S |
376+ SND_SOC_DAIFMT_NB_NF |
377+ SND_SOC_DAIFMT_CBM_CFM;
378+
379+ snd_rpi_wm8804.dai_link = dai;
380+ i2s_node = of_parse_phandle(pdev->dev.of_node,
381+ "i2s-controller", 0);
382+ if (!i2s_node) {
383+ pr_err("Failed to find i2s-controller DT node\n");
384+ return -ENODEV;
385+ }
386+
387+ snd_rpi_wm8804.name = drvdata->card_name;
388+
389+ /* If requested by in drvdata get card & DAI names from DT */
390+ if (drvdata->card_name_dt)
391+ of_property_read_string(i2s_node,
392+ drvdata->card_name_dt,
393+ &snd_rpi_wm8804.name);
394+
395+ if (drvdata->dai_name_dt)
396+ of_property_read_string(i2s_node,
397+ drvdata->dai_name_dt,
398+ &dai->name);
399+
400+ if (drvdata->dai_stream_name_dt)
401+ of_property_read_string(i2s_node,
402+ drvdata->dai_stream_name_dt,
403+ &dai->stream_name);
404+
405+ dai->cpus->of_node = i2s_node;
406+ dai->platforms->of_node = i2s_node;
407+
408+ /*
409+ * clk44gpio and clk48gpio are not required by all cards so
410+ * don't check the error status.
411+ */
412+ snd_clk44gpio =
413+ devm_gpiod_get(&pdev->dev, "clock44", GPIOD_OUT_LOW);
414+
415+ snd_clk48gpio =
416+ devm_gpiod_get(&pdev->dev, "clock48", GPIOD_OUT_LOW);
417+
418+ if (drvdata->probe) {
419+ ret = drvdata->probe(pdev);
420+ if (ret < 0) {
421+ dev_err(&pdev->dev, "Custom probe failed %d\n",
422+ ret);
423+ return ret;
424+ }
425+ }
426+
427+ pr_debug("%s card: %s dai: %s stream: %s\n", __func__,
428+ snd_rpi_wm8804.name,
429+ dai->name, dai->stream_name);
430+ }
431+
432+ ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_wm8804);
433+ if (ret && ret != -EPROBE_DEFER)
434+ dev_err(&pdev->dev, "Failed to register card %d\n", ret);
435+
436+ return ret;
437+}
438+
439+static struct platform_driver snd_rpi_wm8804_driver = {
440+ .driver = {
441+ .name = "snd-rpi-wm8804",
442+ .owner = THIS_MODULE,
443+ .of_match_table = snd_rpi_wm8804_of_match,
444+ },
445+ .probe = snd_rpi_wm8804_probe,
446+};
447+MODULE_DEVICE_TABLE(of, snd_rpi_wm8804_of_match);
448+
449+module_platform_driver(snd_rpi_wm8804_driver);
450+
451+MODULE_AUTHOR("Tim Gover <tim.gover@raspberrypi.org>");
452+MODULE_DESCRIPTION("ASoC Raspberry Pi Hat generic digi driver for WM8804 based cards");
453+MODULE_LICENSE("GPL v2");
454--- linux-5.15.orig/sound/soc/bcm/rpi-simple-soundcard.c 1970-01-01 01:00:00.000000000 +0100
455+++ linux-5.15/sound/soc/bcm/rpi-simple-soundcard.c 2021-12-23 14:16:38.688758934 +0100
456@@ -0,0 +1,419 @@
457+// SPDX-License-Identifier: GPL-2.0
458+/*
459+ * rpi-simple-soundcard.c -- ALSA SoC Raspberry Pi soundcard.
460+ *
461+ * Copyright (C) 2018 Raspberry Pi.
462+ *
463+ * Authors: Tim Gover <tim.gover@raspberrypi.org>
464+ *
465+ * Based on code:
466+ * hifiberry_amp.c, hifiberry_dac.c, rpi-dac.c
467+ * by Florian Meier <florian.meier@koalo.de>
468+ *
469+ * googlevoicehat-soundcard.c
470+ * by Peter Malkin <petermalkin@google.com>
471+ *
472+ * adau1977-adc.c
473+ * by Andrey Grodzovsky <andrey2805@gmail.com>
474+ *
475+ * merus-amp.c
476+ * by Ariel Muszkat <ariel.muszkat@gmail.com>
477+ * Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
478+ *
479+ * This program is free software; you can redistribute it and/or
480+ * modify it under the terms of the GNU General Public License
481+ * version 2 as published by the Free Software Foundation.
482+ *
483+ * This program is distributed in the hope that it will be useful, but
484+ * WITHOUT ANY WARRANTY; without even the implied warranty of
485+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
486+ * General Public License for more details.
487+ */
488+
489+#include <linux/module.h>
490+#include <linux/platform_device.h>
491+#include <linux/gpio/consumer.h>
492+
493+#include <sound/core.h>
494+#include <sound/pcm.h>
495+#include <sound/pcm_params.h>
496+#include <sound/soc.h>
497+
498+/* Parameters for generic RPI functions */
499+struct snd_rpi_simple_drvdata {
500+ struct snd_soc_dai_link *dai;
501+ const char* card_name;
502+ unsigned int fixed_bclk_ratio;
503+};
504+
505+static struct snd_soc_card snd_rpi_simple = {
506+ .driver_name = "RPi-simple",
507+ .owner = THIS_MODULE,
508+ .dai_link = NULL,
509+ .num_links = 1, /* Only a single DAI supported at the moment */
510+};
511+
512+static int snd_rpi_simple_init(struct snd_soc_pcm_runtime *rtd)
513+{
514+ struct snd_rpi_simple_drvdata *drvdata =
515+ snd_soc_card_get_drvdata(rtd->card);
516+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
517+
518+ if (drvdata->fixed_bclk_ratio > 0)
519+ return snd_soc_dai_set_bclk_ratio(cpu_dai,
520+ drvdata->fixed_bclk_ratio);
521+
522+ return 0;
523+}
524+
525+static int pifi_mini_210_init(struct snd_soc_pcm_runtime *rtd)
526+{
527+ struct snd_soc_component *dac;
528+ struct gpio_desc *pdn_gpio, *rst_gpio;
529+ struct snd_soc_dai *codec_dai;
530+ int ret;
531+
532+ snd_rpi_simple_init(rtd);
533+ codec_dai = asoc_rtd_to_codec(rtd, 0);
534+
535+ dac = codec_dai[0].component;
536+
537+ pdn_gpio = devm_gpiod_get_optional(snd_rpi_simple.dev, "pdn",
538+ GPIOD_OUT_LOW);
539+ if (IS_ERR(pdn_gpio)) {
540+ ret = PTR_ERR(pdn_gpio);
541+ dev_err(snd_rpi_simple.dev, "failed to get pdn gpio: %d\n", ret);
542+ return ret;
543+ }
544+
545+ rst_gpio = devm_gpiod_get_optional(snd_rpi_simple.dev, "rst",
546+ GPIOD_OUT_LOW);
547+ if (IS_ERR(rst_gpio)) {
548+ ret = PTR_ERR(rst_gpio);
549+ dev_err(snd_rpi_simple.dev, "failed to get rst gpio: %d\n", ret);
550+ return ret;
551+ }
552+
553+ // Set up cards - pulse power down and reset first, then
554+ // set up according to datasheet
555+ gpiod_set_value_cansleep(pdn_gpio, 1);
556+ gpiod_set_value_cansleep(rst_gpio, 1);
557+ usleep_range(1000, 10000);
558+ gpiod_set_value_cansleep(pdn_gpio, 0);
559+ usleep_range(20000, 30000);
560+ gpiod_set_value_cansleep(rst_gpio, 0);
561+ usleep_range(20000, 30000);
562+
563+ // Oscillator trim
564+ snd_soc_component_write(dac, 0x1b, 0);
565+ usleep_range(60000, 80000);
566+
567+ // MCLK at 64fs, sample rate 44.1 or 48kHz
568+ snd_soc_component_write(dac, 0x00, 0x60);
569+
570+ // Set up for BTL - AD/BD mode - AD is 0x00107772, BD is 0x00987772
571+ snd_soc_component_write(dac, 0x20, 0x00107772);
572+
573+ // End mute
574+ snd_soc_component_write(dac, 0x05, 0x00);
575+
576+ return 0;
577+}
578+
579+static int snd_rpi_simple_hw_params(struct snd_pcm_substream *substream,
580+ struct snd_pcm_hw_params *params)
581+{
582+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
583+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
584+ struct snd_rpi_simple_drvdata *drvdata;
585+ unsigned int sample_bits;
586+
587+ drvdata = snd_soc_card_get_drvdata(rtd->card);
588+
589+ if (drvdata->fixed_bclk_ratio > 0)
590+ return 0; // BCLK is configured in .init
591+
592+ /* The simple drivers just set the bclk_ratio to sample_bits * 2 so
593+ * hard-code this for now. More complex drivers could just replace
594+ * the hw_params routine.
595+ */
596+ sample_bits = snd_pcm_format_physical_width(params_format(params));
597+ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
598+}
599+
600+static struct snd_soc_ops snd_rpi_simple_ops = {
601+ .hw_params = snd_rpi_simple_hw_params,
602+};
603+
604+enum adau1977_clk_id {
605+ ADAU1977_SYSCLK,
606+};
607+
608+enum adau1977_sysclk_src {
609+ ADAU1977_SYSCLK_SRC_MCLK,
610+ ADAU1977_SYSCLK_SRC_LRCLK,
611+};
612+
613+static int adau1977_init(struct snd_soc_pcm_runtime *rtd)
614+{
615+ int ret;
616+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
617+
618+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, 0, 0, 0);
619+ if (ret < 0)
620+ return ret;
621+
622+ return snd_soc_component_set_sysclk(codec_dai->component,
623+ ADAU1977_SYSCLK, ADAU1977_SYSCLK_SRC_MCLK,
624+ 11289600, SND_SOC_CLOCK_IN);
625+}
626+
627+SND_SOC_DAILINK_DEFS(adau1977,
628+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
629+ DAILINK_COMP_ARRAY(COMP_CODEC("adau1977.1-0011", "adau1977-hifi")),
630+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
631+
632+static struct snd_soc_dai_link snd_rpi_adau1977_dai[] = {
633+ {
634+ .name = "adau1977",
635+ .stream_name = "ADAU1977",
636+ .init = adau1977_init,
637+ .dai_fmt = SND_SOC_DAIFMT_I2S |
638+ SND_SOC_DAIFMT_NB_NF |
639+ SND_SOC_DAIFMT_CBM_CFM,
640+ SND_SOC_DAILINK_REG(adau1977),
641+ },
642+};
643+
644+static struct snd_rpi_simple_drvdata drvdata_adau1977 = {
645+ .card_name = "snd_rpi_adau1977_adc",
646+ .dai = snd_rpi_adau1977_dai,
647+};
648+
649+SND_SOC_DAILINK_DEFS(gvchat,
650+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
651+ DAILINK_COMP_ARRAY(COMP_CODEC("voicehat-codec", "voicehat-hifi")),
652+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
653+
654+static struct snd_soc_dai_link snd_googlevoicehat_soundcard_dai[] = {
655+{
656+ .name = "Google voiceHAT SoundCard",
657+ .stream_name = "Google voiceHAT SoundCard HiFi",
658+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
659+ SND_SOC_DAIFMT_CBS_CFS,
660+ SND_SOC_DAILINK_REG(gvchat),
661+},
662+};
663+
664+static struct snd_rpi_simple_drvdata drvdata_googlevoicehat = {
665+ .card_name = "snd_rpi_googlevoicehat_soundcard",
666+ .dai = snd_googlevoicehat_soundcard_dai,
667+};
668+
669+SND_SOC_DAILINK_DEFS(hifiberry_dacplusdsp,
670+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
671+ DAILINK_COMP_ARRAY(COMP_CODEC("dacplusdsp-codec", "dacplusdsp-hifi")),
672+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
673+
674+static struct snd_soc_dai_link snd_hifiberrydacplusdsp_soundcard_dai[] = {
675+{
676+ .name = "Hifiberry DAC+DSP SoundCard",
677+ .stream_name = "Hifiberry DAC+DSP SoundCard HiFi",
678+ .dai_fmt = SND_SOC_DAIFMT_I2S |
679+ SND_SOC_DAIFMT_NB_NF |
680+ SND_SOC_DAIFMT_CBS_CFS,
681+ SND_SOC_DAILINK_REG(hifiberry_dacplusdsp),
682+},
683+};
684+
685+static struct snd_rpi_simple_drvdata drvdata_hifiberrydacplusdsp = {
686+ .card_name = "snd_rpi_hifiberrydacplusdsp_soundcard",
687+ .dai = snd_hifiberrydacplusdsp_soundcard_dai,
688+};
689+
690+SND_SOC_DAILINK_DEFS(hifiberry_amp,
691+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
692+ DAILINK_COMP_ARRAY(COMP_CODEC("tas5713.1-001b", "tas5713-hifi")),
693+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
694+
695+static struct snd_soc_dai_link snd_hifiberry_amp_dai[] = {
696+ {
697+ .name = "HifiBerry AMP",
698+ .stream_name = "HifiBerry AMP HiFi",
699+ .dai_fmt = SND_SOC_DAIFMT_I2S |
700+ SND_SOC_DAIFMT_NB_NF |
701+ SND_SOC_DAIFMT_CBS_CFS,
702+ SND_SOC_DAILINK_REG(hifiberry_amp),
703+ },
704+};
705+
706+static struct snd_rpi_simple_drvdata drvdata_hifiberry_amp = {
707+ .card_name = "snd_rpi_hifiberry_amp",
708+ .dai = snd_hifiberry_amp_dai,
709+ .fixed_bclk_ratio = 64,
710+};
711+
712+SND_SOC_DAILINK_DEFS(hifiberry_dac,
713+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
714+ DAILINK_COMP_ARRAY(COMP_CODEC("pcm5102a-codec", "pcm5102a-hifi")),
715+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
716+
717+static struct snd_soc_dai_link snd_hifiberry_dac_dai[] = {
718+ {
719+ .name = "HifiBerry DAC",
720+ .stream_name = "HifiBerry DAC HiFi",
721+ .dai_fmt = SND_SOC_DAIFMT_I2S |
722+ SND_SOC_DAIFMT_NB_NF |
723+ SND_SOC_DAIFMT_CBS_CFS,
724+ SND_SOC_DAILINK_REG(hifiberry_dac),
725+ },
726+};
727+
728+static struct snd_rpi_simple_drvdata drvdata_hifiberry_dac = {
729+ .card_name = "snd_rpi_hifiberry_dac",
730+ .dai = snd_hifiberry_dac_dai,
731+};
732+
733+SND_SOC_DAILINK_DEFS(rpi_dac,
734+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
735+ DAILINK_COMP_ARRAY(COMP_CODEC("pcm1794a-codec", "pcm1794a-hifi")),
736+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
737+
738+static struct snd_soc_dai_link snd_rpi_dac_dai[] = {
739+{
740+ .name = "RPi-DAC",
741+ .stream_name = "RPi-DAC HiFi",
742+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
743+ SND_SOC_DAIFMT_CBS_CFS,
744+ SND_SOC_DAILINK_REG(rpi_dac),
745+},
746+};
747+
748+static struct snd_rpi_simple_drvdata drvdata_rpi_dac = {
749+ .card_name = "snd_rpi_rpi_dac",
750+ .dai = snd_rpi_dac_dai,
751+ .fixed_bclk_ratio = 64,
752+};
753+
754+SND_SOC_DAILINK_DEFS(merus_amp,
755+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
756+ DAILINK_COMP_ARRAY(COMP_CODEC("ma120x0p.1-0020","ma120x0p-amp")),
757+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
758+
759+static struct snd_soc_dai_link snd_merus_amp_dai[] = {
760+ {
761+ .name = "MerusAmp",
762+ .stream_name = "Merus Audio Amp",
763+ .dai_fmt = SND_SOC_DAIFMT_I2S |
764+ SND_SOC_DAIFMT_NB_NF |
765+ SND_SOC_DAIFMT_CBS_CFS,
766+ SND_SOC_DAILINK_REG(merus_amp),
767+ },
768+};
769+
770+static struct snd_rpi_simple_drvdata drvdata_merus_amp = {
771+ .card_name = "snd_rpi_merus_amp",
772+ .dai = snd_merus_amp_dai,
773+ .fixed_bclk_ratio = 64,
774+};
775+
776+SND_SOC_DAILINK_DEFS(pifi_mini_210,
777+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
778+ DAILINK_COMP_ARRAY(COMP_CODEC("tas571x.1-001a", "tas571x-hifi")),
779+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
780+
781+static struct snd_soc_dai_link snd_pifi_mini_210_dai[] = {
782+ {
783+ .name = "PiFi Mini 210",
784+ .stream_name = "PiFi Mini 210 HiFi",
785+ .init = pifi_mini_210_init,
786+ .dai_fmt = SND_SOC_DAIFMT_I2S |
787+ SND_SOC_DAIFMT_NB_NF |
788+ SND_SOC_DAIFMT_CBS_CFS,
789+ SND_SOC_DAILINK_REG(pifi_mini_210),
790+ },
791+};
792+
793+static struct snd_rpi_simple_drvdata drvdata_pifi_mini_210 = {
794+ .card_name = "snd_pifi_mini_210",
795+ .dai = snd_pifi_mini_210_dai,
796+ .fixed_bclk_ratio = 64,
797+};
798+
799+static const struct of_device_id snd_rpi_simple_of_match[] = {
800+ { .compatible = "adi,adau1977-adc",
801+ .data = (void *) &drvdata_adau1977 },
802+ { .compatible = "googlevoicehat,googlevoicehat-soundcard",
803+ .data = (void *) &drvdata_googlevoicehat },
804+ { .compatible = "hifiberrydacplusdsp,hifiberrydacplusdsp-soundcard",
805+ .data = (void *) &drvdata_hifiberrydacplusdsp },
806+ { .compatible = "hifiberry,hifiberry-amp",
807+ .data = (void *) &drvdata_hifiberry_amp },
808+ { .compatible = "hifiberry,hifiberry-dac",
809+ .data = (void *) &drvdata_hifiberry_dac },
810+ { .compatible = "rpi,rpi-dac", &drvdata_rpi_dac},
811+ { .compatible = "merus,merus-amp",
812+ .data = (void *) &drvdata_merus_amp },
813+ { .compatible = "pifi,pifi-mini-210",
814+ .data = (void *) &drvdata_pifi_mini_210 },
815+ {},
816+};
817+
818+static int snd_rpi_simple_probe(struct platform_device *pdev)
819+{
820+ int ret = 0;
821+ const struct of_device_id *of_id;
822+
823+ snd_rpi_simple.dev = &pdev->dev;
824+ of_id = of_match_node(snd_rpi_simple_of_match, pdev->dev.of_node);
825+
826+ if (pdev->dev.of_node && of_id->data) {
827+ struct device_node *i2s_node;
828+ struct snd_rpi_simple_drvdata *drvdata =
829+ (struct snd_rpi_simple_drvdata *) of_id->data;
830+ struct snd_soc_dai_link *dai = drvdata->dai;
831+
832+ snd_soc_card_set_drvdata(&snd_rpi_simple, drvdata);
833+
834+ /* More complex drivers might override individual functions */
835+ if (!dai->init)
836+ dai->init = snd_rpi_simple_init;
837+ if (!dai->ops)
838+ dai->ops = &snd_rpi_simple_ops;
839+
840+ snd_rpi_simple.name = drvdata->card_name;
841+
842+ snd_rpi_simple.dai_link = dai;
843+ i2s_node = of_parse_phandle(pdev->dev.of_node,
844+ "i2s-controller", 0);
845+ if (!i2s_node) {
846+ pr_err("Failed to find i2s-controller DT node\n");
847+ return -ENODEV;
848+ }
849+
850+ dai->cpus->of_node = i2s_node;
851+ dai->platforms->of_node = i2s_node;
852+ }
853+
854+ ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_simple);
855+ if (ret && ret != -EPROBE_DEFER)
856+ dev_err(&pdev->dev, "Failed to register card %d\n", ret);
857+
858+ return ret;
859+}
860+
861+static struct platform_driver snd_rpi_simple_driver = {
862+ .driver = {
863+ .name = "snd-rpi-simple",
864+ .owner = THIS_MODULE,
865+ .of_match_table = snd_rpi_simple_of_match,
866+ },
867+ .probe = snd_rpi_simple_probe,
868+};
869+MODULE_DEVICE_TABLE(of, snd_rpi_simple_of_match);
870+
871+module_platform_driver(snd_rpi_simple_driver);
872+
873+MODULE_AUTHOR("Tim Gover <tim.gover@raspberrypi.org>");
874+MODULE_DESCRIPTION("ASoC Raspberry Pi simple soundcard driver ");
875+MODULE_LICENSE("GPL v2");
This page took 0.161925 seconds and 4 git commands to generate.