e33553625d9013d8e77cbf2dcb1e594c2a159406
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.19 / 950-0250-ASoC-add-driver-for-3Dlab-Nano-soundcard-2758.patch
1 From e740bd2cc3fcd632fcd6c8881b1fc671bcde5914 Mon Sep 17 00:00:00 2001
2 From: dev-3Dlab <45081440+dev-3Dlab@users.noreply.github.com>
3 Date: Wed, 5 Dec 2018 10:59:11 +0100
4 Subject: [PATCH] ASoC: add driver for 3Dlab Nano soundcard (#2758)
5
6 Signed-off-by: GT <dev@3d-lab-av.com>
7 ---
8 .../overlays/3dlab-nano-player-overlay.dts | 32 ++
9 arch/arm/boot/dts/overlays/Makefile | 1 +
10 arch/arm/boot/dts/overlays/README | 6 +
11 sound/soc/bcm/3dlab-nano-player.c | 370 ++++++++++++++++++
12 sound/soc/bcm/Kconfig | 6 +
13 sound/soc/bcm/Makefile | 2 +
14 8 files changed, 419 insertions(+)
15 create mode 100644 arch/arm/boot/dts/overlays/3dlab-nano-player-overlay.dts
16 create mode 100644 sound/soc/bcm/3dlab-nano-player.c
17
18 --- /dev/null
19 +++ b/arch/arm/boot/dts/overlays/3dlab-nano-player-overlay.dts
20 @@ -0,0 +1,32 @@
21 +// Definitions for 3Dlab Nano Player
22 +/dts-v1/;
23 +/plugin/;
24 +
25 +/ {
26 + compatible = "brcm,bcm2708";
27 +
28 + fragment@0 {
29 + target = <&i2s>;
30 + __overlay__ {
31 + status = "okay";
32 + };
33 + };
34 +
35 + fragment@1 {
36 + target = <&i2c>;
37 + __overlay__ {
38 + #address-cells = <1>;
39 + #size-cells = <0>;
40 + status = "okay";
41 +
42 + nano-player@41 {
43 + compatible = "3dlab,nano-player";
44 + reg = <0x41>;
45 + i2s-controller = <&i2s>;
46 + status = "okay";
47 + };
48 + };
49 + };
50 +};
51 +
52 +// EOF
53 --- a/arch/arm/boot/dts/overlays/Makefile
54 +++ b/arch/arm/boot/dts/overlays/Makefile
55 @@ -1,6 +1,7 @@
56 # Overlays for the Raspberry Pi platform
57
58 dtbo-$(CONFIG_ARCH_BCM2835) += \
59 + 3dlab-nano-player.dtbo \
60 adau1977-adc.dtbo \
61 adau7002-simple.dtbo \
62 ads1015.dtbo \
63 --- a/arch/arm/boot/dts/overlays/README
64 +++ b/arch/arm/boot/dts/overlays/README
65 @@ -199,6 +199,12 @@ Params:
66 and the other i2c baudrate parameters.
67
68
69 +Name: 3dlab-nano-player
70 +Info: Configures the 3Dlab Nano Player
71 +Load: dtoverlay=3dlab-nano-player
72 +Params: <None>
73 +
74 +
75 Name: adau1977-adc
76 Info: Overlay for activation of ADAU1977 ADC codec over I2C for control
77 and I2S for data.
78 --- /dev/null
79 +++ b/sound/soc/bcm/3dlab-nano-player.c
80 @@ -0,0 +1,370 @@
81 +/*
82 + * 3Dlab Nano Player ALSA SoC Audio driver.
83 + *
84 + * Copyright (C) 2018 3Dlab.
85 + *
86 + * Author: GT <dev@3d-lab-av.com>
87 + *
88 + * This program is free software; you can redistribute it and/or
89 + * modify it under the terms of the GNU General Public License
90 + * version 2 as published by the Free Software Foundation.
91 + *
92 + * This program is distributed in the hope that it will be useful, but
93 + * WITHOUT ANY WARRANTY; without even the implied warranty of
94 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
95 + * General Public License for more details.
96 + */
97 +
98 +#include <linux/module.h>
99 +#include <linux/i2c.h>
100 +#include <sound/soc.h>
101 +#include <sound/pcm.h>
102 +#include <sound/pcm_params.h>
103 +#include <sound/control.h>
104 +
105 +#define NANO_ID 0x00
106 +#define NANO_VER 0x01
107 +#define NANO_CFG 0x02
108 +#define NANO_STATUS 0x03
109 +#define NANO_SPI_ADDR 0x04
110 +#define NANO_SPI_DATA 0x05
111 +
112 +#define NANO_ID_VAL 0x3D
113 +#define NANO_CFG_OFF 0x00
114 +#define NANO_CFG_MULT1 0
115 +#define NANO_CFG_MULT2 1
116 +#define NANO_CFG_MULT4 2
117 +#define NANO_CFG_MULT8 3
118 +#define NANO_CFG_MULT16 4
119 +#define NANO_CFG_CLK22 0
120 +#define NANO_CFG_CLK24 BIT(3)
121 +#define NANO_CFG_DSD BIT(4)
122 +#define NANO_CFG_ENA BIT(5)
123 +#define NANO_CFG_BLINK BIT(6)
124 +#define NANO_STATUS_P1 BIT(0)
125 +#define NANO_STATUS_P2 BIT(1)
126 +#define NANO_STATUS_FLG BIT(2)
127 +#define NANO_STATUS_CLK BIT(3)
128 +#define NANO_SPI_READ 0
129 +#define NANO_SPI_WRITE BIT(5)
130 +
131 +#define NANO_DAC_CTRL1 0x00
132 +#define NANO_DAC_CTRL2 0x01
133 +#define NANO_DAC_CTRL3 0x02
134 +#define NANO_DAC_LATT 0x03
135 +#define NANO_DAC_RATT 0x04
136 +
137 +#define NANO_CTRL2_VAL 0x22
138 +
139 +static int nano_player_spi_write(struct regmap *map,
140 + unsigned int reg, unsigned int val)
141 +{
142 + /* indirect register access */
143 + regmap_write(map, NANO_SPI_DATA, val);
144 + regmap_write(map, NANO_SPI_ADDR, reg | NANO_SPI_WRITE);
145 + return 0;
146 +}
147 +
148 +static int nano_player_ctrl_info(struct snd_kcontrol *kcontrol,
149 + struct snd_ctl_elem_info *uinfo)
150 +{
151 + /* describe control element */
152 + if (strstr(kcontrol->id.name, "Volume")) {
153 + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
154 + uinfo->count = 1;
155 + uinfo->value.integer.min = 0;
156 + uinfo->value.integer.max = 100;
157 + } else {
158 + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
159 + uinfo->count = 1;
160 + uinfo->value.integer.min = 0;
161 + uinfo->value.integer.max = 1;
162 + }
163 +
164 + return 0;
165 +}
166 +
167 +static int nano_player_ctrl_put(struct snd_kcontrol *kcontrol,
168 + struct snd_ctl_elem_value *ucontrol)
169 +{
170 + /* program control value to hardware */
171 + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
172 + struct regmap *regmap = snd_soc_card_get_drvdata(card);
173 +
174 + if (strstr(kcontrol->id.name, "Volume")) {
175 + unsigned int vol = ucontrol->value.integer.value[0];
176 + unsigned int att = 255 - (2 * (100 - vol));
177 +
178 + nano_player_spi_write(regmap, NANO_DAC_LATT, att);
179 + nano_player_spi_write(regmap, NANO_DAC_RATT, att);
180 + kcontrol->private_value = vol;
181 + } else {
182 + unsigned int mute = ucontrol->value.integer.value[0];
183 + unsigned int reg = NANO_CTRL2_VAL | mute;
184 +
185 + nano_player_spi_write(regmap, NANO_DAC_CTRL2, reg);
186 + kcontrol->private_value = mute;
187 + }
188 + return 0;
189 +}
190 +
191 +static int nano_player_ctrl_get(struct snd_kcontrol *kcontrol,
192 + struct snd_ctl_elem_value *ucontrol)
193 +{
194 + /* return last programmed value */
195 + ucontrol->value.integer.value[0] = kcontrol->private_value;
196 + return 0;
197 +}
198 +
199 +#define SOC_NANO_PLAYER_CTRL(xname) \
200 +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
201 + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
202 + .info = nano_player_ctrl_info, \
203 + .put = nano_player_ctrl_put, \
204 + .get = nano_player_ctrl_get }
205 +
206 +static const struct snd_kcontrol_new nano_player_controls[] = {
207 + SOC_NANO_PLAYER_CTRL("Master Playback Volume"),
208 + SOC_NANO_PLAYER_CTRL("Master Playback Switch"),
209 +};
210 +
211 +static const unsigned int nano_player_rates[] = {
212 + 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000,
213 + 705600, 768000 /* only possible with fast clocks */
214 +};
215 +
216 +static struct snd_pcm_hw_constraint_list nano_player_constraint_rates = {
217 + .list = nano_player_rates,
218 + .count = ARRAY_SIZE(nano_player_rates),
219 +};
220 +
221 +static int nano_player_init(struct snd_soc_pcm_runtime *rtd)
222 +{
223 + struct snd_soc_card *card = rtd->card;
224 + struct regmap *regmap = snd_soc_card_get_drvdata(card);
225 + struct snd_soc_pcm_stream *cpu = &rtd->cpu_dai->driver->playback;
226 + struct snd_soc_pcm_stream *codec = &rtd->codec_dai->driver->playback;
227 + unsigned int sample_bits = 32;
228 + unsigned int val;
229 +
230 + /* configure cpu dai */
231 + cpu->formats |= SNDRV_PCM_FMTBIT_DSD_U32_LE;
232 + cpu->rate_max = 768000;
233 +
234 + /* configure dummy codec dai */
235 + codec->rate_min = 44100;
236 + codec->rates = SNDRV_PCM_RATE_KNOT;
237 + codec->formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U32_LE;
238 +
239 + /* configure max supported rate */
240 + regmap_read(regmap, NANO_STATUS, &val);
241 + if (val & NANO_STATUS_CLK) {
242 + dev_notice(card->dev, "Board with fast clocks installed\n");
243 + codec->rate_max = 768000;
244 + } else {
245 + dev_notice(card->dev, "Board with normal clocks installed\n");
246 + codec->rate_max = 384000;
247 + }
248 +
249 + /* frame length enforced by hardware */
250 + return snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, sample_bits * 2);
251 +}
252 +
253 +static int nano_player_startup(struct snd_pcm_substream *substream)
254 +{
255 + return snd_pcm_hw_constraint_list(substream->runtime, 0,
256 + SNDRV_PCM_HW_PARAM_RATE,
257 + &nano_player_constraint_rates);
258 +}
259 +
260 +static int nano_player_hw_params(struct snd_pcm_substream *substream,
261 + struct snd_pcm_hw_params *params)
262 +{
263 + struct snd_soc_pcm_runtime *rtd = substream->private_data;
264 + struct snd_soc_card *card = rtd->card;
265 + struct regmap *regmap = snd_soc_card_get_drvdata(card);
266 + unsigned int config = NANO_CFG_ENA;
267 + struct snd_mask *fmt;
268 +
269 + /* configure PCM or DSD */
270 + fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
271 + if (snd_mask_test(fmt, SNDRV_PCM_FORMAT_DSD_U32_LE)) {
272 + /* embed DSD in PCM data */
273 + snd_mask_none(fmt);
274 + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE);
275 + /* enable DSD mode */
276 + config |= NANO_CFG_DSD;
277 + }
278 +
279 + /* configure clocks */
280 + switch (params_rate(params)) {
281 + case 44100:
282 + config |= NANO_CFG_MULT1 | NANO_CFG_CLK22;
283 + break;
284 + case 88200:
285 + config |= NANO_CFG_MULT2 | NANO_CFG_CLK22;
286 + break;
287 + case 176400:
288 + config |= NANO_CFG_MULT4 | NANO_CFG_CLK22;
289 + break;
290 + case 352800:
291 + config |= NANO_CFG_MULT8 | NANO_CFG_CLK22;
292 + break;
293 + case 705600:
294 + config |= NANO_CFG_MULT16 | NANO_CFG_CLK22;
295 + break;
296 + case 48000:
297 + config |= NANO_CFG_MULT1 | NANO_CFG_CLK24;
298 + break;
299 + case 96000:
300 + config |= NANO_CFG_MULT2 | NANO_CFG_CLK24;
301 + break;
302 + case 192000:
303 + config |= NANO_CFG_MULT4 | NANO_CFG_CLK24;
304 + break;
305 + case 384000:
306 + config |= NANO_CFG_MULT8 | NANO_CFG_CLK24;
307 + break;
308 + case 768000:
309 + config |= NANO_CFG_MULT16 | NANO_CFG_CLK24;
310 + break;
311 + default:
312 + return -EINVAL;
313 + }
314 +
315 + dev_dbg(card->dev, "Send CFG register 0x%02X\n", config);
316 + return regmap_write(regmap, NANO_CFG, config);
317 +}
318 +
319 +static struct snd_soc_ops nano_player_ops = {
320 + .startup = nano_player_startup,
321 + .hw_params = nano_player_hw_params,
322 +};
323 +
324 +static struct snd_soc_dai_link nano_player_link = {
325 + .name = "3Dlab Nano Player",
326 + .stream_name = "3Dlab Nano Player HiFi",
327 + .platform_name = "bcm2708-i2s.0",
328 + .cpu_dai_name = "bcm2708-i2s.0",
329 + .codec_name = "snd-soc-dummy",
330 + .codec_dai_name = "snd-soc-dummy-dai",
331 + .dai_fmt = SND_SOC_DAIFMT_I2S |
332 + SND_SOC_DAIFMT_CONT |
333 + SND_SOC_DAIFMT_NB_NF |
334 + SND_SOC_DAIFMT_CBM_CFM,
335 + .init = nano_player_init,
336 + .ops = &nano_player_ops,
337 +};
338 +
339 +static const struct regmap_config nano_player_regmap = {
340 + .reg_bits = 8,
341 + .val_bits = 8,
342 + .max_register = 128,
343 + .cache_type = REGCACHE_RBTREE,
344 +};
345 +
346 +static int nano_player_card_probe(struct snd_soc_card *card)
347 +{
348 + struct regmap *regmap = snd_soc_card_get_drvdata(card);
349 + unsigned int val;
350 +
351 + /* check hardware integrity */
352 + regmap_read(regmap, NANO_ID, &val);
353 + if (val != NANO_ID_VAL) {
354 + dev_err(card->dev, "Invalid ID register 0x%02X\n", val);
355 + return -ENODEV;
356 + }
357 +
358 + /* report version to the user */
359 + regmap_read(regmap, NANO_VER, &val);
360 + dev_notice(card->dev, "Started 3Dlab Nano Player driver (v%d)\n", val);
361 +
362 + /* enable internal audio bus and blink status LED */
363 + return regmap_write(regmap, NANO_CFG, NANO_CFG_ENA | NANO_CFG_BLINK);
364 +}
365 +
366 +static int nano_player_card_remove(struct snd_soc_card *card)
367 +{
368 + /* disable internal audio bus */
369 + struct regmap *regmap = snd_soc_card_get_drvdata(card);
370 +
371 + return regmap_write(regmap, NANO_CFG, NANO_CFG_OFF);
372 +}
373 +
374 +static struct snd_soc_card nano_player_card = {
375 + .name = "3Dlab_Nano_Player",
376 + .owner = THIS_MODULE,
377 + .dai_link = &nano_player_link,
378 + .num_links = 1,
379 + .controls = nano_player_controls,
380 + .num_controls = ARRAY_SIZE(nano_player_controls),
381 + .probe = nano_player_card_probe,
382 + .remove = nano_player_card_remove,
383 +};
384 +
385 +static int nano_player_i2c_probe(struct i2c_client *i2c,
386 + const struct i2c_device_id *id)
387 +{
388 + struct regmap *regmap;
389 + int ret;
390 +
391 + regmap = devm_regmap_init_i2c(i2c, &nano_player_regmap);
392 + if (IS_ERR(regmap)) {
393 + ret = PTR_ERR(regmap);
394 + dev_err(&i2c->dev, "Failed to init regmap %d\n", ret);
395 + return ret;
396 + }
397 +
398 + if (i2c->dev.of_node) {
399 + struct snd_soc_dai_link *dai = &nano_player_link;
400 + struct device_node *node;
401 +
402 + /* cpu handle configured by device tree */
403 + node = of_parse_phandle(i2c->dev.of_node, "i2s-controller", 0);
404 + if (node) {
405 + dai->platform_name = NULL;
406 + dai->platform_of_node = node;
407 + dai->cpu_dai_name = NULL;
408 + dai->cpu_of_node = node;
409 + }
410 + }
411 +
412 + nano_player_card.dev = &i2c->dev;
413 + snd_soc_card_set_drvdata(&nano_player_card, regmap);
414 + ret = devm_snd_soc_register_card(&i2c->dev, &nano_player_card);
415 +
416 + if (ret && ret != -EPROBE_DEFER)
417 + dev_err(&i2c->dev, "Failed to register card %d\n", ret);
418 +
419 + return ret;
420 +}
421 +
422 +static const struct of_device_id nano_player_of_match[] = {
423 + { .compatible = "3dlab,nano-player", },
424 + { }
425 +};
426 +MODULE_DEVICE_TABLE(of, nano_player_of_match);
427 +
428 +static const struct i2c_device_id nano_player_i2c_id[] = {
429 + { "nano-player", 0 },
430 + { }
431 +};
432 +MODULE_DEVICE_TABLE(i2c, nano_player_i2c_id);
433 +
434 +static struct i2c_driver nano_player_i2c_driver = {
435 + .probe = nano_player_i2c_probe,
436 + .id_table = nano_player_i2c_id,
437 + .driver = {
438 + .name = "nano-player",
439 + .owner = THIS_MODULE,
440 + .of_match_table = nano_player_of_match,
441 + },
442 +};
443 +
444 +module_i2c_driver(nano_player_i2c_driver);
445 +
446 +MODULE_DESCRIPTION("ASoC 3Dlab Nano Player driver");
447 +MODULE_AUTHOR("GT <dev@3d-lab-av.com>");
448 +MODULE_LICENSE("GPL v2");
449 +
450 +/* EOF */
451 --- a/sound/soc/bcm/Kconfig
452 +++ b/sound/soc/bcm/Kconfig
453 @@ -17,6 +17,12 @@ config SND_SOC_CYGNUS
454
455 If you don't know what to do here, say N.
456
457 +config SND_BCM2708_SOC_3DLAB_NANO_PLAYER
458 + tristate "Support for 3Dlab Nano Player"
459 + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
460 + help
461 + Say Y or M if you want to add support for 3Dlab Nano Player.
462 +
463 config SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD
464 tristate "Support for Google voiceHAT soundcard"
465 depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
466 --- a/sound/soc/bcm/Makefile
467 +++ b/sound/soc/bcm/Makefile
468 @@ -12,6 +12,7 @@ obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-
469 snd-soc-googlevoicehat-codec-objs := googlevoicehat-codec.o
470
471 # BCM2708 Machine Support
472 +snd-soc-3dlab-nano-player-objs := 3dlab-nano-player.o
473 snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o
474 snd-soc-justboom-dac-objs := justboom-dac.o
475 snd-soc-rpi-cirrus-objs := rpi-cirrus.o
476 @@ -31,6 +32,7 @@ snd-soc-fe-pi-audio-objs := fe-pi-audio.
477 snd-soc-rpi-simple-soundcard-objs := rpi-simple-soundcard.o
478 snd-soc-rpi-wm8804-soundcard-objs := rpi-wm8804-soundcard.o
479
480 +obj-$(CONFIG_SND_BCM2708_SOC_3DLAB_NANO_PLAYER) += snd-soc-3dlab-nano-player.o
481 obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD) += snd-soc-googlevoicehat-codec.o
482 obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o
483 obj-$(CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC) += snd-soc-justboom-dac.o