bcm27xx: switch to 5.15
[openwrt/staging/chunkeey.git] / target / linux / bcm27xx / patches-5.10 / 950-0367-PiFi-40-driver-Makefile-and-Kconfig.patch
1 From 0fa0f5932fff8c960862176fd61a7bccd2f28b90 Mon Sep 17 00:00:00 2001
2 From: David Knell <david.knell@gmail.com>
3 Date: Wed, 28 Oct 2020 14:21:37 +0000
4 Subject: [PATCH] PiFi-40 driver, Makefile and Kconfig
5
6 Signed-off-by: David Knell <david.knell@gmail.com>
7 ---
8 sound/soc/bcm/Kconfig | 8 ++
9 sound/soc/bcm/Makefile | 3 +
10 sound/soc/bcm/pifi-40.c | 283 ++++++++++++++++++++++++++++++++++++++++
11 3 files changed, 294 insertions(+)
12 create mode 100644 sound/soc/bcm/pifi-40.c
13
14 --- a/sound/soc/bcm/Kconfig
15 +++ b/sound/soc/bcm/Kconfig
16 @@ -100,6 +100,14 @@ config SND_BCM2708_SOC_HIFIBERRY_AMP
17 help
18 Say Y or M if you want to add support for the HifiBerry Amp amplifier board.
19
20 + config SND_BCM2708_SOC_PIFI_40
21 + tristate "Support for the PiFi-40 amp"
22 + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
23 + select SND_SOC_TAS571X
24 + select SND_PIFI_40
25 + help
26 + Say Y or M if you want to add support for the PiFi40 amp board
27 +
28 config SND_BCM2708_SOC_RPI_CIRRUS
29 tristate "Support for Cirrus Logic Audio Card"
30 depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
31 --- a/sound/soc/bcm/Makefile
32 +++ b/sound/soc/bcm/Makefile
33 @@ -45,6 +45,7 @@ snd-soc-pisound-objs := pisound.o
34 snd-soc-fe-pi-audio-objs := fe-pi-audio.o
35 snd-soc-rpi-simple-soundcard-objs := rpi-simple-soundcard.o
36 snd-soc-rpi-wm8804-soundcard-objs := rpi-wm8804-soundcard.o
37 +snd-soc-pifi-40-objs := pifi-40.o
38
39 obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD) += snd-soc-googlevoicehat-codec.o
40 obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o
41 @@ -74,3 +75,5 @@ obj-$(CONFIG_SND_PISOUND) += snd-soc-pis
42 obj-$(CONFIG_SND_BCM2708_SOC_FE_PI_AUDIO) += snd-soc-fe-pi-audio.o
43 obj-$(CONFIG_SND_RPI_SIMPLE_SOUNDCARD) += snd-soc-rpi-simple-soundcard.o
44 obj-$(CONFIG_SND_RPI_WM8804_SOUNDCARD) += snd-soc-rpi-wm8804-soundcard.o
45 +obj-$(CONFIG_SND_BCM2708_SOC_PIFI_40) += snd-soc-pifi-40.o
46 +
47 --- /dev/null
48 +++ b/sound/soc/bcm/pifi-40.c
49 @@ -0,0 +1,283 @@
50 +// SPDX-License-Identifier: GPL-2.0-only
51 +/*
52 + * ALSA ASoC Machine Driver for PiFi-40
53 + *
54 + * Author: David Knell <david.knell@gmail.com)
55 + * based on code by Daniel Matuschek <info@crazy-audio.com>
56 + * based on code by Florian Meier <florian.meier@koalo.de>
57 + * Copyright (C) 2020
58 + *
59 + * This program is free software; you can redistribute it and/or
60 + * modify it under the terms of the GNU General Public License
61 + * version 2 as published by the Free Software Foundation.
62 + *
63 + * This program is distributed in the hope that it will be useful, but
64 + * WITHOUT ANY WARRANTY; without even the implied warranty of
65 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
66 + * General Public License for more details.
67 + */
68 +
69 +#include <linux/module.h>
70 +#include <linux/platform_device.h>
71 +#include <linux/gpio/consumer.h>
72 +#include <sound/core.h>
73 +#include <sound/pcm.h>
74 +#include <sound/pcm_params.h>
75 +#include <sound/soc.h>
76 +#include <linux/firmware.h>
77 +#include <linux/delay.h>
78 +#include <sound/tlv.h>
79 +
80 +static struct gpio_desc *pdn_gpio;
81 +static int vol = 0x30;
82 +
83 +// Volume control
84 +static int pifi_40_vol_get(struct snd_kcontrol *kcontrol,
85 + struct snd_ctl_elem_value *ucontrol)
86 +{
87 + ucontrol->value.integer.value[0] = vol;
88 + ucontrol->value.integer.value[1] = vol;
89 + return 0;
90 +}
91 +
92 +static int pifi_40_vol_set(struct snd_kcontrol *kcontrol,
93 + struct snd_ctl_elem_value *ucontrol)
94 +{
95 + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
96 + struct snd_soc_pcm_runtime *rtd;
97 + unsigned int v = ucontrol->value.integer.value[0];
98 + struct snd_soc_component *dac[2];
99 +
100 + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
101 + dac[0] = asoc_rtd_to_codec(rtd, 0)->component;
102 + dac[1] = asoc_rtd_to_codec(rtd, 1)->component;
103 +
104 + snd_soc_component_write(dac[0], 0x07, 255 - v);
105 + snd_soc_component_write(dac[1], 0x07, 255 - v);
106 +
107 + vol = v;
108 + return 1;
109 +}
110 +
111 +static const DECLARE_TLV_DB_SCALE(digital_tlv_master, -10350, 50, 1);
112 +static const struct snd_kcontrol_new pifi_40_controls[] = {
113 + SOC_DOUBLE_R_EXT_TLV("Master Volume", 0x00, 0x01,
114 + 0x00, // Min
115 + 0xff, // Max
116 + 0x01, // Invert
117 + pifi_40_vol_get, pifi_40_vol_set,
118 + digital_tlv_master)
119 +};
120 +
121 +static const char * const codec_ctl_pfx[] = { "Left", "Right" };
122 +
123 +static const char * const codec_ctl_name[] = { "Master Volume",
124 + "Speaker Volume",
125 + "Speaker Switch" };
126 +
127 +static int snd_pifi_40_init(struct snd_soc_pcm_runtime *rtd)
128 +{
129 + struct snd_soc_card *card = rtd->card;
130 + struct snd_soc_component *dac[2];
131 + struct snd_kcontrol *kctl;
132 + int i, j;
133 +
134 + dac[0] = asoc_rtd_to_codec(rtd, 0)->component;
135 + dac[1] = asoc_rtd_to_codec(rtd, 1)->component;
136 +
137 +
138 + // Set up cards - pulse power down first
139 + gpiod_set_value_cansleep(pdn_gpio, 1);
140 + usleep_range(1000, 10000);
141 + gpiod_set_value_cansleep(pdn_gpio, 0);
142 + usleep_range(20000, 30000);
143 +
144 + // Oscillator trim
145 + snd_soc_component_write(dac[0], 0x1b, 0);
146 + snd_soc_component_write(dac[1], 0x1b, 0);
147 + usleep_range(60000, 80000);
148 +
149 + // Common setup
150 + for (i = 0; i < 2; i++) {
151 + // MCLK at 64fs, sample rate 44.1 or 48kHz
152 + snd_soc_component_write(dac[i], 0x00, 0x60);
153 +
154 + // Set up for PBTL
155 + snd_soc_component_write(dac[i], 0x19, 0x3A);
156 + snd_soc_component_write(dac[i], 0x25, 0x01103245);
157 +
158 + // Master vol to -10db
159 + snd_soc_component_write(dac[i], 0x07, 0x44);
160 + }
161 + // Inputs set to L and R respectively
162 + snd_soc_component_write(dac[0], 0x20, 0x00017772);
163 + snd_soc_component_write(dac[1], 0x20, 0x00107772);
164 +
165 + // Remove codec controls
166 + for (i = 0; i < 2; i++) {
167 + for (j = 0; j < 3; j++) {
168 + char cname[256];
169 +
170 + sprintf(cname, "%s %s", codec_ctl_pfx[i],
171 + codec_ctl_name[j]);
172 + kctl = snd_soc_card_get_kcontrol(card, cname);
173 + if (!kctl) {
174 + pr_info("Control %s not found\n",
175 + cname);
176 + } else {
177 + kctl->vd[0].access =
178 + SNDRV_CTL_ELEM_ACCESS_READWRITE;
179 + snd_ctl_remove(card->snd_card, kctl);
180 + }
181 + }
182 + }
183 +
184 + return 0;
185 +}
186 +
187 +static int snd_pifi_40_hw_params(struct snd_pcm_substream *substream,
188 + struct snd_pcm_hw_params *params)
189 +{
190 + struct snd_soc_pcm_runtime *rtd = substream->private_data;
191 + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
192 + unsigned int sample_bits;
193 +
194 + sample_bits = snd_pcm_format_physical_width(params_format(params));
195 + return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
196 +}
197 +
198 +static struct snd_soc_ops snd_pifi_40_ops = { .hw_params =
199 + snd_pifi_40_hw_params };
200 +
201 +static struct snd_soc_dai_link_component pifi_40_codecs[] = {
202 + {
203 + .dai_name = "tas571x-hifi",
204 + },
205 + {
206 + .dai_name = "tas571x-hifi",
207 + },
208 +};
209 +
210 +SND_SOC_DAILINK_DEFS(
211 + pifi_40_dai, DAILINK_COMP_ARRAY(COMP_EMPTY()),
212 + DAILINK_COMP_ARRAY(COMP_CODEC("tas571x.1-001a", "tas571x-hifi"),
213 + COMP_CODEC("tas571x.1-001b", "tas571x-hifi")),
214 + DAILINK_COMP_ARRAY(COMP_EMPTY()));
215 +
216 +static struct snd_soc_dai_link snd_pifi_40_dai[] = {
217 + {
218 + .name = "PiFi40",
219 + .stream_name = "PiFi40",
220 + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
221 + SND_SOC_DAIFMT_CBS_CFS,
222 + .ops = &snd_pifi_40_ops,
223 + .init = snd_pifi_40_init,
224 + SND_SOC_DAILINK_REG(pifi_40_dai),
225 + },
226 +};
227 +
228 +// Machine driver
229 +static struct snd_soc_card snd_pifi_40 = {
230 + .name = "PiFi40",
231 + .owner = THIS_MODULE,
232 + .dai_link = snd_pifi_40_dai,
233 + .num_links = ARRAY_SIZE(snd_pifi_40_dai),
234 + .controls = pifi_40_controls,
235 + .num_controls = ARRAY_SIZE(pifi_40_controls)
236 +};
237 +
238 +static void snd_pifi_40_pdn(struct snd_soc_card *card, int on)
239 +{
240 + if (pdn_gpio)
241 + gpiod_set_value_cansleep(pdn_gpio, on ? 0 : 1);
242 +}
243 +
244 +static int snd_pifi_40_probe(struct platform_device *pdev)
245 +{
246 + struct snd_soc_card *card = &snd_pifi_40;
247 + int ret = 0, i = 0;
248 +
249 + card->dev = &pdev->dev;
250 + platform_set_drvdata(pdev, &snd_pifi_40);
251 +
252 + if (pdev->dev.of_node) {
253 + struct device_node *i2s_node;
254 + struct snd_soc_dai_link *dai;
255 +
256 + dai = &snd_pifi_40_dai[0];
257 + i2s_node = of_parse_phandle(pdev->dev.of_node, "i2s-controller",
258 + 0);
259 + if (i2s_node) {
260 + for (i = 0; i < card->num_links; i++) {
261 + dai->cpus->dai_name = NULL;
262 + dai->cpus->of_node = i2s_node;
263 + dai->platforms->name = NULL;
264 + dai->platforms->of_node = i2s_node;
265 + }
266 + }
267 +
268 + pifi_40_codecs[0].of_node =
269 + of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
270 + pifi_40_codecs[1].of_node =
271 + of_parse_phandle(pdev->dev.of_node, "audio-codec", 1);
272 + if (!pifi_40_codecs[0].of_node || !pifi_40_codecs[1].of_node) {
273 + dev_err(&pdev->dev,
274 + "Property 'audio-codec' missing or invalid\n");
275 + return -EINVAL;
276 + }
277 +
278 + pdn_gpio = devm_gpiod_get_optional(&pdev->dev, "pdn",
279 + GPIOD_OUT_LOW);
280 + if (IS_ERR(pdn_gpio)) {
281 + ret = PTR_ERR(pdn_gpio);
282 + dev_err(&pdev->dev, "failed to get pdn gpio: %d\n",
283 + ret);
284 + return ret;
285 + }
286 +
287 + ret = snd_soc_register_card(&snd_pifi_40);
288 + if (ret < 0) {
289 + dev_err(&pdev->dev,
290 + "snd_soc_register_card() failed: %d\n", ret);
291 + return ret;
292 + }
293 +
294 + return 0;
295 + }
296 +
297 + return -EINVAL;
298 +}
299 +
300 +static int snd_pifi_40_remove(struct platform_device *pdev)
301 +{
302 + struct snd_soc_card *card = platform_get_drvdata(pdev);
303 +
304 + kfree(&card->drvdata);
305 + snd_pifi_40_pdn(&snd_pifi_40, 0);
306 + return snd_soc_unregister_card(&snd_pifi_40);
307 +}
308 +
309 +static const struct of_device_id snd_pifi_40_of_match[] = {
310 + {
311 + .compatible = "pifi,pifi-40",
312 + },
313 + { /* sentinel */ },
314 +};
315 +
316 +MODULE_DEVICE_TABLE(of, snd_pifi_40_of_match);
317 +
318 +static struct platform_driver snd_pifi_40_driver = {
319 + .driver = {
320 + .name = "snd-pifi-40",
321 + .owner = THIS_MODULE,
322 + .of_match_table = snd_pifi_40_of_match,
323 + },
324 + .probe = snd_pifi_40_probe,
325 + .remove = snd_pifi_40_remove,
326 +};
327 +
328 +module_platform_driver(snd_pifi_40_driver);
329 +
330 +MODULE_AUTHOR("David Knell <david.knell@gmail.com>");
331 +MODULE_DESCRIPTION("ALSA ASoC Machine Driver for PiFi-40");
332 +MODULE_LICENSE("GPL v2");