Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
May 2016
- 118 participants
- 260 discussions
[alsa-devel] [PATCH 0/3] ASoC: da7219: Add ACPI initialisation support to driver
by Adam Thomson 10 Jun '16
by Adam Thomson 10 Jun '16
10 Jun '16
This patch set updates the driver to use generic device property & fwnode
related functions to read in either DT and ACPI data for driver initialisation.
Changes are based on v4.6-rc6 Linux Kernel.
Adam Thomson (3):
ASoC: da7219: Convert driver to use generic device/fwnode functions
ASoC: da7219: Add ACPI parsing support
ASoC: da7219: Add initial ACPI id for device
sound/soc/codecs/da7219-aad.c | 96 +++++++++++++++++++++++++++++++------------
sound/soc/codecs/da7219.c | 35 ++++++++++------
2 files changed, 91 insertions(+), 40 deletions(-)
--
1.9.3
4
13
07 Jun '16
ARC AXS10x platforms consist of a mainboard with several peripherals.
One of those peripherals is an HDMI output port controlled by the ADV7511
transmitter.
This patch set adds I2S audio for the AXS10x platform.
NOTE:
Although the mainline I2S driver uses ALSA DMA engine, this controller
can be built without DMA support so it was necessary to add this
custom platform driver so that HDMI audio works in AXS boards.
Changes v7 -> v8:
* Build PIO PCM as module
* Always unmask interrupts even when in DMA mode
* Fallback to PIO mode only if ALSA DMA engine probe fails
Changes v6 -> v7:
* Discard the use of memcpy
* Report IRQ_HANDLED only when there is an IRQ
* Use interrupts to check if PIO mode is in use
* Unmask interrupts only when in PIO mode
* Remove empty functions
Changes v5 -> v6:
* Use SNDRV_DMA_TYPE_CONTINUOUS
Changes v4 -> v5:
* Resolve undefined references when compiling as module
* Dropped adv7511 audio patches
* Use DMA properties in I2S to check which mode to use: PIO or DMA (as suggested by Lars-Peter Clausen)
Changes v3 -> v4:
* Reintroduced custom PCM driver (see note below)
* Use DT boolean to switch between ALSA DMA engine PCM or custom PCM
* Use fifo depth to program I2S FCR
* Update I2S documentation
Changes v2 -> v3:
* Removed pll_config functions (as suggested by Alexey Brodkin)
* Removed HDMI start at adv7511_core (as suggested by Archit Taneja)
* Use NOP functions for adv7511_audio (as suggested by Archit Taneja)
* Added adv7511_audio_exit() function (as suggested by Archit Taneja)
* Moved adv7511 to its own folder (as suggested by Archit Taneja)
* Separated file rename of adv7511_core (as suggested by Emil Velikov)
* Compile adv7511 as module if ALSA SoC is compiled as module
* Load adv7511 audio only if declared in device tree (as suggested by Laurent Pinchart)
* Dropped custom platform driver, using now ALSA DMA engine
* Dropped IRQ handler for I2S
Changes v1 -> v2:
* DT bindings moved to separate patch (as suggested by Alexey Brodkin)
* Removed defconfigs entries (as suggested by Alexey Brodkin)
Cc: Carlos Palminha <palminha(a)synopsys.com>
Cc: Mark Brown <broonie(a)kernel.org>
Cc: Liam Girdwood <lgirdwood(a)gmail.com>
Cc: Jaroslav Kysela <perex(a)perex.cz>
Cc: Takashi Iwai <tiwai(a)suse.com>
Cc: Rob Herring <robh(a)kernel.org>
Cc: Alexey Brodkin <abrodkin(a)synopsys.com>
Cc: linux-snps-arc(a)lists.infradead.org
Cc: alsa-devel(a)alsa-project.org
Cc: linux-kernel(a)vger.kernel.org
Jose Abreu (2):
ASoC: dwc: Add PIO PCM extension
ASoC: dwc: Add irq parameter to DOCUMENTATION
.../devicetree/bindings/sound/designware-i2s.txt | 4 +
sound/soc/dwc/Kconfig | 9 +
sound/soc/dwc/Makefile | 1 +
sound/soc/dwc/designware_i2s.c | 152 ++++++--------
sound/soc/dwc/designware_pcm.c | 222 +++++++++++++++++++++
sound/soc/dwc/local.h | 122 +++++++++++
6 files changed, 422 insertions(+), 88 deletions(-)
create mode 100644 sound/soc/dwc/designware_pcm.c
create mode 100644 sound/soc/dwc/local.h
--
1.9.1
2
3
[alsa-devel] [PATCH v4 0/2] sti: add audio interface to the hdmi driver
by Arnaud Pouliquen 07 Jun '16
by Arnaud Pouliquen 07 Jun '16
07 Jun '16
This patchset implements audio interface in HDMI drm driver. Implementation is based on
ASoC generic hdmi codec driver( https://patchwork.kernel.org/patch/8713141/).
It also proposes helper functions to compute N and CTS parameters
according to HDMI 1.4b specification.
V4:
fixes for "video: hdmi: add helper functions for N and CTS"
- typo error and additional comments
- cts_1_ratio computation
- warning reported by kbuild test robot
- add rounded value for 297/1.001 MHz
V3:
- video: hdmi: add helper function for N and CTS
Also used on Mediatek platform (https://patchwork.kernel.org/patch/8887341)
delta vs V2:
- typo fixes
- if/else code optimisation
- drm: sti: Add ASoC generic hdmi codec support.
- typo fixes
- add audio registers in debugfs information
V2: RFC
https://patchwork.kernel.org/patch/8091531/("video: hdmi: add helper function for N and CTS")
https://patchwork.kernel.org/patch/8091561/("ASoC: hdmi-codec: Add hdmi-codec for external HDMI-encoders")
- patch: video: hdmi: add helper function for N and CTS
Fixes based on Russel King remarks
- Duplicate function to have a separte treatment for coherent and
non-coherent clocks
- Add ratio field for alternate CTS value
- Clock frequency in Hz for TMDS and audio clocks
- Add information concerning clocks and CTS calculation.
V1:
This RFC is the implementation of audio HDMI on sti platform based on generic hdmi-codec driver:
https://patchwork.kernel.org/patch/7215271/ ("ASoC: hdmi-codec: Add hdmi-codec for external HDMI-encoders")
https://patchwork.kernel.org/patch/8062611/ ("video: hdmi: add helper function for N and CTS")
Arnaud Pouliquen (2):
video: hdmi: add helper functions for N and CTS
drm: sti: Add ASoC generic hdmi codec support.
drivers/gpu/drm/sti/Kconfig | 1 +
drivers/gpu/drm/sti/sti_hdmi.c | 248 ++++++++++++++++++++++++++++++++++++++---
drivers/gpu/drm/sti/sti_hdmi.h | 13 +++
drivers/video/hdmi.c | 208 ++++++++++++++++++++++++++++++++++
include/linux/hdmi.h | 24 ++++
5 files changed, 477 insertions(+), 17 deletions(-)
--
1.9.1
2
9
[alsa-devel] [PATCH v3] ASoC: rockchip: Add machine driver for MAX98357A/RT5514/DA7219
by Xing Zheng 07 Jun '16
by Xing Zheng 07 Jun '16
07 Jun '16
There are multi codec devices on the RK3399 platform, we can use
this patch support and control these codecs.
Signed-off-by: Xing Zheng <zhengxing(a)rock-chips.com>
---
Changes in v3:
- rename DOC to rockchip,rk3399-max98357a-rt5514-da7219.txt
- rename compatible to rockchip,rk3399-max98357a-rt5514-da7219
- rename source code to rk3399_max98357a_rt5514_da7219.c
Changes in v2:
- use the FS 256 to set mclks of the max98357a and rt5514 danamically
- add more sample rate for da7219
.../rockchip,rk3399-max98357a-rt5514-da7219.txt | 15 +
sound/soc/rockchip/Kconfig | 11 +
sound/soc/rockchip/Makefile | 2 +
.../soc/rockchip/rk3399_max98357a_rt5514_da7219.c | 333 ++++++++++++++++++++
4 files changed, 361 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/rockchip,rk3399-max98357a-rt5514-da7219.txt
create mode 100644 sound/soc/rockchip/rk3399_max98357a_rt5514_da7219.c
diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3399-max98357a-rt5514-da7219.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3399-max98357a-rt5514-da7219.txt
new file mode 100644
index 0000000..3ae603e
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/rockchip,rk3399-max98357a-rt5514-da7219.txt
@@ -0,0 +1,15 @@
+ROCKCHIP with MAX98357A/RT5514/DA7219 codecs
+
+Required properties:
+- compatible: "rockchip,rk3399-max98357a-rt5514-da7219"
+- rockchip,cpu: The phandle of the Rockchip I2S controller that's
+ connected to the codecs
+- rockchip,codec: The phandle of the MAX98357A/RT5514/DA7219 codecs
+
+Example:
+
+sound {
+ compatible = "rockchip,rk3399-max98357a-rt5514-da7219";
+ rockchip,cpu = <&i2s0 &i2s0 &i2s1>;
+ rockchip,codec = <&max98357a &headsetcodec &codec>;
+};
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index f1e0c70..e5bd1f9 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -41,3 +41,14 @@ config SND_SOC_ROCKCHIP_RT5645
help
Say Y or M here if you want to add support for SoC audio on Rockchip
boards using the RT5645/RT5650 codec, such as Veyron.
+
+config SND_SOC_RK3399_MAX98357A_RT5514_DA7219
+ tristate "ASoC support for Rockchip RK3399 boards using the MAX98357A/RT5514/DA7219"
+ depends on SND_SOC_ROCKCHIP && GPIOLIB
+ select SND_SOC_ROCKCHIP_I2S
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT5514
+ select SND_SOC_DA7219
+ help
+ Say Y or M here if you want to add support for SoC audio on Rockchip RK3399
+ boards using the MAX98357A/RT5514/DA7219.
diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile
index c0bf560..0d9ca0a 100644
--- a/sound/soc/rockchip/Makefile
+++ b/sound/soc/rockchip/Makefile
@@ -7,6 +7,8 @@ obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
snd-soc-rockchip-max98090-objs := rockchip_max98090.o
snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o
+snd-soc-rk3399-max98357a-rt5514-da7219-objs := rk3399_max98357a_rt5514_da7219.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o
+obj-$(CONFIG_SND_SOC_RK3399_MAX98357A_RT5514_DA7219) += snd-soc-rk3399-max98357a-rt5514-da7219.o
diff --git a/sound/soc/rockchip/rk3399_max98357a_rt5514_da7219.c b/sound/soc/rockchip/rk3399_max98357a_rt5514_da7219.c
new file mode 100644
index 0000000..ec88600
--- /dev/null
+++ b/sound/soc/rockchip/rk3399_max98357a_rt5514_da7219.c
@@ -0,0 +1,333 @@
+/*
+ * Rockchip machine ASoC driver for boards using MAX98357A/RT5514/DA7219
+ *
+ * Copyright (c) 2016, ROCKCHIP CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "rockchip_i2s.h"
+#include "../codecs/da7219.h"
+#include "../codecs/da7219-aad.h"
+#include "../codecs/rt5514.h"
+
+#define DRV_NAME "rk3399-max98357a-rt5514-da7219"
+
+#define SOUND_FS 256
+
+static struct snd_soc_jack rockchip_sound_jack;
+
+static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route rockchip_dapm_routes[] = {
+ /* Input Lines */
+ {"MIC", NULL, "Headset Mic"},
+ {"DMIC1L", NULL, "Int Mic"},
+ {"DMIC1R", NULL, "Int Mic"},
+
+ /* Output Lines */
+ {"Headphones", NULL, "HPL"},
+ {"Headphones", NULL, "HPR"},
+ {"Speakers", NULL, "Speakers"},
+};
+
+static const struct snd_kcontrol_new rockchip_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static int rockchip_sound_max98357a_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_dapm_context *dapm = &rtd->card->dapm;
+ unsigned int mclk;
+ int ret;
+
+ /* max98357a supports these sample rates */
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ mclk = params_rate(params) * SOUND_FS;
+ break;
+ default:
+ dev_err(rtd->card->dev, "%s() doesn't support this sample rate: %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, 0);
+ if (ret) {
+ dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",
+ __func__, mclk, ret);
+ return ret;
+ }
+
+ snd_soc_dapm_disable_pin(dapm, "Headphones");
+ snd_soc_dapm_enable_pin(dapm, "Speakers");
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static int rockchip_sound_rt5514_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_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ unsigned int mclk;
+ int ret;
+
+ mclk = params_rate(params) * SOUND_FS;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Can't set cpu clock out %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5514_SCLK_S_MCLK,
+ mclk, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",
+ __func__, params_rate(params) * 512, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_sound_da7219_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_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
+ int mclk, ret;
+
+ /* in bypass mode, the mclk has to be one of the frequencies below */
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Can't set cpu clock out %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Can't set codec clock in %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "Can't set pll sysclk mclk %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_dapm_disable_pin(dapm, "Speakers");
+ snd_soc_dapm_enable_pin(dapm, "Headphones");
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec_dais[0]->codec;
+ int ret;
+
+ /* Enable Headset and 4 Buttons Jack detection */
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &rockchip_sound_jack, NULL, 0);
+
+ if (ret) {
+ dev_err(rtd->card->dev, "New Headset Jack failed! (%d)\n", ret);
+ return ret;
+ }
+
+ da7219_aad_jack_det(codec, &rockchip_sound_jack);
+
+ return 0;
+}
+
+static struct snd_soc_ops rockchip_sound_max98357a_ops = {
+ .hw_params = rockchip_sound_max98357a_hw_params,
+};
+
+static struct snd_soc_ops rockchip_sound_rt5514_ops = {
+ .hw_params = rockchip_sound_rt5514_hw_params,
+};
+
+static struct snd_soc_ops rockchip_sound_da7219_ops = {
+ .hw_params = rockchip_sound_da7219_hw_params,
+};
+
+enum {
+ DAILINK_MAX98357A,
+ DAILINK_RT5514,
+ DAILINK_DA7219,
+};
+
+static struct snd_soc_dai_link rockchip_dailinks[] = {
+ [DAILINK_MAX98357A] = {
+ .name = "MAX98357A",
+ .stream_name = "MAX98357A PCM",
+ .codec_dai_name = "HiFi",
+ .ops = &rockchip_sound_max98357a_ops,
+ /* set max98357a as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ },
+ [DAILINK_RT5514] = {
+ .name = "RT5514",
+ .stream_name = "RT5514 PCM",
+ .codec_dai_name = "rt5514-aif1",
+ .ops = &rockchip_sound_rt5514_ops,
+ /* set rt5514 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ },
+ [DAILINK_DA7219] = {
+ .name = "DA7219",
+ .stream_name = "DA7219 PCM",
+ .codec_dai_name = "da7219-hifi",
+ .init = rockchip_sound_da7219_init,
+ .ops = &rockchip_sound_da7219_ops,
+ /* set da7219 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ },
+};
+
+static struct snd_soc_card rockchip_sound_card = {
+ .name = "rk3399-max98357a-rt5514-da7219",
+ .owner = THIS_MODULE,
+ .dai_link = rockchip_dailinks,
+ .num_links = ARRAY_SIZE(rockchip_dailinks),
+ .dapm_widgets = rockchip_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets),
+ .dapm_routes = rockchip_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rockchip_dapm_routes),
+ .controls = rockchip_controls,
+ .num_controls = ARRAY_SIZE(rockchip_controls),
+};
+
+static int rockchip_sound_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &rockchip_sound_card;
+ struct device_node *cpu_node;
+ int i, ret;
+
+ for (i = 0; i < card->num_links; i++) {
+ cpu_node = of_parse_phandle(pdev->dev.of_node,
+ "rockchip,cpu", i);
+ if (!cpu_node) {
+ dev_err(&pdev->dev, "Property[%d] 'rockchip,cpu' missing or invalid\n", i);
+ return -EINVAL;
+ }
+
+ rockchip_dailinks[i].platform_of_node = cpu_node;
+ rockchip_dailinks[i].cpu_of_node = cpu_node;
+
+ rockchip_dailinks[i].codec_of_node =
+ of_parse_phandle(pdev->dev.of_node, "rockchip,codec", i);
+ if (!rockchip_dailinks[i].codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property[%d] 'rockchip,codec' missing or invalid\n", i);
+ return -EINVAL;
+ }
+ }
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static const struct of_device_id rockchip_sound_of_match[] = {
+ { .compatible = "rockchip,rk3399-max98357a-rt5514-da7219", },
+ {},
+};
+
+static struct platform_driver rockchip_sound_driver = {
+ .probe = rockchip_sound_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = rockchip_sound_of_match,
+#ifdef CONFIG_PM
+ .pm = &snd_soc_pm_ops,
+#endif
+ },
+};
+
+module_platform_driver(rockchip_sound_driver);
+
+MODULE_AUTHOR("Xing Zheng <zhengxing(a)rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip ASoC Machine Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, rockchip_sound_of_match);
--
1.7.9.5
3
13
[alsa-devel] [PATCH v5] ASoC: cs53l30: Add support for Cirrus Logic CS53L30
by tim.howeï¼ cirrus.com 06 Jun '16
by tim.howeï¼ cirrus.com 06 Jun '16
06 Jun '16
From: Tim Howe <tim.howe(a)cirrus.com>
Signed-off-by: Tim Howe <tim.howe(a)cirrus.com>
---
.../devicetree/bindings/sound/cs53l30.txt | 21 +
sound/soc/codecs/Kconfig | 6 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/cs53l30.c | 943 +++++++++++++++++++++
sound/soc/codecs/cs53l30.h | 282 ++++++
5 files changed, 1254 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/cs53l30.txt
create mode 100644 sound/soc/codecs/cs53l30.c
create mode 100644 sound/soc/codecs/cs53l30.h
diff --git a/Documentation/devicetree/bindings/sound/cs53l30.txt b/Documentation/devicetree/bindings/sound/cs53l30.txt
new file mode 100644
index 0000000..a2dff95
+++ b/Documentation/devicetree/bindings/sound/cs53l30.txt
@@ -0,0 +1,21 @@
+CS53L30 audio CODEC
+
+Required properties:
+
+ - compatible : "cirrus,cs53l30"
+
+ - reg : the I2C address of the device
+
+Optional properties:
+
+ - reset-gpios : a GPIO spec for the reset pin.
+
+
+Example:
+
+codec: cs53l30@48 {
+ compatible = "cirrus,cs53l30";
+ reg = <0x48>;
+ reset-gpios = <&gpio 54 0>;
+};
+
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b282169..2dd97d4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -60,6 +60,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS4271_SPI if SPI_MASTER
select SND_SOC_CS42XX8_I2C if I2C
select SND_SOC_CS4349 if I2C
+ select SND_SOC_CS53L30 if I2C
select SND_SOC_CX20442 if TTY
select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
select SND_SOC_DA7213 if I2C
@@ -449,6 +450,11 @@ config SND_SOC_CS4349
tristate "Cirrus Logic CS4349 CODEC"
depends on I2C
+# Cirrus Logic Quad-Channel ADC
+config SND_SOC_CS53L30
+ tristate "Cirrus Logic CS53L30 CODEC"
+ depends on I2C
+
config SND_SOC_CX20442
tristate
depends on TTY
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 81324bc..88dff59 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -52,6 +52,7 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o
snd-soc-cs42xx8-objs := cs42xx8.o
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
snd-soc-cs4349-objs := cs4349.o
+snd-soc-cs53l30-objs := cs53l30.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
@@ -252,6 +253,7 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
+obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
new file mode 100644
index 0000000..2002ecd
+++ b/sound/soc/codecs/cs53l30.c
@@ -0,0 +1,943 @@
+/*
+ * cs53l30.c -- CS53l30 ALSA Soc Audio driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Authors: Paul Handrigan <Paul.Handrigan(a)cirrus.com>,
+ * Tim Howe <Tim.Howe(a)cirrus.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "cs53l30.h"
+
+struct cs53l30_private {
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ u8 asp_config_ctl;
+ u32 mclk;
+};
+
+static const struct reg_default cs53l30_reg_defaults[] = {
+ { CS53L30_PWRCTL, CS53L30_THMS_PDN },
+ { CS53L30_MCLKCTL, CS53L30_MCLK_DIV_DFLT },
+ { CS53L30_INT_SR_CTL, CS53L30_INTRNL_FS_DFLT },
+ { CS53L30_MICBIAS_CTL, CS53L30_MIC_BIAS_DFLT },
+ { CS53L30_ASPCFG_CTL, CS53L30_ASP_RATE_48K },
+ { CS53L30_ASP1_CTL, CS53L30_ASP1_TDM_PDN },
+ { CS53L30_ASP1_TDMTX_CTL1, CS53L30_ASP1_CHTX_SLT47 },
+ { CS53L30_ASP1_TDMTX_CTL2, CS53L30_ASP1_CHTX_SLT47 },
+ { CS53L30_ASP1_TDMTX_CTL3, CS53L30_ASP1_CHTX_SLT47 },
+ { CS53L30_ASP1_TDMTX_CTL4, CS53L30_ASP1_CHTX_SLT47 },
+ { CS53L30_ASP1_TDMTX_EN1, CS53L30_ASP_TX_DISABLED },
+ { CS53L30_ASP1_TDMTX_EN2, CS53L30_ASP_TX_DISABLED },
+ { CS53L30_ASP1_TDMTX_EN3, CS53L30_ASP_TX_DISABLED },
+ { CS53L30_ASP1_TDMTX_EN4, CS53L30_ASP_TX_DISABLED },
+ { CS53L30_ASP1_TDMTX_EN5, CS53L30_ASP_TX_DISABLED },
+ { CS53L30_ASP1_TDMTX_EN6, CS53L30_ASP_TX_DISABLED },
+ { CS53L30_ASP2_CTL, CS53L30_ASP2_CTRL_DFLT },
+ { CS53L30_SFT_RAMP, CS53L30_SFT_RMP_DFLT },
+ { CS53L30_LRCLK_CTL1, CS53L30_LRCK_CTLX_DFLT },
+ { CS53L30_LRCLK_CTL2, CS53L30_LRCK_CTLX_DFLT },
+ { CS53L30_MUTEP_CTL1, CS53L30_MUTE_CTRL1_DFLT },
+ { CS53L30_MUTEP_CTL2, CS53L30_MUTE_PDN_ULP },
+ { CS53L30_INBIAS_CTL1, CS53L30_INBIAS_X_DFLT },
+ { CS53L30_INBIAS_CTL2, CS53L30_INBIAS_X_DFLT },
+ { CS53L30_DMIC1_STR_CTL, CS53L30_DMIC1_ST_DFLT },
+ { CS53L30_DMIC2_STR_CTL, CS53L30_DMIC2_ST_DFLT },
+ { CS53L30_ADCDMIC1_CTL1, CS53L30_ADC1_ON_AB_IN },
+ { CS53L30_ADCDMIC1_CTL2, CS53L30_A1_D1_CTL2_DFLT },
+ { CS53L30_ADC1_CTL3, CS53L30_ADC1_HPF_EN },
+ { CS53L30_ADC1_NG_CTL, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_ADC1A_AFE_CTL, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_ADC1B_AFE_CTL, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_ADC1A_DIG_VOL, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_ADC1B_DIG_VOL, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_ADCDMIC2_CTL1, CS53L30_ADC2_ON_AB_IN },
+ { CS53L30_ADCDMIC2_CTL2, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_ADC2_CTL3, CS53L30_ADC2_HPF_EN },
+ { CS53L30_ADC2_NG_CTL, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_ADC2A_AFE_CTL, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_ADC2B_AFE_CTL, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_ADC2A_DIG_VOL, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_ADC2B_DIG_VOL, CS53L30_ADCX_ZERO_DFLT },
+ { CS53L30_INT_MASK, CS53L30_DEVICE_INT_MASK },
+};
+
+static bool cs53l30_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg == CS53L30_IS)
+ return true;
+ else
+ return false;
+}
+
+static bool cs53l30_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS53L30_DEVID_AB:
+ case CS53L30_DEVID_CD:
+ case CS53L30_DEVID_E:
+ case CS53L30_REVID:
+ case CS53L30_PWRCTL:
+ case CS53L30_MCLKCTL:
+ case CS53L30_INT_SR_CTL:
+ case CS53L30_MICBIAS_CTL:
+ case CS53L30_ASPCFG_CTL:
+ case CS53L30_ASP1_CTL:
+ case CS53L30_ASP1_TDMTX_CTL1:
+ case CS53L30_ASP1_TDMTX_CTL2:
+ case CS53L30_ASP1_TDMTX_CTL3:
+ case CS53L30_ASP1_TDMTX_CTL4:
+ case CS53L30_ASP1_TDMTX_EN1:
+ case CS53L30_ASP1_TDMTX_EN2:
+ case CS53L30_ASP1_TDMTX_EN3:
+ case CS53L30_ASP1_TDMTX_EN4:
+ case CS53L30_ASP1_TDMTX_EN5:
+ case CS53L30_ASP1_TDMTX_EN6:
+ case CS53L30_ASP2_CTL:
+ case CS53L30_SFT_RAMP:
+ case CS53L30_LRCLK_CTL1:
+ case CS53L30_LRCLK_CTL2:
+ case CS53L30_MUTEP_CTL1:
+ case CS53L30_MUTEP_CTL2:
+ case CS53L30_INBIAS_CTL1:
+ case CS53L30_INBIAS_CTL2:
+ case CS53L30_DMIC1_STR_CTL:
+ case CS53L30_DMIC2_STR_CTL:
+ case CS53L30_ADCDMIC1_CTL1:
+ case CS53L30_ADCDMIC1_CTL2:
+ case CS53L30_ADC1_CTL3:
+ case CS53L30_ADC1_NG_CTL:
+ case CS53L30_ADC1A_AFE_CTL:
+ case CS53L30_ADC1B_AFE_CTL:
+ case CS53L30_ADC1A_DIG_VOL:
+ case CS53L30_ADC1B_DIG_VOL:
+ case CS53L30_ADCDMIC2_CTL1:
+ case CS53L30_ADCDMIC2_CTL2:
+ case CS53L30_ADC2_CTL3:
+ case CS53L30_ADC2_NG_CTL:
+ case CS53L30_ADC2A_AFE_CTL:
+ case CS53L30_ADC2B_AFE_CTL:
+ case CS53L30_ADC2A_DIG_VOL:
+ case CS53L30_ADC2B_DIG_VOL:
+ case CS53L30_INT_MASK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2000, 0);
+static DECLARE_TLV_DB_SCALE(adc_ng_boost_tlv, 0, 3000, 0);
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(dig_tlv, -9600, 100, 1);
+
+static const char * const input1_sel_text[] = { "DMIC1 On AB In",
+ "DMIC1 On A In", "DMIC1 On B In", "ADC1 On AB In", "ADC1 On A In",
+ "ADC1 On B In", "DMIC1 Off ADC1 Off", };
+
+unsigned int const input1_sel_values[] = { CS53L30_DMIC1_ON_AB_IN,
+ CS53L30_DMIC1_ON_A_IN, CS53L30_DMIC1_ON_B_IN, CS53L30_ADC1_ON_AB_IN,
+ CS53L30_ADC1_ON_A_IN, CS53L30_ADC1_ON_B_IN, CS53L30_D1_OFF_A1_OFF, };
+
+static const char * const input2_sel_text[] = { "DMIC2 On AB In",
+ "DMIC2 On A In", "DMIC2 On B In", "ADC2 On AB In", "ADC2 On A In",
+ "ADC2 On B In", "DMIC2 Off ADC2 Off", };
+
+unsigned int const input2_sel_values[] = { CS53L30_DMIC2_ON_AB_IN,
+ CS53L30_DMIC2_ON_A_IN, CS53L30_DMIC2_ON_B_IN, CS53L30_ADC2_ON_AB_IN,
+ CS53L30_ADC2_ON_A_IN, CS53L30_ADC2_ON_B_IN, CS53L30_D2_OFF_A2_OFF, };
+
+static const char * const input1_route_sel_text[] = { "ADC1_SEL",
+ "DMIC1_SEL" };
+
+static const struct soc_enum input1_route_sel_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, 0,
+ ARRAY_SIZE(input1_route_sel_text), input1_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input1_sel_enum, CS53L30_ADCDMIC1_CTL1, 0,
+ CS53L30_A1_D1_PDN_MASK, input1_sel_text, input1_sel_values);
+
+static const struct snd_kcontrol_new input1_route_sel_mux =
+ SOC_DAPM_ENUM("Input 1 Route", input1_route_sel_enum);
+
+static const char * const input2_route_sel_text[] = { "ADC2_SEL",
+ "DMIC2_SEL" };
+
+/* Note: CS53L30_ADCDMIC1_CTL1 CH_TYPE controls inputs 1 and 2 */
+static const struct soc_enum input2_route_sel_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, 0,
+ ARRAY_SIZE(input2_route_sel_text), input2_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input2_sel_enum, CS53L30_ADCDMIC2_CTL1, 0,
+ CS53L30_A1_D1_PDN_MASK, input2_sel_text, input2_sel_values);
+
+static const struct snd_kcontrol_new input2_route_sel_mux =
+ SOC_DAPM_ENUM("Input 2 Route", input2_route_sel_enum);
+
+/*
+ * TB = 6144*(MCLK(int) scaling factor)/MCLK(internal)
+ * TB - Time base
+ * NOTE: If MCLK_INT_SCALE = 0, then TB=1
+ */
+static const char * const cs53l30_ng_delay_text[] = {
+ "TB*50ms", "TB*100ms", "TB*150ms", "TB*200ms" };
+
+static const struct soc_enum adc1_ng_delay_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, 0,
+ ARRAY_SIZE(cs53l30_ng_delay_text), cs53l30_ng_delay_text);
+
+static const struct soc_enum adc2_ng_delay_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, 0,
+ ARRAY_SIZE(cs53l30_ng_delay_text), cs53l30_ng_delay_text);
+
+/* The noise gate threshold selected will depend on NG Boost */
+static const char * const cs53l30_ng_thres_text[] = {
+ "-64dB/-34dB", "-66dB/-36dB", "-70dB/-40dB", "-73dB/-43dB",
+ "-76dB/-46dB", "-82dB/-52dB", "-58dB", "-64dB"};
+
+static const struct soc_enum adc1_ng_thres_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, 2,
+ ARRAY_SIZE(cs53l30_ng_thres_text), cs53l30_ng_thres_text);
+
+static const struct soc_enum adc2_ng_thres_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, 2,
+ ARRAY_SIZE(cs53l30_ng_thres_text), cs53l30_ng_thres_text);
+
+/* ADC Preamp gain select */
+static const char * const cs53l30_preamp_gain_sel_text[] = {
+ "0dB", "10dB", "20dB"};
+
+static const struct soc_enum adc1a_preamp_gain_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1A_AFE_CTL, 6,
+ ARRAY_SIZE(cs53l30_preamp_gain_sel_text),
+ cs53l30_preamp_gain_sel_text);
+
+static const struct soc_enum adc1b_preamp_gain_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1B_AFE_CTL, 6,
+ ARRAY_SIZE(cs53l30_preamp_gain_sel_text),
+ cs53l30_preamp_gain_sel_text);
+
+static const struct soc_enum adc2a_preamp_gain_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2A_AFE_CTL, 6,
+ ARRAY_SIZE(cs53l30_preamp_gain_sel_text),
+ cs53l30_preamp_gain_sel_text);
+
+static const struct soc_enum adc2b_preamp_gain_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2B_AFE_CTL, 6,
+ ARRAY_SIZE(cs53l30_preamp_gain_sel_text),
+ cs53l30_preamp_gain_sel_text);
+
+/* Set MIC Bias Voltage Control */
+static const char * const cs53l30_micbias_text[] = {
+ "HiZ", "1.8V", "2.75V"};
+
+static const struct soc_enum micbias_enum =
+ SOC_ENUM_SINGLE(CS53L30_MICBIAS_CTL, 0,
+ ARRAY_SIZE(cs53l30_micbias_text),
+ cs53l30_micbias_text);
+
+/* Corner frequencies are with an Fs of 48kHz. */
+static const char * const hpf_corner_freq_text[] = {
+ "1.86Hz", "120Hz", "235Hz", "466Hz"};
+
+static const struct soc_enum adc1_hpf_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_CTL3, 1,
+ ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct soc_enum adc2_hpf_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_CTL3, 1,
+ ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct snd_kcontrol_new cs53l30_snd_controls[] = {
+ SOC_SINGLE("Digital Soft-Ramp Switch", CS53L30_SFT_RAMP, 5, 1, 0),
+ SOC_SINGLE("ADC1 Noise Gate Ganging Switch",
+ CS53L30_ADC1_CTL3, 0, 1, 0),
+ SOC_SINGLE("ADC2 Noise Gate Ganging Switch",
+ CS53L30_ADC2_CTL3, 0, 1, 0),
+ SOC_SINGLE("ADC1A Noise Gate Enable Switch",
+ CS53L30_ADC1_NG_CTL, 6, 1, 0),
+ SOC_SINGLE("ADC1B Noise Gate Enable Switch",
+ CS53L30_ADC1_NG_CTL, 7, 1, 0),
+ SOC_SINGLE("ADC2A Noise Gate Enable Switch",
+ CS53L30_ADC2_NG_CTL, 6, 1, 0),
+ SOC_SINGLE("ADC2B Noise Gate Enable Switch",
+ CS53L30_ADC2_NG_CTL, 7, 1, 0),
+ SOC_SINGLE("ADC1 Notch Filter Switch",
+ CS53L30_ADCDMIC1_CTL2, 7, 1, 1),
+ SOC_SINGLE("ADC2 Notch Filter Switch",
+ CS53L30_ADCDMIC2_CTL2, 7, 1, 1),
+ SOC_SINGLE("ADC1A Invert Switch",
+ CS53L30_ADCDMIC1_CTL2, 4, 1, 0),
+ SOC_SINGLE("ADC1B Invert Switch",
+ CS53L30_ADCDMIC1_CTL2, 5, 1, 0),
+ SOC_SINGLE("ADC2A Invert Switch",
+ CS53L30_ADCDMIC2_CTL2, 4, 1, 0),
+ SOC_SINGLE("ADC2B Invert Switch",
+ CS53L30_ADCDMIC2_CTL2, 5, 1, 0),
+
+ SOC_SINGLE_TLV("ADC1A Digital Boost Volume",
+ CS53L30_ADCDMIC1_CTL2, 0, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC1B Digital Boost Volume",
+ CS53L30_ADCDMIC1_CTL2, 1, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC2A Digital Boost Volume",
+ CS53L30_ADCDMIC2_CTL2, 0, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC2B Digital Boost Volume",
+ CS53L30_ADCDMIC2_CTL2, 1, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC1 NG Boost Volume",
+ CS53L30_ADC1_NG_CTL, 5, 1, 0, adc_ng_boost_tlv),
+ SOC_SINGLE_TLV("ADC2 NG Boost Volume",
+ CS53L30_ADC2_NG_CTL, 5, 1, 0, adc_ng_boost_tlv),
+
+ SOC_ENUM("Input 1 Channel Select", input1_sel_enum),
+ SOC_ENUM("Input 2 Channel Select", input2_sel_enum),
+
+ SOC_ENUM("ADC1 HPF Select", adc1_hpf_enum),
+ SOC_ENUM("ADC2 HPF Select", adc2_hpf_enum),
+ SOC_ENUM("ADC1 NG Threshold", adc1_ng_thres_enum),
+ SOC_ENUM("ADC2 NG Threshold", adc2_ng_thres_enum),
+ SOC_ENUM("ADC1 NG Delay", adc1_ng_delay_enum),
+ SOC_ENUM("ADC2 NG Delay", adc2_ng_delay_enum),
+ SOC_ENUM("ADC1A Pre Amp Gain", adc1a_preamp_gain_enum),
+ SOC_ENUM("ADC1B Pre Amp Gain", adc1b_preamp_gain_enum),
+ SOC_ENUM("ADC2A Pre Amp Gain", adc2a_preamp_gain_enum),
+ SOC_ENUM("ADC2B Pre Amp Gain", adc2b_preamp_gain_enum),
+ SOC_ENUM("Mic Bias Voltage Select", micbias_enum),
+
+ SOC_SINGLE_SX_TLV("ADC1A PGA Volume",
+ CS53L30_ADC1A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC1B PGA Volume",
+ CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC2A PGA Volume",
+ CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC2B PGA Volume",
+ CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+
+ SOC_SINGLE_SX_TLV("ADC1A Digital Volume",
+ CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC1B Digital Volume",
+ CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC2A Digital Volume",
+ CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC2B Digital Volume",
+ CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+};
+
+static int cs53l30_asp_sdout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(priv->regmap, CS53L30_ASP1_CTL,
+ CS53L30_ASP1_3ST, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(priv->regmap, CS53L30_ASP1_CTL,
+ CS53L30_ASP1_3ST, 1);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs53l30_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN1_DMIC1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3_DMIC2"),
+ SND_SOC_DAPM_INPUT("IN4"),
+ SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS53L30_MICBIAS_CTL, 4, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS53L30_MICBIAS_CTL, 5, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC3 Bias", CS53L30_MICBIAS_CTL, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC4 Bias", CS53L30_MICBIAS_CTL, 7, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT_E("ASP_SDOUT1", NULL, 0, CS53L30_ASP1_CTL,
+ CS53L30_ASP1_SDOUT_PDN, 1,
+ cs53l30_asp_sdout_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+ SND_SOC_DAPM_AIF_OUT_E("ASP_SDOUT2", NULL, 0, CS53L30_ASP2_CTL,
+ CS53L30_ASP2_SDOUT_PDN, 1,
+ cs53l30_asp_sdout_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+
+ SND_SOC_DAPM_MUX("Input Mux 1", SND_SOC_NOPM, 0, 0,
+ &input1_route_sel_mux),
+ SND_SOC_DAPM_MUX("Input Mux 2", SND_SOC_NOPM, 0, 0,
+ &input2_route_sel_mux),
+
+ SND_SOC_DAPM_ADC("ADC1A", NULL, CS53L30_ADCDMIC1_CTL1, 6, 1),
+ SND_SOC_DAPM_ADC("ADC1B", NULL, CS53L30_ADCDMIC1_CTL1, 7, 1),
+ SND_SOC_DAPM_ADC("ADC2A", NULL, CS53L30_ADCDMIC2_CTL1, 6, 1),
+ SND_SOC_DAPM_ADC("ADC2B", NULL, CS53L30_ADCDMIC2_CTL1, 7, 1),
+ SND_SOC_DAPM_ADC("DMIC1", NULL, CS53L30_ADCDMIC1_CTL1, 2, 1),
+ SND_SOC_DAPM_ADC("DMIC2", NULL, CS53L30_ADCDMIC2_CTL1, 2, 1),
+};
+
+static const struct snd_soc_dapm_route cs53l30_audio_map[] = {
+
+ /* ADC Input Paths */
+ {"ADC1A", NULL, "IN1_DMIC1"},
+ {"Input Mux 1", "ADC1_SEL", "ADC1A"},
+ {"ADC1B", NULL, "IN2"},
+
+ {"ADC2A", NULL, "IN3_DMIC2"},
+ {"Input Mux 2", "ADC2_SEL", "ADC2A"},
+ {"ADC2B", NULL, "IN4"},
+
+ /* MIC Bias Paths */
+ {"ADC1A", NULL, "MIC1 Bias"},
+ {"ADC1B", NULL, "MIC2 Bias"},
+ {"ADC2A", NULL, "MIC3 Bias"},
+ {"ADC2B", NULL, "MIC4 Bias"},
+
+ /* DMIC Paths */
+ {"DMIC1", NULL, "IN1_DMIC1"},
+ {"Input Mux 1", "DMIC1_SEL", "DMIC1"},
+
+ {"DMIC2", NULL, "IN3_DMIC2"},
+ {"Input Mux 2", "DMIC2_SEL", "DMIC2"},
+
+ /* Output Paths */
+ {"ASP_SDOUT1", NULL, "ADC1A" },
+ {"ASP_SDOUT1", NULL, "Input Mux 1"},
+ {"ASP_SDOUT1", NULL, "ADC1B"},
+
+ {"ASP_SDOUT2", NULL, "ADC2A"},
+ {"ASP_SDOUT2", NULL, "Input Mux 2"},
+ {"ASP_SDOUT2", NULL, "ADC2B"},
+
+ {"ASP1 Capture", NULL, "ASP_SDOUT1"},
+ {"ASP2 Capture", NULL, "ASP_SDOUT2"},
+};
+
+struct cs53l30_mclk_div {
+ u32 mclk;
+ u32 srate;
+ u8 asp_rate;
+ u8 internal_fs_ratio;
+ u8 mclk_int_scale;
+};
+
+static struct cs53l30_mclk_div cs53l30_mclk_coeffs[] = {
+ /* NOTE: Enable MCLK_INT_SCALE to save power. */
+
+ /* MCLK, Sample Rate, asp_rate, internal_fs_ratio, mclk_int_scale */
+ {5644800, 11025, 0x4, 1, 1},
+ {5644800, 22050, 0x8, 1, 1},
+ {5644800, 44100, 0xC, 1, 1},
+
+ {6000000, 8000, 0x1, 0, 1},
+ {6000000, 11025, 0x2, 0, 1},
+ {6000000, 12000, 0x4, 0, 1},
+ {6000000, 16000, 0x5, 0, 1},
+ {6000000, 22050, 0x6, 0, 1},
+ {6000000, 24000, 0x8, 0, 1},
+ {6000000, 32000, 0x9, 0, 1},
+ {6000000, 44100, 0xA, 0, 1},
+ {6000000, 48000, 0xC, 0, 1},
+
+ {6144000, 8000, 0x1, 1, 1},
+ {6144000, 11025, 0x2, 1, 1},
+ {6144000, 12000, 0x4, 1, 1},
+ {6144000, 16000, 0x5, 1, 1},
+ {6144000, 22050, 0x6, 1, 1},
+ {6144000, 24000, 0x8, 1, 1},
+ {6144000, 32000, 0x9, 1, 1},
+ {6144000, 44100, 0xA, 1, 1},
+ {6144000, 48000, 0xC, 1, 1},
+
+ {6400000, 8000, 0x1, 1, 1},
+ {6400000, 11025, 0x2, 1, 1},
+ {6400000, 12000, 0x4, 1, 1},
+ {6400000, 16000, 0x5, 1, 1},
+ {6400000, 22050, 0x6, 1, 1},
+ {6400000, 24000, 0x8, 1, 1},
+ {6400000, 32000, 0x9, 1, 1},
+ {6400000, 44100, 0xA, 1, 1},
+ {6400000, 48000, 0xC, 1, 1},
+};
+
+struct cs53l30_mclkx_div {
+ u32 mclkx;
+ u8 ratio;
+ u8 mclkdiv;
+};
+
+static struct cs53l30_mclkx_div cs53l30_mclkx_coeffs[] = {
+ {5644800, 1, 0},
+ {6000000, 1, 0},
+ {6144000, 1, 0},
+ {11289600, 2, 1},
+ {12288000, 2, 1},
+ {12000000, 2, 1},
+ {19200000, 3, 2},
+};
+
+static int cs53l30_get_mclkx_coeff(int mclkx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30_mclkx_coeffs); i++) {
+ if (cs53l30_mclkx_coeffs[i].mclkx == mclkx)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs53l30_get_mclk_coeff(int mclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30_mclk_coeffs); i++) {
+ if (cs53l30_mclk_coeffs[i].mclk == mclk &&
+ cs53l30_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+ return -EINVAL;
+
+}
+
+static int cs53l30_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ int mclkx_coeff;
+ u32 mclk;
+ unsigned int mclk_ctl;
+
+ /* MCLKX -> MCLK */
+ mclkx_coeff = cs53l30_get_mclkx_coeff(freq);
+ if (mclkx_coeff < 0)
+ return mclkx_coeff;
+
+ mclk = cs53l30_mclkx_coeffs[mclkx_coeff].mclkx /
+ cs53l30_mclkx_coeffs[mclkx_coeff].ratio;
+
+ regmap_read(priv->regmap, CS53L30_MCLKCTL, &mclk_ctl);
+ mclk_ctl &= ~CS53L30_MCLK_DIV;
+ mclk_ctl |= cs53l30_mclkx_coeffs[mclkx_coeff].mclkdiv;
+
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL, CS53L30_MCLK_DIV,
+ mclk_ctl << CS53L30_MCLK_DIV);
+ priv->mclk = mclk;
+ return 0;
+}
+
+static int cs53l30_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int asp_config_ctl;
+
+ regmap_read(priv->regmap, CS53L30_ASPCFG_CTL, &asp_config_ctl);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ asp_config_ctl |= CS53L30_ASP_MS;
+ break;
+
+ case SND_SOC_DAIFMT_CBS_CFS:
+ asp_config_ctl &= ~CS53L30_ASP_MS;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Check to see if the SCLK is inverted */
+ if (fmt & (SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_IB_IF))
+ asp_config_ctl |= CS53L30_ASP_SCLK_INV;
+ else
+ asp_config_ctl &= ~CS53L30_ASP_SCLK_INV;
+
+ priv->asp_config_ctl = asp_config_ctl;
+ return 0;
+}
+
+static int cs53l30_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+ int mclk_coeff;
+ int srate = params_rate(params);
+ unsigned int int_sr_ctl, mclk_ctl;
+
+ /* MCLK -> srate */
+ mclk_coeff = cs53l30_get_mclk_coeff(priv->mclk, srate);
+ if (mclk_coeff < 0)
+ return -EINVAL;
+
+ regmap_read(priv->regmap, CS53L30_INT_SR_CTL, &int_sr_ctl);
+ if (cs53l30_mclk_coeffs[mclk_coeff].internal_fs_ratio)
+ int_sr_ctl |= CS53L30_INTRNL_FS_RATIO;
+ else
+ int_sr_ctl &= ~CS53L30_INTRNL_FS_RATIO;
+ regmap_update_bits(priv->regmap, CS53L30_INT_SR_CTL,
+ CS53L30_INTL_FS_RAT_MSK, int_sr_ctl <<
+ CS53L30_INTL_FS_RAT_SFT);
+
+ regmap_read(priv->regmap, CS53L30_MCLKCTL, &mclk_ctl);
+ if (cs53l30_mclk_coeffs[mclk_coeff].mclk_int_scale)
+ mclk_ctl |= CS53L30_MCLK_INT_SCALE;
+ else
+ mclk_ctl &= ~CS53L30_MCLK_INT_SCALE;
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCK_INT_SCL_MSK, mclk_ctl <<
+ CS53L30_MCK_INT_SCL_SFT);
+
+ priv->asp_config_ctl &= CS53L30_ASP_CNFG_MASK;
+ priv->asp_config_ctl |= (cs53l30_mclk_coeffs[mclk_coeff].asp_rate
+ & CS53L30_ASP_RATE_MASK);
+ regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+ CS53L30_ASP_RATE_MASK, priv->asp_config_ctl);
+
+ return 0;
+}
+
+static int cs53l30_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg;
+ int i, inter_max_check;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_LP, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (dapm->bias_level == SND_SOC_BIAS_OFF) {
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIS, 0);
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP, 0);
+ msleep(50);
+ } else {
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_LP, CS53L30_PDN_LP);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+ CS53L30_PDN_DONE, 0);
+ /* If digital softramp is set, the amount of time required
+ * for power down increases and depends on the digital
+ * volume setting.
+ */
+
+ /* Set the max possible time if digsft is set */
+ regmap_read(priv->regmap, CS53L30_SFT_RAMP, ®);
+ if (reg & CS53L30_DIGSFT)
+ inter_max_check = CS53L30_PDN_POLL_MAX;
+ else
+ inter_max_check = 10;
+
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP, CS53L30_PDN_ULP);
+ msleep(20); /* PDN_DONE will take a min of 20ms to be set.*/
+ regmap_read(priv->regmap, CS53L30_IS, ®); /* Clr status */
+ for (i = 0; i < inter_max_check; i++) {
+ if (inter_max_check < 10) {
+ usleep_range(1000, 1100);
+ regmap_read(priv->regmap, CS53L30_IS, ®);
+ if (reg & CS53L30_PDN_DONE)
+ break;
+ } else {
+ usleep_range(10000, 10100);
+ regmap_read(priv->regmap, CS53L30_IS, ®);
+ if (reg & CS53L30_PDN_DONE)
+ break;
+ }
+ }
+ /* PDN_DONE is set. We now can disable the MCLK */
+ regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+ CS53L30_PDN_DONE, CS53L30_PDN_DONE);
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIS, CS53L30_MCLK_DIS);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs53l30_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ return regmap_update_bits(priv->regmap, CS53L30_ASP1_CTL,
+ CS53L30_ASP1_3ST,
+ (CS53L30_ASP1_3ST_VAL(tristate) &
+ CS53L30_ASP1_3ST));
+}
+
+unsigned int const cs53l30_src_rates[] = {
+ 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list src_constraints = {
+ .count = ARRAY_SIZE(cs53l30_src_rates),
+ .list = cs53l30_src_rates,
+};
+
+static int cs53l30_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &src_constraints);
+
+ return 0;
+}
+
+/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */
+#define CS53L30_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+#define CS53L30_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops cs53l30_ops = {
+ .startup = cs53l30_pcm_startup,
+ .hw_params = cs53l30_pcm_hw_params,
+ .set_fmt = cs53l30_set_dai_fmt,
+ .set_sysclk = cs53l30_set_sysclk,
+ .set_tristate = cs53l30_set_tristate,
+};
+
+static struct snd_soc_dai_driver cs53l30_dai[] = {
+ {
+ .name = "cs53l30-asp1",
+ .id = CS53L30_ASP1,
+ .capture = {
+ .stream_name = "ASP1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS53L30_RATES,
+ .formats = CS53L30_FORMATS,
+ },
+ .ops = &cs53l30_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "cs53l30-asp2",
+ .id = CS53L30_ASP2,
+ .capture = {
+ .stream_name = "ASP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS53L30_RATES,
+ .formats = CS53L30_FORMATS,
+ },
+ .ops = &cs53l30_ops,
+ .symmetric_rates = 1,
+ }
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_cs53l30 = {
+ .set_bias_level = cs53l30_set_bias_level,
+
+ .dapm_widgets = cs53l30_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs53l30_dapm_widgets),
+ .dapm_routes = cs53l30_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs53l30_audio_map),
+
+ .controls = cs53l30_snd_controls,
+ .num_controls = ARRAY_SIZE(cs53l30_snd_controls),
+};
+
+static struct regmap_config cs53l30_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS53L30_MAX_REGISTER,
+ .reg_defaults = cs53l30_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs53l30_reg_defaults),
+ .volatile_reg = cs53l30_volatile_register,
+ .readable_reg = cs53l30_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs53l30_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cs53l30_private *cs53l30;
+ int ret = 0;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs53l30 = devm_kzalloc(&client->dev,
+ sizeof(struct cs53l30_private), GFP_KERNEL);
+ if (!cs53l30) {
+ dev_err(&client->dev, "could not allocate codec\n");
+ return -ENOMEM;
+ }
+
+ /* Reset the Device */
+ cs53l30->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs53l30->reset_gpio))
+ return PTR_ERR(cs53l30->reset_gpio);
+
+ if (cs53l30->reset_gpio)
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+ i2c_set_clientdata(client, cs53l30);
+
+ cs53l30->mclk = 0;
+
+ cs53l30->regmap = devm_regmap_init_i2c(client, &cs53l30_regmap);
+ if (IS_ERR(cs53l30->regmap)) {
+ ret = PTR_ERR(cs53l30->regmap);
+ dev_err(&client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+ /* initialize codec */
+ ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_AB, ®);
+ devid = reg << 12;
+
+ ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_CD, ®);
+ devid |= reg << 4;
+
+ ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_E, ®);
+ devid |= (reg & 0xF0) >> 4;
+
+ if (devid != CS53L30_DEVID) {
+ ret = -ENODEV;
+ dev_err(&client->dev,
+ "CS53L30 Device ID (%X). Expected %X\n",
+ devid, CS53L30_DEVID);
+ return ret;
+ }
+
+ ret = regmap_read(cs53l30->regmap, CS53L30_REVID, ®);
+ if (ret < 0) {
+ dev_err(&client->dev, "Get Revision ID failed\n");
+ return ret;
+ }
+
+ dev_info(&client->dev,
+ "Cirrus Logic CS53L30, Revision: %02X\n", reg & 0xFF);
+
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_dev_cs53l30, cs53l30_dai,
+ ARRAY_SIZE(cs53l30_dai));
+ return ret;
+}
+
+static int cs53l30_i2c_remove(struct i2c_client *client)
+{
+ struct cs53l30_private *cs53l30 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+
+ /* Hold down reset */
+ if (cs53l30->reset_gpio)
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs53l30_runtime_suspend(struct device *dev)
+{
+ struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs53l30->regmap, true);
+
+ /* Hold down reset */
+ if (cs53l30->reset_gpio)
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+ return 0;
+}
+
+static int cs53l30_runtime_resume(struct device *dev)
+{
+ struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+
+ if (cs53l30->reset_gpio)
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+ regcache_cache_only(cs53l30->regmap, false);
+ regcache_sync(cs53l30->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs53l30_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs53l30_of_match[] = {
+ { .compatible = "cirrus,cs53l30", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs53l30_of_match);
+
+static const struct i2c_device_id cs53l30_id[] = {
+ {"cs53l30", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs53l30_id);
+
+static struct i2c_driver cs53l30_i2c_driver = {
+ .driver = {
+ .name = "cs53l30",
+ },
+ .id_table = cs53l30_id,
+ .probe = cs53l30_i2c_probe,
+ .remove = cs53l30_i2c_remove,
+
+};
+
+module_i2c_driver(cs53l30_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS53L30 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan(a)cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs53l30.h b/sound/soc/codecs/cs53l30.h
new file mode 100644
index 0000000..116e358
+++ b/sound/soc/codecs/cs53l30.h
@@ -0,0 +1,282 @@
+/*
+ * ALSA SoC CS53L30 codec driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan(a)cirrus.com>,
+ * Tim Howe <Tim.Howe(a)cirrus.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.
+ *
+ */
+
+#ifndef __CS53L30_H__
+#define __CS53L30_H__
+
+/* I2C Registers */
+#define CS53L30_DEVID_AB 0x01 /* Device ID A & B [RO]. */
+#define CS53L30_DEVID_CD 0x02 /* Device ID C & D [RO]. */
+#define CS53L30_DEVID_E 0x03 /* Device ID E [RO]. */
+#define CS53L30_REVID 0x05 /* Revision ID [RO]. */
+#define CS53L30_PWRCTL 0x06 /* Power Control. */
+#define CS53L30_MCLKCTL 0x07 /* MCLK Control. */
+#define CS53L30_INT_SR_CTL 0x08 /* Internal Sample Rate Control. */
+#define CS53L30_MICBIAS_CTL 0x0A /* Mic Bias Control. */
+#define CS53L30_ASPCFG_CTL 0x0C /* ASP Config Control. */
+#define CS53L30_ASP1_CTL 0x0D /* ASP1 Control. */
+#define CS53L30_ASP1_TDMTX_CTL1 0x0E /* ASP1 TDM TX Control 1 */
+#define CS53L30_ASP1_TDMTX_CTL2 0x0F /* ASP1 TDM TX Control 2 */
+#define CS53L30_ASP1_TDMTX_CTL3 0x10 /* ASP1 TDM TX Control 3 */
+#define CS53L30_ASP1_TDMTX_CTL4 0x11 /* ASP1 TDM TX Control 4 */
+#define CS53L30_ASP1_TDMTX_EN1 0x12 /* ASP1 TDM TX Enable 1 */
+#define CS53L30_ASP1_TDMTX_EN2 0x13 /* ASP1 TDM TX Enable 2 */
+#define CS53L30_ASP1_TDMTX_EN3 0x14 /* ASP1 TDM TX Enable 3 */
+#define CS53L30_ASP1_TDMTX_EN4 0x15 /* ASP1 TDM TX Enable 4 */
+#define CS53L30_ASP1_TDMTX_EN5 0x16 /* ASP1 TDM TX Enable 5 */
+#define CS53L30_ASP1_TDMTX_EN6 0x17 /* ASP1 TDM TX Enable 6 */
+#define CS53L30_ASP2_CTL 0x18 /* ASP2 Control. */
+#define CS53L30_SFT_RAMP 0x1A /* Soft Ramp Control. */
+#define CS53L30_LRCLK_CTL1 0x1B /* LRCLK Control 1. */
+#define CS53L30_LRCLK_CTL2 0x1C /* LRCLK Control 2. */
+#define CS53L30_MUTEP_CTL1 0x1F /* Mute Pin Control 1. */
+#define CS53L30_MUTEP_CTL2 0x20 /* Mute Pin Control 2. */
+#define CS53L30_INBIAS_CTL1 0x21 /* Input Bias Control 1. */
+#define CS53L30_INBIAS_CTL2 0x22 /* Input Bias Control 2. */
+#define CS53L30_DMIC1_STR_CTL 0x23 /* DMIC1 Stereo Control. */
+#define CS53L30_DMIC2_STR_CTL 0x24 /* DMIC2 Stereo Control. */
+#define CS53L30_ADCDMIC1_CTL1 0x25 /* ADC1/DMIC1 Control 1. */
+#define CS53L30_ADCDMIC1_CTL2 0x26 /* ADC1/DMIC1 Control 2. */
+#define CS53L30_ADC1_CTL3 0x27 /* ADC1 Control 3. */
+#define CS53L30_ADC1_NG_CTL 0x28 /* ADC1 Noise Gate Control. */
+#define CS53L30_ADC1A_AFE_CTL 0x29 /* ADC1A AFE Control. */
+#define CS53L30_ADC1B_AFE_CTL 0x2A /* ADC1B AFE Control. */
+#define CS53L30_ADC1A_DIG_VOL 0x2B /* ADC1A Digital Volume. */
+#define CS53L30_ADC1B_DIG_VOL 0x2C /* ADC1B Digital Volume. */
+#define CS53L30_ADCDMIC2_CTL1 0x2D /* ADC2/DMIC2 Control 1. */
+#define CS53L30_ADCDMIC2_CTL2 0x2E /* ADC2/DMIC2 Control 2. */
+#define CS53L30_ADC2_CTL3 0x2F /* ADC2 Control 3. */
+#define CS53L30_ADC2_NG_CTL 0x30 /* ADC2 Noise Gate Control. */
+#define CS53L30_ADC2A_AFE_CTL 0x31 /* ADC2A AFE Control. */
+#define CS53L30_ADC2B_AFE_CTL 0x32 /* ADC2B AFE Control. */
+#define CS53L30_ADC2A_DIG_VOL 0x33 /* ADC2A Digital Volume. */
+#define CS53L30_ADC2B_DIG_VOL 0x34 /* ADC2B Digital Volume. */
+#define CS53L30_INT_MASK 0x35 /* Interrupt Mask. */
+#define CS53L30_IS 0x36 /* Interrupt Status. */
+#define CS53L30_MAX_REGISTER 0x36
+
+/* Device ID */
+#define CS53L30_DEVID 0x53A30
+
+/* PDN_DONE Poll Maximum
+ * If soft ramp is set it will take much longer to power down
+ * the system.
+ */
+#define CS53L30_PDN_POLL_MAX 90
+
+/* Bitfield Definitions */
+
+/* CS53L30_PWRCTL */
+#define CS53L30_PDN_ULP (1 << 7)
+#define CS53L30_PDN_LP (1 << 6)
+#define CS53L30_DISCHARGE_FILT (1 << 5)
+#define CS53L30_THMS_PDN (1 << 4)
+
+/* CS53L30_MCLKCTL */
+#define CS53L30_MCLK_DIS (1 << 7)
+#define CS53L30_MCLK_INT_SCALE (1 << 6)
+#define CS53L30_DMIC_DRIVE (1 << 5)
+#define CS53L30_MCLK_DIV (3 << 2)
+#define CS53L30_MCLK_DIV_DFLT (1 << 2)
+#define CS53L30_SYNC_EN (1 << 1)
+#define CS53L30_MCK_INT_SCL_MSK 0x40
+#define CS53L30_MCK_INT_SCL_SFT 6
+
+/* CS53L30_INT_SR_CTL */
+#define CS53L30_INTRNL_FS_RATIO (1 << 4)
+#define CS53L30_INTRNL_FS_DFLT (7 << 2)
+#define CS53L30_MCLK_19MHZ_EN (1 << 0)
+#define CS53L30_INTL_FS_RAT_MSK 0x10
+#define CS53L30_INTL_FS_RAT_SFT 4
+
+/* CS53L30_MICBIAS_CTL */
+#define CS53L30_MIC4_BIAS_PDN (1 << 7)
+#define CS53L30_MIC3_BIAS_PDN (1 << 6)
+#define CS53L30_MIC2_BIAS_PDN (1 << 5)
+#define CS53L30_MIC1_BIAS_PDN (1 << 4)
+#define CS53L30_VP_MIN (1 << 2)
+#define CS53L30_MIC_BIAS_CTRL (3 << 0)
+#define CS53L30_BIAS_ALL_PDN 0xF0
+#define CS53L30_MIC_BIAS_DFLT (CS53L30_BIAS_ALL_PDN | CS53L30_VP_MIN)
+
+/* CS53L30_ASPCFG_CTL */
+#define CS53L30_ASP_MS (1 << 7)
+#define CS53L30_ASP_SCLK_INV (1 << 4)
+#define CS53L30_ASP_RATE_48K (3 << 2)
+#define CS53L30_ASP_RATE_MASK 0x0F
+#define CS53L30_ASP_CNFG_MASK 0xF0
+
+/* CS53L30_ASP1_CTL */
+#define CS53L30_ASP1_TDM_PDN (1 << 7)
+#define CS53L30_ASP1_SDOUT_PDN (1 << 6)
+#define CS53L30_ASP1_3ST (1 << 5)
+#define CS53L30_SHIFT_LEFT (1 << 4)
+#define CS53L30_ASP1_DRIVE (1 << 0)
+#define CS53L30_ASP1_3ST_VAL(x) ((x) << 5)
+
+/* CS53L30_ASP1_TDMTX_CTL */
+#define CS53L30_ASP1_CHX_TX_ST (1 << 7)
+#define CS53L30_ASP1_CHX_TX_LOC 0x3F
+#define CS53L30_ASP1_CHTX_SLT47 0x2F
+#define CS53L30_ASP_TX_DISABLED 0x00
+
+/* CS53L30_ASP2_CTL */
+#define CS53L30_ASP2_SDOUT_PDN (1 << 6)
+#define CS53L30_ASP2_DRIVE (1 << 0)
+#define CS53L30_ASP2_CTRL_DFLT 0x00
+
+/* CS53L30_SFT_RAMP */
+#define CS53L30_DIGSFT (1 << 5)
+#define CS53L30_SFT_RMP_DFLT 0x00
+
+/* CS53L30_LRCLK_CTL2 */
+#define CS53L30_LRCK_50_NPW (1 << 3)
+#define CS53L30_LRCK_TPWH (7 << 0)
+#define CS53L30_LRCK_CTLX_DFLT 0x00
+
+/* CS53L30_MUTEP_CTL */
+#define CS53L30_MUTE_PDN_ULP (1 << 7)
+#define CS53L30_MUTE_PDN_LP (1 << 6)
+#define CS53L30_MUTE_M4B_PDN (1 << 4)
+#define CS53L30_MUTE_M3B_PDN (1 << 3)
+#define CS53L30_MUTE_M2B_PDN (1 << 2)
+#define CS53L30_MUTE_M1B_PDN (1 << 1)
+#define CS53L30_MUTE_MB_ALL_PDN (1 << 0)
+#define CS53L30_MUTE_CTRL1_DFLT 0x00
+
+/* CS53L30_MUTEP_CTL2 */
+#define CS53L30_MUTE_PIN_PLRTY (1 << 7)
+#define CS53L30_MUTE_ASPTDM_PDN (1 << 6)
+#define CS53L30_MTE_ASPSDO2_PDN (1 << 5)
+#define CS53L30_MTE_ASPSDO1_PDN (1 << 4)
+#define CS53L30_MUTE_ADC2B_PDN (1 << 3)
+#define CS53L30_MUTE_ADC2A_PDN (1 << 2)
+#define CS53L30_MUTE_ADC1B_PDN (1 << 1)
+#define CS53L30_MUTE_ADC1A_PDN (1 << 0)
+
+/* CS53L30_INBIAS_CTL1 */
+#define CS53L30_IN4M_BIAS (3 << 6)
+#define CS53L30_IN4P_BIAS (3 << 4)
+#define CS53L30_IN3M_BIAS (3 << 2)
+#define CS53L30_IN3P_BIAS (3 << 0)
+
+/* CS53L30_INBIAS_CTL2 */
+#define CS53L30_IN2M_BIAS (3 << 6)
+#define CS53L30_IN2P_BIAS (3 << 4)
+#define CS53L30_IN1M_BIAS (3 << 2)
+#define CS53L30_IN1P_BIAS (3 << 0)
+#define CS53L30_INBIAS_X_DFLT 0xAA
+
+/* CS53L30_DMIC1_STR_CTL */
+#define CS53L30_DMIC1_STERO_EN (1 << 5)
+#define CS53L30_DMIC1_ST_DFLT 0xA8
+
+/* CS53L30_DMIC2_STR_CTL */
+#define CS53L30_DMIC2_STERO_EN (1 << 5)
+#define CS53L30_DMIC2_ST_DFLT 0xEC
+
+/* CS53L30_ADCDMIC1_CTL1 */
+#define CS53L30_ADC1B_PDN (1 << 7)
+#define CS53L30_ADC1A_PDN (1 << 6)
+#define CS53L30_DMIC1_PDN (1 << 2)
+#define CS53L30_DMIC1_SCLK_DIV (1 << 1)
+#define CS53L30_CH_TYPE (1 << 0)
+#define CS53L30_DMIC1_ON_AB_IN (CS53L30_CH_TYPE)
+#define CS53L30_DMIC1_ON_A_IN (CS53L30_ADC1B_PDN | CS53L30_CH_TYPE)
+#define CS53L30_DMIC1_ON_B_IN (CS53L30_ADC1A_PDN | CS53L30_CH_TYPE)
+#define CS53L30_ADC1_ON_AB_IN (CS53L30_DMIC1_PDN)
+#define CS53L30_ADC1_ON_A_IN (CS53L30_ADC1B_PDN | CS53L30_DMIC1_PDN)
+#define CS53L30_ADC1_ON_B_IN (CS53L30_ADC1A_PDN | CS53L30_DMIC1_PDN)
+#define CS53L30_D1_OFF_A1_OFF (CS53L30_ADC1A_PDN | \
+ CS53L30_ADC1B_PDN | \
+ CS53L30_DMIC1_PDN)
+#define CS53L30_A1_D1_PDN_MASK 0xFF
+
+/* CS53L30_ADCDMIC1_CTL2 */
+#define CS53L30_ADC1_NOTCH_DIS (1 << 7)
+#define CS53L30_ADC1B_INV (1 << 5)
+#define CS53L30_ADC1A_INV (1 << 4)
+#define CS53L30_ADC1B_DIG_BOOST (1 << 1)
+#define CS53L30_ADC1A_DIG_BOOST (1 << 0)
+#define CS53L30_A1_D1_CTL2_DFLT 0x00
+
+/* CS53L30_ADC1_CTL3 */
+#define CS53L30_ADC1_HPF_EN (1 << 3)
+#define CS53L30_ADC1_HPF_CF (3 << 1)
+#define CS53L30_ADC1_NG_ALL (1 << 0)
+
+/* CS53L30_ADC1_NG_CTL */
+#define CS53L30_ADC1B_NG (1 << 7)
+#define CS53L30_ADC1A_NG (1 << 6)
+#define CS53L30_ADC1_NG_BOOST (1 << 5)
+#define CS53L30_ADC1_NG_THRESH (7 << 2)
+#define CS53L30_ADC1_NG_DELAY (3 << 0)
+#define CS53L30_ADCX_ZERO_DFLT 0x00
+
+/* CS53L30_ADC1A_AFE_CTL */
+#define CS53L30_ADC1A_PREAMP (3 << 6)
+#define CS53L30_ADC1A_PGA_VOL 0x3F
+
+/* CS53L30_ADC1B_AFE_CTL */
+#define CS53L30_ADC1B_PREAMP (3 << 6)
+#define CS53L30_ADC1B_PGA_VOL 0x3F
+
+/* CS53L30_ADCXX_DIG_VOL */
+#define CS53L30_MUTE_DIG_OUT (1 << 7)
+
+/* CS53L30_ADCDMIC2_CTL1 */
+#define CS53L30_ADC2B_PDN (1 << 7)
+#define CS53L30_ADC2A_PDN (1 << 6)
+#define CS53L30_DMIC2_PDN (1 << 2)
+#define CS53L30_DMIC2_CLKDIV (1 << 1)
+#define CS53L30_DMIC2_ON_AB_IN 0x00 /* CH_TYPE must = 1 */
+#define CS53L30_DMIC2_ON_A_IN (CS53L30_ADC2B_PDN) /* CH_TYPE must = 1 */
+#define CS53L30_DMIC2_ON_B_IN (CS53L30_ADC2A_PDN) /* CH_TYPE must = 1 */
+#define CS53L30_ADC2_ON_AB_IN (CS53L30_DMIC2_PDN) /* CH_TYPE must = 0 */
+#define CS53L30_ADC2_ON_A_IN (CS53L30_ADC2B_PDN | \
+ CS53L30_DMIC2_PDN)
+#define CS53L30_ADC2_ON_B_IN (CS53L30_ADC2A_PDN | \
+ CS53L30_DMIC2_PDN)
+#define CS53L30_D2_OFF_A2_OFF (CS53L30_ADC2A_PDN | \
+ CS53L30_ADC2B_PDN | \
+ CS53L30_DMIC2_PDN)
+
+/* CS53L30_ADCDMIC2_CTL2 */
+#define CS53L30_ADC2_NOTCH_DIS (1 << 7)
+#define CS53L30_ADC2B_INV (1 << 5)
+#define CS53L30_ADC2A_INV (1 << 4)
+#define CS53L30_ADC2B_DIG_BOOST (1 << 1)
+#define CS53L30_ADC2A_DIG_BOOST (1 << 0)
+
+/* CS53L30_ADC2_CTL3 */
+#define CS53L30_ADC2_HPF_EN (1 << 3)
+#define CS53L30_ADC2_HPF_CF (3 << 1)
+#define CS53L30_ADC2_NG_ALL (1 << 0)
+
+/* CS53L30_INT */
+#define CS53L30_PDN_DONE (1 << 7)
+#define CS53L30_THMS_TRIP (1 << 6)
+#define CS53L30_SYNC_DONE (1 << 5)
+#define CS53L30_ADC2B_OVFL (1 << 4)
+#define CS53L30_ADC2A_OVFL (1 << 3)
+#define CS53L30_ADC1B_OVFL (1 << 2)
+#define CS53L30_ADC1A_OVFL (1 << 1)
+#define CS53L30_MUTE_PIN (1 << 0)
+#define CS53L30_DEVICE_INT_MASK 0xFF
+
+/* Serial Ports */
+#define CS53L30_ASP1 0
+#define CS53L30_ASP2 1
+
+#endif /* __CS53L30_H__ */
--
2.4.5
5
12
[alsa-devel] [REGRESSION] Headphones no longer working on MacPro6, 1 with 4.4
by Laura Abbott 06 Jun '16
by Laura Abbott 06 Jun '16
06 Jun '16
Hi,
We received a bug report https://bugzilla.redhat.com/show_bug.cgi?id=1316119
that the headphone jack on a MacPro6,1 stopped working on an upgrade from 4.3
to 4.4.
The bugzilla has the alsainfo, diffing shows that the Amp-Out vals are
different. I tried a revert of 9f660a1c4 (" ALSA: hda/realtek - Fix silent
headphone output on MacPro 4,1 (v2)") but that didn't help.
Any ideas before asking for a bisect? Does this hardware version need to have
the vref fixup as well?
Thanks,
Laura
2
9
[alsa-devel] mcasp: stream has more channels (2) than are enabled in mcasp (0)
by Rick Mann 03 Jun '16
by Rick Mann 03 Jun '16
03 Jun '16
I've got a Beaglebone Green with a custom cape (CODEC and amp and some other stuff).
Kernel 4.4.6-bone-rt-r6. When I load the overlay that should set up my CODEC in the device tree, I get the following in the log.
[ 367.172337] davinci-mcasp 48038000.mcasp: stream has more channels (2) than are enabled in mcasp (0)
[ 367.172374] davinci-mcasp 48038000.mcasp: ASoC: can't set 48038000.mcasp hw params: -22
You can see a little more log context, along with the DTS, here:
http://pastebin.com/UPB1xjPF
Can anyone tell me what this means? Thanks!
--
Rick Mann
rmann(a)latencyzero.com
3
14
The device has multiple control interfaces, I2C and SPI. The I2C interface
mainly controls the register settings of codec. The SPI interface is in
order to provide the high speed transmission of data. For example, high
bandwidth memory read/write of DSP. The patch adds the rt5514 SPI driver
for loading the firmware of DSP and retrieving the voice data from DSP
after the system is waked up by specific voice.
Signed-off-by: Oder Chiou <oder_chiou(a)realtek.com>
---
sound/soc/codecs/Kconfig | 3 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/rt5514-spi.c | 457 ++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/rt5514-spi.h | 38 ++++
sound/soc/codecs/rt5514.c | 134 ++++++++++++-
sound/soc/codecs/rt5514.h | 4 +
6 files changed, 637 insertions(+), 1 deletion(-)
create mode 100644 sound/soc/codecs/rt5514-spi.c
create mode 100644 sound/soc/codecs/rt5514-spi.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index c6f86ef..90749d2 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -639,6 +639,9 @@ config SND_SOC_RT298
config SND_SOC_RT5514
tristate
+config SND_SOC_RT5514_SPI
+ tristate
+
config SND_SOC_RT5616
tristate "Realtek RT5616 CODEC"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 94873ee..9ab7016 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -99,6 +99,7 @@ snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt298-objs := rt298.o
snd-soc-rt5514-objs := rt5514.o
+snd-soc-rt5514-spi-objs := rt5514-spi.o
snd-soc-rt5616-objs := rt5616.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
@@ -312,6 +313,7 @@ obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o
+obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c
new file mode 100644
index 0000000..32b5fb6
--- /dev/null
+++ b/sound/soc/codecs/rt5514-spi.c
@@ -0,0 +1,457 @@
+/*
+ * rt5514-spi.c -- RT5514 SPI driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou(a)realtek.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.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt5514-spi.h"
+
+static struct spi_device *rt5514_spi;
+
+struct rt5514_dsp {
+ struct device *dev;
+ struct delayed_work copy_work;
+ struct mutex dma_lock;
+ struct snd_pcm_substream *substream;
+ unsigned int buf_base, buf_limit, buf_rp;
+ size_t buf_size;
+ size_t dma_offset;
+ size_t dsp_offset;
+};
+
+static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = 0x20000 / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 0x20000,
+};
+
+static struct snd_soc_dai_driver rt5514_spi_dai = {
+ .name = "rt5514-dsp-cpu-dai",
+ .id = 0,
+ .capture = {
+ .stream_name = "DSP Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static void rt5514_spi_copy_work(struct work_struct *work)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ container_of(work, struct rt5514_dsp, copy_work.work);
+ struct snd_pcm_runtime *runtime = rt5514_dsp->substream->runtime;
+ size_t period_bytes, truncated_bytes = 0;
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ if (!rt5514_dsp->substream) {
+ dev_err(rt5514_dsp->dev, "No pcm substream\n");
+ goto done;
+ }
+
+ period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream);
+
+ if (rt5514_dsp->buf_size - rt5514_dsp->dsp_offset < period_bytes)
+ period_bytes = rt5514_dsp->buf_size - rt5514_dsp->dsp_offset;
+
+ if (rt5514_dsp->buf_rp + period_bytes <= rt5514_dsp->buf_limit) {
+ rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+ runtime->dma_area + rt5514_dsp->dma_offset,
+ period_bytes);
+
+ if (rt5514_dsp->buf_rp + period_bytes == rt5514_dsp->buf_limit)
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base;
+ else
+ rt5514_dsp->buf_rp += period_bytes;
+ } else {
+ truncated_bytes = rt5514_dsp->buf_limit - rt5514_dsp->buf_rp;
+ rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+ runtime->dma_area + rt5514_dsp->dma_offset,
+ truncated_bytes);
+
+ rt5514_spi_burst_read(rt5514_dsp->buf_base,
+ runtime->dma_area + rt5514_dsp->dma_offset +
+ truncated_bytes, period_bytes - truncated_bytes);
+
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base +
+ period_bytes - truncated_bytes;
+ }
+
+ rt5514_dsp->dma_offset += period_bytes;
+ if (rt5514_dsp->dma_offset >= runtime->dma_bytes)
+ rt5514_dsp->dma_offset = 0;
+
+ rt5514_dsp->dsp_offset += period_bytes;
+
+ snd_pcm_period_elapsed(rt5514_dsp->substream);
+
+ if (rt5514_dsp->dsp_offset < rt5514_dsp->buf_size)
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+done:
+ mutex_unlock(&rt5514_dsp->dma_lock);
+}
+
+/* PCM for streaming audio from the DSP buffer */
+static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware);
+
+ return 0;
+}
+
+static int rt5514_spi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ int ret;
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ rt5514_dsp->substream = substream;
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ return ret;
+}
+
+static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ rt5514_dsp->substream = NULL;
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int rt5514_spi_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ u8 buf[8];
+
+ rt5514_dsp->dma_offset = 0;
+ rt5514_dsp->dsp_offset = 0;
+
+ /**
+ * The address area x1800XXXX is the register address, and it cannot
+ * support spi burst read perfectly. So we use the spi burst read
+ * individually to make sure the data correctly.
+ */
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_RP, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_SIZE, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_size = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ return 0;
+}
+
+static int rt5514_spi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit &&
+ rt5514_dsp->buf_rp && rt5514_dsp->buf_size)
+ schedule_delayed_work(&rt5514_dsp->copy_work, 0);
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ return bytes_to_frames(runtime, rt5514_dsp->dma_offset);
+}
+
+static struct snd_pcm_ops rt5514_spi_pcm_ops = {
+ .open = rt5514_spi_pcm_open,
+ .hw_params = rt5514_spi_hw_params,
+ .hw_free = rt5514_spi_hw_free,
+ .trigger = rt5514_spi_trigger,
+ .prepare = rt5514_spi_prepare,
+ .pointer = rt5514_spi_pcm_pointer,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
+{
+ struct rt5514_dsp *rt5514_dsp;
+
+ rt5514_dsp = devm_kzalloc(platform->dev, sizeof(*rt5514_dsp),
+ GFP_KERNEL);
+
+ rt5514_dsp->dev = &rt5514_spi->dev;
+ mutex_init(&rt5514_dsp->dma_lock);
+ INIT_DELAYED_WORK(&rt5514_dsp->copy_work, rt5514_spi_copy_work);
+ snd_soc_platform_set_drvdata(platform, rt5514_dsp);
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver rt5514_spi_platform = {
+ .probe = rt5514_spi_pcm_probe,
+ .ops = &rt5514_spi_pcm_ops,
+};
+
+static const struct snd_soc_component_driver rt5514_spi_dai_component = {
+ .name = "rt5514-spi-dai",
+};
+
+/**
+ * rt5514_spi_burst_read - Read data from SPI by rt5514 address.
+ * @addr: Start address.
+ * @rxbuf: Data Buffer for reading.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len)
+{
+ u8 spi_cmd = RT5514_SPI_CMD_BURST_READ;
+ int status;
+ u8 write_buf[8];
+ unsigned int i, end, offset = 0;
+
+ struct spi_message message;
+ struct spi_transfer x[3];
+
+ while (offset < len) {
+ if (offset + RT5514_SPI_BUF_LEN <= len)
+ end = RT5514_SPI_BUF_LEN;
+ else
+ end = len % RT5514_SPI_BUF_LEN;
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ spi_message_init(&message);
+ memset(x, 0, sizeof(x));
+
+ x[0].len = 5;
+ x[0].tx_buf = write_buf;
+ spi_message_add_tail(&x[0], &message);
+
+ x[1].len = 4;
+ x[1].tx_buf = write_buf;
+ spi_message_add_tail(&x[1], &message);
+
+ x[2].len = end;
+ x[2].rx_buf = rxbuf + offset;
+ spi_message_add_tail(&x[2], &message);
+
+ status = spi_sync(rt5514_spi, &message);
+
+ if (status)
+ return false;
+
+ offset += RT5514_SPI_BUF_LEN;
+ }
+
+ for (i = 0; i < len; i += 8) {
+ write_buf[0] = rxbuf[i + 0];
+ write_buf[1] = rxbuf[i + 1];
+ write_buf[2] = rxbuf[i + 2];
+ write_buf[3] = rxbuf[i + 3];
+ write_buf[4] = rxbuf[i + 4];
+ write_buf[5] = rxbuf[i + 5];
+ write_buf[6] = rxbuf[i + 6];
+ write_buf[7] = rxbuf[i + 7];
+
+ rxbuf[i + 0] = write_buf[7];
+ rxbuf[i + 1] = write_buf[6];
+ rxbuf[i + 2] = write_buf[5];
+ rxbuf[i + 3] = write_buf[4];
+ rxbuf[i + 4] = write_buf[3];
+ rxbuf[i + 5] = write_buf[2];
+ rxbuf[i + 6] = write_buf[1];
+ rxbuf[i + 7] = write_buf[0];
+ }
+
+ return true;
+}
+
+/**
+ * rt5514_spi_burst_write - Write data to SPI by rt5514 address.
+ * @addr: Start address.
+ * @txbuf: Data Buffer for writng.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len)
+{
+ u8 spi_cmd = RT5514_SPI_CMD_BURST_WRITE;
+ u8 *write_buf;
+ unsigned int i, end, offset = 0;
+
+ write_buf = kmalloc(RT5514_SPI_BUF_LEN + 6, GFP_KERNEL);
+
+ if (write_buf == NULL)
+ return -ENOMEM;
+
+ while (offset < len) {
+ if (offset + RT5514_SPI_BUF_LEN <= len)
+ end = RT5514_SPI_BUF_LEN;
+ else
+ end = len % RT5514_SPI_BUF_LEN;
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ for (i = 0; i < end; i += 8) {
+ write_buf[i + 12] = txbuf[offset + i + 0];
+ write_buf[i + 11] = txbuf[offset + i + 1];
+ write_buf[i + 10] = txbuf[offset + i + 2];
+ write_buf[i + 9] = txbuf[offset + i + 3];
+ write_buf[i + 8] = txbuf[offset + i + 4];
+ write_buf[i + 7] = txbuf[offset + i + 5];
+ write_buf[i + 6] = txbuf[offset + i + 6];
+ write_buf[i + 5] = txbuf[offset + i + 7];
+ }
+
+ write_buf[end + 5] = spi_cmd;
+
+ spi_write(rt5514_spi, write_buf, end + 6);
+
+ offset += RT5514_SPI_BUF_LEN;
+ }
+
+ kfree(write_buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_burst_write);
+
+static int rt5514_spi_probe(struct spi_device *spi)
+{
+ int ret;
+
+ rt5514_spi = spi;
+
+ ret = snd_soc_register_platform(&spi->dev, &rt5514_spi_platform);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register platform.\n");
+ goto err_plat;
+ }
+
+ ret = snd_soc_register_component(&spi->dev, &rt5514_spi_dai_component,
+ &rt5514_spi_dai, 1);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register component.\n");
+ goto err_comp;
+ }
+
+ return 0;
+err_comp:
+ snd_soc_unregister_platform(&spi->dev);
+err_plat:
+
+ return 0;
+}
+
+static int rt5514_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_component(&spi->dev);
+ snd_soc_unregister_platform(&spi->dev);
+
+ return 0;
+}
+
+static const struct of_device_id rt5514_of_match[] = {
+ { .compatible = "realtek,rt5514", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5514_of_match);
+
+static struct spi_driver rt5514_spi_driver = {
+ .driver = {
+ .name = "rt5514",
+ .of_match_table = of_match_ptr(rt5514_of_match),
+ },
+ .probe = rt5514_spi_probe,
+ .remove = rt5514_spi_remove,
+};
+module_spi_driver(rt5514_spi_driver);
+
+MODULE_DESCRIPTION("RT5514 SPI driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou(a)realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5514-spi.h b/sound/soc/codecs/rt5514-spi.h
new file mode 100644
index 0000000..f69b1cd
--- /dev/null
+++ b/sound/soc/codecs/rt5514-spi.h
@@ -0,0 +1,38 @@
+/*
+ * rt5514-spi.h -- RT5514 driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou(a)realtek.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.
+ */
+
+#ifndef __RT5514_SPI_H__
+#define __RT5514_SPI_H__
+
+/**
+ * RT5514_SPI_BUF_LEN is the buffer size of SPI master controller.
+*/
+#define RT5514_SPI_BUF_LEN 240
+
+#define RT5514_BUFFER_VOICE_BASE 0x18001034
+#define RT5514_BUFFER_VOICE_LIMIT 0x18001038
+#define RT5514_BUFFER_VOICE_RP 0x1800103c
+#define RT5514_BUFFER_VOICE_SIZE 0x18001040
+
+/* SPI Command */
+enum {
+ RT5514_SPI_CMD_16_READ = 0,
+ RT5514_SPI_CMD_16_WRITE,
+ RT5514_SPI_CMD_32_READ,
+ RT5514_SPI_CMD_32_WRITE,
+ RT5514_SPI_CMD_BURST_READ,
+ RT5514_SPI_CMD_BURST_WRITE,
+};
+
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len);
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len);
+
+#endif /* __RT5514_SPI_H__ */
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index 879bf60..475b932 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -30,6 +30,9 @@
#include "rl6231.h"
#include "rt5514.h"
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+#include "rt5514-spi.h"
+#endif
static const struct reg_sequence rt5514_i2c_patch[] = {
{0x1800101c, 0x00000000},
@@ -110,6 +113,35 @@ static const struct reg_default rt5514_reg[] = {
{RT5514_VENDOR_ID2, 0x10ec5514},
};
+static void rt5514_enable_dsp_prepare(struct rt5514_priv *rt5514)
+{
+ /* Reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002000, 0x000010ec);
+ /* LDO_I_limit */
+ regmap_write(rt5514->i2c_regmap, 0x18002200, 0x00028604);
+ /* I2C bypass enable */
+ regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000001);
+ /* mini-core reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x0005514b);
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055149);
+ /* I2C bypass disable */
+ regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000000);
+ /* PIN config */
+ regmap_write(rt5514->i2c_regmap, 0x18002070, 0x00000040);
+ /* PLL3(QN)=RCOSC*(10+2) */
+ regmap_write(rt5514->i2c_regmap, 0x18002240, 0x0000000a);
+ /* PLL3 source=RCOSC, fsi=rt_clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002100, 0x0000000b);
+ /* Power on RCOSC, pll3 */
+ regmap_write(rt5514->i2c_regmap, 0x18002004, 0x00808b81);
+ /* DSP clk source = pll3, ENABLE DSP clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002f08, 0x00000005);
+ /* Enable DSP clk auto switch */
+ regmap_write(rt5514->i2c_regmap, 0x18001114, 0x00000001);
+ /* Reduce DSP power */
+ regmap_write(rt5514->i2c_regmap, 0x18001118, 0x00000001);
+}
+
static bool rt5514_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -248,6 +280,74 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv,
static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static int rt5514_dsp_voice_wake_up_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt5514->dsp_enabled;
+
+ return 0;
+}
+
+static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_codec *codec = rt5514->codec;
+ const struct firmware *fw = NULL;
+
+ if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled)
+ return 0;
+
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ rt5514->dsp_enabled = ucontrol->value.integer.value[0];
+
+ if (rt5514->dsp_enabled) {
+ rt5514_enable_dsp_prepare(rt5514);
+
+ request_firmware(&fw, RT5514_FIRMWARE1, codec->dev);
+ if (fw) {
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ff60000, fw->data,
+ ((fw->size/8)+1)*8);
+#else
+ dev_err(codec->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ request_firmware(&fw, RT5514_FIRMWARE2, codec->dev);
+ if (fw) {
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ffc0000, fw->data,
+ ((fw->size/8)+1)*8);
+#else
+ dev_err(codec->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ /* DSP run */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00,
+ 0x00055148);
+ } else {
+ regcache_mark_dirty(rt5514->i2c_regmap);
+ regcache_mark_dirty(rt5514->regmap);
+ regcache_sync(rt5514->i2c_regmap);
+ regcache_sync(rt5514->regmap);
+ }
+ }
+
+ return 0;
+}
+
static const struct snd_kcontrol_new rt5514_snd_controls[] = {
SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST,
RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv),
@@ -257,6 +357,8 @@ static const struct snd_kcontrol_new rt5514_snd_controls[] = {
SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1,
RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
adc_vol_tlv),
+ SOC_SINGLE_EXT("DSP Voice Wake Up", SND_SOC_NOPM, 0, 1, 0,
+ rt5514_dsp_voice_wake_up_get, rt5514_dsp_voice_wake_up_put),
};
/* ADC Mixer*/
@@ -365,6 +467,35 @@ static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
return 0;
}
+static int rt5514_pre_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /**
+ * If the DSP is enabled in start of recording, the DSP
+ * should be disabled, and sync back to normal recording
+ * settings to make sure recording properly.
+ */
+ if (rt5514->dsp_enabled) {
+ rt5514->dsp_enabled = 0;
+ regcache_mark_dirty(rt5514->i2c_regmap);
+ regcache_mark_dirty(rt5514->regmap);
+ regcache_sync(rt5514->i2c_regmap);
+ regcache_sync(rt5514->regmap);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
/* Input Lines */
SND_SOC_DAPM_INPUT("DMIC1L"),
@@ -472,6 +603,8 @@ static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
/* Audio Interface */
SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_PRE("DAPM Pre", rt5514_pre_event),
};
static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
@@ -871,7 +1004,6 @@ static const struct regmap_config rt5514_i2c_regmap = {
.reg_bits = 32,
.val_bits = 32,
- .max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2,
.readable_reg = rt5514_i2c_readable_register,
.cache_type = REGCACHE_NONE,
diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h
index 6ad8a61..6e89e7d 100644
--- a/sound/soc/codecs/rt5514.h
+++ b/sound/soc/codecs/rt5514.h
@@ -225,6 +225,9 @@
#define RT5514_PLL_INP_MAX 40000000
#define RT5514_PLL_INP_MIN 256000
+#define RT5514_FIRMWARE1 "rt5514_dsp_fw1.bin"
+#define RT5514_FIRMWARE2 "rt5514_dsp_fw2.bin"
+
/* System Clock Source */
enum {
RT5514_SCLK_S_MCLK,
@@ -247,6 +250,7 @@ struct rt5514_priv {
int pll_src;
int pll_in;
int pll_out;
+ int dsp_enabled;
};
#endif /* __RT5514_H__ */
--
1.8.1.1.439.g50a6b54
2
3
[alsa-devel] DPCM: skip DAPM_STREAM_STOP event to BE, if still used by other FE
by Kai Chieh Chuang 03 Jun '16
by Kai Chieh Chuang 03 Jun '16
03 Jun '16
Hi,
Let me illustrate the system first.
FE1 -----+----> BE
|
FE2 -----+
When there is multi-FE stream to a single BE, the BE will receive
SND_SOC_DAPM_STREAM_STOP if one of the FE shutdown.
However, the BE is stilled used by another, this will let BE cpu_dai and
codec_dai think they are inactive. The later power check will go wrong.
I have check the latest linux version, and didn't find any related
change.
The following is my change to address this, and will re-send a formal
patch if it's ok.
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 70e8088..9c2d159 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -163,6 +163,13 @@ int dpcm_dapm_stream_event(struct
snd_soc_pcm_runtime *fe, int dir,
dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n",
be->dai_link->name, event, dir);
+ /* don't sent stop event if this BE is used by other FE */
+ if (event == SND_SOC_DAPM_STREAM_STOP &&
+ be->dpcm[dir].users >= 1) {
+ pr_warn("kc, %s(), be->dai_link->name %s skip stop event\n",
__func__, be->dai_link->name);
+ continue;
+ }
+
snd_soc_dapm_stream_event(be, dir, event);
}
Sincerely,
Kai Chieh
2
1
SND_SOC_HDMI_CODEC can be enabled without HDMI support, leading
to a link error:
In function `hdmi_codec_hw_params':
sound/soc/codecs/hdmi-codec.c:188: undefined reference to `hdmi_audio_infoframe_init'
sound/built-in.o:(.debug_addr+0x1a5c0): undefined reference to `hdmi_audio_infoframe_init'
This changes the Kconfig file to select HDMI, as the other codec using
hdmi_audio_infoframe_init already does.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
Fixes: 09184118a8ab ("ASoC: hdmi-codec: Add hdmi-codec for external HDMI-encoders")
---
sound/soc/codecs/Kconfig | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 4d82a58ff6b0..f3fb98f0a995 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -483,9 +483,10 @@ config SND_SOC_DMIC
tristate
config SND_SOC_HDMI_CODEC
- tristate
- select SND_PCM_ELD
- select SND_PCM_IEC958
+ tristate
+ select SND_PCM_ELD
+ select SND_PCM_IEC958
+ select HDMI
config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC"
--
2.7.0
3
2