mirror of https://github.com/hak5/openwrt-owl.git
1096 lines
32 KiB
Diff
1096 lines
32 KiB
Diff
|
From e84e7c3905da79d7fc5f2f60453673a577dddbd3 Mon Sep 17 00:00:00 2001
|
||
|
From: Gordon Garrity <gordon@iqaudio.com>
|
||
|
Date: Sat, 8 Mar 2014 16:56:57 +0000
|
||
|
Subject: [PATCH 185/196] Add IQaudIO Sound Card support for Raspberry Pi
|
||
|
|
||
|
---
|
||
|
arch/arm/configs/bcmrpi_defconfig | 1 +
|
||
|
arch/arm/mach-bcm2708/bcm2708.c | 22 ++
|
||
|
sound/soc/bcm/Kconfig | 7 +
|
||
|
sound/soc/bcm/Makefile | 3 +
|
||
|
sound/soc/bcm/iqaudio-dac.c | 111 +++++++
|
||
|
sound/soc/codecs/Kconfig | 4 +
|
||
|
sound/soc/codecs/Makefile | 2 +
|
||
|
sound/soc/codecs/pcm512x.c | 677 ++++++++++++++++++++++++++++++++++++++
|
||
|
sound/soc/codecs/pcm512x.h | 142 ++++++++
|
||
|
9 files changed, 969 insertions(+)
|
||
|
create mode 100644 sound/soc/bcm/iqaudio-dac.c
|
||
|
create mode 100644 sound/soc/codecs/pcm512x.c
|
||
|
create mode 100644 sound/soc/codecs/pcm512x.h
|
||
|
|
||
|
diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig
|
||
|
index ca40634..90fe241 100644
|
||
|
--- a/arch/arm/configs/bcmrpi_defconfig
|
||
|
+++ b/arch/arm/configs/bcmrpi_defconfig
|
||
|
@@ -744,6 +744,7 @@ CONFIG_SND_BCM2708_SOC_I2S=m
|
||
|
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
|
||
|
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m
|
||
|
CONFIG_SND_BCM2708_SOC_RPI_DAC=m
|
||
|
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
|
||
|
CONFIG_SOUND_PRIME=m
|
||
|
CONFIG_HIDRAW=y
|
||
|
CONFIG_HID_A4TECH=m
|
||
|
diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c
|
||
|
index caa9f48..68d78f1 100644
|
||
|
--- a/arch/arm/mach-bcm2708/bcm2708.c
|
||
|
+++ b/arch/arm/mach-bcm2708/bcm2708.c
|
||
|
@@ -685,6 +685,22 @@ static struct platform_device snd_pcm1794a_codec_device = {
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
+
|
||
|
+#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE)
|
||
|
+static struct platform_device snd_rpi_iqaudio_dac_device = {
|
||
|
+ .name = "snd-rpi-iqaudio-dac",
|
||
|
+ .id = 0,
|
||
|
+ .num_resources = 0,
|
||
|
+};
|
||
|
+
|
||
|
+// Use the actual device name rather than generic driver name
|
||
|
+static struct i2c_board_info __initdata snd_pcm512x_i2c_devices[] = {
|
||
|
+ {
|
||
|
+ I2C_BOARD_INFO("pcm5122", 0x4c)
|
||
|
+ },
|
||
|
+};
|
||
|
+#endif
|
||
|
+
|
||
|
int __init bcm_register_device(struct platform_device *pdev)
|
||
|
{
|
||
|
int ret;
|
||
|
@@ -831,6 +847,12 @@ void __init bcm2708_init(void)
|
||
|
bcm_register_device(&snd_pcm1794a_codec_device);
|
||
|
#endif
|
||
|
|
||
|
+#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE)
|
||
|
+ bcm_register_device(&snd_rpi_iqaudio_dac_device);
|
||
|
+ i2c_register_board_info(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices));
|
||
|
+#endif
|
||
|
+
|
||
|
+
|
||
|
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
|
||
|
struct amba_device *d = amba_devs[i];
|
||
|
amba_device_register(d, &iomem_resource);
|
||
|
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
|
||
|
index 7e2afc2..0fe398f 100644
|
||
|
--- a/sound/soc/bcm/Kconfig
|
||
|
+++ b/sound/soc/bcm/Kconfig
|
||
|
@@ -29,3 +29,10 @@ config SND_BCM2708_SOC_RPI_DAC
|
||
|
select SND_SOC_PCM1794A
|
||
|
help
|
||
|
Say Y or M if you want to add support for RPi-DAC.
|
||
|
+
|
||
|
+config SND_BCM2708_SOC_IQAUDIO_DAC
|
||
|
+ tristate "Support for IQaudIO-DAC"
|
||
|
+ depends on SND_BCM2708_SOC_I2S
|
||
|
+ select SND_SOC_PCM512x
|
||
|
+ help
|
||
|
+ Say Y or M if you want to add support for IQaudIO-DAC.
|
||
|
diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
|
||
|
index 54e37eb..918f460 100644
|
||
|
--- a/sound/soc/bcm/Makefile
|
||
|
+++ b/sound/soc/bcm/Makefile
|
||
|
@@ -7,7 +7,10 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o
|
||
|
snd-soc-hifiberry-dac-objs := hifiberry_dac.o
|
||
|
snd-soc-hifiberry-digi-objs := hifiberry_digi.o
|
||
|
snd-soc-rpi-dac-objs := rpi-dac.o
|
||
|
+snd-soc-iqaudio-dac-objs := iqaudio-dac.o
|
||
|
|
||
|
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o
|
||
|
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o
|
||
|
obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o
|
||
|
+obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o
|
||
|
+
|
||
|
diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c
|
||
|
new file mode 100644
|
||
|
index 0000000..6be0c0e
|
||
|
--- /dev/null
|
||
|
+++ b/sound/soc/bcm/iqaudio-dac.c
|
||
|
@@ -0,0 +1,111 @@
|
||
|
+/*
|
||
|
+ * ASoC Driver for IQaudIO DAC
|
||
|
+ *
|
||
|
+ * Author: Florian Meier <florian.meier@koalo.de>
|
||
|
+ * Copyright 2013
|
||
|
+ *
|
||
|
+ * 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/platform_device.h>
|
||
|
+
|
||
|
+#include <sound/core.h>
|
||
|
+#include <sound/pcm.h>
|
||
|
+#include <sound/pcm_params.h>
|
||
|
+#include <sound/soc.h>
|
||
|
+#include <sound/jack.h>
|
||
|
+
|
||
|
+static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd)
|
||
|
+{
|
||
|
+// NOT USED struct snd_soc_codec *codec = rtd->codec;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int snd_rpi_iqaudio_dac_hw_params(struct snd_pcm_substream *substream,
|
||
|
+ struct snd_pcm_hw_params *params)
|
||
|
+{
|
||
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||
|
+// NOT USED struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||
|
+// NOT USED struct snd_soc_codec *codec = rtd->codec;
|
||
|
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||
|
+
|
||
|
+ unsigned int sample_bits =
|
||
|
+ snd_pcm_format_physical_width(params_format(params));
|
||
|
+
|
||
|
+ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
|
||
|
+}
|
||
|
+
|
||
|
+/* machine stream operations */
|
||
|
+static struct snd_soc_ops snd_rpi_iqaudio_dac_ops = {
|
||
|
+ .hw_params = snd_rpi_iqaudio_dac_hw_params,
|
||
|
+};
|
||
|
+
|
||
|
+static struct snd_soc_dai_link snd_rpi_iqaudio_dac_dai[] = {
|
||
|
+{
|
||
|
+ .name = "IQaudIO DAC",
|
||
|
+ .stream_name = "IQaudIO DAC HiFi",
|
||
|
+ .cpu_dai_name = "bcm2708-i2s.0",
|
||
|
+ .codec_dai_name = "pcm512x-hifi",
|
||
|
+ .platform_name = "bcm2708-i2s.0",
|
||
|
+ .codec_name = "pcm512x.1-004c",
|
||
|
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||
|
+ SND_SOC_DAIFMT_CBS_CFS,
|
||
|
+ .ops = &snd_rpi_iqaudio_dac_ops,
|
||
|
+ .init = snd_rpi_iqaudio_dac_init,
|
||
|
+},
|
||
|
+};
|
||
|
+
|
||
|
+/* audio machine driver */
|
||
|
+static struct snd_soc_card snd_rpi_iqaudio_dac = {
|
||
|
+ .name = "snd_rpi_iqaudio_dac",
|
||
|
+ .dai_link = snd_rpi_iqaudio_dac_dai,
|
||
|
+ .num_links = ARRAY_SIZE(snd_rpi_iqaudio_dac_dai),
|
||
|
+};
|
||
|
+
|
||
|
+static int snd_rpi_iqaudio_dac_probe(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ int ret = 0;
|
||
|
+
|
||
|
+ snd_rpi_iqaudio_dac.dev = &pdev->dev;
|
||
|
+ ret = snd_soc_register_card(&snd_rpi_iqaudio_dac);
|
||
|
+ if (ret)
|
||
|
+ dev_err(&pdev->dev,
|
||
|
+ "snd_soc_register_card() failed: %d\n", ret);
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static int snd_rpi_iqaudio_dac_remove(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ return snd_soc_unregister_card(&snd_rpi_iqaudio_dac);
|
||
|
+}
|
||
|
+
|
||
|
+static const struct of_device_id iqaudio_of_match[] = {
|
||
|
+ { .compatible = "iqaudio,iqaudio-dac", },
|
||
|
+ {},
|
||
|
+};
|
||
|
+
|
||
|
+static struct platform_driver snd_rpi_iqaudio_dac_driver = {
|
||
|
+ .driver = {
|
||
|
+ .name = "snd-rpi-iqaudio-dac",
|
||
|
+ .owner = THIS_MODULE,
|
||
|
+ .of_match_table = iqaudio_of_match,
|
||
|
+ },
|
||
|
+ .probe = snd_rpi_iqaudio_dac_probe,
|
||
|
+ .remove = snd_rpi_iqaudio_dac_remove,
|
||
|
+};
|
||
|
+
|
||
|
+module_platform_driver(snd_rpi_iqaudio_dac_driver);
|
||
|
+
|
||
|
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
|
||
|
+MODULE_DESCRIPTION("ASoC Driver for IQAudio DAC");
|
||
|
+MODULE_LICENSE("GPL v2");
|
||
|
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
|
||
|
index ef9ac1e..d0e7539 100644
|
||
|
--- a/sound/soc/codecs/Kconfig
|
||
|
+++ b/sound/soc/codecs/Kconfig
|
||
|
@@ -57,6 +57,7 @@ config SND_SOC_ALL_CODECS
|
||
|
select SND_SOC_PCM3008
|
||
|
select SND_SOC_PCM1794A
|
||
|
select SND_SOC_PCM5102A
|
||
|
+ select SND_SOC_PCM512x if SND_SOC_I2C_AND_SPI
|
||
|
select SND_SOC_RT5631 if I2C
|
||
|
select SND_SOC_SGTL5000 if I2C
|
||
|
select SND_SOC_SI476X if MFD_SI476X_CORE
|
||
|
@@ -301,6 +302,9 @@ config SND_SOC_PCM1794A
|
||
|
config SND_SOC_PCM5102A
|
||
|
tristate
|
||
|
|
||
|
+config SND_SOC_PCM512x
|
||
|
+ tristate
|
||
|
+
|
||
|
config SND_SOC_RT5631
|
||
|
tristate
|
||
|
|
||
|
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
|
||
|
index e98b865..e630e06 100644
|
||
|
--- a/sound/soc/codecs/Makefile
|
||
|
+++ b/sound/soc/codecs/Makefile
|
||
|
@@ -45,6 +45,7 @@ snd-soc-omap-hdmi-codec-objs := omap-hdmi.o
|
||
|
snd-soc-pcm3008-objs := pcm3008.o
|
||
|
snd-soc-pcm1794a-objs := pcm1794a.o
|
||
|
snd-soc-pcm5102a-objs := pcm5102a.o
|
||
|
+snd-soc-pcm512x-objs := pcm512x.o
|
||
|
snd-soc-rt5631-objs := rt5631.o
|
||
|
snd-soc-sgtl5000-objs := sgtl5000.o
|
||
|
snd-soc-alc5623-objs := alc5623.o
|
||
|
@@ -174,6 +175,7 @@ obj-$(CONFIG_SND_SOC_OMAP_HDMI_CODEC) += snd-soc-omap-hdmi-codec.o
|
||
|
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
|
||
|
obj-$(CONFIG_SND_SOC_PCM1794A) += snd-soc-pcm1794a.o
|
||
|
obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o
|
||
|
+obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
|
||
|
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
|
||
|
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
|
||
|
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
|
||
|
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
|
||
|
new file mode 100644
|
||
|
index 0000000..ed36a4a
|
||
|
--- /dev/null
|
||
|
+++ b/sound/soc/codecs/pcm512x.c
|
||
|
@@ -0,0 +1,677 @@
|
||
|
+/*
|
||
|
+ * Driver for the PCM512x CODECs
|
||
|
+ *
|
||
|
+ * Author: Mark Brown <broonie@linaro.org>
|
||
|
+ * Copyright 2014 Linaro Ltd
|
||
|
+ *
|
||
|
+ * 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/init.h>
|
||
|
+#include <linux/module.h>
|
||
|
+#include <linux/clk.h>
|
||
|
+#include <linux/i2c.h>
|
||
|
+#include <linux/pm_runtime.h>
|
||
|
+#include <linux/regmap.h>
|
||
|
+#include <linux/regulator/consumer.h>
|
||
|
+#include <linux/spi/spi.h>
|
||
|
+#include <sound/soc.h>
|
||
|
+#include <sound/soc-dapm.h>
|
||
|
+#include <sound/tlv.h>
|
||
|
+
|
||
|
+#include "pcm512x.h"
|
||
|
+
|
||
|
+#define PCM512x_NUM_SUPPLIES 3
|
||
|
+static const char *pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = {
|
||
|
+ "AVDD",
|
||
|
+ "DVDD",
|
||
|
+ "CPVDD",
|
||
|
+};
|
||
|
+
|
||
|
+struct pcm512x_priv {
|
||
|
+ struct regmap *regmap;
|
||
|
+ struct clk *sclk;
|
||
|
+ struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES];
|
||
|
+ struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES];
|
||
|
+};
|
||
|
+
|
||
|
+/*
|
||
|
+ * We can't use the same notifier block for more than one supply and
|
||
|
+ * there's no way I can see to get from a callback to the caller
|
||
|
+ * except container_of().
|
||
|
+ */
|
||
|
+#define PCM512x_REGULATOR_EVENT(n) \
|
||
|
+static int pcm512x_regulator_event_##n(struct notifier_block *nb, \
|
||
|
+ unsigned long event, void *data) \
|
||
|
+{ \
|
||
|
+ struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \
|
||
|
+ supply_nb[n]); \
|
||
|
+ if (event & REGULATOR_EVENT_DISABLE) { \
|
||
|
+ regcache_mark_dirty(pcm512x->regmap); \
|
||
|
+ regcache_cache_only(pcm512x->regmap, true); \
|
||
|
+ } \
|
||
|
+ return 0; \
|
||
|
+}
|
||
|
+
|
||
|
+PCM512x_REGULATOR_EVENT(0)
|
||
|
+PCM512x_REGULATOR_EVENT(1)
|
||
|
+PCM512x_REGULATOR_EVENT(2)
|
||
|
+
|
||
|
+static const struct reg_default pcm512x_reg_defaults[] = {
|
||
|
+ { PCM512x_RESET, 0x00 },
|
||
|
+ { PCM512x_POWER, 0x00 },
|
||
|
+ { PCM512x_MUTE, 0x00 },
|
||
|
+ { PCM512x_DSP, 0x00 },
|
||
|
+ { PCM512x_PLL_REF, 0x00 },
|
||
|
+ { PCM512x_DAC_ROUTING, 0x11 },
|
||
|
+ { PCM512x_DSP_PROGRAM, 0x01 },
|
||
|
+ { PCM512x_CLKDET, 0x00 },
|
||
|
+ { PCM512x_AUTO_MUTE, 0x00 },
|
||
|
+ { PCM512x_ERROR_DETECT, 0x00 },
|
||
|
+ { PCM512x_DIGITAL_VOLUME_1, 0x00 },
|
||
|
+ { PCM512x_DIGITAL_VOLUME_2, 0x30 },
|
||
|
+ { PCM512x_DIGITAL_VOLUME_3, 0x30 },
|
||
|
+ { PCM512x_DIGITAL_MUTE_1, 0x22 },
|
||
|
+ { PCM512x_DIGITAL_MUTE_2, 0x00 },
|
||
|
+ { PCM512x_DIGITAL_MUTE_3, 0x07 },
|
||
|
+};
|
||
|
+
|
||
|
+static bool pcm512x_readable(struct device *dev, unsigned int reg)
|
||
|
+{
|
||
|
+ switch (reg) {
|
||
|
+ case PCM512x_RESET:
|
||
|
+ case PCM512x_POWER:
|
||
|
+ case PCM512x_MUTE:
|
||
|
+ case PCM512x_PLL_EN:
|
||
|
+ case PCM512x_SPI_MISO_FUNCTION:
|
||
|
+ case PCM512x_DSP:
|
||
|
+ case PCM512x_GPIO_EN:
|
||
|
+ case PCM512x_BCLK_LRCLK_CFG:
|
||
|
+ case PCM512x_DSP_GPIO_INPUT:
|
||
|
+ case PCM512x_MASTER_MODE:
|
||
|
+ case PCM512x_PLL_REF:
|
||
|
+ case PCM512x_PLL_COEFF_0:
|
||
|
+ case PCM512x_PLL_COEFF_1:
|
||
|
+ case PCM512x_PLL_COEFF_2:
|
||
|
+ case PCM512x_PLL_COEFF_3:
|
||
|
+ case PCM512x_PLL_COEFF_4:
|
||
|
+ case PCM512x_DSP_CLKDIV:
|
||
|
+ case PCM512x_DAC_CLKDIV:
|
||
|
+ case PCM512x_NCP_CLKDIV:
|
||
|
+ case PCM512x_OSR_CLKDIV:
|
||
|
+ case PCM512x_MASTER_CLKDIV_1:
|
||
|
+ case PCM512x_MASTER_CLKDIV_2:
|
||
|
+ case PCM512x_FS_SPEED_MODE:
|
||
|
+ case PCM512x_IDAC_1:
|
||
|
+ case PCM512x_IDAC_2:
|
||
|
+ case PCM512x_ERROR_DETECT:
|
||
|
+ case PCM512x_I2S_1:
|
||
|
+ case PCM512x_I2S_2:
|
||
|
+ case PCM512x_DAC_ROUTING:
|
||
|
+ case PCM512x_DSP_PROGRAM:
|
||
|
+ case PCM512x_CLKDET:
|
||
|
+ case PCM512x_AUTO_MUTE:
|
||
|
+ case PCM512x_DIGITAL_VOLUME_1:
|
||
|
+ case PCM512x_DIGITAL_VOLUME_2:
|
||
|
+ case PCM512x_DIGITAL_VOLUME_3:
|
||
|
+ case PCM512x_DIGITAL_MUTE_1:
|
||
|
+ case PCM512x_DIGITAL_MUTE_2:
|
||
|
+ case PCM512x_DIGITAL_MUTE_3:
|
||
|
+ case PCM512x_GPIO_OUTPUT_1:
|
||
|
+ case PCM512x_GPIO_OUTPUT_2:
|
||
|
+ case PCM512x_GPIO_OUTPUT_3:
|
||
|
+ case PCM512x_GPIO_OUTPUT_4:
|
||
|
+ case PCM512x_GPIO_OUTPUT_5:
|
||
|
+ case PCM512x_GPIO_OUTPUT_6:
|
||
|
+ case PCM512x_GPIO_CONTROL_1:
|
||
|
+ case PCM512x_GPIO_CONTROL_2:
|
||
|
+ case PCM512x_OVERFLOW:
|
||
|
+ case PCM512x_RATE_DET_1:
|
||
|
+ case PCM512x_RATE_DET_2:
|
||
|
+ case PCM512x_RATE_DET_3:
|
||
|
+ case PCM512x_RATE_DET_4:
|
||
|
+ case PCM512x_ANALOG_MUTE_DET:
|
||
|
+ case PCM512x_GPIN:
|
||
|
+ case PCM512x_DIGITAL_MUTE_DET:
|
||
|
+ return true;
|
||
|
+ default:
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static bool pcm512x_volatile(struct device *dev, unsigned int reg)
|
||
|
+{
|
||
|
+ switch (reg) {
|
||
|
+ case PCM512x_PLL_EN:
|
||
|
+ case PCM512x_OVERFLOW:
|
||
|
+ case PCM512x_RATE_DET_1:
|
||
|
+ case PCM512x_RATE_DET_2:
|
||
|
+ case PCM512x_RATE_DET_3:
|
||
|
+ case PCM512x_RATE_DET_4:
|
||
|
+ case PCM512x_ANALOG_MUTE_DET:
|
||
|
+ case PCM512x_GPIN:
|
||
|
+ case PCM512x_DIGITAL_MUTE_DET:
|
||
|
+ return true;
|
||
|
+ default:
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1);
|
||
|
+
|
||
|
+static const char *pcm512x_dsp_program_texts[] = {
|
||
|
+ "FIR interpolation with de-emphasis",
|
||
|
+ "Low latency IIR with de-emphasis",
|
||
|
+ "High attenuation with de-emphasis",
|
||
|
+ "Ringing-less low latency FIR",
|
||
|
+};
|
||
|
+
|
||
|
+static const unsigned int pcm512x_dsp_program_values[] = {
|
||
|
+ 1,
|
||
|
+ 2,
|
||
|
+ 3,
|
||
|
+ 5,
|
||
|
+ 7,
|
||
|
+};
|
||
|
+
|
||
|
+static const SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program,
|
||
|
+ PCM512x_DSP_PROGRAM, 0, 0x1f,
|
||
|
+ pcm512x_dsp_program_texts,
|
||
|
+ pcm512x_dsp_program_values);
|
||
|
+
|
||
|
+static const char *pcm512x_clk_missing_text[] = {
|
||
|
+ "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s"
|
||
|
+};
|
||
|
+
|
||
|
+static const struct soc_enum pcm512x_clk_missing =
|
||
|
+ SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 7, pcm512x_clk_missing_text);
|
||
|
+
|
||
|
+static const char *pcm512x_autom_text[] = {
|
||
|
+ "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s"
|
||
|
+};
|
||
|
+
|
||
|
+static const struct soc_enum pcm512x_autom_l =
|
||
|
+ SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 7,
|
||
|
+ pcm512x_autom_text);
|
||
|
+
|
||
|
+static const struct soc_enum pcm512x_autom_r =
|
||
|
+ SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 7,
|
||
|
+ pcm512x_autom_text);
|
||
|
+
|
||
|
+static const char *pcm512x_ramp_rate_text[] = {
|
||
|
+ "1 sample/update", "2 samples/update", "4 samples/update",
|
||
|
+ "Immediate"
|
||
|
+};
|
||
|
+
|
||
|
+static const struct soc_enum pcm512x_vndf =
|
||
|
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4,
|
||
|
+ pcm512x_ramp_rate_text);
|
||
|
+
|
||
|
+static const struct soc_enum pcm512x_vnuf =
|
||
|
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4,
|
||
|
+ pcm512x_ramp_rate_text);
|
||
|
+
|
||
|
+static const struct soc_enum pcm512x_vedf =
|
||
|
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4,
|
||
|
+ pcm512x_ramp_rate_text);
|
||
|
+
|
||
|
+static const char *pcm512x_ramp_step_text[] = {
|
||
|
+ "4dB/step", "2dB/step", "1dB/step", "0.5dB/step"
|
||
|
+};
|
||
|
+
|
||
|
+static const struct soc_enum pcm512x_vnds =
|
||
|
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4,
|
||
|
+ pcm512x_ramp_step_text);
|
||
|
+
|
||
|
+static const struct soc_enum pcm512x_vnus =
|
||
|
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4,
|
||
|
+ pcm512x_ramp_step_text);
|
||
|
+
|
||
|
+static const struct soc_enum pcm512x_veds =
|
||
|
+ SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
|
||
|
+ pcm512x_ramp_step_text);
|
||
|
+
|
||
|
+static const struct snd_kcontrol_new pcm512x_controls[] = {
|
||
|
+SOC_DOUBLE_R_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2,
|
||
|
+ PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
|
||
|
+SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
|
||
|
+ PCM512x_RQMR_SHIFT, 1, 1),
|
||
|
+
|
||
|
+SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
|
||
|
+SOC_VALUE_ENUM("DSP Program", pcm512x_dsp_program),
|
||
|
+
|
||
|
+SOC_ENUM("Clock Missing Period", pcm512x_clk_missing),
|
||
|
+SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l),
|
||
|
+SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r),
|
||
|
+SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3,
|
||
|
+ PCM512x_ACTL_SHIFT, 1, 0),
|
||
|
+SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT,
|
||
|
+ PCM512x_AMLR_SHIFT, 1, 0),
|
||
|
+
|
||
|
+SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf),
|
||
|
+SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds),
|
||
|
+SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf),
|
||
|
+SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus),
|
||
|
+SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf),
|
||
|
+SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = {
|
||
|
+SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
|
||
|
+SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
|
||
|
+
|
||
|
+SND_SOC_DAPM_OUTPUT("OUTL"),
|
||
|
+SND_SOC_DAPM_OUTPUT("OUTR"),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = {
|
||
|
+ { "DACL", NULL, "Playback" },
|
||
|
+ { "DACR", NULL, "Playback" },
|
||
|
+
|
||
|
+ { "OUTL", NULL, "DACL" },
|
||
|
+ { "OUTR", NULL, "DACR" },
|
||
|
+};
|
||
|
+
|
||
|
+static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
|
||
|
+ enum snd_soc_bias_level level)
|
||
|
+{
|
||
|
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev);
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ switch (level) {
|
||
|
+ case SND_SOC_BIAS_ON:
|
||
|
+ case SND_SOC_BIAS_PREPARE:
|
||
|
+ break;
|
||
|
+
|
||
|
+ case SND_SOC_BIAS_STANDBY:
|
||
|
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
|
||
|
+ PCM512x_RQST, 0);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(codec->dev, "Failed to remove standby: %d\n",
|
||
|
+ ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+
|
||
|
+ case SND_SOC_BIAS_OFF:
|
||
|
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
|
||
|
+ PCM512x_RQST, PCM512x_RQST);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(codec->dev, "Failed to request standby: %d\n",
|
||
|
+ ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ codec->dapm.bias_level = level;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static struct snd_soc_dai_driver pcm512x_dai = {
|
||
|
+ .name = "pcm512x-hifi",
|
||
|
+ .playback = {
|
||
|
+ .stream_name = "Playback",
|
||
|
+ .channels_min = 2,
|
||
|
+ .channels_max = 2,
|
||
|
+ .rates = SNDRV_PCM_RATE_8000_192000,
|
||
|
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||
|
+ SNDRV_PCM_FMTBIT_S24_LE |
|
||
|
+ SNDRV_PCM_FMTBIT_S32_LE
|
||
|
+ },
|
||
|
+};
|
||
|
+
|
||
|
+static struct snd_soc_codec_driver pcm512x_codec_driver = {
|
||
|
+ .set_bias_level = pcm512x_set_bias_level,
|
||
|
+ .idle_bias_off = true,
|
||
|
+
|
||
|
+ .controls = pcm512x_controls,
|
||
|
+ .num_controls = ARRAY_SIZE(pcm512x_controls),
|
||
|
+ .dapm_widgets = pcm512x_dapm_widgets,
|
||
|
+ .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets),
|
||
|
+ .dapm_routes = pcm512x_dapm_routes,
|
||
|
+ .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct regmap_config pcm512x_regmap = {
|
||
|
+ .reg_bits = 8,
|
||
|
+ .val_bits = 8,
|
||
|
+
|
||
|
+ .readable_reg = pcm512x_readable,
|
||
|
+ .volatile_reg = pcm512x_volatile,
|
||
|
+
|
||
|
+ .max_register = PCM512x_MAX_REGISTER,
|
||
|
+ .reg_defaults = pcm512x_reg_defaults,
|
||
|
+ .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults),
|
||
|
+ .cache_type = REGCACHE_RBTREE,
|
||
|
+};
|
||
|
+
|
||
|
+static const struct of_device_id pcm512x_of_match[] = {
|
||
|
+ { .compatible = "ti,pcm5121", },
|
||
|
+ { .compatible = "ti,pcm5122", },
|
||
|
+ { }
|
||
|
+};
|
||
|
+MODULE_DEVICE_TABLE(of, pcm512x_of_match);
|
||
|
+
|
||
|
+static int pcm512x_probe(struct device *dev, struct regmap *regmap)
|
||
|
+{
|
||
|
+ struct pcm512x_priv *pcm512x;
|
||
|
+ int i, ret;
|
||
|
+
|
||
|
+ pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL);
|
||
|
+ if (!pcm512x)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ dev_set_drvdata(dev, pcm512x);
|
||
|
+ pcm512x->regmap = regmap;
|
||
|
+
|
||
|
+ for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++)
|
||
|
+ pcm512x->supplies[i].supply = pcm512x_supply_names[i];
|
||
|
+
|
||
|
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies),
|
||
|
+ pcm512x->supplies);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to get supplies: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0;
|
||
|
+ pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1;
|
||
|
+ pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
|
||
|
+
|
||
|
+ for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
|
||
|
+ ret = regulator_register_notifier(pcm512x->supplies[i].consumer,
|
||
|
+ &pcm512x->supply_nb[i]);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev,
|
||
|
+ "Failed to register regulator notifier: %d\n",
|
||
|
+ ret);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
|
||
|
+ pcm512x->supplies);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Reset the device, verifying I/O in the process for I2C */
|
||
|
+ ret = regmap_write(regmap, PCM512x_RESET,
|
||
|
+ PCM512x_RSTM | PCM512x_RSTR);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to reset device: %d\n", ret);
|
||
|
+ goto err;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = regmap_write(regmap, PCM512x_RESET, 0);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to reset device: %d\n", ret);
|
||
|
+ goto err;
|
||
|
+ }
|
||
|
+
|
||
|
+ pcm512x->sclk = devm_clk_get(dev, NULL);
|
||
|
+ if (IS_ERR(pcm512x->sclk)) {
|
||
|
+ if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
|
||
|
+ return -EPROBE_DEFER;
|
||
|
+
|
||
|
+ dev_info(dev, "No SCLK, using BCLK: %ld\n",
|
||
|
+ PTR_ERR(pcm512x->sclk));
|
||
|
+
|
||
|
+ /* Disable reporting of missing SCLK as an error */
|
||
|
+ regmap_update_bits(regmap, PCM512x_ERROR_DETECT,
|
||
|
+ PCM512x_IDCH, PCM512x_IDCH);
|
||
|
+
|
||
|
+ /* Switch PLL input to BCLK */
|
||
|
+ regmap_update_bits(regmap, PCM512x_PLL_REF,
|
||
|
+ PCM512x_SREF, PCM512x_SREF);
|
||
|
+ } else {
|
||
|
+ ret = clk_prepare_enable(pcm512x->sclk);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to enable SCLK: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Default to standby mode */
|
||
|
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
|
||
|
+ PCM512x_RQST, PCM512x_RQST);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to request standby: %d\n",
|
||
|
+ ret);
|
||
|
+ goto err_clk;
|
||
|
+ }
|
||
|
+
|
||
|
+ pm_runtime_set_active(dev);
|
||
|
+ pm_runtime_enable(dev);
|
||
|
+ pm_runtime_idle(dev);
|
||
|
+
|
||
|
+ ret = snd_soc_register_codec(dev, &pcm512x_codec_driver,
|
||
|
+ &pcm512x_dai, 1);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
|
||
|
+ goto err_pm;
|
||
|
+ }
|
||
|
+
|
||
|
+ dev_info(dev, "Completed initialisation - pcm512x_probe");
|
||
|
+
|
||
|
+ return 0;
|
||
|
+
|
||
|
+err_pm:
|
||
|
+ pm_runtime_disable(dev);
|
||
|
+err_clk:
|
||
|
+ if (!IS_ERR(pcm512x->sclk))
|
||
|
+ clk_disable_unprepare(pcm512x->sclk);
|
||
|
+err:
|
||
|
+ regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
|
||
|
+ pcm512x->supplies);
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static void pcm512x_remove(struct device *dev)
|
||
|
+{
|
||
|
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
|
||
|
+
|
||
|
+ snd_soc_unregister_codec(dev);
|
||
|
+ pm_runtime_disable(dev);
|
||
|
+ if (!IS_ERR(pcm512x->sclk))
|
||
|
+ clk_disable_unprepare(pcm512x->sclk);
|
||
|
+ regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
|
||
|
+ pcm512x->supplies);
|
||
|
+}
|
||
|
+
|
||
|
+/* TODO
|
||
|
+static int pcm512x_suspend(struct device *dev)
|
||
|
+{
|
||
|
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
|
||
|
+ PCM512x_RQPD, PCM512x_RQPD);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to request power down: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
|
||
|
+ pcm512x->supplies);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to disable supplies: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!IS_ERR(pcm512x->sclk))
|
||
|
+ clk_disable_unprepare(pcm512x->sclk);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int pcm512x_resume(struct device *dev)
|
||
|
+{
|
||
|
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ if (!IS_ERR(pcm512x->sclk)) {
|
||
|
+ ret = clk_prepare_enable(pcm512x->sclk);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to enable SCLK: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
|
||
|
+ pcm512x->supplies);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ regcache_cache_only(pcm512x->regmap, false);
|
||
|
+ ret = regcache_sync(pcm512x->regmap);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to sync cache: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
|
||
|
+ PCM512x_RQPD, 0);
|
||
|
+ if (ret != 0) {
|
||
|
+ dev_err(dev, "Failed to remove power down: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+// END OF PCM512x_suspend and resume calls TODO
|
||
|
+*/
|
||
|
+
|
||
|
+static const struct dev_pm_ops pcm512x_pm_ops = {
|
||
|
+ SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
|
||
|
+};
|
||
|
+
|
||
|
+#if IS_ENABLED(CONFIG_I2C)
|
||
|
+static int pcm512x_i2c_probe(struct i2c_client *i2c,
|
||
|
+ const struct i2c_device_id *id)
|
||
|
+{
|
||
|
+ struct regmap *regmap;
|
||
|
+
|
||
|
+ regmap = devm_regmap_init_i2c(i2c, &pcm512x_regmap);
|
||
|
+ if (IS_ERR(regmap))
|
||
|
+ return PTR_ERR(regmap);
|
||
|
+
|
||
|
+ return pcm512x_probe(&i2c->dev, regmap);
|
||
|
+}
|
||
|
+
|
||
|
+static int pcm512x_i2c_remove(struct i2c_client *i2c)
|
||
|
+{
|
||
|
+ pcm512x_remove(&i2c->dev);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct i2c_device_id pcm512x_i2c_id[] = {
|
||
|
+ { "pcm5121", },
|
||
|
+ { "pcm5122", },
|
||
|
+ { }
|
||
|
+};
|
||
|
+MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
|
||
|
+
|
||
|
+static struct i2c_driver pcm512x_i2c_driver = {
|
||
|
+ .probe = pcm512x_i2c_probe,
|
||
|
+ .remove = pcm512x_i2c_remove,
|
||
|
+ .id_table = pcm512x_i2c_id,
|
||
|
+ .driver = {
|
||
|
+ .name = "pcm512x",
|
||
|
+ .owner = THIS_MODULE,
|
||
|
+ .of_match_table = pcm512x_of_match,
|
||
|
+ .pm = &pcm512x_pm_ops,
|
||
|
+ },
|
||
|
+};
|
||
|
+#endif
|
||
|
+
|
||
|
+#if defined(CONFIG_SPI_MASTER)
|
||
|
+static int pcm512x_spi_probe(struct spi_device *spi)
|
||
|
+{
|
||
|
+ struct regmap *regmap;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ regmap = devm_regmap_init_spi(spi, &pcm512x_regmap);
|
||
|
+ if (IS_ERR(regmap)) {
|
||
|
+ ret = PTR_ERR(regmap);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ return pcm512x_probe(&spi->dev, regmap);
|
||
|
+}
|
||
|
+
|
||
|
+static int pcm512x_spi_remove(struct spi_device *spi)
|
||
|
+{
|
||
|
+ pcm512x_remove(&spi->dev);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct spi_device_id pcm512x_spi_id[] = {
|
||
|
+ { "pcm5121", },
|
||
|
+ { "pcm5122", },
|
||
|
+ { },
|
||
|
+};
|
||
|
+MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
|
||
|
+
|
||
|
+static struct spi_driver pcm512x_spi_driver = {
|
||
|
+ .probe = pcm512x_spi_probe,
|
||
|
+ .remove = pcm512x_spi_remove,
|
||
|
+ .id_table = pcm512x_spi_id,
|
||
|
+ .driver = {
|
||
|
+ .name = "pcm512x",
|
||
|
+ .owner = THIS_MODULE,
|
||
|
+ .of_match_table = pcm512x_of_match,
|
||
|
+ .pm = &pcm512x_pm_ops,
|
||
|
+ },
|
||
|
+};
|
||
|
+#endif
|
||
|
+
|
||
|
+static int __init pcm512x_modinit(void)
|
||
|
+{
|
||
|
+ int ret = 0;
|
||
|
+
|
||
|
+#if IS_ENABLED(CONFIG_I2C)
|
||
|
+ ret = i2c_add_driver(&pcm512x_i2c_driver);
|
||
|
+ if (ret) {
|
||
|
+ printk(KERN_ERR "Failed to register pcm512x I2C driver: %d\n",
|
||
|
+ ret);
|
||
|
+ }
|
||
|
+#endif
|
||
|
+#if defined(CONFIG_SPI_MASTER)
|
||
|
+ ret = spi_register_driver(&pcm512x_spi_driver);
|
||
|
+ if (ret != 0) {
|
||
|
+ printk(KERN_ERR "Failed to register pcm512x SPI driver: %d\n",
|
||
|
+ ret);
|
||
|
+ }
|
||
|
+#endif
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+module_init(pcm512x_modinit);
|
||
|
+
|
||
|
+static void __exit pcm512x_exit(void)
|
||
|
+{
|
||
|
+#if IS_ENABLED(CONFIG_I2C)
|
||
|
+ i2c_del_driver(&pcm512x_i2c_driver);
|
||
|
+#endif
|
||
|
+#if defined(CONFIG_SPI_MASTER)
|
||
|
+ spi_unregister_driver(&pcm512x_spi_driver);
|
||
|
+#endif
|
||
|
+}
|
||
|
+module_exit(pcm512x_exit);
|
||
|
+
|
||
|
+MODULE_DESCRIPTION("ASoC PCM512x codec driver");
|
||
|
+MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
|
||
|
+MODULE_LICENSE("GPL v2");
|
||
|
diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h
|
||
|
new file mode 100644
|
||
|
index 0000000..b2f518e
|
||
|
--- /dev/null
|
||
|
+++ b/sound/soc/codecs/pcm512x.h
|
||
|
@@ -0,0 +1,142 @@
|
||
|
+/*
|
||
|
+ * Driver for the PCM512x CODECs
|
||
|
+ *
|
||
|
+ * Author: Mark Brown <broonie@linaro.org>
|
||
|
+ * Copyright 2014 Linaro Ltd
|
||
|
+ *
|
||
|
+ * 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.
|
||
|
+ */
|
||
|
+
|
||
|
+#ifndef _SND_SOC_PCM512X
|
||
|
+#define _SND_SOC_PCM512X
|
||
|
+
|
||
|
+#define PCM512x_PAGE_0_BASE 0
|
||
|
+
|
||
|
+#define PCM512x_PAGE 0
|
||
|
+
|
||
|
+#define PCM512x_RESET (PCM512x_PAGE_0_BASE + 1)
|
||
|
+#define PCM512x_POWER (PCM512x_PAGE_0_BASE + 2)
|
||
|
+#define PCM512x_MUTE (PCM512x_PAGE_0_BASE + 3)
|
||
|
+#define PCM512x_PLL_EN (PCM512x_PAGE_0_BASE + 4)
|
||
|
+#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_0_BASE + 6)
|
||
|
+#define PCM512x_DSP (PCM512x_PAGE_0_BASE + 7)
|
||
|
+#define PCM512x_GPIO_EN (PCM512x_PAGE_0_BASE + 8)
|
||
|
+#define PCM512x_BCLK_LRCLK_CFG (PCM512x_PAGE_0_BASE + 9)
|
||
|
+#define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_0_BASE + 10)
|
||
|
+#define PCM512x_MASTER_MODE (PCM512x_PAGE_0_BASE + 12)
|
||
|
+#define PCM512x_PLL_REF (PCM512x_PAGE_0_BASE + 13)
|
||
|
+#define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_0_BASE + 20)
|
||
|
+#define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_0_BASE + 21)
|
||
|
+#define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_0_BASE + 22)
|
||
|
+#define PCM512x_PLL_COEFF_3 (PCM512x_PAGE_0_BASE + 23)
|
||
|
+#define PCM512x_PLL_COEFF_4 (PCM512x_PAGE_0_BASE + 24)
|
||
|
+#define PCM512x_DSP_CLKDIV (PCM512x_PAGE_0_BASE + 27)
|
||
|
+#define PCM512x_DAC_CLKDIV (PCM512x_PAGE_0_BASE + 28)
|
||
|
+#define PCM512x_NCP_CLKDIV (PCM512x_PAGE_0_BASE + 29)
|
||
|
+#define PCM512x_OSR_CLKDIV (PCM512x_PAGE_0_BASE + 30)
|
||
|
+#define PCM512x_MASTER_CLKDIV_1 (PCM512x_PAGE_0_BASE + 32)
|
||
|
+#define PCM512x_MASTER_CLKDIV_2 (PCM512x_PAGE_0_BASE + 33)
|
||
|
+#define PCM512x_FS_SPEED_MODE (PCM512x_PAGE_0_BASE + 34)
|
||
|
+#define PCM512x_IDAC_1 (PCM512x_PAGE_0_BASE + 35)
|
||
|
+#define PCM512x_IDAC_2 (PCM512x_PAGE_0_BASE + 36)
|
||
|
+#define PCM512x_ERROR_DETECT (PCM512x_PAGE_0_BASE + 37)
|
||
|
+#define PCM512x_I2S_1 (PCM512x_PAGE_0_BASE + 40)
|
||
|
+#define PCM512x_I2S_2 (PCM512x_PAGE_0_BASE + 41)
|
||
|
+#define PCM512x_DAC_ROUTING (PCM512x_PAGE_0_BASE + 42)
|
||
|
+#define PCM512x_DSP_PROGRAM (PCM512x_PAGE_0_BASE + 43)
|
||
|
+#define PCM512x_CLKDET (PCM512x_PAGE_0_BASE + 44)
|
||
|
+#define PCM512x_AUTO_MUTE (PCM512x_PAGE_0_BASE + 59)
|
||
|
+#define PCM512x_DIGITAL_VOLUME_1 (PCM512x_PAGE_0_BASE + 60)
|
||
|
+#define PCM512x_DIGITAL_VOLUME_2 (PCM512x_PAGE_0_BASE + 61)
|
||
|
+#define PCM512x_DIGITAL_VOLUME_3 (PCM512x_PAGE_0_BASE + 62)
|
||
|
+#define PCM512x_DIGITAL_MUTE_1 (PCM512x_PAGE_0_BASE + 63)
|
||
|
+#define PCM512x_DIGITAL_MUTE_2 (PCM512x_PAGE_0_BASE + 64)
|
||
|
+#define PCM512x_DIGITAL_MUTE_3 (PCM512x_PAGE_0_BASE + 65)
|
||
|
+#define PCM512x_GPIO_OUTPUT_1 (PCM512x_PAGE_0_BASE + 80)
|
||
|
+#define PCM512x_GPIO_OUTPUT_2 (PCM512x_PAGE_0_BASE + 81)
|
||
|
+#define PCM512x_GPIO_OUTPUT_3 (PCM512x_PAGE_0_BASE + 82)
|
||
|
+#define PCM512x_GPIO_OUTPUT_4 (PCM512x_PAGE_0_BASE + 83)
|
||
|
+#define PCM512x_GPIO_OUTPUT_5 (PCM512x_PAGE_0_BASE + 84)
|
||
|
+#define PCM512x_GPIO_OUTPUT_6 (PCM512x_PAGE_0_BASE + 85)
|
||
|
+#define PCM512x_GPIO_CONTROL_1 (PCM512x_PAGE_0_BASE + 86)
|
||
|
+#define PCM512x_GPIO_CONTROL_2 (PCM512x_PAGE_0_BASE + 87)
|
||
|
+#define PCM512x_OVERFLOW (PCM512x_PAGE_0_BASE + 90)
|
||
|
+#define PCM512x_RATE_DET_1 (PCM512x_PAGE_0_BASE + 91)
|
||
|
+#define PCM512x_RATE_DET_2 (PCM512x_PAGE_0_BASE + 92)
|
||
|
+#define PCM512x_RATE_DET_3 (PCM512x_PAGE_0_BASE + 93)
|
||
|
+#define PCM512x_RATE_DET_4 (PCM512x_PAGE_0_BASE + 94)
|
||
|
+#define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_0_BASE + 108)
|
||
|
+#define PCM512x_GPIN (PCM512x_PAGE_0_BASE + 119)
|
||
|
+#define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_0_BASE + 120)
|
||
|
+
|
||
|
+#define PCM512x_MAX_REGISTER (PCM512x_PAGE_0_BASE + 120)
|
||
|
+
|
||
|
+/* Page 0, Register 1 - reset */
|
||
|
+#define PCM512x_RSTR (1 << 0)
|
||
|
+#define PCM512x_RSTM (1 << 4)
|
||
|
+
|
||
|
+/* Page 0, Register 2 - power */
|
||
|
+#define PCM512x_RQPD (1 << 0)
|
||
|
+#define PCM512x_RQPD_SHIFT 0
|
||
|
+#define PCM512x_RQST (1 << 4)
|
||
|
+#define PCM512x_RQST_SHIFT 4
|
||
|
+
|
||
|
+/* Page 0, Register 3 - mute */
|
||
|
+#define PCM512x_RQMR_SHIFT 0
|
||
|
+#define PCM512x_RQML_SHIFT 4
|
||
|
+
|
||
|
+/* Page 0, Register 4 - PLL */
|
||
|
+#define PCM512x_PLCE (1 << 0)
|
||
|
+#define PCM512x_RLCE_SHIFT 0
|
||
|
+#define PCM512x_PLCK (1 << 4)
|
||
|
+#define PCM512x_PLCK_SHIFT 4
|
||
|
+
|
||
|
+/* Page 0, Register 7 - DSP */
|
||
|
+#define PCM512x_SDSL (1 << 0)
|
||
|
+#define PCM512x_SDSL_SHIFT 0
|
||
|
+#define PCM512x_DEMP (1 << 4)
|
||
|
+#define PCM512x_DEMP_SHIFT 4
|
||
|
+
|
||
|
+/* Page 0, Register 13 - PLL reference */
|
||
|
+#define PCM512x_SREF (1 << 4)
|
||
|
+
|
||
|
+/* Page 0, Register 37 - Error detection */
|
||
|
+#define PCM512x_IPLK (1 << 0)
|
||
|
+#define PCM512x_DCAS (1 << 1)
|
||
|
+#define PCM512x_IDCM (1 << 2)
|
||
|
+#define PCM512x_IDCH (1 << 3)
|
||
|
+#define PCM512x_IDSK (1 << 4)
|
||
|
+#define PCM512x_IDBK (1 << 5)
|
||
|
+#define PCM512x_IDFS (1 << 6)
|
||
|
+
|
||
|
+/* Page 0, Register 42 - DAC routing */
|
||
|
+#define PCM512x_AUPR_SHIFT 0
|
||
|
+#define PCM512x_AUPL_SHIFT 4
|
||
|
+
|
||
|
+/* Page 0, Register 59 - auto mute */
|
||
|
+#define PCM512x_ATMR_SHIFT 0
|
||
|
+#define PCM512x_ATML_SHIFT 4
|
||
|
+
|
||
|
+/* Page 0, Register 63 - ramp rates */
|
||
|
+#define PCM512x_VNDF_SHIFT 6
|
||
|
+#define PCM512x_VNDS_SHIFT 4
|
||
|
+#define PCM512x_VNUF_SHIFT 2
|
||
|
+#define PCM512x_VNUS_SHIFT 0
|
||
|
+
|
||
|
+/* Page 0, Register 64 - emergency ramp rates */
|
||
|
+#define PCM512x_VEDF_SHIFT 6
|
||
|
+#define PCM512x_VEDS_SHIFT 4
|
||
|
+
|
||
|
+/* Page 0, Register 65 - Digital mute enables */
|
||
|
+#define PCM512x_ACTL_SHIFT 2
|
||
|
+#define PCM512x_AMLE_SHIFT 1
|
||
|
+#define PCM512x_AMLR_SHIFT 0
|
||
|
+
|
||
|
+#endif
|
||
|
--
|
||
|
1.9.1
|
||
|
|