OpenWrt – Rev 1

Subversion Repositories:
Rev:
From 09a4e02366e56076b344dd1fa14eea7618a11d5a Mon Sep 17 00:00:00 2001
From: dev-3Dlab <45081440+dev-3Dlab@users.noreply.github.com>
Date: Wed, 5 Dec 2018 10:59:11 +0100
Subject: [PATCH 450/454] ASoC: add driver for 3Dlab Nano soundcard (#2758)

Signed-off-by: GT <dev@3d-lab-av.com>
---
 .../overlays/3dlab-nano-player-overlay.dts    |  32 ++
 arch/arm/boot/dts/overlays/Makefile           |   1 +
 arch/arm/boot/dts/overlays/README             |   6 +
 arch/arm/configs/bcm2709_defconfig            |   1 +
 arch/arm/configs/bcmrpi_defconfig             |   1 +
 sound/soc/bcm/3dlab-nano-player.c             | 370 ++++++++++++++++++
 sound/soc/bcm/Kconfig                         |   6 +
 sound/soc/bcm/Makefile                        |   2 +
 8 files changed, 419 insertions(+)
 create mode 100644 arch/arm/boot/dts/overlays/3dlab-nano-player-overlay.dts
 create mode 100644 sound/soc/bcm/3dlab-nano-player.c

--- /dev/null
+++ b/arch/arm/boot/dts/overlays/3dlab-nano-player-overlay.dts
@@ -0,0 +1,32 @@
+// Definitions for 3Dlab Nano Player
+/dts-v1/;
+/plugin/;
+
+/ {
+       compatible = "brcm,bcm2708";
+
+       fragment@0 {
+               target = <&i2s>;
+               __overlay__ {
+                       status = "okay";
+               };
+       };
+
+       fragment@1 {
+               target = <&i2c>;
+               __overlay__ {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "okay";
+
+                       nano-player@41 {
+                               compatible = "3dlab,nano-player";
+                               reg = <0x41>;
+                               i2s-controller = <&i2s>;
+                               status = "okay";
+                       };
+               };
+       };
+};
+
+// EOF
--- a/arch/arm/boot/dts/overlays/Makefile
+++ b/arch/arm/boot/dts/overlays/Makefile
@@ -1,6 +1,7 @@
 # Overlays for the Raspberry Pi platform
 
 dtbo-$(CONFIG_ARCH_BCM2835) += \
+       3dlab-nano-player.dtbo \
        adau1977-adc.dtbo \
        adau7002-simple.dtbo \
        ads1015.dtbo \
--- a/arch/arm/boot/dts/overlays/README
+++ b/arch/arm/boot/dts/overlays/README
@@ -199,6 +199,12 @@ Params:
         and the other i2c baudrate parameters.
 
 
+Name:   3dlab-nano-player
+Info:   Configures the 3Dlab Nano Player
+Load:   dtoverlay=3dlab-nano-player
+Params: <None>
+
+
 Name:   adau1977-adc
 Info:   Overlay for activation of ADAU1977 ADC codec over I2C for control
         and I2S for data.
--- a/arch/arm/configs/bcm2709_defconfig
+++ b/arch/arm/configs/bcm2709_defconfig
@@ -889,6 +889,7 @@ CONFIG_SND_USB_6FIRE=m
 CONFIG_SND_USB_HIFACE=m
 CONFIG_SND_SOC=m
 CONFIG_SND_BCM2835_SOC_I2S=m
+CONFIG_SND_BCM2708_SOC_3DLAB_NANO_PLAYER=m
 CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
 CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
 CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
--- a/arch/arm/configs/bcmrpi_defconfig
+++ b/arch/arm/configs/bcmrpi_defconfig
@@ -882,6 +882,7 @@ CONFIG_SND_USB_6FIRE=m
 CONFIG_SND_USB_HIFACE=m
 CONFIG_SND_SOC=m
 CONFIG_SND_BCM2835_SOC_I2S=m
+CONFIG_SND_BCM2708_SOC_3DLAB_NANO_PLAYER=m
 CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
 CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
 CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
--- /dev/null
+++ b/sound/soc/bcm/3dlab-nano-player.c
@@ -0,0 +1,370 @@
+/*
+ * 3Dlab Nano Player ALSA SoC Audio driver.
+ *
+ * Copyright (C) 2018 3Dlab.
+ *
+ * Author: GT <dev@3d-lab-av.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+
+#define NANO_ID                0x00
+#define NANO_VER       0x01
+#define NANO_CFG       0x02
+#define NANO_STATUS    0x03
+#define NANO_SPI_ADDR  0x04
+#define NANO_SPI_DATA  0x05
+
+#define NANO_ID_VAL    0x3D
+#define NANO_CFG_OFF   0x00
+#define NANO_CFG_MULT1 0
+#define NANO_CFG_MULT2 1
+#define NANO_CFG_MULT4 2
+#define NANO_CFG_MULT8 3
+#define NANO_CFG_MULT16        4
+#define NANO_CFG_CLK22 0
+#define NANO_CFG_CLK24 BIT(3)
+#define NANO_CFG_DSD   BIT(4)
+#define NANO_CFG_ENA   BIT(5)
+#define NANO_CFG_BLINK BIT(6)
+#define NANO_STATUS_P1  BIT(0)
+#define NANO_STATUS_P2  BIT(1)
+#define NANO_STATUS_FLG BIT(2)
+#define NANO_STATUS_CLK BIT(3)
+#define NANO_SPI_READ  0
+#define NANO_SPI_WRITE BIT(5)
+
+#define NANO_DAC_CTRL1 0x00
+#define NANO_DAC_CTRL2 0x01
+#define NANO_DAC_CTRL3 0x02
+#define NANO_DAC_LATT  0x03
+#define NANO_DAC_RATT  0x04
+
+#define NANO_CTRL2_VAL 0x22
+
+static int nano_player_spi_write(struct regmap *map,
+                                unsigned int reg, unsigned int val)
+{
+       /* indirect register access */
+       regmap_write(map, NANO_SPI_DATA, val);
+       regmap_write(map, NANO_SPI_ADDR, reg | NANO_SPI_WRITE);
+       return 0;
+}
+
+static int nano_player_ctrl_info(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_info *uinfo)
+{
+       /* describe control element */
+       if (strstr(kcontrol->id.name, "Volume")) {
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+               uinfo->count = 1;
+               uinfo->value.integer.min = 0;
+               uinfo->value.integer.max = 100;
+       } else {
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+               uinfo->count = 1;
+               uinfo->value.integer.min = 0;
+               uinfo->value.integer.max = 1;
+       }
+
+       return 0;
+}
+
+static int nano_player_ctrl_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       /* program control value to hardware */
+       struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+       struct regmap *regmap = snd_soc_card_get_drvdata(card);
+
+       if (strstr(kcontrol->id.name, "Volume")) {
+               unsigned int vol = ucontrol->value.integer.value[0];
+               unsigned int att = 255 - (2 * (100 - vol));
+
+               nano_player_spi_write(regmap, NANO_DAC_LATT, att);
+               nano_player_spi_write(regmap, NANO_DAC_RATT, att);
+               kcontrol->private_value = vol;
+       } else {
+               unsigned int mute = ucontrol->value.integer.value[0];
+               unsigned int reg = NANO_CTRL2_VAL | mute;
+
+               nano_player_spi_write(regmap, NANO_DAC_CTRL2, reg);
+               kcontrol->private_value = mute;
+       }
+       return 0;
+}
+
+static int nano_player_ctrl_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       /* return last programmed value */
+       ucontrol->value.integer.value[0] = kcontrol->private_value;
+       return 0;
+}
+
+#define SOC_NANO_PLAYER_CTRL(xname) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .info = nano_player_ctrl_info, \
+       .put = nano_player_ctrl_put, \
+       .get = nano_player_ctrl_get }
+
+static const struct snd_kcontrol_new nano_player_controls[] = {
+       SOC_NANO_PLAYER_CTRL("Master Playback Volume"),
+       SOC_NANO_PLAYER_CTRL("Master Playback Switch"),
+};
+
+static const unsigned int nano_player_rates[] = {
+       44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000,
+       705600, 768000 /* only possible with fast clocks */
+};
+
+static struct snd_pcm_hw_constraint_list nano_player_constraint_rates = {
+       .list   = nano_player_rates,
+       .count  = ARRAY_SIZE(nano_player_rates),
+};
+
+static int nano_player_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_card *card = rtd->card;
+       struct regmap *regmap = snd_soc_card_get_drvdata(card);
+       struct snd_soc_pcm_stream *cpu = &rtd->cpu_dai->driver->playback;
+       struct snd_soc_pcm_stream *codec = &rtd->codec_dai->driver->playback;
+       unsigned int sample_bits = 32;
+       unsigned int val;
+
+       /* configure cpu dai */
+       cpu->formats |= SNDRV_PCM_FMTBIT_DSD_U32_LE;
+       cpu->rate_max = 768000;
+
+       /* configure dummy codec dai */
+       codec->rate_min = 44100;
+       codec->rates = SNDRV_PCM_RATE_KNOT;
+       codec->formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U32_LE;
+
+       /* configure max supported rate */
+       regmap_read(regmap, NANO_STATUS, &val);
+       if (val & NANO_STATUS_CLK) {
+               dev_notice(card->dev, "Board with fast clocks installed\n");
+               codec->rate_max = 768000;
+       } else {
+               dev_notice(card->dev, "Board with normal clocks installed\n");
+               codec->rate_max = 384000;
+       }
+
+       /* frame length enforced by hardware */
+       return snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, sample_bits * 2);
+}
+
+static int nano_player_startup(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                                         SNDRV_PCM_HW_PARAM_RATE,
+                                         &nano_player_constraint_rates);
+}
+
+static int nano_player_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_card *card = rtd->card;
+       struct regmap *regmap = snd_soc_card_get_drvdata(card);
+       unsigned int config = NANO_CFG_ENA;
+       struct snd_mask *fmt;
+
+       /* configure PCM or DSD */
+       fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       if (snd_mask_test(fmt, SNDRV_PCM_FORMAT_DSD_U32_LE)) {
+               /* embed DSD in PCM data */
+               snd_mask_none(fmt);
+               snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE);
+               /* enable DSD mode */
+               config |= NANO_CFG_DSD;
+       }
+
+       /* configure clocks */
+       switch (params_rate(params)) {
+       case 44100:
+               config |= NANO_CFG_MULT1 | NANO_CFG_CLK22;
+               break;
+       case 88200:
+               config |= NANO_CFG_MULT2 | NANO_CFG_CLK22;
+               break;
+       case 176400:
+               config |= NANO_CFG_MULT4 | NANO_CFG_CLK22;
+               break;
+       case 352800:
+               config |= NANO_CFG_MULT8 | NANO_CFG_CLK22;
+               break;
+       case 705600:
+               config |= NANO_CFG_MULT16 | NANO_CFG_CLK22;
+               break;
+       case 48000:
+               config |= NANO_CFG_MULT1 | NANO_CFG_CLK24;
+               break;
+       case 96000:
+               config |= NANO_CFG_MULT2 | NANO_CFG_CLK24;
+               break;
+       case 192000:
+               config |= NANO_CFG_MULT4 | NANO_CFG_CLK24;
+               break;
+       case 384000:
+               config |= NANO_CFG_MULT8 | NANO_CFG_CLK24;
+               break;
+       case 768000:
+               config |= NANO_CFG_MULT16 | NANO_CFG_CLK24;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dev_dbg(card->dev, "Send CFG register 0x%02X\n", config);
+       return regmap_write(regmap, NANO_CFG, config);
+}
+
+static struct snd_soc_ops nano_player_ops = {
+       .startup        = nano_player_startup,
+       .hw_params      = nano_player_hw_params,
+};
+
+static struct snd_soc_dai_link nano_player_link = {
+       .name           = "3Dlab Nano Player",
+       .stream_name    = "3Dlab Nano Player HiFi",
+       .platform_name  = "bcm2708-i2s.0",
+       .cpu_dai_name   = "bcm2708-i2s.0",
+       .codec_name     = "snd-soc-dummy",
+       .codec_dai_name = "snd-soc-dummy-dai",
+       .dai_fmt        = SND_SOC_DAIFMT_I2S |
+                         SND_SOC_DAIFMT_CONT |
+                         SND_SOC_DAIFMT_NB_NF |
+                         SND_SOC_DAIFMT_CBM_CFM,
+       .init           = nano_player_init,
+       .ops            = &nano_player_ops,
+};
+
+static const struct regmap_config nano_player_regmap = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = 128,
+       .cache_type     = REGCACHE_RBTREE,
+};
+
+static int nano_player_card_probe(struct snd_soc_card *card)
+{
+       struct regmap *regmap = snd_soc_card_get_drvdata(card);
+       unsigned int val;
+
+       /* check hardware integrity */
+       regmap_read(regmap, NANO_ID, &val);
+       if (val != NANO_ID_VAL) {
+               dev_err(card->dev, "Invalid ID register 0x%02X\n", val);
+               return -ENODEV;
+       }
+
+       /* report version to the user */
+       regmap_read(regmap, NANO_VER, &val);
+       dev_notice(card->dev, "Started 3Dlab Nano Player driver (v%d)\n", val);
+
+       /* enable internal audio bus and blink status LED */
+       return regmap_write(regmap, NANO_CFG, NANO_CFG_ENA | NANO_CFG_BLINK);
+}
+
+static int nano_player_card_remove(struct snd_soc_card *card)
+{
+       /* disable internal audio bus */
+       struct regmap *regmap = snd_soc_card_get_drvdata(card);
+
+       return regmap_write(regmap, NANO_CFG, NANO_CFG_OFF);
+}
+
+static struct snd_soc_card nano_player_card = {
+       .name           = "3Dlab_Nano_Player",
+       .owner          = THIS_MODULE,
+       .dai_link       = &nano_player_link,
+       .num_links      = 1,
+       .controls       = nano_player_controls,
+       .num_controls   = ARRAY_SIZE(nano_player_controls),
+       .probe          = nano_player_card_probe,
+       .remove         = nano_player_card_remove,
+};
+
+static int nano_player_i2c_probe(struct i2c_client *i2c,
+                                const struct i2c_device_id *id)
+{
+       struct regmap *regmap;
+       int ret;
+
+       regmap = devm_regmap_init_i2c(i2c, &nano_player_regmap);
+       if (IS_ERR(regmap)) {
+               ret = PTR_ERR(regmap);
+               dev_err(&i2c->dev, "Failed to init regmap %d\n", ret);
+               return ret;
+       }
+
+       if (i2c->dev.of_node) {
+               struct snd_soc_dai_link *dai = &nano_player_link;
+               struct device_node *node;
+
+               /* cpu handle configured by device tree */
+               node = of_parse_phandle(i2c->dev.of_node, "i2s-controller", 0);
+               if (node) {
+                       dai->platform_name = NULL;
+                       dai->platform_of_node = node;
+                       dai->cpu_dai_name = NULL;
+                       dai->cpu_of_node = node;
+               }
+       }
+
+       nano_player_card.dev = &i2c->dev;
+       snd_soc_card_set_drvdata(&nano_player_card, regmap);
+       ret = devm_snd_soc_register_card(&i2c->dev, &nano_player_card);
+
+       if (ret && ret != -EPROBE_DEFER)
+               dev_err(&i2c->dev, "Failed to register card %d\n", ret);
+
+       return ret;
+}
+
+static const struct of_device_id nano_player_of_match[] = {
+       { .compatible = "3dlab,nano-player", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, nano_player_of_match);
+
+static const struct i2c_device_id nano_player_i2c_id[] = {
+       { "nano-player", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, nano_player_i2c_id);
+
+static struct i2c_driver nano_player_i2c_driver = {
+       .probe          = nano_player_i2c_probe,
+       .id_table       = nano_player_i2c_id,
+       .driver         = {
+               .name           = "nano-player",
+               .owner          = THIS_MODULE,
+               .of_match_table = nano_player_of_match,
+       },
+};
+
+module_i2c_driver(nano_player_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC 3Dlab Nano Player driver");
+MODULE_AUTHOR("GT <dev@3d-lab-av.com>");
+MODULE_LICENSE("GPL v2");
+
+/* EOF */
--- a/sound/soc/bcm/Kconfig
+++ b/sound/soc/bcm/Kconfig
@@ -18,6 +18,12 @@ config SND_SOC_CYGNUS
 
          If you don't know what to do here, say N.
 
+config SND_BCM2708_SOC_3DLAB_NANO_PLAYER
+       tristate "Support for 3Dlab Nano Player"
+       depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+       help
+         Say Y or M if you want to add support for 3Dlab Nano Player.
+
 config SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD
        tristate "Support for Google voiceHAT soundcard"
        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
--- a/sound/soc/bcm/Makefile
+++ b/sound/soc/bcm/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-
 snd-soc-googlevoicehat-codec-objs := googlevoicehat-codec.o
 
 # BCM2708 Machine Support
+snd-soc-3dlab-nano-player-objs := 3dlab-nano-player.o
 snd-soc-adau1977-adc-objs := adau1977-adc.o
 snd-soc-googlevoicehat-soundcard-objs := googlevoicehat-soundcard.o
 snd-soc-hifiberry-amp-objs := hifiberry_amp.o
@@ -38,6 +39,7 @@ snd-soc-allo-katana-codec-objs := allo-k
 snd-soc-pisound-objs := pisound.o
 snd-soc-fe-pi-audio-objs := fe-pi-audio.o
 
+obj-$(CONFIG_SND_BCM2708_SOC_3DLAB_NANO_PLAYER) += snd-soc-3dlab-nano-player.o
 obj-$(CONFIG_SND_BCM2708_SOC_ADAU1977_ADC) += snd-soc-adau1977-adc.o
 obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD) += snd-soc-googlevoicehat-soundcard.o
 obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD)  += snd-soc-googlevoicehat-codec.o