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
4 DSL/PON chips (bcm63158, bcm63178)
6 If you don't know what to do here, say N
8 +config SND_RPI_WM8804_SOUNDCARD
9 + tristate "Support for Raspberry Pi generic WM8804 soundcards"
11 + Say Y or M if you want to add support for the Raspberry Pi
12 + generic driver for WM8804 based soundcards.
13 --- linux-5.9.orig/sound/soc/bcm/Makefile 2020-10-11 23:15:50.000000000 +0200
14 +++ linux-5.9/sound/soc/bcm/Makefile 2020-12-14 01:05:51.274295380 +0100
16 # BCM63XX Platform Support
17 snd-soc-63xx-objs := bcm63xx-i2s-whistler.o bcm63xx-pcm-whistler.o
19 -obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o
20 \ No newline at end of file
21 +obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o
23 +snd-soc-rpi-wm8804-soundcard-objs := rpi-wm8804-soundcard.o
25 +obj-$(CONFIG_SND_RPI_WM8804_SOUNDCARD) += snd-soc-rpi-wm8804-soundcard.o
26 --- linux-5.9.orig/sound/soc/bcm/rpi-wm8804-soundcard.c 1970-01-01 01:00:00.000000000 +0100
27 +++ linux-5.9/sound/soc/bcm/rpi-wm8804-soundcard.c 2020-12-14 01:02:38.688758934 +0100
29 +// SPDX-License-Identifier: GPL-2.0
31 + * rpi--wm8804.c -- ALSA SoC Raspberry Pi soundcard.
33 + * Copyright (C) 2018 Raspberry Pi.
35 + * Authors: Tim Gover <tim.gover@raspberrypi.org>
37 + * Generic driver for Pi Hat WM8804 digi sounds cards
39 + * Based upon code from:
41 + * by Milan Neskovic <info@justboom.co>
44 + * by Daniel Matuschek <info@crazy-audio.com>
47 + * by Baswaraj <jaikumar@cem-solutions.net>
50 + * Daniel Matuschek <info@crazy-audio.com>
52 + * This program is free software; you can redistribute it and/or
53 + * modify it under the terms of the GNU General Public License
54 + * version 2 as published by the Free Software Foundation.
56 + * This program is distributed in the hope that it will be useful, but
57 + * WITHOUT ANY WARRANTY; without even the implied warranty of
58 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
59 + * General Public License for more details.
62 +#include <linux/gpio/consumer.h>
63 +#include <linux/platform_device.h>
64 +#include <linux/module.h>
66 +#include <sound/core.h>
67 +#include <sound/pcm.h>
68 +#include <sound/pcm_params.h>
69 +#include <sound/soc.h>
71 +#include "../codecs/wm8804.h"
73 +struct wm8804_clk_cfg {
74 + unsigned int sysclk_freq;
75 + unsigned int mclk_freq;
76 + unsigned int mclk_div;
79 +/* Parameters for generic functions */
80 +struct snd_rpi_wm8804_drvdata {
81 + /* Required - pointer to the DAI structure */
82 + struct snd_soc_dai_link *dai;
83 + /* Required - snd_soc_card name */
84 + const char *card_name;
85 + /* Optional DT node names if card info is defined in DT */
86 + const char *card_name_dt;
87 + const char *dai_name_dt;
88 + const char *dai_stream_name_dt;
89 + /* Optional probe extension - called prior to register_card */
90 + int (*probe)(struct platform_device *pdev);
93 +static struct gpio_desc *snd_clk44gpio;
94 +static struct gpio_desc *snd_clk48gpio;
95 +static int wm8804_samplerate = 0;
97 +/* Forward declarations */
98 +static struct snd_soc_dai_link snd_allo_digione_dai[];
99 +static struct snd_soc_card snd_rpi_wm8804;
102 +#define CLK_44EN_RATE 22579200UL
103 +#define CLK_48EN_RATE 24576000UL
105 +static unsigned int snd_rpi_wm8804_enable_clock(unsigned int samplerate)
107 + switch (samplerate) {
113 + gpiod_set_value_cansleep(snd_clk44gpio, 1);
114 + gpiod_set_value_cansleep(snd_clk48gpio, 0);
115 + return CLK_44EN_RATE;
117 + gpiod_set_value_cansleep(snd_clk48gpio, 1);
118 + gpiod_set_value_cansleep(snd_clk44gpio, 0);
119 + return CLK_48EN_RATE;
123 +static void snd_rpi_wm8804_clk_cfg(unsigned int samplerate,
124 + struct wm8804_clk_cfg *clk_cfg)
126 + clk_cfg->sysclk_freq = 27000000;
128 + if (samplerate <= 96000 ||
129 + snd_rpi_wm8804.dai_link == snd_allo_digione_dai) {
130 + clk_cfg->mclk_freq = samplerate * 256;
131 + clk_cfg->mclk_div = WM8804_MCLKDIV_256FS;
133 + clk_cfg->mclk_freq = samplerate * 128;
134 + clk_cfg->mclk_div = WM8804_MCLKDIV_128FS;
137 + if (!(IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio)))
138 + clk_cfg->sysclk_freq = snd_rpi_wm8804_enable_clock(samplerate);
141 +static int snd_rpi_wm8804_hw_params(struct snd_pcm_substream *substream,
142 + struct snd_pcm_hw_params *params)
144 + struct snd_soc_pcm_runtime *rtd = substream->private_data;
145 + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
146 + struct snd_soc_component *component = codec_dai->component;
147 + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
148 + int sampling_freq = 1;
150 + struct wm8804_clk_cfg clk_cfg;
151 + int samplerate = params_rate(params);
153 + if (samplerate == wm8804_samplerate)
156 + /* clear until all clocks are setup properly */
157 + wm8804_samplerate = 0;
159 + snd_rpi_wm8804_clk_cfg(samplerate, &clk_cfg);
161 + pr_debug("%s samplerate: %d mclk_freq: %u mclk_div: %u sysclk: %u\n",
162 + __func__, samplerate, clk_cfg.mclk_freq,
163 + clk_cfg.mclk_div, clk_cfg.sysclk_freq);
165 + switch (samplerate) {
167 + sampling_freq = 0x03;
170 + sampling_freq = 0x00;
173 + sampling_freq = 0x02;
176 + sampling_freq = 0x08;
179 + sampling_freq = 0x0a;
182 + sampling_freq = 0x0c;
185 + sampling_freq = 0x0e;
188 + dev_err(rtd->card->dev,
189 + "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n",
193 + snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, clk_cfg.mclk_div);
194 + snd_soc_dai_set_pll(codec_dai, 0, 0,
195 + clk_cfg.sysclk_freq, clk_cfg.mclk_freq);
197 + ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
198 + clk_cfg.sysclk_freq, SND_SOC_CLOCK_OUT);
200 + dev_err(rtd->card->dev,
201 + "Failed to set WM8804 SYSCLK: %d\n", ret);
205 + wm8804_samplerate = samplerate;
207 + /* set sampling frequency status bits */
208 + snd_soc_component_update_bits(component, WM8804_SPDTX4, 0x0f,
211 + return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
214 +static struct snd_soc_ops snd_rpi_wm8804_ops = {
215 + .hw_params = snd_rpi_wm8804_hw_params,
218 +SND_SOC_DAILINK_DEFS(justboom_digi,
219 + DAILINK_COMP_ARRAY(COMP_EMPTY()),
220 + DAILINK_COMP_ARRAY(COMP_EMPTY()),
221 + DAILINK_COMP_ARRAY(COMP_EMPTY()));
223 +static struct snd_soc_dai_link snd_justboom_digi_dai[] = {
225 + .name = "JustBoom Digi",
226 + .stream_name = "JustBoom Digi HiFi",
227 + SND_SOC_DAILINK_REG(justboom_digi),
231 +static struct snd_rpi_wm8804_drvdata drvdata_justboom_digi = {
232 + .card_name = "snd_rpi_justboom_digi",
233 + .dai = snd_justboom_digi_dai,
236 +SND_SOC_DAILINK_DEFS(iqaudio_digi,
237 + DAILINK_COMP_ARRAY(COMP_EMPTY()),
238 + DAILINK_COMP_ARRAY(COMP_EMPTY()),
239 + DAILINK_COMP_ARRAY(COMP_EMPTY()));
241 +static struct snd_soc_dai_link snd_iqaudio_digi_dai[] = {
243 + .name = "IQAudIO Digi",
244 + .stream_name = "IQAudIO Digi HiFi",
245 + SND_SOC_DAILINK_REG(iqaudio_digi),
249 +static struct snd_rpi_wm8804_drvdata drvdata_iqaudio_digi = {
250 + .card_name = "IQAudIODigi",
251 + .dai = snd_iqaudio_digi_dai,
252 + .card_name_dt = "wm8804-digi,card-name",
253 + .dai_name_dt = "wm8804-digi,dai-name",
254 + .dai_stream_name_dt = "wm8804-digi,dai-stream-name",
257 +static int snd_allo_digione_probe(struct platform_device *pdev)
259 + pr_debug("%s\n", __func__);
261 + if (IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio)) {
262 + dev_err(&pdev->dev, "devm_gpiod_get() failed\n");
268 +SND_SOC_DAILINK_DEFS(allo_digione,
269 + DAILINK_COMP_ARRAY(COMP_EMPTY()),
270 + DAILINK_COMP_ARRAY(COMP_EMPTY()),
271 + DAILINK_COMP_ARRAY(COMP_EMPTY()));
273 +static struct snd_soc_dai_link snd_allo_digione_dai[] = {
275 + .name = "Allo DigiOne",
276 + .stream_name = "Allo DigiOne HiFi",
277 + SND_SOC_DAILINK_REG(allo_digione),
281 +static struct snd_rpi_wm8804_drvdata drvdata_allo_digione = {
282 + .card_name = "snd_allo_digione",
283 + .dai = snd_allo_digione_dai,
284 + .probe = snd_allo_digione_probe,
287 +SND_SOC_DAILINK_DEFS(hifiberry_digi,
288 + DAILINK_COMP_ARRAY(COMP_EMPTY()),
289 + DAILINK_COMP_ARRAY(COMP_EMPTY()),
290 + DAILINK_COMP_ARRAY(COMP_EMPTY()));
292 +static struct snd_soc_dai_link snd_hifiberry_digi_dai[] = {
294 + .name = "HifiBerry Digi",
295 + .stream_name = "HifiBerry Digi HiFi",
296 + SND_SOC_DAILINK_REG(hifiberry_digi),
300 +static int snd_hifiberry_digi_probe(struct platform_device *pdev)
302 + pr_debug("%s\n", __func__);
304 + if (IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio))
307 + snd_hifiberry_digi_dai->name = "HiFiBerry Digi+ Pro";
308 + snd_hifiberry_digi_dai->stream_name = "HiFiBerry Digi+ Pro HiFi";
312 +static struct snd_rpi_wm8804_drvdata drvdata_hifiberry_digi = {
313 + .card_name = "snd_rpi_hifiberry_digi",
314 + .dai = snd_hifiberry_digi_dai,
315 + .probe = snd_hifiberry_digi_probe,
318 +static const struct of_device_id snd_rpi_wm8804_of_match[] = {
319 + { .compatible = "justboom,justboom-digi",
320 + .data = (void *) &drvdata_justboom_digi },
321 + { .compatible = "iqaudio,wm8804-digi",
322 + .data = (void *) &drvdata_iqaudio_digi },
323 + { .compatible = "allo,allo-digione",
324 + .data = (void *) &drvdata_allo_digione },
325 + { .compatible = "hifiberry,hifiberry-digi",
326 + .data = (void *) &drvdata_hifiberry_digi },
330 +static struct snd_soc_card snd_rpi_wm8804 = {
331 + .driver_name = "RPi-WM8804",
332 + .owner = THIS_MODULE,
337 +static int snd_rpi_wm8804_probe(struct platform_device *pdev)
340 + const struct of_device_id *of_id;
342 + snd_rpi_wm8804.dev = &pdev->dev;
343 + of_id = of_match_node(snd_rpi_wm8804_of_match, pdev->dev.of_node);
345 + if (pdev->dev.of_node && of_id->data) {
346 + struct device_node *i2s_node;
347 + struct snd_rpi_wm8804_drvdata *drvdata =
348 + (struct snd_rpi_wm8804_drvdata *) of_id->data;
349 + struct snd_soc_dai_link *dai = drvdata->dai;
351 + snd_soc_card_set_drvdata(&snd_rpi_wm8804, drvdata);
354 + dai->ops = &snd_rpi_wm8804_ops;
355 + if (!dai->codecs->dai_name)
356 + dai->codecs->dai_name = "wm8804-spdif";
357 + if (!dai->codecs->name)
358 + dai->codecs->name = "wm8804.1-003b";
360 + dai->dai_fmt = SND_SOC_DAIFMT_I2S |
361 + SND_SOC_DAIFMT_NB_NF |
362 + SND_SOC_DAIFMT_CBM_CFM;
364 + snd_rpi_wm8804.dai_link = dai;
365 + i2s_node = of_parse_phandle(pdev->dev.of_node,
366 + "i2s-controller", 0);
368 + pr_err("Failed to find i2s-controller DT node\n");
372 + snd_rpi_wm8804.name = drvdata->card_name;
374 + /* If requested by in drvdata get card & DAI names from DT */
375 + if (drvdata->card_name_dt)
376 + of_property_read_string(i2s_node,
377 + drvdata->card_name_dt,
378 + &snd_rpi_wm8804.name);
380 + if (drvdata->dai_name_dt)
381 + of_property_read_string(i2s_node,
382 + drvdata->dai_name_dt,
385 + if (drvdata->dai_stream_name_dt)
386 + of_property_read_string(i2s_node,
387 + drvdata->dai_stream_name_dt,
388 + &dai->stream_name);
390 + dai->cpus->of_node = i2s_node;
391 + dai->platforms->of_node = i2s_node;
394 + * clk44gpio and clk48gpio are not required by all cards so
395 + * don't check the error status.
398 + devm_gpiod_get(&pdev->dev, "clock44", GPIOD_OUT_LOW);
401 + devm_gpiod_get(&pdev->dev, "clock48", GPIOD_OUT_LOW);
403 + if (drvdata->probe) {
404 + ret = drvdata->probe(pdev);
406 + dev_err(&pdev->dev, "Custom probe failed %d\n",
412 + pr_debug("%s card: %s dai: %s stream: %s\n", __func__,
413 + snd_rpi_wm8804.name,
414 + dai->name, dai->stream_name);
417 + ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_wm8804);
418 + if (ret && ret != -EPROBE_DEFER)
419 + dev_err(&pdev->dev, "Failed to register card %d\n", ret);
424 +static struct platform_driver snd_rpi_wm8804_driver = {
426 + .name = "snd-rpi-wm8804",
427 + .owner = THIS_MODULE,
428 + .of_match_table = snd_rpi_wm8804_of_match,
430 + .probe = snd_rpi_wm8804_probe,
432 +MODULE_DEVICE_TABLE(of, snd_rpi_wm8804_of_match);
434 +module_platform_driver(snd_rpi_wm8804_driver);
436 +MODULE_AUTHOR("Tim Gover <tim.gover@raspberrypi.org>");
437 +MODULE_DESCRIPTION("ASoC Raspberry Pi Hat generic digi driver for WM8804 based cards");
438 +MODULE_LICENSE("GPL v2");