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