]> git.pld-linux.org Git - packages/kernel.git/blame - rpi-wm8804.patch
i686 updates
[packages/kernel.git] / rpi-wm8804.patch
CommitLineData
cb548207
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
798c76a1 3@@ -26,3 +26,9 @@
cb548207
JP
4 DSL/PON chips (bcm63158, bcm63178)
5
6 If you don't know what to do here, say N
7+
798c76a1
JP
8+config SND_RPI_WM8804_SOUNDCARD
9+ tristate "Support for Raspberry Pi generic WM8804 soundcards"
cb548207 10+ help
798c76a1
JP
11+ Say Y or M if you want to add support for the Raspberry Pi
12+ generic driver for WM8804 based soundcards.
cb548207
JP
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
15@@ -12,4 +12,8 @@
16 # BCM63XX Platform Support
17 snd-soc-63xx-objs := bcm63xx-i2s-whistler.o bcm63xx-pcm-whistler.o
18
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
22+
23+snd-soc-rpi-wm8804-soundcard-objs := rpi-wm8804-soundcard.o
24+
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
28@@ -0,0 +1,410 @@
29+// SPDX-License-Identifier: GPL-2.0
30+/*
31+ * rpi--wm8804.c -- ALSA SoC Raspberry Pi soundcard.
32+ *
33+ * Copyright (C) 2018 Raspberry Pi.
34+ *
35+ * Authors: Tim Gover <tim.gover@raspberrypi.org>
36+ *
37+ * Generic driver for Pi Hat WM8804 digi sounds cards
38+ *
39+ * Based upon code from:
40+ * justboom-digi.c
41+ * by Milan Neskovic <info@justboom.co>
42+ *
43+ * iqaudio_digi.c
44+ * by Daniel Matuschek <info@crazy-audio.com>
45+ *
46+ * allo-digione.c
47+ * by Baswaraj <jaikumar@cem-solutions.net>
48+ *
49+ * hifiberry-digi.c
50+ * Daniel Matuschek <info@crazy-audio.com>
51+ *
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.
55+ *
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.
60+ */
61+
62+#include <linux/gpio/consumer.h>
63+#include <linux/platform_device.h>
64+#include <linux/module.h>
65+
66+#include <sound/core.h>
67+#include <sound/pcm.h>
68+#include <sound/pcm_params.h>
69+#include <sound/soc.h>
70+
71+#include "../codecs/wm8804.h"
72+
73+struct wm8804_clk_cfg {
74+ unsigned int sysclk_freq;
75+ unsigned int mclk_freq;
76+ unsigned int mclk_div;
77+};
78+
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);
91+};
92+
93+static struct gpio_desc *snd_clk44gpio;
94+static struct gpio_desc *snd_clk48gpio;
95+static int wm8804_samplerate = 0;
96+
97+/* Forward declarations */
98+static struct snd_soc_dai_link snd_allo_digione_dai[];
99+static struct snd_soc_card snd_rpi_wm8804;
100+
101+
102+#define CLK_44EN_RATE 22579200UL
103+#define CLK_48EN_RATE 24576000UL
104+
105+static unsigned int snd_rpi_wm8804_enable_clock(unsigned int samplerate)
106+{
107+ switch (samplerate) {
108+ case 11025:
109+ case 22050:
110+ case 44100:
111+ case 88200:
112+ case 176400:
113+ gpiod_set_value_cansleep(snd_clk44gpio, 1);
114+ gpiod_set_value_cansleep(snd_clk48gpio, 0);
115+ return CLK_44EN_RATE;
116+ default:
117+ gpiod_set_value_cansleep(snd_clk48gpio, 1);
118+ gpiod_set_value_cansleep(snd_clk44gpio, 0);
119+ return CLK_48EN_RATE;
120+ }
121+}
122+
123+static void snd_rpi_wm8804_clk_cfg(unsigned int samplerate,
124+ struct wm8804_clk_cfg *clk_cfg)
125+{
126+ clk_cfg->sysclk_freq = 27000000;
127+
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;
132+ } else {
133+ clk_cfg->mclk_freq = samplerate * 128;
134+ clk_cfg->mclk_div = WM8804_MCLKDIV_128FS;
135+ }
136+
137+ if (!(IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio)))
138+ clk_cfg->sysclk_freq = snd_rpi_wm8804_enable_clock(samplerate);
139+}
140+
141+static int snd_rpi_wm8804_hw_params(struct snd_pcm_substream *substream,
142+ struct snd_pcm_hw_params *params)
143+{
144+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
798c76a1
JP
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);
cb548207
JP
148+ int sampling_freq = 1;
149+ int ret;
150+ struct wm8804_clk_cfg clk_cfg;
151+ int samplerate = params_rate(params);
152+
153+ if (samplerate == wm8804_samplerate)
154+ return 0;
155+
156+ /* clear until all clocks are setup properly */
157+ wm8804_samplerate = 0;
158+
159+ snd_rpi_wm8804_clk_cfg(samplerate, &clk_cfg);
160+
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);
164+
165+ switch (samplerate) {
166+ case 32000:
167+ sampling_freq = 0x03;
168+ break;
169+ case 44100:
170+ sampling_freq = 0x00;
171+ break;
172+ case 48000:
173+ sampling_freq = 0x02;
174+ break;
175+ case 88200:
176+ sampling_freq = 0x08;
177+ break;
178+ case 96000:
179+ sampling_freq = 0x0a;
180+ break;
181+ case 176400:
182+ sampling_freq = 0x0c;
183+ break;
184+ case 192000:
185+ sampling_freq = 0x0e;
186+ break;
187+ default:
188+ dev_err(rtd->card->dev,
189+ "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n",
190+ samplerate);
191+ }
192+
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);
196+
197+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
198+ clk_cfg.sysclk_freq, SND_SOC_CLOCK_OUT);
199+ if (ret < 0) {
200+ dev_err(rtd->card->dev,
201+ "Failed to set WM8804 SYSCLK: %d\n", ret);
202+ return ret;
203+ }
204+
205+ wm8804_samplerate = samplerate;
206+
207+ /* set sampling frequency status bits */
208+ snd_soc_component_update_bits(component, WM8804_SPDTX4, 0x0f,
209+ sampling_freq);
210+
211+ return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
212+}
213+
214+static struct snd_soc_ops snd_rpi_wm8804_ops = {
215+ .hw_params = snd_rpi_wm8804_hw_params,
216+};
217+
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()));
222+
223+static struct snd_soc_dai_link snd_justboom_digi_dai[] = {
224+{
225+ .name = "JustBoom Digi",
226+ .stream_name = "JustBoom Digi HiFi",
227+ SND_SOC_DAILINK_REG(justboom_digi),
228+},
229+};
230+
231+static struct snd_rpi_wm8804_drvdata drvdata_justboom_digi = {
232+ .card_name = "snd_rpi_justboom_digi",
233+ .dai = snd_justboom_digi_dai,
234+};
235+
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()));
240+
241+static struct snd_soc_dai_link snd_iqaudio_digi_dai[] = {
242+{
243+ .name = "IQAudIO Digi",
244+ .stream_name = "IQAudIO Digi HiFi",
245+ SND_SOC_DAILINK_REG(iqaudio_digi),
246+},
247+};
248+
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",
255+};
256+
257+static int snd_allo_digione_probe(struct platform_device *pdev)
258+{
259+ pr_debug("%s\n", __func__);
260+
261+ if (IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio)) {
262+ dev_err(&pdev->dev, "devm_gpiod_get() failed\n");
263+ return -EINVAL;
264+ }
265+ return 0;
266+}
267+
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()));
272+
273+static struct snd_soc_dai_link snd_allo_digione_dai[] = {
274+{
275+ .name = "Allo DigiOne",
276+ .stream_name = "Allo DigiOne HiFi",
277+ SND_SOC_DAILINK_REG(allo_digione),
278+},
279+};
280+
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,
285+};
286+
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()));
291+
292+static struct snd_soc_dai_link snd_hifiberry_digi_dai[] = {
293+{
294+ .name = "HifiBerry Digi",
295+ .stream_name = "HifiBerry Digi HiFi",
296+ SND_SOC_DAILINK_REG(hifiberry_digi),
297+},
298+};
299+
300+static int snd_hifiberry_digi_probe(struct platform_device *pdev)
301+{
302+ pr_debug("%s\n", __func__);
303+
304+ if (IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio))
305+ return 0;
306+
307+ snd_hifiberry_digi_dai->name = "HiFiBerry Digi+ Pro";
308+ snd_hifiberry_digi_dai->stream_name = "HiFiBerry Digi+ Pro HiFi";
309+ return 0;
310+}
311+
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,
316+};
317+
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 },
327+ {},
328+};
329+
330+static struct snd_soc_card snd_rpi_wm8804 = {
331+ .driver_name = "RPi-WM8804",
332+ .owner = THIS_MODULE,
333+ .dai_link = NULL,
334+ .num_links = 1,
335+};
336+
337+static int snd_rpi_wm8804_probe(struct platform_device *pdev)
338+{
339+ int ret = 0;
340+ const struct of_device_id *of_id;
341+
342+ snd_rpi_wm8804.dev = &pdev->dev;
343+ of_id = of_match_node(snd_rpi_wm8804_of_match, pdev->dev.of_node);
344+
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;
350+
351+ snd_soc_card_set_drvdata(&snd_rpi_wm8804, drvdata);
352+
353+ if (!dai->ops)
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";
359+ if (!dai->dai_fmt)
360+ dai->dai_fmt = SND_SOC_DAIFMT_I2S |
361+ SND_SOC_DAIFMT_NB_NF |
362+ SND_SOC_DAIFMT_CBM_CFM;
363+
364+ snd_rpi_wm8804.dai_link = dai;
365+ i2s_node = of_parse_phandle(pdev->dev.of_node,
366+ "i2s-controller", 0);
367+ if (!i2s_node) {
368+ pr_err("Failed to find i2s-controller DT node\n");
369+ return -ENODEV;
370+ }
371+
372+ snd_rpi_wm8804.name = drvdata->card_name;
373+
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);
379+
380+ if (drvdata->dai_name_dt)
381+ of_property_read_string(i2s_node,
382+ drvdata->dai_name_dt,
383+ &dai->name);
384+
385+ if (drvdata->dai_stream_name_dt)
386+ of_property_read_string(i2s_node,
387+ drvdata->dai_stream_name_dt,
388+ &dai->stream_name);
389+
390+ dai->cpus->of_node = i2s_node;
391+ dai->platforms->of_node = i2s_node;
392+
393+ /*
394+ * clk44gpio and clk48gpio are not required by all cards so
395+ * don't check the error status.
396+ */
397+ snd_clk44gpio =
398+ devm_gpiod_get(&pdev->dev, "clock44", GPIOD_OUT_LOW);
399+
400+ snd_clk48gpio =
401+ devm_gpiod_get(&pdev->dev, "clock48", GPIOD_OUT_LOW);
402+
403+ if (drvdata->probe) {
404+ ret = drvdata->probe(pdev);
405+ if (ret < 0) {
406+ dev_err(&pdev->dev, "Custom probe failed %d\n",
407+ ret);
408+ return ret;
409+ }
410+ }
411+
412+ pr_debug("%s card: %s dai: %s stream: %s\n", __func__,
413+ snd_rpi_wm8804.name,
414+ dai->name, dai->stream_name);
415+ }
416+
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);
420+
421+ return ret;
422+}
423+
424+static struct platform_driver snd_rpi_wm8804_driver = {
425+ .driver = {
426+ .name = "snd-rpi-wm8804",
427+ .owner = THIS_MODULE,
428+ .of_match_table = snd_rpi_wm8804_of_match,
429+ },
430+ .probe = snd_rpi_wm8804_probe,
431+};
432+MODULE_DEVICE_TABLE(of, snd_rpi_wm8804_of_match);
433+
434+module_platform_driver(snd_rpi_wm8804_driver);
435+
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");
This page took 0.101699 seconds and 4 git commands to generate.