5c584f49710dd911813549f70f792fbca1bf6a4c
[openwrt/staging/ldir.git] / target / linux / bcm27xx / patches-5.4 / 950-1001-PiFi-40-driver-Makefile-and-Kconfig.patch
1 From b7bbdd72e5c9e1b7538b74deb584a7ab0f85847d 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 | 282 ++++++++++++++++++++++++++++++++++++++++
11 3 files changed, 293 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 @@ -91,6 +91,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 @@ -40,6 +40,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 @@ -69,3 +70,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,282 @@
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].name);
101 + dac[0] = rtd->codec_dais[0]->component;
102 + dac[1] = rtd->codec_dais[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] = rtd->codec_dais[0]->component;
135 + dac[1] = rtd->codec_dais[1]->component;
136 +
137 + // Set up cards - pulse power down first
138 + gpiod_set_value_cansleep(pdn_gpio, 1);
139 + usleep_range(1000, 10000);
140 + gpiod_set_value_cansleep(pdn_gpio, 0);
141 + usleep_range(20000, 30000);
142 +
143 + // Oscillator trim
144 + snd_soc_component_write(dac[0], 0x1b, 0);
145 + snd_soc_component_write(dac[1], 0x1b, 0);
146 + usleep_range(60000, 80000);
147 +
148 + // Common setup
149 + for (i = 0; i < 2; i++) {
150 + // MCLK at 64fs, sample rate 44.1 or 48kHz
151 + snd_soc_component_write(dac[i], 0x00, 0x60);
152 +
153 + // Set up for PBTL
154 + snd_soc_component_write(dac[i], 0x19, 0x3A);
155 + snd_soc_component_write(dac[i], 0x25, 0x01103245);
156 +
157 + // Master vol to -10db
158 + snd_soc_component_write(dac[i], 0x07, 0x44);
159 + }
160 + // Inputs set to L and R respectively
161 + snd_soc_component_write(dac[0], 0x20, 0x00017772);
162 + snd_soc_component_write(dac[1], 0x20, 0x00107772);
163 +
164 + // Remove codec controls
165 + for (i = 0; i < 2; i++) {
166 + for (j = 0; j < 3; j++) {
167 + char cname[256];
168 +
169 + sprintf(cname, "%s %s", codec_ctl_pfx[i],
170 + codec_ctl_name[j]);
171 + kctl = snd_soc_card_get_kcontrol(card, cname);
172 + if (!kctl) {
173 + pr_info("Control %s not found\n",
174 + cname);
175 + } else {
176 + kctl->vd[0].access =
177 + SNDRV_CTL_ELEM_ACCESS_READWRITE;
178 + snd_ctl_remove(card->snd_card, kctl);
179 + }
180 + }
181 + }
182 +
183 + return 0;
184 +}
185 +
186 +static int snd_pifi_40_hw_params(struct snd_pcm_substream *substream,
187 + struct snd_pcm_hw_params *params)
188 +{
189 + struct snd_soc_pcm_runtime *rtd = substream->private_data;
190 + struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
191 + unsigned int sample_bits;
192 +
193 + sample_bits = snd_pcm_format_physical_width(params_format(params));
194 + return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
195 +}
196 +
197 +static struct snd_soc_ops snd_pifi_40_ops = { .hw_params =
198 + snd_pifi_40_hw_params };
199 +
200 +static struct snd_soc_dai_link_component pifi_40_codecs[] = {
201 + {
202 + .dai_name = "tas571x-hifi",
203 + },
204 + {
205 + .dai_name = "tas571x-hifi",
206 + },
207 +};
208 +
209 +SND_SOC_DAILINK_DEFS(
210 + pifi_40_dai, DAILINK_COMP_ARRAY(COMP_EMPTY()),
211 + DAILINK_COMP_ARRAY(COMP_CODEC("tas571x.1-001a", "tas571x-hifi"),
212 + COMP_CODEC("tas571x.1-001b", "tas571x-hifi")),
213 + DAILINK_COMP_ARRAY(COMP_EMPTY()));
214 +
215 +static struct snd_soc_dai_link snd_pifi_40_dai[] = {
216 + {
217 + .name = "PiFi40",
218 + .stream_name = "PiFi40",
219 + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
220 + SND_SOC_DAIFMT_CBS_CFS,
221 + .ops = &snd_pifi_40_ops,
222 + .init = snd_pifi_40_init,
223 + SND_SOC_DAILINK_REG(pifi_40_dai),
224 + },
225 +};
226 +
227 +// Machine driver
228 +static struct snd_soc_card snd_pifi_40 = {
229 + .name = "PiFi40",
230 + .owner = THIS_MODULE,
231 + .dai_link = snd_pifi_40_dai,
232 + .num_links = ARRAY_SIZE(snd_pifi_40_dai),
233 + .controls = pifi_40_controls,
234 + .num_controls = ARRAY_SIZE(pifi_40_controls)
235 +};
236 +
237 +static void snd_pifi_40_pdn(struct snd_soc_card *card, int on)
238 +{
239 + if (pdn_gpio)
240 + gpiod_set_value_cansleep(pdn_gpio, on ? 0 : 1);
241 +}
242 +
243 +static int snd_pifi_40_probe(struct platform_device *pdev)
244 +{
245 + struct snd_soc_card *card = &snd_pifi_40;
246 + int ret = 0, i = 0;
247 +
248 + card->dev = &pdev->dev;
249 + platform_set_drvdata(pdev, &snd_pifi_40);
250 +
251 + if (pdev->dev.of_node) {
252 + struct device_node *i2s_node;
253 + struct snd_soc_dai_link *dai;
254 +
255 + dai = &snd_pifi_40_dai[0];
256 + i2s_node = of_parse_phandle(pdev->dev.of_node, "i2s-controller",
257 + 0);
258 + if (i2s_node) {
259 + for (i = 0; i < card->num_links; i++) {
260 + dai->cpus->dai_name = NULL;
261 + dai->cpus->of_node = i2s_node;
262 + dai->platforms->name = NULL;
263 + dai->platforms->of_node = i2s_node;
264 + }
265 + }
266 +
267 + pifi_40_codecs[0].of_node =
268 + of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
269 + pifi_40_codecs[1].of_node =
270 + of_parse_phandle(pdev->dev.of_node, "audio-codec", 1);
271 + if (!pifi_40_codecs[0].of_node || !pifi_40_codecs[1].of_node) {
272 + dev_err(&pdev->dev,
273 + "Property 'audio-codec' missing or invalid\n");
274 + return -EINVAL;
275 + }
276 +
277 + pdn_gpio = devm_gpiod_get_optional(&pdev->dev, "pdn",
278 + GPIOD_OUT_LOW);
279 + if (IS_ERR(pdn_gpio)) {
280 + ret = PTR_ERR(pdn_gpio);
281 + dev_err(&pdev->dev, "failed to get pdn gpio: %d\n",
282 + ret);
283 + return ret;
284 + }
285 +
286 + ret = snd_soc_register_card(&snd_pifi_40);
287 + if (ret < 0) {
288 + dev_err(&pdev->dev,
289 + "snd_soc_register_card() failed: %d\n", ret);
290 + return ret;
291 + }
292 +
293 + return 0;
294 + }
295 +
296 + return -EINVAL;
297 +}
298 +
299 +static int snd_pifi_40_remove(struct platform_device *pdev)
300 +{
301 + struct snd_soc_card *card = platform_get_drvdata(pdev);
302 +
303 + kfree(&card->drvdata);
304 + snd_pifi_40_pdn(&snd_pifi_40, 0);
305 + return snd_soc_unregister_card(&snd_pifi_40);
306 +}
307 +
308 +static const struct of_device_id snd_pifi_40_of_match[] = {
309 + {
310 + .compatible = "pifi,pifi-40",
311 + },
312 + { /* sentinel */ },
313 +};
314 +
315 +MODULE_DEVICE_TABLE(of, snd_pifi_40_of_match);
316 +
317 +static struct platform_driver snd_pifi_40_driver = {
318 + .driver = {
319 + .name = "snd-pifi-40",
320 + .owner = THIS_MODULE,
321 + .of_match_table = snd_pifi_40_of_match,
322 + },
323 + .probe = snd_pifi_40_probe,
324 + .remove = snd_pifi_40_remove,
325 +};
326 +
327 +module_platform_driver(snd_pifi_40_driver);
328 +
329 +MODULE_AUTHOR("David Knell <david.knell@gmail.com>");
330 +MODULE_DESCRIPTION("ALSA ASoC Machine Driver for PiFi-40");
331 +MODULE_LICENSE("GPL v2");