Alsa-devel
Threads by month
- ----- 2024 -----
- 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
December 2016
- 110 participants
- 229 discussions
[alsa-devel] [PATCH v2 0/2] register atmel-ssc as sound DAI w/o platform driver
by Peter Rosin 15 Dec '16
by Peter Rosin 15 Dec '16
15 Dec '16
Hi!
v1 -> v2 changes
- add ack from Rob Herring on patch 1/2.
- add reasons why breaking compatibility is ok for patch 2/2 (requested
by Rob).
- add #sound-dai-cells to the ssc0 node in the devcietree example.
The Atmel SSC is currently not usable as an audio DAI unless someone
registers it with ASoC. This is currently delegated to a platform
driver for every possible audio use, and prevents the SSC from being
used as a cpu DAI with the simple-audio-card driver.
The first patch fixes this.
The second patch simplifies one of these platform drivers, since it
can now rely on the SSC to register itself with ASoC. However, this
may not be a possible simplification for other, older, drivers since
it also requires device tree changes.
Cheers,
Peter
Peter Rosin (2):
misc: atmel-ssc: register as sound DAI if #sound-dai-cells is present
ASoC: atmel: tse850: rely on the ssc to register as a cpu dai by
itself
.../devicetree/bindings/misc/atmel-ssc.txt | 2 +
.../bindings/sound/axentia,tse850-pcm5142.txt | 11 +++--
drivers/misc/atmel-ssc.c | 50 ++++++++++++++++++++++
include/linux/atmel-ssc.h | 1 +
sound/soc/atmel/tse850-pcm5142.c | 23 ++--------
5 files changed, 64 insertions(+), 23 deletions(-)
--
2.1.4
4
5
15 Dec '16
From: Jeeja KP <jeeja.kp(a)intel.com>
This patch series includes driver updates for the handling scenarios
during System S0/S3 transitions when audio stream is stopped during
system.
o move DMA configuration in DSP Gateway Module widget event handler to
ensure DMA is configured after resume.
o Enable SSP MLCK using Supply widget. This will ensure the MCLK is
enabled when the widget is power on.
o Check DMA decouple register state before coupling/decoupling the DMA.
o In case of HDMI pass-through pipe, reset only the FE Pipe when stream
in in XRUN state.
o Resume the stream from HW renderer position.
o HDMI Codec pin and converter state need to be restored when stream
is resumed.
Changes in v2:
- Addressed Mark's comment on patch 1, split this into 2 as this had
additional changes other than configuring the DMA.
- Addressed Takashi's comment on the usage of snd_hdac_updatel()
Jeeja KP (13):
ASoC: Intel: Skylake: Update link_index and format in pipe params
ASoC: Intel: Skylake: Add helper function to setup host/link dma
ASoC: Intel: Skylake: Configure DMA in PRE_PMD handler of Mixer
ASoC: Intel: Skylake: Removed unused skl_get_format()
ALSA: hda: check stream decoupled register state
ASoC: Intel: Skylake: Add set_tristate DAI ops to enable SSP MCLK
ASoC: Intel: Skylake: Remove unused SSP BE prepare DAI ops
ASoC: Intel: Skylake: Add supply widget as non DSP widget
ASoC: Intel: Skylake: Add supply widget in skl_nau_max machine
ASoC: Intel: Skylake: Add supply widget in bxt_da_max machine
ASoC: Intel: Skylake: Don't reset pass-through pipe in BE prepare
ASoC: Intel: Skylake: set the resume point to LPIB
ASoC: hdac_hdmi: Enable pin and converter in prepare
sound/hda/ext/hdac_ext_stream.c | 15 +-
sound/soc/codecs/hdac_hdmi.c | 28 ++--
sound/soc/intel/boards/bxt_da7219_max98357a.c | 34 ++++
sound/soc/intel/boards/skl_nau88l25_max98357a.c | 34 ++++
sound/soc/intel/skylake/skl-pcm.c | 210 +++++++++++++-----------
sound/soc/intel/skylake/skl-topology.c | 27 ++-
sound/soc/intel/skylake/skl-topology.h | 6 +
7 files changed, 240 insertions(+), 114 deletions(-)
--
2.5.0
4
17
15 Dec '16
Add support for Cirrus Logic CS43130 codec.
Support I2C control and I2S audio playback.
Signed-off-by: Li Xu <li.xu(a)cirrus.com>
---
sound/soc/codecs/Kconfig | 6 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/cs43130.c | 1153 ++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/cs43130.h | 268 ++++++++++
4 files changed, 1429 insertions(+)
create mode 100644 sound/soc/codecs/cs43130.c
create mode 100644 sound/soc/codecs/cs43130.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 9e1718a..d6ede2b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -59,6 +59,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS4271_I2C if I2C
select SND_SOC_CS4271_SPI if SPI_MASTER
select SND_SOC_CS42XX8_I2C if I2C
+ select SND_SOC_CS43130 if I2C
select SND_SOC_CS4349 if I2C
select SND_SOC_CS47L24 if MFD_CS47L24
select SND_SOC_CS53L30 if I2C
@@ -473,6 +474,11 @@ config SND_SOC_CS42XX8_I2C
select SND_SOC_CS42XX8
select REGMAP_I2C
+# Cirrus Logic CS43130 HiFi DAC
+config SND_SOC_CS43130
+ tristate "Cirrus Logic CS43130 CODEC"
+ depends on I2C
+
# Cirrus Logic CS4349 HiFi DAC
config SND_SOC_CS4349
tristate "Cirrus Logic CS4349 CODEC"
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7e1dad7..2f15228 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -52,6 +52,7 @@ snd-soc-cs4271-i2c-objs := cs4271-i2c.o
snd-soc-cs4271-spi-objs := cs4271-spi.o
snd-soc-cs42xx8-objs := cs42xx8.o
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
+snd-soc-cs43130-objs := cs43130.o
snd-soc-cs4349-objs := cs4349.o
snd-soc-cs47l24-objs := cs47l24.o
snd-soc-cs53l30-objs := cs53l30.o
@@ -281,6 +282,7 @@ obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o
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_CS43130) += snd-soc-cs43130.o
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
new file mode 100644
index 0000000..016d1b3
--- /dev/null
+++ b/sound/soc/codecs/cs43130.c
@@ -0,0 +1,1153 @@
+/*
+ * cs43130.c -- CS43130 ALSA Soc Audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Authors: Li Xu <li.xu(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/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 <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_irq.h>
+
+#include "cs43130.h"
+
+
+static const struct reg_default cs43130_reg_defaults[] = {
+ { CS43130_SYS_CLK_CTL_1, 0x06 },
+ { CS43130_SP_SRATE, 0x01 },
+ { CS43130_SP_BITSIZE, 0x05 },
+ { CS43130_PAD_INT_CFG, 0x03 },
+ { CS43130_PWDN_CTL, 0xFE },
+ { CS43130_CRYSTAL_SET, 0x04 },
+ { CS43130_PLL_SET_1, 0x00 },
+ { CS43130_PLL_SET_2, 0x00 },
+ { CS43130_PLL_SET_3, 0x00 },
+ { CS43130_PLL_SET_4, 0x00 },
+ { CS43130_PLL_SET_5, 0x40 },
+ { CS43130_PLL_SET_6, 0x10 },
+ { CS43130_PLL_SET_7, 0x80 },
+ { CS43130_PLL_SET_8, 0x03 },
+ { CS43130_PLL_SET_9, 0x02 },
+ { CS43130_PLL_SET_10, 0x02 },
+ { CS43130_CLKOUT_CTL, 0x00 },
+ { CS43130_ASP_NUM_1, 0x01 },
+ { CS43130_ASP_NUM_2, 0x00 },
+ { CS43130_ASP_DENOM_1, 0x08 },
+ { CS43130_ASP_DENOM_2, 0x00 },
+ { CS43130_ASP_LRCK_HI_TIME_1, 0x1F },
+ { CS43130_ASP_LRCK_HI_TIME_2, 0x00 },
+ { CS43130_ASP_LRCK_PERIOD_1, 0x3F },
+ { CS43130_ASP_LRCK_PERIOD_2, 0x00 },
+ { CS43130_ASP_CLOCK_CONF, 0x0C },
+ { CS43130_ASP_FRAME_CONF, 0x0A },
+ { CS43130_XSP_NUM_1, 0x01 },
+ { CS43130_XSP_NUM_2, 0x00 },
+ { CS43130_XSP_DENOM_1, 0x02 },
+ { CS43130_XSP_DENOM_2, 0x00 },
+ { CS43130_XSP_LRCK_HI_TIME_1, 0x1F },
+ { CS43130_XSP_LRCK_HI_TIME_2, 0x00 },
+ { CS43130_XSP_LRCK_PERIOD_1, 0x3F },
+ { CS43130_XSP_LRCK_PERIOD_2, 0x00 },
+ { CS43130_XSP_CLOCK_CONF, 0x0C },
+ { CS43130_XSP_FRAME_CONF, 0x0A },
+ { CS43130_ASP_CH_1_LOC, 0x00 },
+ { CS43130_ASP_CH_2_LOC, 0x00 },
+ { CS43130_ASP_CH_1_SZ_EN, 0x06 },
+ { CS43130_ASP_CH_2_SZ_EN, 0x0E },
+ { CS43130_XSP_CH_1_LOC, 0x00 },
+ { CS43130_XSP_CH_2_LOC, 0x00 },
+ { CS43130_XSP_CH_1_SZ_EN, 0x06 },
+ { CS43130_XSP_CH_2_SZ_EN, 0x0E },
+ { CS43130_DSD_VOL_B, 0x78 },
+ { CS43130_DSD_VOL_A, 0x78 },
+ { CS43130_DSD_PATH_CTL_1, 0xA8 },
+ { CS43130_DSD_INT_CFG, 0x00 },
+ { CS43130_DSD_PATH_CTL_2, 0x00 },
+ { CS43130_DSD_PCM_MIX_CTL, 0x00 },
+ { CS43130_DSD_PATH_CTL_3, 0x40 },
+ { CS43130_HP_OUT_CTL_1, 0x30 },
+ { CS43130_PCM_FILT_OPT, 0x02 },
+ { CS43130_PCM_VOL_B, 0x78 },
+ { CS43130_PCM_VOL_A, 0x78 },
+ { CS43130_PCM_PATH_CTL_1, 0xA8 },
+ { CS43130_PCM_PATH_CTL_2, 0x00 },
+ { CS43130_CLASS_H_CTL, 0x1E },
+ { CS43130_HP_DETECT, 0x04 },
+ { CS43130_HP_LOAD_1, 0x00 },
+ { CS43130_HP_MEAS_LOAD_1, 0x00 },
+ { CS43130_HP_MEAS_LOAD_2, 0x00 },
+ { CS43130_INT_MASK_1, 0xFF },
+ { CS43130_INT_MASK_2, 0xFF },
+ { CS43130_INT_MASK_3, 0xFF },
+ { CS43130_INT_MASK_4, 0xFF },
+ { CS43130_INT_MASK_5, 0xFF },
+};
+
+static bool cs43130_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs43130_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_DEVID_AB ... CS43130_SYS_CLK_CTL_1:
+ case CS43130_SP_SRATE ... CS43130_PAD_INT_CFG:
+ case CS43130_DXD1:
+ case CS43130_PWDN_CTL:
+ case CS43130_DXD2:
+ case CS43130_CRYSTAL_SET:
+ case CS43130_PLL_SET_1 ... CS43130_PLL_SET_5:
+ case CS43130_PLL_SET_6:
+ case CS43130_PLL_SET_7:
+ case CS43130_PLL_SET_8:
+ case CS43130_PLL_SET_9:
+ case CS43130_PLL_SET_10:
+ case CS43130_CLKOUT_CTL:
+ case CS43130_ASP_NUM_1 ... CS43130_ASP_FRAME_CONF:
+ case CS43130_XSP_NUM_1 ... CS43130_XSP_FRAME_CONF:
+ case CS43130_ASP_CH_1_LOC:
+ case CS43130_ASP_CH_2_LOC:
+ case CS43130_ASP_CH_1_SZ_EN:
+ case CS43130_ASP_CH_2_SZ_EN:
+ case CS43130_XSP_CH_1_LOC:
+ case CS43130_XSP_CH_2_LOC:
+ case CS43130_XSP_CH_1_SZ_EN:
+ case CS43130_XSP_CH_2_SZ_EN:
+ case CS43130_DSD_VOL_B ... CS43130_DSD_PATH_CTL_3:
+ case CS43130_HP_OUT_CTL_1:
+ case CS43130_PCM_FILT_OPT ... CS43130_PCM_PATH_CTL_2:
+ case CS43130_CLASS_H_CTL:
+ case CS43130_HP_DETECT:
+ case CS43130_HP_STATUS:
+ case CS43130_HP_LOAD_1:
+ case CS43130_HP_MEAS_LOAD_1:
+ case CS43130_HP_MEAS_LOAD_2:
+ case CS43130_HP_DC_STAT_1:
+ case CS43130_HP_DC_STAT_2:
+ case CS43130_HP_AC_STAT_1:
+ case CS43130_HP_AC_STAT_2:
+ case CS43130_HP_LOAD_STAT:
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ case CS43130_INT_MASK_1 ... CS43130_INT_MASK_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs43130_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct cs43130_pll_params {
+ u32 pll_in;
+ u8 mclk_int;
+ u8 sclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ u32 pll_out;
+ u8 pll_cal_ratio;
+};
+
+static const struct cs43130_pll_params pll_ratio_table[] = {
+ { 9600000, 1, 0x02, 0x49, 0x800000, 0x00, 0x08, 22579200, 151 },
+ { 9600000, 0, 0x02, 0x50, 0x000000, 0x00, 0x08, 24576000, 164 },
+
+ { 11289600, 1, 0x02, 0X40, 0, 0x01, 0x08, 22579200, 128 },
+ { 11289600, 0, 0x02, 0x44, 0x06F700, 0x0, 0x08, 24576000, 139 },
+
+ { 12000000, 1, 0x02, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120 },
+ { 12000000, 0, 0x02, 0x40, 0x000000, 0x00, 0x08, 24576000, 131 },
+
+ { 12288000, 1, 0x02, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118 },
+ { 12288000, 0, 0x02, 0x40, 0x000000, 0x01, 0x08, 24576000, 128 },
+
+ { 13000000, 1, 0x02, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118 },
+ { 13000000, 0, 0x02, 0x40, 0x000000, 0x01, 0x08, 24576000, 128 },
+
+ { 19200000, 1, 0x02, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111 },
+ { 19200000, 0, 0x02, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121 },
+
+ { 22579200, 1, 0, 0, 0, 0, 0, 22579200, 0 },
+ { 22579200, 0, 0x03, 0x44, 0x06F700, 0x00, 0x08, 24576000, 139 },
+
+ { 24000000, 1, 0x03, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120 },
+ { 24000000, 0, 0x03, 0x40, 0x000000, 0x00, 0x08, 24576000, 131 },
+
+ { 24576000, 1, 0x03, 0x49, 0x800000, 0x01, 0x0A, 22579200, 128 },
+ { 24576000, 0, 0, 0, 0, 0, 0, 24576000, 0 },
+
+ { 26000000, 1, 0x03, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111 },
+ { 26000000, 0, 0x03, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121 },
+};
+
+static int cs43130_pll_config(struct snd_soc_codec *codec)
+{
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ dev_dbg(codec->dev, "%s: cs43130->mclk = %d, cs43130->pll_out = %d",
+ __func__, cs43130->mclk, cs43130->pll_out);
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].pll_in == cs43130->mclk &&
+ pll_ratio_table[i].pll_out == cs43130->pll_out) {
+
+ cs43130->mclk_int = pll_ratio_table[i].mclk_int;
+
+ if (pll_ratio_table[i].pll_cal_ratio == 0) {
+ if (cs43130->xtal_ibias > 0) {
+ usleep_range(1000, 1050);
+ /*PDN_XTAL = 0,enable*/
+ regmap_update_bits(cs43130->regmap,
+ CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK,
+ 0 << CS43130_PDN_XTAL_SHIFT);
+ }
+
+ /* PLL_START = 0, disable PLL_START */
+ regmap_update_bits(cs43130->regmap,
+ CS43130_PLL_SET_1,
+ CS43130_PLL_START_MASK,
+ 0 << CS43130_PLL_START_MASK);
+
+ cs43130->pll_bypass = true;
+ return 0;
+ }
+
+ /* PDN_PLL= 0,enable */
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK,
+ 0 << CS43130_PDN_PLL_SHIFT);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_9,
+ CS43130_PLL_REF_PREDIV_MASK,
+ pll_ratio_table[i].sclk_prediv);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_5,
+ pll_ratio_table[i].pll_div_int);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_2,
+ pll_ratio_table[i].pll_div_frac &
+ CS43130_7_0_MASK);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_3,
+ (pll_ratio_table[i].pll_div_frac &
+ CS43130_15_8_MASK) >> 8);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_4,
+ (pll_ratio_table[i].pll_div_frac &
+ CS43130_23_16_MASK) >> 16);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_8,
+ CS43130_PLL_MODE_MASK,
+ pll_ratio_table[i].pll_mode
+ << CS43130_PLL_MODE_SHIFT);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_6,
+ pll_ratio_table[i].pll_divout);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_7,
+ pll_ratio_table[i].pll_cal_ratio);
+
+ /* PLL_START = 1, enable PLL_START */
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_1,
+ CS43130_PLL_START_MASK, CS43130_PLL_START_MASK);
+ cs43130->pll_bypass = false;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int cs43130_format_config(struct snd_soc_codec *codec)
+{
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ int period_size = 0;
+ int pulse_width = 0;
+ int asp_fsd;
+ int asp_stp;
+ int bick_inv;
+ int asp_m = 0;
+ int asp_sprate = 0;
+ int ret = 0;
+ unsigned int bitwidth_sclk = (cs43130->sclk / cs43130->fs) / 2;
+ unsigned int bitwidth_dai = (cs43130->dai_bit + 1) * 8;
+
+ if (cs43130->fs) {
+ if (bitwidth_sclk < bitwidth_dai) {
+ dev_err(codec->dev, "Format not supported\n");
+ return -EINVAL;
+ }
+ period_size = cs43130->sclk / cs43130->fs;
+ pulse_width = period_size/2;
+
+ if (cs43130->sclk != 0)
+ asp_m = cs43130->pll_out / cs43130->sclk;
+ }
+ dev_dbg(codec->dev, "%s: cs43130->sclk = %d, cs43130->fs = %d, cs43130->dai_bit = %d",
+ __func__, cs43130->sclk, cs43130->fs, cs43130->dai_bit);
+ dev_dbg(codec->dev, "%s: period_size = %d, pulse_width = %d, asp_m = %d",
+ __func__, period_size, pulse_width, asp_m);
+
+ if (cs43130->dai_format) {
+ /*MSB*/
+ bick_inv = 0;
+ asp_fsd = 0;
+ asp_stp = 1;
+
+ } else {
+ /*I2S*/
+ bick_inv = 1;
+ asp_fsd = 2; /* one bick delay */
+ asp_stp = 0;
+ }
+ dev_dbg(codec->dev,
+ "%s: cs43130->dai_format = %d, bick_inv = %d, asp_fsd = %d, asp_stp = %d",
+ __func__, cs43130->dai_format, bick_inv, asp_fsd, asp_stp);
+
+ switch (cs43130->fs) {
+ case 32000:
+ asp_sprate = CS43130_ASP_SPRATE_32K;
+ break;
+ case 44100:
+ asp_sprate = CS43130_ASP_SPRATE_44_1K;
+ break;
+ case 48000:
+ asp_sprate = CS43130_ASP_SPRATE_48K;
+ break;
+ case 88200:
+ asp_sprate = CS43130_ASP_SPRATE_88_2K;
+ break;
+ case 96000:
+ asp_sprate = CS43130_ASP_SPRATE_96K;
+ break;
+ case 176400:
+ asp_sprate = CS43130_ASP_SPRATE_176_4K;
+ break;
+ case 192000:
+ asp_sprate = CS43130_ASP_SPRATE_192K;
+ break;
+ case 352800:
+ asp_sprate = CS43130_ASP_SPRATE_352_8K;
+ break;
+ case 384000:
+ asp_sprate = CS43130_ASP_SPRATE_384K;
+ break;
+ default:
+ dev_err(codec->dev, "sample rate(%d) not supported\n",
+ cs43130->fs);
+ return -EINVAL;
+ }
+ dev_dbg(codec->dev, "%s: asp_sprate = %d, cs43130->asp_size = %d",
+ __func__, asp_sprate, cs43130->asp_size);
+
+ /* ASP_SPRATE = fs*/
+ regmap_write(cs43130->regmap, CS43130_SP_SRATE, asp_sprate);
+ /*ASP_SPSIZE*/
+ regmap_update_bits(cs43130->regmap, CS43130_SP_BITSIZE,
+ CS43130_SP_BITSIZE_ASP_MASK, cs43130->asp_size);
+
+
+ /* Set up slave mode */
+ /*BICK = ASP_N/ASP_M * PLL_OUT */
+ /* ASP_N = 1 */
+ regmap_write(cs43130->regmap, CS43130_ASP_NUM_1, 1);
+ regmap_write(cs43130->regmap, CS43130_ASP_NUM_2, 0);
+
+ /*ASP_M*/
+ regmap_write(cs43130->regmap, CS43130_ASP_DENOM_1, asp_m & 0xff);
+ regmap_write(cs43130->regmap, CS43130_ASP_DENOM_2, (asp_m >> 8) & 0x3f);
+
+
+ /* H / L ratio of LRCK*/
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_1,
+ (pulse_width-1) & 0xff);
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_2,
+ ((pulse_width-1) >> 8) & 0xff);
+
+ /* the period of LRCK*/
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_1,
+ (period_size-1) & 0xff);
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_2,
+ ((period_size-1) >> 8) & 0xff);
+
+ /*resolution*/
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_1_SZ_EN,
+ CS43130_SP_BITSIZE_ASP_MASK, cs43130->dai_bit);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_2_SZ_EN,
+ CS43130_SP_BITSIZE_ASP_MASK, cs43130->dai_bit);
+
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_FRAME_CONF,
+ CS43130_ASP_FSD_MASK, asp_fsd << CS43130_ASP_FSD_SHIFT);
+
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_FRAME_CONF,
+ CS43130_ASP_STP_MASK, asp_stp << CS43130_ASP_STP_SHIFT);
+
+ /* set clk master/slave */
+ dev_dbg(codec->dev, "%s: cs43130->dai_mode = %d",
+ __func__, cs43130->dai_mode);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CLOCK_CONF,
+ CS43130_ASP_MODE_MASK, cs43130->dai_mode << CS43130_ASP_MODE_SHIFT);
+
+ return ret;
+}
+
+static int cs43130_change_clksrc(struct snd_soc_codec *codec,
+ enum cs43130_mclk_src_sel src)
+{
+ int ret = 0;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_SRC_SEL_MASK, src << CS43130_MCLK_SRC_SEL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_INT_MASK, cs43130->mclk_int << CS43130_MCLK_INT_SHIFT);
+
+ usleep_range(150, 200);
+
+ return ret;
+}
+
+static int cs43130_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ unsigned int bitwidth;
+ int ret = 0;
+
+ cs43130->fs = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_8;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_32;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_16;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_24;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_24;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_32;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_8;
+ break;
+ default:
+ dev_err(codec->dev, "Format(%d) not supported",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ bitwidth = (cs43130->dai_bit+1)*8;
+ dev_dbg(codec->dev, "(data bit)%d: (rate)%d",
+ bitwidth, cs43130->fs);
+
+ ret = cs43130_format_config(codec);
+ return ret;
+}
+
+static const DECLARE_TLV_DB_SCALE(pcm_vol_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new cs43130_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ CS43130_PCM_VOL_A, CS43130_PCM_VOL_B, 0, 0xFF, 1,
+ pcm_vol_tlv),
+ SOC_SINGLE("Swap L/R", CS43130_PCM_PATH_CTL_2, 1, 1, 0),
+ SOC_SINGLE("Copy L/R", CS43130_PCM_PATH_CTL_2, 0, 1, 0),
+};
+
+static int cs43130_aspin_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs43130->pll_bypass)
+ cs43130_change_clksrc(codec, CS43130_MCLK_SRC_XTAL);
+ else
+ cs43130_change_clksrc(codec, CS43130_MCLK_SRC_PLL);
+
+ usleep_range(10000, 10050);
+ /* ASP_3ST = 0 in master mode */
+ if (cs43130->dai_mode)
+ regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG,
+ 0x01, 0x00);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_CLKOUT_MASK, 0
+ << CS43130_PDN_CLKOUT_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+ default:
+ dev_err(codec->dev, "Invalid ASPOUT event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_dac_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ cs43130_change_clksrc(codec, CS43130_MCLK_SRC_RCO);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK, 1
+ << CS43130_PDN_XTAL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK, 1
+ << CS43130_PDN_PLL_SHIFT);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAC event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_hpin_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x99);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_OUT_CTL_1,
+ CS43130_HP_IN_EN_MASK, 0 << CS43130_HP_IN_EN_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_DXD2, 0x00);
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x99);
+ regmap_write(cs43130->regmap, CS43130_DXD2, 0x01);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_OUT_CTL_1,
+ CS43130_HP_IN_EN_MASK, 1 << CS43130_HP_IN_EN_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x00);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid HPIN event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs43130_dapm_widgets[] = {
+
+ SND_SOC_DAPM_OUTPUT("HPOUTA"),
+ SND_SOC_DAPM_OUTPUT("HPOUTB"),
+
+ SND_SOC_DAPM_AIF_IN_E("ASPIN", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_ASP_SHIFT, 1, cs43130_aspin_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+
+ SND_SOC_DAPM_DAC_E("HiFi DAC",
+ NULL, CS43130_PWDN_CTL, CS43130_PDN_HP_SHIFT, 1,
+ cs43130_dac_event,
+ (SND_SOC_DAPM_PRE_PMD)
+ ),
+
+ SND_SOC_DAPM_LINE("Analog Playback", cs43130_hpin_event),
+};
+
+static const struct snd_soc_dapm_route cs43130_routes[] = {
+ {"ASPIN", NULL, "DAC Playback"},
+ {"HiFi DAC", NULL, "ASPIN"},
+
+ {"HPOUTA", NULL, "HiFi DAC"},
+ {"HPOUTB", NULL, "HiFi DAC"},
+ {"HPOUTA", NULL, "Analog Playback"},
+ {"HPOUTB", NULL, "Analog Playback"},
+};
+
+static const unsigned int cs43130_src_rates[] = {
+ 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000
+};
+
+static const struct snd_pcm_hw_constraint_list cs43130_constraints = {
+ .count = ARRAY_SIZE(cs43130_src_rates),
+ .list = cs43130_src_rates,
+};
+
+static int cs43130_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, &cs43130_constraints);
+ return 0;
+}
+
+static int cs43130_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs43130->dai_mode = CS43130_SLAVE_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs43130->dai_mode = CS43130_MASTER_MODE;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported i2s master mode\n");
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs43130->dai_format = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cs43130->dai_format = 1;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported audio format except I2S and MSB\n");
+ return -EINVAL;
+ }
+
+ /* BICK/LRCK pority */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ cs43130->bick_invert = false;
+ cs43130->lrck_invert = false;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cs43130->bick_invert = true;
+ cs43130->lrck_invert = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ cs43130->bick_invert = false;
+ cs43130->lrck_invert = true;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ cs43130->bick_invert = true;
+ cs43130->lrck_invert = true;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported audio polarity\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int cs43130_set_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ unsigned int reg;
+ u8 mute_reg;
+
+ regmap_read(cs43130->regmap, CS43130_PCM_PATH_CTL_1, ®);
+ mute_reg = reg & 0xfc;
+ if (mute)
+ regmap_write(cs43130->regmap, CS43130_PCM_PATH_CTL_1,
+ mute_reg | 0x03);
+ else
+ regmap_write(cs43130->regmap, CS43130_PCM_PATH_CTL_1, mute_reg);
+
+ return ret;
+}
+
+static int cs43130_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+
+ switch (div_id) {
+ case CS43130_AIF_BICK_RATE:
+ cs43130->bick = div;
+ break;
+ default:
+ dev_err(codec->dev,
+ "Unsupported divide value: div_id = %d", div_id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ int ret = 0;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ if (freq_in < 9600000 || freq_in > 26000000) {
+ dev_err(codec->dev,
+ "unsupported pll input reference clock:%d\n", freq_in);
+ return -EINVAL;
+ }
+
+ switch (freq_in) {
+ case 9600000:
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 19200000:
+ case 22579200:
+ case 24000000:
+ case 24576000:
+ case 26000000:
+ cs43130->mclk = freq_in;
+ break;
+ default:
+ dev_err(codec->dev,
+ "unsupported pll input reference clock:%d\n", freq_in);
+ return -EINVAL;
+ }
+
+ switch (freq_out) {
+ case 22579200:
+ cs43130->pll_out = freq_out;
+ cs43130->mclk_int = 1;
+ break;
+ case 24576000:
+ cs43130->pll_out = freq_out;
+ cs43130->mclk_int = 0;
+ break;
+ default:
+ dev_err(codec->dev,
+ "unsupported pll output reference clock:%d\n",
+ freq_out);
+ return -EINVAL;
+ }
+
+ ret = cs43130_pll_config(codec);
+ dev_dbg(codec->dev, "%s: cs43130->pll_bypass = %d",
+ __func__, cs43130->pll_bypass);
+ return ret;
+}
+
+static int cs43130_dai_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: clk_id = %d, freq = %d, dir = %d",
+ __func__, clk_id, freq, dir);
+ cs43130->sclk = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs43130_dai_ops = {
+ .startup = cs43130_pcm_startup,
+ .hw_params = cs43130_pcm_hw_params,
+ .set_sysclk = cs43130_dai_set_sysclk,
+ .set_fmt = cs43130_set_dai_fmt,
+ .digital_mute = cs43130_set_mute,
+ .set_clkdiv = cs43130_set_clkdiv,
+};
+
+static struct snd_soc_dai_driver cs43130_dai[] = {
+ {
+ .name = "cs43130_hifi",
+ .id = 0,
+ .playback = {
+ .stream_name = "DAC Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_ASP_FORMATS,
+ },
+ .ops = &cs43130_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "cs43130-xsp",
+ .id = 1,
+ .playback = {
+ .stream_name = "XSP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_XSP_FORMATS,
+ },
+ .symmetric_rates = 1,
+ },
+};
+
+static int cs43130_codec_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ /* 24576000 is not supported */
+ unsigned int mclk_int_freq = 22579200;
+
+ dev_dbg(codec->dev, "%s: clk_id = %d, source = %d, freq = %d, dir = %d\n",
+ __func__, clk_id, source, freq, dir);
+ /*
+ * freq is external mclk freq
+ * if freq == mclk_int_freq, pll is bypassed
+ * modify mclk_int_freq as needed for application
+ */
+ cs43130_set_pll(codec, 0, 0, freq, mclk_int_freq);
+ return 0;
+}
+
+static irqreturn_t cs43130_irq_thread(int irq, void *data)
+{
+ struct cs43130_private *cs43130 =
+ (struct cs43130_private *)data;
+ struct snd_soc_codec *codec = cs43130->codec;
+ unsigned int stickies[CS43130_NUM_INT];
+ unsigned int masks[CS43130_NUM_INT];
+ unsigned int i;
+
+ /* Read all INT status and mask reg */
+ regmap_bulk_read(cs43130->regmap, CS43130_INT_STATUS_1,
+ stickies, CS43130_NUM_INT * sizeof(unsigned int));
+ regmap_bulk_read(cs43130->regmap, CS43130_INT_MASK_1,
+ masks, CS43130_NUM_INT * sizeof(unsigned int));
+
+ for (i = 0; i < ARRAY_SIZE(stickies); i++)
+ stickies[i] = stickies[i] & (~masks[i]);
+
+ if (stickies[0] & CS43130_XTAL_RDY_INT)
+ dev_dbg(codec->dev, "%s: Crystal ready", __func__);
+
+ if (stickies[0] & CS43130_XTAL_ERR_INT)
+ dev_err(codec->dev, "%s: Crystal err", __func__);
+
+ return IRQ_HANDLED;
+}
+
+static int cs43130_probe(struct snd_soc_codec *codec)
+{
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ cs43130->codec = codec;
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs43130 = {
+ .probe = cs43130_probe,
+ .component_driver = {
+ .controls = cs43130_snd_controls,
+ .num_controls = ARRAY_SIZE(cs43130_snd_controls),
+ .dapm_widgets = cs43130_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs43130_dapm_widgets),
+ .dapm_routes = cs43130_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs43130_routes),
+ },
+ .set_sysclk = cs43130_codec_set_sysclk,
+ .set_pll = cs43130_set_pll,
+};
+
+static const struct regmap_config cs43130_regmap = {
+ .reg_bits = 24,
+ .pad_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS43130_LASTREG,
+ .reg_defaults = cs43130_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs43130_reg_defaults),
+ .readable_reg = cs43130_readable_register,
+ .precious_reg = cs43130_precious_register,
+ .volatile_reg = cs43130_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs43130_handle_device_data(
+ struct i2c_client *i2c_client, struct cs43130_private *cs43130)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ unsigned int val;
+ int ret = 0;
+
+ of_property_read_u32(np, "cirrus,xtal-ibias", &val);
+ switch (val) {
+ case 1:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_7_5UA;
+ break;
+ case 2:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_12_5UA;
+ break;
+ case 3:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA;
+ break;
+ default:
+ dev_info(&i2c_client->dev,
+ "cirrus,xtal-ibias value or xtal unused %d",
+ val);
+ }
+ return ret;
+}
+
+static int cs43130_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cs43130_private *cs43130;
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+ int i;
+
+ cs43130 = devm_kzalloc(&client->dev, sizeof(*cs43130), GFP_KERNEL);
+ if (cs43130 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, cs43130);
+
+ cs43130->regmap = devm_regmap_init_i2c(client, &cs43130_regmap);
+ if (IS_ERR(cs43130->regmap)) {
+ ret = PTR_ERR(cs43130->regmap);
+ return ret;
+ }
+
+ if (client->dev.of_node) {
+ ret = cs43130_handle_device_data(client, cs43130);
+ if (ret != 0)
+ return ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(cs43130->supplies); i++)
+ cs43130->supplies[i].supply = cs43130_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(&client->dev,
+ "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(&client->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ cs43130->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs43130->reset_gpio))
+ return PTR_ERR(cs43130->reset_gpio);
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 1);
+
+ usleep_range(2000, 2050);
+
+ /* initialize codec */
+ ret = regmap_read(cs43130->regmap, CS43130_DEVID_AB, ®);
+
+ devid = (reg & 0xFF) << 12;
+ ret = regmap_read(cs43130->regmap, CS43130_DEVID_CD, ®);
+ devid |= (reg & 0xFF) << 4;
+ ret = regmap_read(cs43130->regmap, CS43130_DEVID_E, ®);
+ devid |= (reg & 0xF0) >> 4;
+
+ switch (devid) {
+ case CS43130_CHIP_ID:
+ break;
+ case CS4399_CHIP_ID:
+ break;
+ default:
+ dev_err(&client->dev,
+ "CS43130 Device ID (%X). Expected ID %X or %X\n",
+ devid, CS43130_CHIP_ID, CS4399_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ cs43130->dev_id = devid;
+ ret = regmap_read(cs43130->regmap, CS43130_REV_ID, ®);
+ if (ret < 0) {
+ dev_err(&client->dev, "Get Revision ID failed\n");
+ goto err;
+ }
+
+ dev_info(&client->dev,
+ "Cirrus Logic CS43130 (%x), Revision: %02X\n", devid,
+ reg & 0xFF);
+
+ /* Enable interrupt handler */
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq,
+ NULL, cs43130_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs43130", cs43130);
+ if (ret != 0) {
+ dev_err(&client->dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+
+ /* Unmask INT */
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT | CS43130_XTAL_ERR_INT, 0);
+
+ regmap_write(cs43130->regmap,
+ CS43130_CRYSTAL_SET, cs43130->xtal_ibias);
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_dev_cs43130, cs43130_dai,
+ ARRAY_SIZE(cs43130_dai));
+
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: snd_soc_register_codec failed with ret = %d\n",
+ __func__, ret);
+ goto err;
+ }
+ return 0;
+err:
+ return ret;
+
+}
+
+static int cs43130_i2c_remove(struct i2c_client *client)
+{
+ struct cs43130_private *cs43130 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+
+ if (cs43130->reset_gpio)
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+
+ pm_runtime_disable(&client->dev);
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES,
+ cs43130->supplies);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs43130_runtime_suspend(struct device *dev)
+{
+ struct cs43130_private *cs43130 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs43130->regmap, true);
+ regcache_mark_dirty(cs43130->regmap);
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES,
+ cs43130->supplies);
+ return 0;
+}
+
+static int cs43130_runtime_resume(struct device *dev)
+{
+ struct cs43130_private *cs43130 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(CS43130_NUM_SUPPLIES,
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs43130->regmap, false);
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 1);
+
+ usleep_range(2000, 2050);
+
+ ret = regcache_sync(cs43130->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+ return 0;
+err:
+ regcache_cache_only(cs43130->regmap, true);
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES,
+ cs43130->supplies);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops cs43130_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs43130_of_match[] = {
+ { .compatible = "cirrus,cs43130", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs43130_of_match);
+
+static const struct i2c_device_id cs43130_i2c_id[] = {
+ {"cs43130", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id);
+
+static struct i2c_driver cs43130_i2c_driver = {
+ .driver = {
+ .name = "cs43130",
+ .of_match_table = cs43130_of_match,
+ },
+ .id_table = cs43130_i2c_id,
+ .probe = cs43130_i2c_probe,
+ .remove = cs43130_i2c_remove,
+};
+
+module_i2c_driver(cs43130_i2c_driver);
+
+MODULE_AUTHOR("Li Xu <li.xu(a)cirrus.com>");
+MODULE_DESCRIPTION("Cirrus Logic CS43130 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h
new file mode 100644
index 0000000..bceae76
--- /dev/null
+++ b/sound/soc/codecs/cs43130.h
@@ -0,0 +1,268 @@
+/*
+ * ALSA SoC CS43130 codec driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Li Xu <li.xu(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.
+ *
+ * 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 __CS43130_H__
+#define __CS43130_H__
+
+/* CS43130 registers addresses */
+/* all reg address is shifted by a byte for control byte to be LSB */
+#define CS43130_FIRSTREG 0x010000
+#define CS43130_LASTREG 0x0F0014
+#define CS43130_CHIP_ID 0x00043130
+#define CS4399_CHIP_ID 0x00043990
+#define CS43130_DEVID_AB 0x010000 /*Device ID A & B [RO]*/
+#define CS43130_DEVID_CD 0x010001 /*Device ID C & D [RO]*/
+#define CS43130_DEVID_E 0x010002 /*Device ID E [RO]*/
+#define CS43130_FAB_ID 0x010003 /*Fab ID [RO]*/
+#define CS43130_REV_ID 0x010004 /*Revision ID [RO]*/
+#define CS43130_SUBREV_ID 0x010005 /*Subrevision ID*/
+#define CS43130_SYS_CLK_CTL_1 0x010006 /*System Clocking Ctl 1*/
+#define CS43130_SP_SRATE 0x01000B /*Serial Port Sample Rate*/
+#define CS43130_SP_BITSIZE 0x01000C /*Serial Port Bit Size*/
+#define CS43130_PAD_INT_CFG 0x01000D /*Pad Interface Config*/
+#define CS43130_DXD1 0x010010 /*DXD1*/
+#define CS43130_PWDN_CTL 0x020000 /*Power Down Ctl*/
+#define CS43130_DXD2 0x020019 /*DXD2*/
+#define CS43130_CRYSTAL_SET 0x020052 /*Crystal Setting*/
+#define CS43130_PLL_SET_1 0x030001 /*PLL Setting 1*/
+#define CS43130_PLL_SET_2 0x030002 /*PLL Setting 2*/
+#define CS43130_PLL_SET_3 0x030003 /*PLL Setting 3*/
+#define CS43130_PLL_SET_4 0x030004 /*PLL Setting 4*/
+#define CS43130_PLL_SET_5 0x030005 /*PLL Setting 5*/
+#define CS43130_PLL_SET_6 0x030008 /*PLL Setting 6*/
+#define CS43130_PLL_SET_7 0x03000A /*PLL Setting 7*/
+#define CS43130_PLL_SET_8 0x03001B /*PLL Setting 8*/
+#define CS43130_PLL_SET_9 0x040002 /*PLL Setting 9*/
+#define CS43130_PLL_SET_10 0x040003 /*PLL Setting 10*/
+#define CS43130_CLKOUT_CTL 0x040004 /*CLKOUT Ctl*/
+#define CS43130_ASP_NUM_1 0x040010 /*ASP Numerator 1*/
+#define CS43130_ASP_NUM_2 0x040011 /*ASP Numerator 2*/
+#define CS43130_ASP_DENOM_1 0x040012 /*ASP Denominator 1*/
+#define CS43130_ASP_DENOM_2 0x040013 /*ASP Denominator 2*/
+#define CS43130_ASP_LRCK_HI_TIME_1 0x040014 /*ASP LRCK High Time 1*/
+#define CS43130_ASP_LRCK_HI_TIME_2 0x040015 /*ASP LRCK High Time 2*/
+#define CS43130_ASP_LRCK_PERIOD_1 0x040016 /*ASP LRCK Period 1*/
+#define CS43130_ASP_LRCK_PERIOD_2 0x040017 /*ASP LRCK Period 2*/
+#define CS43130_ASP_CLOCK_CONF 0x040018 /*ASP Clock Config*/
+#define CS43130_ASP_FRAME_CONF 0x040019 /*ASP Frame Config*/
+#define CS43130_XSP_NUM_1 0x040020 /*XSP Numerator 1*/
+#define CS43130_XSP_NUM_2 0x040021 /*XSP Numerator 2*/
+#define CS43130_XSP_DENOM_1 0x040022 /*XSP Denominator 1*/
+#define CS43130_XSP_DENOM_2 0x040023 /*XSP Denominator 2*/
+#define CS43130_XSP_LRCK_HI_TIME_1 0x040024 /*XSP LRCK High Time 1*/
+#define CS43130_XSP_LRCK_HI_TIME_2 0x040025 /*XSP LRCK High Time 2*/
+#define CS43130_XSP_LRCK_PERIOD_1 0x040026 /*XSP LRCK Period 1*/
+#define CS43130_XSP_LRCK_PERIOD_2 0x040027 /*XSP LRCK Period 2*/
+#define CS43130_XSP_CLOCK_CONF 0x040028 /*XSP Clock Config*/
+#define CS43130_XSP_FRAME_CONF 0x040029 /*XSP Frame Config*/
+#define CS43130_ASP_CH_1_LOC 0x050000 /*ASP Chan 1 Location*/
+#define CS43130_ASP_CH_2_LOC 0x050001 /*ASP Chan 2 Location*/
+#define CS43130_ASP_CH_1_SZ_EN 0x05000A /*ASP Chan 1 Size, Enable*/
+#define CS43130_ASP_CH_2_SZ_EN 0x05000B /*ASP Chan 2 Size, Enable*/
+#define CS43130_XSP_CH_1_LOC 0x060000 /*XSP Chan 1 Location*/
+#define CS43130_XSP_CH_2_LOC 0x060001 /*XSP Chan 2 Location*/
+#define CS43130_XSP_CH_1_SZ_EN 0x06000A /*XSP Chan 1 Size, Enable*/
+#define CS43130_XSP_CH_2_SZ_EN 0x06000B /*XSP Chan 2 Size, Enable*/
+#define CS43130_DSD_VOL_B 0x070000 /*DSD Volume B*/
+#define CS43130_DSD_VOL_A 0x070001 /*DSD Volume A*/
+#define CS43130_DSD_PATH_CTL_1 0x070002 /*DSD Proc Path Sig Ctl 1*/
+#define CS43130_DSD_INT_CFG 0x070003 /*DSD Interface Config*/
+#define CS43130_DSD_PATH_CTL_2 0x070004 /*DSD Proc Path Sig Ctl 2*/
+#define CS43130_DSD_PCM_MIX_CTL 0x070005 /*DSD and PCM Mixing Ctl*/
+#define CS43130_DSD_PATH_CTL_3 0x070006 /*DSD Proc Path Sig Ctl 3*/
+#define CS43130_HP_OUT_CTL_1 0x080000 /*HP Output Ctl 1*/
+#define CS43130_PCM_FILT_OPT 0x090000 /*PCM Filter Option*/
+#define CS43130_PCM_VOL_B 0x090001 /*PCM Volume B*/
+#define CS43130_PCM_VOL_A 0x090002 /*PCM Volume A*/
+#define CS43130_PCM_PATH_CTL_1 0x090003 /*PCM Path Signal Ctl 1*/
+#define CS43130_PCM_PATH_CTL_2 0x090004 /*PCM Path Signal Ctl 2*/
+#define CS43130_CLASS_H_CTL 0x0B0000 /*Class H Ctl*/
+#define CS43130_HP_DETECT 0x0D0000 /*HP Detect*/
+#define CS43130_HP_STATUS 0x0D0001 /*HP Status [RO]*/
+#define CS43130_HP_LOAD_1 0x0E0000 /*HP Load 1*/
+#define CS43130_HP_MEAS_LOAD_1 0x0E0003 /*HP Load Measurement 1*/
+#define CS43130_HP_MEAS_LOAD_2 0x0E0004 /*HP Load Measurement 2*/
+#define CS43130_HP_DC_STAT_1 0x0E000D /*HP DC Load Status 0 [RO]*/
+#define CS43130_HP_DC_STAT_2 0x0E000E /*HP DC Load Status 1 [RO]*/
+#define CS43130_HP_AC_STAT_1 0x0E0010 /*HP AC Load Status 0 [RO]*/
+#define CS43130_HP_AC_STAT_2 0x0E0011 /*HP AC Load Status 1 [RO]*/
+#define CS43130_HP_LOAD_STAT 0x0E001A /*HP Load Status [RO]*/
+#define CS43130_INT_STATUS_1 0x0F0000 /*Interrupt Status 1*/
+#define CS43130_INT_STATUS_2 0x0F0001 /*Interrupt Status 2*/
+#define CS43130_INT_STATUS_3 0x0F0002 /*Interrupt Status 3*/
+#define CS43130_INT_STATUS_4 0x0F0003 /*Interrupt Status 4*/
+#define CS43130_INT_STATUS_5 0x0F0004 /*Interrupt Status 5*/
+#define CS43130_INT_MASK_1 0x0F0010 /*Interrupt Mask 1*/
+#define CS43130_INT_MASK_2 0x0F0011 /*Interrupt Mask 2*/
+#define CS43130_INT_MASK_3 0x0F0012 /*Interrupt Mask 3*/
+#define CS43130_INT_MASK_4 0x0F0013 /*Interrupt Mask 4*/
+#define CS43130_INT_MASK_5 0x0F0014 /*Interrupt Mask 5*/
+
+#define CS43130_MCLK_SRC_SEL_MASK 0x03
+#define CS43130_MCLK_SRC_SEL_SHIFT 0
+#define CS43130_MCLK_INT_MASK 0x04
+#define CS43130_MCLK_INT_SHIFT 2
+#define CS43130_SP_SRATE_MASK 0x0F
+#define CS43130_SP_SRATE_SHIFT 0
+#define CS43130_SP_BITSIZE_ASP_MASK 0x03
+#define CS43130_SP_BITSIZE_ASP_SHIFT 0
+#define CS43130_HP_DETECT_CTRL_SHIFT 6
+#define CS43130_HP_DETECT_CTRL_MASK (0x03 << CS43130_HP_DETECT_CTRL_SHIFT)
+#define CS43130_HP_DETECT_INV_SHIFT 5
+#define CS43130_HP_DETECT_INV_MASK (1 << CS43130_HP_DETECT_INV_SHIFT)
+
+/* CS43130_INT_MASK_1 */
+#define CS43130_HP_PLUG_INT_SHIFT 6
+#define CS43130_HP_PLUG_INT (1 << CS43130_HP_PLUG_INT_SHIFT)
+#define CS43130_HP_UNPLUG_INT_SHIFT 5
+#define CS43130_HP_UNPLUG_INT (1 << CS43130_HP_UNPLUG_INT_SHIFT)
+#define CS43130_XTAL_RDY_INT_SHIFT 4
+#define CS43130_XTAL_RDY_INT (1 << CS43130_XTAL_RDY_INT_SHIFT)
+#define CS43130_XTAL_ERR_INT_SHIFT 3
+#define CS43130_XTAL_ERR_INT (1 << CS43130_XTAL_ERR_INT_SHIFT)
+
+/*Reg CS43130_SP_BITSIZE*/
+#define CS43130_SP_BIT_SIZE_8 0x00
+#define CS43130_SP_BIT_SIZE_16 0x01
+#define CS43130_SP_BIT_SIZE_24 0x02
+#define CS43130_SP_BIT_SIZE_32 0x03
+
+/*PLL*/
+#define CS43130_PLL_START_MASK (0x1<<0)
+#define CS43130_PLL_MODE_MASK 0x02
+#define CS43130_PLL_MODE_SHIFT 1
+
+#define CS43130_PLL_REF_PREDIV_MASK 0x3
+
+#define CS43130_ASP_STP_MASK 0x10
+#define CS43130_ASP_STP_SHIFT 4
+#define CS43130_ASP_5050_MASK 0x08
+#define CS43130_ASP_5050_SHIFT 3
+#define CS43130_ASP_FSD_MASK 0x07
+#define CS43130_ASP_FSD_SHIFT 0
+
+#define CS43130_ASP_MODE_MASK 0x10
+#define CS43130_ASP_MODE_SHIFT 4
+#define CS43130_ASP_SCPOL_OUT_MASK 0x08
+#define CS43130_ASP_SCPOL_OUT_SHIFT 3
+#define CS43130_ASP_SCPOL_IN_MASK 0x04
+#define CS43130_ASP_SCPOL_IN_SHIFT 2
+#define CS43130_ASP_LCPOL_OUT_MASK 0x02
+#define CS43130_ASP_LCPOL_OUT_SHIFT 1
+#define CS43130_ASP_LCPOL_IN_MASK 0x01
+#define CS43130_ASP_LCPOL_IN_SHIFT 0
+
+/*Reg CS43130_PWDN_CTL*/
+#define CS43130_PDN_XSP_MASK 0x80
+#define CS43130_PDN_XSP_SHIFT 7
+#define CS43130_PDN_ASP_MASK 0x40
+#define CS43130_PDN_ASP_SHIFT 6
+#define CS43130_PDN_DSPIF_MASK 0x20
+#define CS43130_PDN_DSDIF_SHIFT 5
+#define CS43130_PDN_HP_MASK 0x10
+#define CS43130_PDN_HP_SHIFT 4
+#define CS43130_PDN_XTAL_MASK 0x08
+#define CS43130_PDN_XTAL_SHIFT 3
+#define CS43130_PDN_PLL_MASK 0x04
+#define CS43130_PDN_PLL_SHIFT 2
+#define CS43130_PDN_CLKOUT_MASK 0x02
+#define CS43130_PDN_CLKOUT_SHIFT 1
+
+#define CS43130_7_0_MASK 0xFF
+#define CS43130_15_8_MASK 0xFF00
+#define CS43130_23_16_MASK 0xFF0000
+
+/* Reg CS43130_HP_OUT_CTL_1 */
+#define CS43130_HP_IN_EN_SHIFT 3
+#define CS43130_HP_IN_EN_MASK 0x08
+
+#define CS43130_ASP_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS43130_XSP_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+enum cs43130_asp_rate {
+ CS43130_ASP_SPRATE_32K = 0,
+ CS43130_ASP_SPRATE_44_1K,
+ CS43130_ASP_SPRATE_48K,
+ CS43130_ASP_SPRATE_88_2K,
+ CS43130_ASP_SPRATE_96K,
+ CS43130_ASP_SPRATE_176_4K,
+ CS43130_ASP_SPRATE_192K,
+ CS43130_ASP_SPRATE_352_8K,
+ CS43130_ASP_SPRATE_384K,
+};
+
+enum cs43130_mclk_src_sel {
+ CS43130_MCLK_SRC_XTAL = 0,
+ CS43130_MCLK_SRC_PLL,
+ CS43130_MCLK_SRC_RCO
+};
+
+enum cs43130_mode {
+ CS43130_SLAVE_MODE = 0,
+ CS43130_MASTER_MODE
+};
+
+enum cs43130_xtal_ibias {
+ CS43130_XTAL_IBIAS_15UA = 2,
+ CS43130_XTAL_IBIAS_12_5UA = 4,
+ CS43130_XTAL_IBIAS_7_5UA = 6,
+};
+
+#define CS43130_AIF_BICK_RATE 1
+#define CS43130_SYSCLK_MCLK 1
+#define CS43130_NUM_SUPPLIES 5
+static const char *const cs43130_supply_names[CS43130_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+ "VCP",
+ "VD",
+ "VL",
+};
+
+#define CS43130_NUM_INT 5 /* number of interrupt status reg */
+
+struct cs43130_private {
+ struct snd_soc_codec *codec;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[CS43130_NUM_SUPPLIES];
+ /* codec device ID */
+ unsigned int dev_id;
+ int mclk;
+ int sclk;
+ int xtal_ibias;
+
+ bool pll_bypass;
+ int pll_out;
+ int mclk_int;
+ int dai_format;
+ int dai_mode;
+ int dai_bit;
+ int asp_size;
+ int fs;
+ bool bick_invert;
+ bool lrck_invert;
+ int bick;
+ struct gpio_desc *reset_gpio;
+};
+
+#endif /* __CS43130_H__ */
--
1.9.1
2
1
[alsa-devel] [PATCH] ASoC: Intel: bytcr_rt5640: fallback mechanism if MCLK is not enabled
by Pierre-Louis Bossart 15 Dec '16
by Pierre-Louis Bossart 15 Dec '16
15 Dec '16
Commit df1a2776a795 ("ASoC: Intel: bytcr_rt5640: add MCLK support")
was merged but the corresponding clock framework patches have not,
after being bumped from audio to clock to x86 domains. The missing
clock-related patches result in a regression starting with 4.9 with
the audio card not being created.
Rather than reverting this commit and all following updates already
queued up for 4.10, handle run-time dependency on MCLK and fall back
to the previous bit-clock mode. This provides the same functionality
as in 4.8 for Baytrail devices. On Baytrail-CR most devices remain
silent with this fallback but additional patches are needed anyway.
This patch should be applied to -stable as well as ASoC 4.10 fixes
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart(a)linux.intel.com>
---
sound/soc/intel/boards/bytcr_rt5640.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 507a86a..4a8b509 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -828,7 +828,9 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
PTR_ERR(priv->mclk));
- return PTR_ERR(priv->mclk);
+
+ /* Fall back to bitclock only */
+ byt_rt5640_quirk &= ~BYT_RT5640_MCLK_EN;
}
}
--
2.9.3
3
5
We can no longer rely on the return value of
devm_snd_dmaengine_pcm_register(...) to check if the DMA
handle is declared in the DT.
Previously this check activated PIO mode but currently
dma_request_chan returns either a valid channel or -EPROBE_DEFER.
In order to activate PIO mode check instead if the interrupt
line is declared. This reflects better what is documented in
the DT bindings (see Documentation/devicetree/bindings/sound/
designware-i2s.txt).
Also, initialize use_pio variable which was never being set
causing PIO mode to never work.
Signed-off-by: Jose Abreu <joabreu(a)synopsys.com>
Cc: Carlos Palminha <palminha(a)synopsys.com>
Cc: Liam Girdwood <lgirdwood(a)gmail.com>
Cc: Mark Brown <broonie(a)kernel.org>
Cc: Jaroslav Kysela <perex(a)perex.cz>
Cc: Takashi Iwai <tiwai(a)suse.com>
Cc: alsa-devel(a)alsa-project.org
Cc: linux-kernel(a)vger.kernel.org
---
sound/soc/dwc/designware_i2s.c | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index 2998954..bdf8398 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -681,22 +681,19 @@ static int dw_i2s_probe(struct platform_device *pdev)
}
if (!pdata) {
- ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
- if (ret == -EPROBE_DEFER) {
- dev_err(&pdev->dev,
- "failed to register PCM, deferring probe\n");
- return ret;
- } else if (ret) {
- dev_err(&pdev->dev,
- "Could not register DMA PCM: %d\n"
- "falling back to PIO mode\n", ret);
+ if (irq >= 0) {
ret = dw_pcm_register(pdev);
- if (ret) {
- dev_err(&pdev->dev,
- "Could not register PIO PCM: %d\n",
+ dev->use_pio = true;
+ } else {
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ 0);
+ dev->use_pio = false;
+ }
+
+ if (ret) {
+ dev_err(&pdev->dev, "could not register pcm: %d\n",
ret);
- goto err_clk_disable;
- }
+ goto err_clk_disable;
}
}
--
1.9.1
2
1
14 Dec '16
On Wed, Dec 14, 2016 at 04:40:15PM +0800, John Hsu wrote:
> On 12/13/2016 2:12 PM, Ben Zhang wrote:
> > +static SOC_ENUM_SINGLE_DECL(
> > + nau8825_class_g_enum, SND_SOC_NOPM, 0, nau8825_class_g_src);
> > +
> The register SND_SOC_NOPM can't use at the enum put and get function
> in the older kernel. Ex. kernel 3.14. The driver has to modify the
> DAPM in the platform with older kernel.
Older kernels are not relevant upstream. If you need to backport that
should be handled separately.
1
0
[alsa-devel] [PATCH] alsa-lib: update linked hw_ptr and appl_ptr
by sutar.mounesh@gmail.com 14 Dec '16
by sutar.mounesh@gmail.com 14 Dec '16
14 Dec '16
From: Andreas Pape <apape(a)de.adit-jv.com>
Plugin file provides no private hw_ptr and appl_ptr but instead links them to the slave pcm.
If the slave pcm itself changes its hw_ptr or app_prt this needs to be done in file plugin, too.
Plugin 'plug' is such a candidate changing the hw_ptr and app_ptr in hw_params call dependent on the automatically inserted plugins.
ALSA unfortunately has no support for automatically updating chained pointers.
A notification on pointer change seems to be prepared inside the snd_pcm_set_ptr() routine via
rbptr->changed(), but it is not (yet) implemented so that we need to care for it manually.
Signed-off-by: Andreas Pape <apape(a)de.adit-jv.com>
---
src/pcm/pcm_file.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c
index fec76eb..1e3d35b 100644
--- a/src/pcm/pcm_file.c
+++ b/src/pcm/pcm_file.c
@@ -634,6 +634,14 @@ static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
return err;
}
}
+
+ /* pointer may have changed - e.g if plug is used. */
+ snd_pcm_unlink_hw_ptr(pcm, file->gen.slave);
+ snd_pcm_unlink_appl_ptr(pcm, file->gen.slave);
+
+ snd_pcm_link_hw_ptr(pcm, file->gen.slave);
+ snd_pcm_link_appl_ptr(pcm, file->gen.slave);
+
return 0;
}
--
1.7.9.5
3
3
13 Dec '16
Add devicetree bindings documentation file for Cirrus
Logic CS43130 codec.
Signed-off-by: Li Xu <li.xu(a)cirrus.com>
---
.../devicetree/bindings/sound/cs43130.txt | 41 ++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/cs43130.txt
diff --git a/Documentation/devicetree/bindings/sound/cs43130.txt b/Documentation/devicetree/bindings/sound/cs43130.txt
new file mode 100644
index 0000000..1af6b78
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cs43130.txt
@@ -0,0 +1,41 @@
+CS43130 DAC
+
+Required properties:
+
+ - compatible : "cirrus,cs43130"
+
+ - reg : the I2C address of the device for I2C
+
+ - VA-supply, VP-supply, VL-supply, VCP-supply, VD-supply:
+ power supplies for the device, as covered in
+ Documentation/devicetree/bindings/regulator/regulator.txt.
+
+
+Optional properties:
+
+ - reset-gpios : Active low GPIO used to reset the device
+
+ - cirrus,xtal-ibias:
+ When external MCLK is generated by external crystal
+ oscillator, CS43130 can be used to provide bias current
+ for external crystal. Amount of bias current sent is
+ set as:
+ 1 = 7.5uA
+ 2 = 12.5uA
+ 3 = 15uA
+
+Example:
+
+cs43130: audio-codec@30 {
+ compatible = "cirrus,cs43130";
+ reg = <0x30>;
+ reset-gpios = <&axi_gpio 54 1>;
+ VA-supply = <&dummy_vreg>;
+ VP-supply = <&dummy_vreg>;
+ VL-supply = <&dummy_vreg>;
+ VCP-supply = <&dummy_vreg>;
+ VD-supply = <&dummy_vreg>;
+ cirrus,xtal-ibias = <2>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <55 8>;
+};
--
1.9.1
2
1
Add support for Cirrus Logic CS43130 codec.
Support I2C control and I2S audio playback.
Signed-off-by: Li Xu <li.xu(a)cirrus.com>
---
sound/soc/codecs/Kconfig | 6 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/cs43130.c | 1106 ++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/cs43130.h | 268 +++++++++++
4 files changed, 1382 insertions(+)
create mode 100644 sound/soc/codecs/cs43130.c
create mode 100644 sound/soc/codecs/cs43130.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 9e1718a..c07bc0d 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -62,6 +62,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS4349 if I2C
select SND_SOC_CS47L24 if MFD_CS47L24
select SND_SOC_CS53L30 if I2C
+ select SND_SOC_CS43130 if I2C
select SND_SOC_CX20442 if TTY
select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
select SND_SOC_DA7213 if I2C
@@ -486,6 +487,11 @@ config SND_SOC_CS53L30
tristate "Cirrus Logic CS53L30 CODEC"
depends on I2C
+# Cirrus Logic CS43130 HiFi DAC
+config SND_SOC_CS43130
+ tristate "Cirrus Logic CS43130 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 7e1dad7..7e41526 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -55,6 +55,7 @@ snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
snd-soc-cs4349-objs := cs4349.o
snd-soc-cs47l24-objs := cs47l24.o
snd-soc-cs53l30-objs := cs53l30.o
+snd-soc-cs43130-objs := cs43130.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
@@ -284,6 +285,7 @@ obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
+obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.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/cs43130.c b/sound/soc/codecs/cs43130.c
new file mode 100644
index 0000000..cf1c60c
--- /dev/null
+++ b/sound/soc/codecs/cs43130.c
@@ -0,0 +1,1106 @@
+/*
+ * cs43130.c -- CS43130 ALSA Soc Audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Authors: Li Xu <li.xu(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/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 <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_irq.h>
+
+#include "cs43130.h"
+
+
+static const struct reg_default cs43130_reg_defaults[] = {
+ { CS43130_SYS_CLK_CTL_1, 0x06 },
+ { CS43130_SP_SRATE, 0x01 },
+ { CS43130_SP_BITSIZE, 0x05 },
+ { CS43130_PAD_INT_CFG, 0x03 },
+ { CS43130_PWDN_CTL, 0xFE },
+ { CS43130_CRYSTAL_SET, 0x04 },
+ { CS43130_PLL_SET_1, 0x00 },
+ { CS43130_PLL_SET_2, 0x00 },
+ { CS43130_PLL_SET_3, 0x00 },
+ { CS43130_PLL_SET_4, 0x00 },
+ { CS43130_PLL_SET_5, 0x40 },
+ { CS43130_PLL_SET_6, 0x10 },
+ { CS43130_PLL_SET_7, 0x80 },
+ { CS43130_PLL_SET_8, 0x03 },
+ { CS43130_PLL_SET_9, 0x02 },
+ { CS43130_PLL_SET_10, 0x02 },
+ { CS43130_CLKOUT_CTL, 0x00 },
+ { CS43130_ASP_NUM_1, 0x01 },
+ { CS43130_ASP_NUM_2, 0x00 },
+ { CS43130_ASP_DENOM_1, 0x08 },
+ { CS43130_ASP_DENOM_2, 0x00 },
+ { CS43130_ASP_LRCK_HI_TIME_1, 0x1F },
+ { CS43130_ASP_LRCK_HI_TIME_2, 0x00 },
+ { CS43130_ASP_LRCK_PERIOD_1, 0x3F },
+ { CS43130_ASP_LRCK_PERIOD_2, 0x00 },
+ { CS43130_ASP_CLOCK_CONF, 0x0C },
+ { CS43130_ASP_FRAME_CONF, 0x0A },
+ { CS43130_XSP_NUM_1, 0x01 },
+ { CS43130_XSP_NUM_2, 0x00 },
+ { CS43130_XSP_DENOM_1, 0x02 },
+ { CS43130_XSP_DENOM_2, 0x00 },
+ { CS43130_XSP_LRCK_HI_TIME_1, 0x1F },
+ { CS43130_XSP_LRCK_HI_TIME_2, 0x00 },
+ { CS43130_XSP_LRCK_PERIOD_1, 0x3F },
+ { CS43130_XSP_LRCK_PERIOD_2, 0x00 },
+ { CS43130_XSP_CLOCK_CONF, 0x0C },
+ { CS43130_XSP_FRAME_CONF, 0x0A },
+ { CS43130_ASP_CH_1_LOC, 0x00 },
+ { CS43130_ASP_CH_2_LOC, 0x00 },
+ { CS43130_ASP_CH_1_SZ_EN, 0x06 },
+ { CS43130_ASP_CH_2_SZ_EN, 0x0E },
+ { CS43130_XSP_CH_1_LOC, 0x00 },
+ { CS43130_XSP_CH_2_LOC, 0x00 },
+ { CS43130_XSP_CH_1_SZ_EN, 0x06 },
+ { CS43130_XSP_CH_2_SZ_EN, 0x0E },
+ { CS43130_DSD_VOL_B, 0x78 },
+ { CS43130_DSD_VOL_A, 0x78 },
+ { CS43130_DSD_PATH_CTL_1, 0xA8 },
+ { CS43130_DSD_INT_CFG, 0x00 },
+ { CS43130_DSD_PATH_CTL_2, 0x00 },
+ { CS43130_DSD_PCM_MIX_CTL, 0x00 },
+ { CS43130_DSD_PATH_CTL_3, 0x40 },
+ { CS43130_HP_OUT_CTL_1, 0x30 },
+ { CS43130_PCM_FILT_OPT, 0x02 },
+ { CS43130_PCM_VOL_B, 0x78 },
+ { CS43130_PCM_VOL_A, 0x78 },
+ { CS43130_PCM_PATH_CTL_1, 0xA8 },
+ { CS43130_PCM_PATH_CTL_2, 0x00 },
+ { CS43130_CLASS_H_CTL, 0x1E },
+ { CS43130_HP_DETECT, 0x04 },
+ { CS43130_HP_LOAD_1, 0x00 },
+ { CS43130_HP_MEAS_LOAD_1, 0x00 },
+ { CS43130_HP_MEAS_LOAD_2, 0x00 },
+ { CS43130_INT_MASK_1, 0xFF },
+ { CS43130_INT_MASK_2, 0xFF },
+ { CS43130_INT_MASK_3, 0xFF },
+ { CS43130_INT_MASK_4, 0xFF },
+ { CS43130_INT_MASK_5, 0xFF },
+};
+
+static bool cs43130_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_DEVID_AB ... CS43130_SUBREV_ID:
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs43130_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_DEVID_AB ... CS43130_SYS_CLK_CTL_1:
+ case CS43130_SP_SRATE ... CS43130_PAD_INT_CFG:
+ case CS43130_DXD1:
+ case CS43130_PWDN_CTL:
+ case CS43130_DXD2:
+ case CS43130_CRYSTAL_SET:
+ case CS43130_PLL_SET_1 ... CS43130_PLL_SET_5:
+ case CS43130_PLL_SET_6:
+ case CS43130_PLL_SET_7:
+ case CS43130_PLL_SET_8:
+ case CS43130_PLL_SET_9:
+ case CS43130_PLL_SET_10:
+ case CS43130_CLKOUT_CTL:
+ case CS43130_ASP_NUM_1 ... CS43130_ASP_FRAME_CONF:
+ case CS43130_XSP_NUM_1 ... CS43130_XSP_FRAME_CONF:
+ case CS43130_ASP_CH_1_LOC:
+ case CS43130_ASP_CH_2_LOC:
+ case CS43130_ASP_CH_1_SZ_EN:
+ case CS43130_ASP_CH_2_SZ_EN:
+ case CS43130_XSP_CH_1_LOC:
+ case CS43130_XSP_CH_2_LOC:
+ case CS43130_XSP_CH_1_SZ_EN:
+ case CS43130_XSP_CH_2_SZ_EN:
+ case CS43130_DSD_VOL_B ... CS43130_DSD_PATH_CTL_3:
+ case CS43130_HP_OUT_CTL_1:
+ case CS43130_PCM_FILT_OPT ... CS43130_PCM_PATH_CTL_2:
+ case CS43130_CLASS_H_CTL:
+ case CS43130_HP_DETECT:
+ case CS43130_HP_STATUS:
+ case CS43130_HP_LOAD_1:
+ case CS43130_HP_MEAS_LOAD_1:
+ case CS43130_HP_MEAS_LOAD_2:
+ case CS43130_HP_DC_STAT_1:
+ case CS43130_HP_DC_STAT_2:
+ case CS43130_HP_AC_STAT_1:
+ case CS43130_HP_AC_STAT_2:
+ case CS43130_HP_LOAD_STAT:
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ case CS43130_INT_MASK_1 ... CS43130_INT_MASK_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs43130_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct cs43130_pll_params {
+ u32 pll_in;
+ u8 mclk_int;
+ u8 sclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ u32 pll_out;
+ u8 pll_cal_ratio;
+};
+
+static const struct cs43130_pll_params pll_ratio_table[] = {
+ { 9600000, 1, 0x02, 0x49, 0x800000, 0x00, 0x08, 22579200, 151 },
+ { 9600000, 0, 0x02, 0x50, 0x000000, 0x00, 0x08, 24576000, 164 },
+
+ { 11289600, 1, 0x02, 0X40, 0, 0x01, 0x08, 22579200, 128 },
+ { 11289600, 0, 0x02, 0x44, 0x06F700, 0x0, 0x08, 24576000, 139 },
+
+ { 12000000, 1, 0x02, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120 },
+ { 12000000, 0, 0x02, 0x40, 0x000000, 0x00, 0x08, 24576000, 131 },
+
+ { 12288000, 1, 0x02, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118 },
+ { 12288000, 0, 0x02, 0x40, 0x000000, 0x01, 0x08, 24576000, 128 },
+
+ { 13000000, 1, 0x02, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118 },
+ { 13000000, 0, 0x02, 0x40, 0x000000, 0x01, 0x08, 24576000, 128 },
+
+ { 19200000, 1, 0x02, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111 },
+ { 19200000, 0, 0x02, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121 },
+
+ { 22579200, 1, 0, 0, 0, 0, 0, 22579200, 0 },
+ { 22579200, 0, 0x03, 0x44, 0x06F700, 0x00, 0x08, 24576000, 139 },
+
+ { 24000000, 1, 0x03, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120 },
+ { 24000000, 0, 0x03, 0x40, 0x000000, 0x00, 0x08, 24576000, 131 },
+
+ { 24576000, 1, 0x03, 0x49, 0x800000, 0x01, 0x0A, 22579200, 128 },
+ { 24576000, 0, 0, 0, 0, 0, 0, 24576000, 0 },
+
+ { 26000000, 1, 0x03, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111 },
+ { 26000000, 0, 0x03, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121 },
+};
+
+static int cs43130_pll_config(struct snd_soc_codec *codec)
+{
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ dev_dbg(codec->dev, "%s: cs43130->mclk = %d, cs43130->pll_out = %d",
+ __func__, cs43130->mclk, cs43130->pll_out);
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].pll_in == cs43130->mclk &&
+ pll_ratio_table[i].pll_out == cs43130->pll_out) {
+
+ cs43130->mclk_int = pll_ratio_table[i].mclk_int;
+
+ if (pll_ratio_table[i].pll_cal_ratio == 0) {
+ if (cs43130->xtal_ibias > 0) {
+ usleep_range(1000, 1050);
+ /*PDN_XTAL = 0,enable*/
+ regmap_update_bits(cs43130->regmap,
+ CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK, 0
+ << CS43130_PDN_XTAL_SHIFT);
+ }
+
+ /* PLL_START = 0, disable PLL_START */
+ regmap_update_bits(cs43130->regmap,
+ CS43130_PLL_SET_1,
+ CS43130_PLL_START_MASK,
+ 0 << CS43130_PLL_START_MASK);
+
+ cs43130->pll_bypass = true;
+ return 0;
+ }
+
+ /*PDN_PLL= 0,enable*/
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK, 0
+ << CS43130_PDN_PLL_SHIFT);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_9,
+ CS43130_PLL_REF_PREDIV_MASK,
+ pll_ratio_table[i].sclk_prediv
+ );
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_5,
+ pll_ratio_table[i].pll_div_int);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_2,
+ pll_ratio_table[i].pll_div_frac &
+ CS43130_7_0_MASK);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_3,
+ (pll_ratio_table[i].pll_div_frac &
+ CS43130_15_8_MASK) >> 8);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_4,
+ (pll_ratio_table[i].pll_div_frac &
+ CS43130_23_16_MASK) >> 16);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_8,
+ CS43130_PLL_MODE_MASK,
+ pll_ratio_table[i].pll_mode
+ << CS43130_PLL_MODE_SHIFT);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_6,
+ pll_ratio_table[i].pll_divout);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_7,
+ pll_ratio_table[i].pll_cal_ratio);
+
+ /* PLL_START = 1, enable PLL_START */
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_1,
+ CS43130_PLL_START_MASK,
+ CS43130_PLL_START_MASK);
+ cs43130->pll_bypass = false;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int cs43130_format_config(struct snd_soc_codec *codec)
+{
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ int period_size = 0;
+ int pulse_width = 0;
+ int asp_fsd;
+ int asp_stp;
+ int bick_inv;
+ int asp_m = 0;
+ int asp_sprate = 0;
+ int ret = 0;
+ unsigned int bitwidth_sclk = (cs43130->sclk / cs43130->fs) / 2;
+ unsigned int bitwidth_dai = (cs43130->dai_bit + 1) * 8;
+
+ if (cs43130->fs) {
+ if (bitwidth_sclk < bitwidth_dai) {
+ dev_err(codec->dev, "Format not supported\n");
+ return -EINVAL;
+ }
+ period_size = cs43130->sclk / cs43130->fs;
+ pulse_width = period_size/2;
+
+ if (cs43130->sclk != 0)
+ asp_m = cs43130->pll_out / cs43130->sclk;
+ }
+ dev_dbg(codec->dev, "%s: cs43130->sclk = %d, cs43130->fs = %d, cs43130->dai_bit = %d",
+ __func__, cs43130->sclk, cs43130->fs, cs43130->dai_bit);
+ dev_dbg(codec->dev, "%s: period_size = %d, pulse_width = %d, asp_m = %d",
+ __func__, period_size, pulse_width, asp_m);
+
+ if (cs43130->dai_format) {
+ /*MSB*/
+ bick_inv = 0;
+ asp_fsd = 0;
+ asp_stp = 1;
+
+ } else {
+ /*I2S*/
+ bick_inv = 1;
+ asp_fsd = 2; /* one bick delay */
+ asp_stp = 0;
+ }
+ dev_dbg(codec->dev,
+ "%s: cs43130->dai_format = %d, bick_inv = %d, asp_fsd = %d, asp_stp = %d",
+ __func__, cs43130->dai_format, bick_inv, asp_fsd, asp_stp);
+
+ switch (cs43130->fs) {
+ case 32000:
+ asp_sprate = CS43130_ASP_SPRATE_32K;
+ break;
+ case 44100:
+ asp_sprate = CS43130_ASP_SPRATE_44_1K;
+ break;
+ case 48000:
+ asp_sprate = CS43130_ASP_SPRATE_48K;
+ break;
+ case 88200:
+ asp_sprate = CS43130_ASP_SPRATE_88_2K;
+ break;
+ case 96000:
+ asp_sprate = CS43130_ASP_SPRATE_96K;
+ break;
+ case 176400:
+ asp_sprate = CS43130_ASP_SPRATE_176_4K;
+ break;
+ case 192000:
+ asp_sprate = CS43130_ASP_SPRATE_192K;
+ break;
+ case 352800:
+ asp_sprate = CS43130_ASP_SPRATE_352_8K;
+ break;
+ case 384000:
+ asp_sprate = CS43130_ASP_SPRATE_384K;
+ break;
+ default:
+ dev_err(codec->dev, "sample rate(%d) not supported\n",
+ cs43130->fs);
+ return -EINVAL;
+ }
+ dev_dbg(codec->dev, "%s: asp_sprate = %d, cs43130->asp_size = %d",
+ __func__, asp_sprate, cs43130->asp_size);
+
+ /* ASP_SPRATE = fs*/
+ regmap_write(cs43130->regmap, CS43130_SP_SRATE, asp_sprate);
+ /*ASP_SPSIZE*/
+ regmap_update_bits(cs43130->regmap, CS43130_SP_BITSIZE,
+ CS43130_SP_BITSIZE_ASP_MASK, cs43130->asp_size);
+
+
+ /* Set up slave mode */
+ /*BICK = ASP_N/ASP_M * PLL_OUT */
+ /* ASP_N = 1 */
+ regmap_write(cs43130->regmap, CS43130_ASP_NUM_1, 1);
+ regmap_write(cs43130->regmap, CS43130_ASP_NUM_2, 0);
+
+ /*ASP_M*/
+ regmap_write(cs43130->regmap, CS43130_ASP_DENOM_1, asp_m & 0xff);
+ regmap_write(cs43130->regmap, CS43130_ASP_DENOM_2, (asp_m >> 8) & 0x3f);
+
+
+ /* H / L ratio of LRCK*/
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_1,
+ (pulse_width-1) & 0xff);
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_2,
+ ((pulse_width-1) >> 8) & 0xff);
+
+ /* the period of LRCK*/
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_1,
+ (period_size-1) & 0xff);
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_2,
+ ((period_size-1) >> 8) & 0xff);
+
+ /*resolution*/
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_1_SZ_EN,
+ CS43130_SP_BITSIZE_ASP_MASK, cs43130->dai_bit);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_2_SZ_EN,
+ CS43130_SP_BITSIZE_ASP_MASK, cs43130->dai_bit);
+
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_FRAME_CONF,
+ CS43130_ASP_FSD_MASK, asp_fsd << CS43130_ASP_FSD_SHIFT);
+
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_FRAME_CONF,
+ CS43130_ASP_STP_MASK, asp_stp << CS43130_ASP_STP_SHIFT);
+
+ /* set clk master/slave */
+ dev_dbg(codec->dev, "%s: cs43130->dai_mode = %d",
+ __func__, cs43130->dai_mode);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CLOCK_CONF,
+ CS43130_ASP_MODE_MASK, cs43130->dai_mode << CS43130_ASP_MODE_SHIFT);
+
+ return ret;
+}
+
+static int cs43130_change_clksrc(struct snd_soc_codec *codec,
+ enum cs43130_mclk_src_sel src)
+{
+ int ret = 0;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_SRC_SEL_MASK, src << CS43130_MCLK_SRC_SEL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_INT_MASK, cs43130->mclk_int << CS43130_MCLK_INT_SHIFT);
+
+ usleep_range(150, 200);
+
+ return ret;
+}
+
+static int cs43130_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ unsigned int bitwidth;
+ int ret = 0;
+
+ cs43130->fs = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_8;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_32;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_16;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_24;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_24;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_32;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_8;
+ break;
+ default:
+ dev_err(codec->dev, "Format(%d) not supported",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ bitwidth = (cs43130->dai_bit+1)*8;
+ dev_dbg(codec->dev, "(data bit)%d: (rate)%d",
+ bitwidth, cs43130->fs);
+
+ ret = cs43130_format_config(codec);
+ return ret;
+}
+
+static const DECLARE_TLV_DB_SCALE(pcm_vol_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new cs43130_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ CS43130_PCM_VOL_A, CS43130_PCM_VOL_B, 0, 0xFF, 1,
+ pcm_vol_tlv),
+ SOC_SINGLE("Swap L/R", CS43130_PCM_PATH_CTL_2, 1, 1, 0),
+ SOC_SINGLE("Copy L/R", CS43130_PCM_PATH_CTL_2, 0, 1, 0),
+};
+
+static int cs43130_aspin_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs43130->pll_bypass)
+ cs43130_change_clksrc(codec, CS43130_MCLK_SRC_XTAL);
+ else
+ cs43130_change_clksrc(codec, CS43130_MCLK_SRC_PLL);
+
+ usleep_range(10000, 10050);
+ /*ASP_3ST = 0 in master mode*/
+ if (cs43130->dai_mode)
+ regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG,
+ 0x01, 0x00);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_CLKOUT_MASK, 0
+ << CS43130_PDN_CLKOUT_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+ default:
+ dev_err(codec->dev, "Invalid ASPOUT event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_dac_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ cs43130_change_clksrc(codec, CS43130_MCLK_SRC_RCO);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK, 1
+ << CS43130_PDN_XTAL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK, 1
+ << CS43130_PDN_PLL_SHIFT);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAC event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_hpin_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x99);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_OUT_CTL_1,
+ CS43130_HP_IN_EN_MASK, 0 << CS43130_HP_IN_EN_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_DXD2, 0x00);
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x99);
+ regmap_write(cs43130->regmap, CS43130_DXD2, 0x01);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_OUT_CTL_1,
+ CS43130_HP_IN_EN_MASK, 1 << CS43130_HP_IN_EN_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x00);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid HPIN event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs43130_dapm_widgets[] = {
+
+ SND_SOC_DAPM_OUTPUT("HPOUTA"),
+ SND_SOC_DAPM_OUTPUT("HPOUTB"),
+
+ SND_SOC_DAPM_AIF_IN_E("ASPIN", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_ASP_SHIFT, 1, cs43130_aspin_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+
+ SND_SOC_DAPM_DAC_E("HiFi DAC",
+ NULL, CS43130_PWDN_CTL, CS43130_PDN_HP_SHIFT, 1,
+ cs43130_dac_event,
+ (SND_SOC_DAPM_PRE_PMD)
+ ),
+
+ SND_SOC_DAPM_LINE("Analog Playback", cs43130_hpin_event),
+};
+
+static const struct snd_soc_dapm_route cs43130_routes[] = {
+ {"ASPIN", NULL, "DAC Playback"},
+ {"HiFi DAC", NULL, "ASPIN"},
+
+ {"HPOUTA", NULL, "HiFi DAC"},
+ {"HPOUTB", NULL, "HiFi DAC"},
+ {"HPOUTA", NULL, "Analog Playback"},
+ {"HPOUTB", NULL, "Analog Playback"},
+};
+
+static const unsigned int cs43130_src_rates[] = {
+ 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000
+};
+
+static const struct snd_pcm_hw_constraint_list cs43130_constraints = {
+ .count = ARRAY_SIZE(cs43130_src_rates),
+ .list = cs43130_src_rates,
+};
+
+static int cs43130_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, &cs43130_constraints);
+ return 0;
+}
+
+static int cs43130_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs43130->dai_mode = CS43130_SLAVE_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs43130->dai_mode = CS43130_MASTER_MODE;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported i2s master mode\n");
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs43130->dai_format = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cs43130->dai_format = 1;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported audio format except I2S and MSB\n");
+ return -EINVAL;
+ }
+
+ /* BICK/LRCK pority */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ cs43130->bick_invert = false;
+ cs43130->lrck_invert = false;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cs43130->bick_invert = true;
+ cs43130->lrck_invert = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ cs43130->bick_invert = false;
+ cs43130->lrck_invert = true;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ cs43130->bick_invert = true;
+ cs43130->lrck_invert = true;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported audio polarity\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int cs43130_set_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ unsigned int reg;
+ u8 mute_reg;
+
+ regmap_read(cs43130->regmap, CS43130_PCM_PATH_CTL_1, ®);
+ mute_reg = reg & 0xfc;
+ if (mute)
+ regmap_write(cs43130->regmap, CS43130_PCM_PATH_CTL_1,
+ mute_reg | 0x03);
+ else
+ regmap_write(cs43130->regmap, CS43130_PCM_PATH_CTL_1, mute_reg);
+
+ return ret;
+}
+
+static int cs43130_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+
+ switch (div_id) {
+ case CS43130_AIF_BICK_RATE:
+ cs43130->bick = div;
+ break;
+ default:
+ dev_err(codec->dev,
+ "Unsupported divide value: div_id = %d", div_id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ int ret = 0;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ if (freq_in < 9600000 || freq_in > 26000000) {
+ dev_err(codec->dev,
+ "unsupported pll input reference clock:%d\n", freq_in);
+ return -EINVAL;
+ }
+
+ switch (freq_in) {
+ case 9600000:
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 19200000:
+ case 22579200:
+ case 24000000:
+ case 24576000:
+ case 26000000:
+ cs43130->mclk = freq_in;
+ break;
+ default:
+ dev_err(codec->dev,
+ "unsupported pll input reference clock:%d\n", freq_in);
+ return -EINVAL;
+ }
+
+ switch (freq_out) {
+ case 22579200:
+ cs43130->pll_out = freq_out;
+ cs43130->mclk_int = 1;
+ break;
+ case 24576000:
+ cs43130->pll_out = freq_out;
+ cs43130->mclk_int = 0;
+ break;
+ default:
+ dev_err(codec->dev,
+ "unsupported pll output reference clock:%d\n",
+ freq_out);
+ return -EINVAL;
+ }
+
+ ret = cs43130_pll_config(codec);
+ dev_dbg(codec->dev, "%s: cs43130->pll_bypass = %d",
+ __func__, cs43130->pll_bypass);
+ return ret;
+}
+
+static int cs43130_dai_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: clk_id = %d, freq = %d, dir = %d",
+ __func__, clk_id, freq, dir);
+ cs43130->sclk = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs43130_dai_ops = {
+ .startup = cs43130_pcm_startup,
+ .hw_params = cs43130_pcm_hw_params,
+ .set_sysclk = cs43130_dai_set_sysclk,
+ .set_fmt = cs43130_set_dai_fmt,
+ .digital_mute = cs43130_set_mute,
+ .set_clkdiv = cs43130_set_clkdiv,
+};
+
+static struct snd_soc_dai_driver cs43130_dai[] = {
+ {
+ .name = "cs43130_hifi",
+ .id = 0,
+ .playback = {
+ .stream_name = "DAC Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_ASP_FORMATS,
+ },
+ .ops = &cs43130_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "cs43130-xsp",
+ .id = 1,
+ .playback = {
+ .stream_name = "XSP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_XSP_FORMATS,
+ },
+ .symmetric_rates = 1,
+ },
+};
+
+static int cs43130_codec_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ /* 24576000 is not supported */
+ unsigned int mclk_int_freq = 22579200;
+
+ dev_dbg(codec->dev, "%s: clk_id = %d, source = %d, freq = %d, dir = %d\n",
+ __func__, clk_id, source, freq, dir);
+ /*
+ * freq is external mclk freq
+ * if freq == mclk_int_freq, pll is bypassed
+ * modify mclk_int_freq as needed for application
+ */
+ cs43130_set_pll(codec, 0, 0, freq, mclk_int_freq);
+ return 0;
+}
+
+static irqreturn_t cs43130_irq_thread(int irq, void *data)
+{
+ struct cs43130_private *cs43130 =
+ (struct cs43130_private *)data;
+ unsigned int stickies[CS43130_NUM_INT];
+ unsigned int masks[CS43130_NUM_INT];
+ unsigned int i;
+
+ /* Read all INT status and mask reg */
+ regmap_bulk_read(cs43130->regmap, CS43130_INT_STATUS_1,
+ stickies, CS43130_NUM_INT * sizeof(unsigned int));
+ regmap_bulk_read(cs43130->regmap, CS43130_INT_MASK_1,
+ masks, CS43130_NUM_INT * sizeof(unsigned int));
+
+ for (i = 0; i < ARRAY_SIZE(stickies); i++)
+ stickies[i] = stickies[i] & (~masks[i]);
+
+ if (stickies[0] & CS43130_XTAL_RDY_INT)
+ pr_debug("%s: Crystal ready\n", __func__);
+
+ if (stickies[0] & CS43130_XTAL_ERR_INT)
+ pr_debug("%s: Crystal err\n", __func__);
+
+ if (stickies[0] & CS43130_HP_PLUG_INT)
+ pr_debug("%s: HP plugged\n", __func__);
+
+ if (stickies[0] & CS43130_HP_UNPLUG_INT)
+ pr_debug("%s: HP unplugged\n", __func__);
+
+ return IRQ_HANDLED;
+}
+static struct snd_soc_codec_driver soc_codec_dev_cs43130 = {
+ .component_driver = {
+ .controls = cs43130_snd_controls,
+ .num_controls = ARRAY_SIZE(cs43130_snd_controls),
+ .dapm_widgets = cs43130_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs43130_dapm_widgets),
+ .dapm_routes = cs43130_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs43130_routes),
+ },
+ .set_sysclk = cs43130_codec_set_sysclk,
+ .set_pll = cs43130_set_pll,
+};
+
+static const struct regmap_config cs43130_regmap = {
+ .reg_bits = 24,
+ .pad_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS43130_LASTREG,
+ .reg_defaults = cs43130_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs43130_reg_defaults),
+ .readable_reg = cs43130_readable_register,
+ .precious_reg = cs43130_precious_register,
+ .volatile_reg = cs43130_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs43130_handle_device_data(
+ struct i2c_client *i2c_client, struct cs43130_private *cs43130)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ unsigned int val;
+ int ret = 0;
+
+ of_property_read_u32(np, "cirrus,xtal-ibias", &val);
+ switch (val) {
+ case 1:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_7_5UA;
+ break;
+ case 2:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_12_5UA;
+ break;
+ case 3:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA;
+ break;
+ default:
+ dev_info(&i2c_client->dev,
+ "cirrus,xtal-ibias value or xtal unused %d",
+ val);
+ }
+ return ret;
+}
+
+static int cs43130_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cs43130_private *cs43130;
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+ int i;
+
+ cs43130 = devm_kzalloc(&client->dev, sizeof(*cs43130), GFP_KERNEL);
+ if (cs43130 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, cs43130);
+
+ cs43130->regmap = devm_regmap_init_i2c(client, &cs43130_regmap);
+ if (IS_ERR(cs43130->regmap)) {
+ ret = PTR_ERR(cs43130->regmap);
+ return ret;
+ }
+
+ if (client->dev.of_node) {
+ ret = cs43130_handle_device_data(client, cs43130);
+ if (ret != 0)
+ return ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(cs43130->supplies); i++)
+ cs43130->supplies[i].supply = cs43130_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(&client->dev,
+ "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(&client->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ cs43130->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs43130->reset_gpio))
+ return PTR_ERR(cs43130->reset_gpio);
+
+ usleep_range(2000, 2050);
+
+ /* initialize codec */
+ ret = regmap_read(cs43130->regmap, CS43130_DEVID_AB, ®);
+
+ devid = (reg & 0xFF) << 12;
+ ret = regmap_read(cs43130->regmap, CS43130_DEVID_CD, ®);
+ devid |= (reg & 0xFF) << 4;
+ ret = regmap_read(cs43130->regmap, CS43130_DEVID_E, ®);
+ devid |= (reg & 0xF0) >> 4;
+
+ switch (devid) {
+ case CS43130_CHIP_ID:
+ break;
+ case CS4399_CHIP_ID:
+ break;
+ default:
+ dev_err(&client->dev,
+ "CS43130 Device ID (%X). Expected ID %X or %X\n",
+ devid, CS43130_CHIP_ID, CS4399_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ cs43130->dev_id = devid;
+ ret = regmap_read(cs43130->regmap, CS43130_REV_ID, ®);
+ if (ret < 0) {
+ dev_err(&client->dev, "Get Revision ID failed\n");
+ goto err;
+ }
+
+ dev_info(&client->dev,
+ "Cirrus Logic CS43130 (%x), Revision: %02X\n", devid,
+ reg & 0xFF);
+
+ /* Enable interrupt handler */
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq,
+ NULL, cs43130_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs43130", cs43130);
+ if (ret != 0) {
+ dev_err(&client->dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+
+ /* Unmask INT */
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT | CS43130_XTAL_ERR_INT |
+ CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT, 0);
+
+ /* Enable HP detect */
+ regmap_update_bits(cs43130->regmap, CS43130_HP_DETECT,
+ CS43130_HP_DETECT_CTRL_MASK, CS43130_HP_DETECT_CTRL_MASK);
+
+ regmap_write(cs43130->regmap,
+ CS43130_CRYSTAL_SET, cs43130->xtal_ibias);
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_dev_cs43130, cs43130_dai,
+ ARRAY_SIZE(cs43130_dai));
+
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: snd_soc_register_codec failed with ret = %d\n",
+ __func__, ret);
+ goto err;
+ }
+ return 0;
+err:
+ return ret;
+
+}
+
+static int cs43130_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs43130_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int cs43130_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs43130_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs43130_of_match[] = {
+ { .compatible = "cirrus,cs43130", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs43130_of_match);
+
+static const struct i2c_device_id cs43130_i2c_id[] = {
+ {"cs43130", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id);
+
+static struct i2c_driver cs43130_i2c_driver = {
+ .driver = {
+ .name = "cs43130",
+ .of_match_table = cs43130_of_match,
+ },
+ .id_table = cs43130_i2c_id,
+ .probe = cs43130_i2c_probe,
+ .remove = cs43130_i2c_remove,
+};
+
+module_i2c_driver(cs43130_i2c_driver);
+
+MODULE_AUTHOR("Li Xu <li.xu(a)cirrus.com>");
+MODULE_DESCRIPTION("Cirrus Logic CS43130 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h
new file mode 100644
index 0000000..bceae76
--- /dev/null
+++ b/sound/soc/codecs/cs43130.h
@@ -0,0 +1,268 @@
+/*
+ * ALSA SoC CS43130 codec driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Li Xu <li.xu(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.
+ *
+ * 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 __CS43130_H__
+#define __CS43130_H__
+
+/* CS43130 registers addresses */
+/* all reg address is shifted by a byte for control byte to be LSB */
+#define CS43130_FIRSTREG 0x010000
+#define CS43130_LASTREG 0x0F0014
+#define CS43130_CHIP_ID 0x00043130
+#define CS4399_CHIP_ID 0x00043990
+#define CS43130_DEVID_AB 0x010000 /*Device ID A & B [RO]*/
+#define CS43130_DEVID_CD 0x010001 /*Device ID C & D [RO]*/
+#define CS43130_DEVID_E 0x010002 /*Device ID E [RO]*/
+#define CS43130_FAB_ID 0x010003 /*Fab ID [RO]*/
+#define CS43130_REV_ID 0x010004 /*Revision ID [RO]*/
+#define CS43130_SUBREV_ID 0x010005 /*Subrevision ID*/
+#define CS43130_SYS_CLK_CTL_1 0x010006 /*System Clocking Ctl 1*/
+#define CS43130_SP_SRATE 0x01000B /*Serial Port Sample Rate*/
+#define CS43130_SP_BITSIZE 0x01000C /*Serial Port Bit Size*/
+#define CS43130_PAD_INT_CFG 0x01000D /*Pad Interface Config*/
+#define CS43130_DXD1 0x010010 /*DXD1*/
+#define CS43130_PWDN_CTL 0x020000 /*Power Down Ctl*/
+#define CS43130_DXD2 0x020019 /*DXD2*/
+#define CS43130_CRYSTAL_SET 0x020052 /*Crystal Setting*/
+#define CS43130_PLL_SET_1 0x030001 /*PLL Setting 1*/
+#define CS43130_PLL_SET_2 0x030002 /*PLL Setting 2*/
+#define CS43130_PLL_SET_3 0x030003 /*PLL Setting 3*/
+#define CS43130_PLL_SET_4 0x030004 /*PLL Setting 4*/
+#define CS43130_PLL_SET_5 0x030005 /*PLL Setting 5*/
+#define CS43130_PLL_SET_6 0x030008 /*PLL Setting 6*/
+#define CS43130_PLL_SET_7 0x03000A /*PLL Setting 7*/
+#define CS43130_PLL_SET_8 0x03001B /*PLL Setting 8*/
+#define CS43130_PLL_SET_9 0x040002 /*PLL Setting 9*/
+#define CS43130_PLL_SET_10 0x040003 /*PLL Setting 10*/
+#define CS43130_CLKOUT_CTL 0x040004 /*CLKOUT Ctl*/
+#define CS43130_ASP_NUM_1 0x040010 /*ASP Numerator 1*/
+#define CS43130_ASP_NUM_2 0x040011 /*ASP Numerator 2*/
+#define CS43130_ASP_DENOM_1 0x040012 /*ASP Denominator 1*/
+#define CS43130_ASP_DENOM_2 0x040013 /*ASP Denominator 2*/
+#define CS43130_ASP_LRCK_HI_TIME_1 0x040014 /*ASP LRCK High Time 1*/
+#define CS43130_ASP_LRCK_HI_TIME_2 0x040015 /*ASP LRCK High Time 2*/
+#define CS43130_ASP_LRCK_PERIOD_1 0x040016 /*ASP LRCK Period 1*/
+#define CS43130_ASP_LRCK_PERIOD_2 0x040017 /*ASP LRCK Period 2*/
+#define CS43130_ASP_CLOCK_CONF 0x040018 /*ASP Clock Config*/
+#define CS43130_ASP_FRAME_CONF 0x040019 /*ASP Frame Config*/
+#define CS43130_XSP_NUM_1 0x040020 /*XSP Numerator 1*/
+#define CS43130_XSP_NUM_2 0x040021 /*XSP Numerator 2*/
+#define CS43130_XSP_DENOM_1 0x040022 /*XSP Denominator 1*/
+#define CS43130_XSP_DENOM_2 0x040023 /*XSP Denominator 2*/
+#define CS43130_XSP_LRCK_HI_TIME_1 0x040024 /*XSP LRCK High Time 1*/
+#define CS43130_XSP_LRCK_HI_TIME_2 0x040025 /*XSP LRCK High Time 2*/
+#define CS43130_XSP_LRCK_PERIOD_1 0x040026 /*XSP LRCK Period 1*/
+#define CS43130_XSP_LRCK_PERIOD_2 0x040027 /*XSP LRCK Period 2*/
+#define CS43130_XSP_CLOCK_CONF 0x040028 /*XSP Clock Config*/
+#define CS43130_XSP_FRAME_CONF 0x040029 /*XSP Frame Config*/
+#define CS43130_ASP_CH_1_LOC 0x050000 /*ASP Chan 1 Location*/
+#define CS43130_ASP_CH_2_LOC 0x050001 /*ASP Chan 2 Location*/
+#define CS43130_ASP_CH_1_SZ_EN 0x05000A /*ASP Chan 1 Size, Enable*/
+#define CS43130_ASP_CH_2_SZ_EN 0x05000B /*ASP Chan 2 Size, Enable*/
+#define CS43130_XSP_CH_1_LOC 0x060000 /*XSP Chan 1 Location*/
+#define CS43130_XSP_CH_2_LOC 0x060001 /*XSP Chan 2 Location*/
+#define CS43130_XSP_CH_1_SZ_EN 0x06000A /*XSP Chan 1 Size, Enable*/
+#define CS43130_XSP_CH_2_SZ_EN 0x06000B /*XSP Chan 2 Size, Enable*/
+#define CS43130_DSD_VOL_B 0x070000 /*DSD Volume B*/
+#define CS43130_DSD_VOL_A 0x070001 /*DSD Volume A*/
+#define CS43130_DSD_PATH_CTL_1 0x070002 /*DSD Proc Path Sig Ctl 1*/
+#define CS43130_DSD_INT_CFG 0x070003 /*DSD Interface Config*/
+#define CS43130_DSD_PATH_CTL_2 0x070004 /*DSD Proc Path Sig Ctl 2*/
+#define CS43130_DSD_PCM_MIX_CTL 0x070005 /*DSD and PCM Mixing Ctl*/
+#define CS43130_DSD_PATH_CTL_3 0x070006 /*DSD Proc Path Sig Ctl 3*/
+#define CS43130_HP_OUT_CTL_1 0x080000 /*HP Output Ctl 1*/
+#define CS43130_PCM_FILT_OPT 0x090000 /*PCM Filter Option*/
+#define CS43130_PCM_VOL_B 0x090001 /*PCM Volume B*/
+#define CS43130_PCM_VOL_A 0x090002 /*PCM Volume A*/
+#define CS43130_PCM_PATH_CTL_1 0x090003 /*PCM Path Signal Ctl 1*/
+#define CS43130_PCM_PATH_CTL_2 0x090004 /*PCM Path Signal Ctl 2*/
+#define CS43130_CLASS_H_CTL 0x0B0000 /*Class H Ctl*/
+#define CS43130_HP_DETECT 0x0D0000 /*HP Detect*/
+#define CS43130_HP_STATUS 0x0D0001 /*HP Status [RO]*/
+#define CS43130_HP_LOAD_1 0x0E0000 /*HP Load 1*/
+#define CS43130_HP_MEAS_LOAD_1 0x0E0003 /*HP Load Measurement 1*/
+#define CS43130_HP_MEAS_LOAD_2 0x0E0004 /*HP Load Measurement 2*/
+#define CS43130_HP_DC_STAT_1 0x0E000D /*HP DC Load Status 0 [RO]*/
+#define CS43130_HP_DC_STAT_2 0x0E000E /*HP DC Load Status 1 [RO]*/
+#define CS43130_HP_AC_STAT_1 0x0E0010 /*HP AC Load Status 0 [RO]*/
+#define CS43130_HP_AC_STAT_2 0x0E0011 /*HP AC Load Status 1 [RO]*/
+#define CS43130_HP_LOAD_STAT 0x0E001A /*HP Load Status [RO]*/
+#define CS43130_INT_STATUS_1 0x0F0000 /*Interrupt Status 1*/
+#define CS43130_INT_STATUS_2 0x0F0001 /*Interrupt Status 2*/
+#define CS43130_INT_STATUS_3 0x0F0002 /*Interrupt Status 3*/
+#define CS43130_INT_STATUS_4 0x0F0003 /*Interrupt Status 4*/
+#define CS43130_INT_STATUS_5 0x0F0004 /*Interrupt Status 5*/
+#define CS43130_INT_MASK_1 0x0F0010 /*Interrupt Mask 1*/
+#define CS43130_INT_MASK_2 0x0F0011 /*Interrupt Mask 2*/
+#define CS43130_INT_MASK_3 0x0F0012 /*Interrupt Mask 3*/
+#define CS43130_INT_MASK_4 0x0F0013 /*Interrupt Mask 4*/
+#define CS43130_INT_MASK_5 0x0F0014 /*Interrupt Mask 5*/
+
+#define CS43130_MCLK_SRC_SEL_MASK 0x03
+#define CS43130_MCLK_SRC_SEL_SHIFT 0
+#define CS43130_MCLK_INT_MASK 0x04
+#define CS43130_MCLK_INT_SHIFT 2
+#define CS43130_SP_SRATE_MASK 0x0F
+#define CS43130_SP_SRATE_SHIFT 0
+#define CS43130_SP_BITSIZE_ASP_MASK 0x03
+#define CS43130_SP_BITSIZE_ASP_SHIFT 0
+#define CS43130_HP_DETECT_CTRL_SHIFT 6
+#define CS43130_HP_DETECT_CTRL_MASK (0x03 << CS43130_HP_DETECT_CTRL_SHIFT)
+#define CS43130_HP_DETECT_INV_SHIFT 5
+#define CS43130_HP_DETECT_INV_MASK (1 << CS43130_HP_DETECT_INV_SHIFT)
+
+/* CS43130_INT_MASK_1 */
+#define CS43130_HP_PLUG_INT_SHIFT 6
+#define CS43130_HP_PLUG_INT (1 << CS43130_HP_PLUG_INT_SHIFT)
+#define CS43130_HP_UNPLUG_INT_SHIFT 5
+#define CS43130_HP_UNPLUG_INT (1 << CS43130_HP_UNPLUG_INT_SHIFT)
+#define CS43130_XTAL_RDY_INT_SHIFT 4
+#define CS43130_XTAL_RDY_INT (1 << CS43130_XTAL_RDY_INT_SHIFT)
+#define CS43130_XTAL_ERR_INT_SHIFT 3
+#define CS43130_XTAL_ERR_INT (1 << CS43130_XTAL_ERR_INT_SHIFT)
+
+/*Reg CS43130_SP_BITSIZE*/
+#define CS43130_SP_BIT_SIZE_8 0x00
+#define CS43130_SP_BIT_SIZE_16 0x01
+#define CS43130_SP_BIT_SIZE_24 0x02
+#define CS43130_SP_BIT_SIZE_32 0x03
+
+/*PLL*/
+#define CS43130_PLL_START_MASK (0x1<<0)
+#define CS43130_PLL_MODE_MASK 0x02
+#define CS43130_PLL_MODE_SHIFT 1
+
+#define CS43130_PLL_REF_PREDIV_MASK 0x3
+
+#define CS43130_ASP_STP_MASK 0x10
+#define CS43130_ASP_STP_SHIFT 4
+#define CS43130_ASP_5050_MASK 0x08
+#define CS43130_ASP_5050_SHIFT 3
+#define CS43130_ASP_FSD_MASK 0x07
+#define CS43130_ASP_FSD_SHIFT 0
+
+#define CS43130_ASP_MODE_MASK 0x10
+#define CS43130_ASP_MODE_SHIFT 4
+#define CS43130_ASP_SCPOL_OUT_MASK 0x08
+#define CS43130_ASP_SCPOL_OUT_SHIFT 3
+#define CS43130_ASP_SCPOL_IN_MASK 0x04
+#define CS43130_ASP_SCPOL_IN_SHIFT 2
+#define CS43130_ASP_LCPOL_OUT_MASK 0x02
+#define CS43130_ASP_LCPOL_OUT_SHIFT 1
+#define CS43130_ASP_LCPOL_IN_MASK 0x01
+#define CS43130_ASP_LCPOL_IN_SHIFT 0
+
+/*Reg CS43130_PWDN_CTL*/
+#define CS43130_PDN_XSP_MASK 0x80
+#define CS43130_PDN_XSP_SHIFT 7
+#define CS43130_PDN_ASP_MASK 0x40
+#define CS43130_PDN_ASP_SHIFT 6
+#define CS43130_PDN_DSPIF_MASK 0x20
+#define CS43130_PDN_DSDIF_SHIFT 5
+#define CS43130_PDN_HP_MASK 0x10
+#define CS43130_PDN_HP_SHIFT 4
+#define CS43130_PDN_XTAL_MASK 0x08
+#define CS43130_PDN_XTAL_SHIFT 3
+#define CS43130_PDN_PLL_MASK 0x04
+#define CS43130_PDN_PLL_SHIFT 2
+#define CS43130_PDN_CLKOUT_MASK 0x02
+#define CS43130_PDN_CLKOUT_SHIFT 1
+
+#define CS43130_7_0_MASK 0xFF
+#define CS43130_15_8_MASK 0xFF00
+#define CS43130_23_16_MASK 0xFF0000
+
+/* Reg CS43130_HP_OUT_CTL_1 */
+#define CS43130_HP_IN_EN_SHIFT 3
+#define CS43130_HP_IN_EN_MASK 0x08
+
+#define CS43130_ASP_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS43130_XSP_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+enum cs43130_asp_rate {
+ CS43130_ASP_SPRATE_32K = 0,
+ CS43130_ASP_SPRATE_44_1K,
+ CS43130_ASP_SPRATE_48K,
+ CS43130_ASP_SPRATE_88_2K,
+ CS43130_ASP_SPRATE_96K,
+ CS43130_ASP_SPRATE_176_4K,
+ CS43130_ASP_SPRATE_192K,
+ CS43130_ASP_SPRATE_352_8K,
+ CS43130_ASP_SPRATE_384K,
+};
+
+enum cs43130_mclk_src_sel {
+ CS43130_MCLK_SRC_XTAL = 0,
+ CS43130_MCLK_SRC_PLL,
+ CS43130_MCLK_SRC_RCO
+};
+
+enum cs43130_mode {
+ CS43130_SLAVE_MODE = 0,
+ CS43130_MASTER_MODE
+};
+
+enum cs43130_xtal_ibias {
+ CS43130_XTAL_IBIAS_15UA = 2,
+ CS43130_XTAL_IBIAS_12_5UA = 4,
+ CS43130_XTAL_IBIAS_7_5UA = 6,
+};
+
+#define CS43130_AIF_BICK_RATE 1
+#define CS43130_SYSCLK_MCLK 1
+#define CS43130_NUM_SUPPLIES 5
+static const char *const cs43130_supply_names[CS43130_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+ "VCP",
+ "VD",
+ "VL",
+};
+
+#define CS43130_NUM_INT 5 /* number of interrupt status reg */
+
+struct cs43130_private {
+ struct snd_soc_codec *codec;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[CS43130_NUM_SUPPLIES];
+ /* codec device ID */
+ unsigned int dev_id;
+ int mclk;
+ int sclk;
+ int xtal_ibias;
+
+ bool pll_bypass;
+ int pll_out;
+ int mclk_int;
+ int dai_format;
+ int dai_mode;
+ int dai_bit;
+ int asp_size;
+ int fs;
+ bool bick_invert;
+ bool lrck_invert;
+ int bick;
+ struct gpio_desc *reset_gpio;
+};
+
+#endif /* __CS43130_H__ */
--
1.9.1
3
4
[alsa-devel] [PATCH v3 2/2] ASoC: cs35l35: Add device tree documentation for CS35L35
by Li Xu 13 Dec '16
by Li Xu 13 Dec '16
13 Dec '16
Add device tree documentation for Cirrus Logic CS35L35
speaker amplifier
Signed-off-by: Li Xu <li.xu(a)cirrus.com>
---
.../devicetree/bindings/sound/cs35l35.txt | 172 +++++++++++++++++++++
1 file changed, 172 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/cs35l35.txt
diff --git a/Documentation/devicetree/bindings/sound/cs35l35.txt b/Documentation/devicetree/bindings/sound/cs35l35.txt
new file mode 100644
index 0000000..80a1ed7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cs35l35.txt
@@ -0,0 +1,172 @@
+CS35L35 Speaker Amplifier
+
+Required properties:
+
+ - compatible : "cirrus,cs35l35"
+
+ - reg : the I2C address of the device for I2C
+
+ - VA-supply, VP-supply : power supplies for the device,
+ as covered in
+ Documentation/devicetree/bindings/regulator/regulator.txt.
+
+ - interrupt-parent : Specifies the phandle of the interrupt controller to
+ which the IRQs from CS35L35 are delivered to.
+ - interrupts : IRQ line info CS35L35.
+ (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+ for further information relating to interrupt properties)
+
+Optional properties:
+ - cirrus,reset-gpios : Active low GPIO used to reset the amplifier
+
+ - cirrus,stereo-config : Boolean to determine if there are 2 AMPs for a
+ Stereo configuration
+
+ - cirrus,audio-channel : Set Location of Audio Signal on Serial Port
+ 0 = Data Packet received on Left I2S Channel
+ 1 = Data Packet received on Right I2S Channel
+
+ - cirrus,advisory-channel : Set Location of Advisory Signal on Serial Port
+ 0 = Data Packet received on Left I2S Channel
+ 1 = Data Packet received on Right I2S Channel
+
+ - cirrus,shared-boost : Boolean to enable ClassH tracking of Advisory Signal
+ if 2 Devices share Boost BST_CTL
+
+ - cirrus,sp-drv-strength : Value for setting the Serial Port drive strength
+ Table 3-10 of the datasheet lists drive-strength specifications
+ 0 = 1x (Default)
+ 1 = .5x
+
+ - cirrus,bst-pdn-fet-on : Boolean to determine if the Boost PDN control
+ powers down with a rectification FET On or Off. If VSPK is supplied
+ externally then FET is off.
+
+ - cirrus,boost-ctl-millivolt : Boost Converter control word. Step Size of 100mV
+ 0x00 = (Default) VP
+ 0x01 = 2600mV
+ 0x41 = 9000mV
+
+ - cirrus,boost-ipk-milliamp : Boost-converter peak current limit.
+ Configures the peak current by monitoring the current through the boost FET.
+ Step size: 112mA
+ 0x00 = 1680mA
+ 0x19 = 4480mA
+
+ - cirrus,amp-gain-zc : Boolean to determine if to use Amplifier gain-change
+ zero-cross
+
+Optional H/G Algorithm sub-node:
+
+ The cs35l35 node can have a single "cirrus,classh-internal-algo" sub-node
+ that will disable automatic control of the internal H/G Algorithm.
+
+ It is strongly recommended that the Datasheet be referenced when adjusting
+ or using these Class H Algorithm controls over the internal Algorithm.
+ Serious damage can occur to the Device and surrounding components.
+
+ - cirrus,classh-internal-algo : Sub-node for the Internal Class H Algorithm
+ See Section 4.3 Internal Class H Algorithm in the Datasheet.
+ If not used, the device manages the ClassH Algorithm internally.
+
+Optional properties for the "cirrus,classh-internal-algo" Sub-node
+
+ Section 7.29 Class H Control
+ - cirrus,classh-bst-overide : Boolean
+ - cirrus,classh-bst-max-limit
+ - cirrus,classh-mem-depth
+
+ Section 7.30 Class H Headroom Control
+ - cirrus,classh-headroom
+
+ Section 7.31 Class H Release Rate
+ - cirrus,classh-release-rate
+
+ Section 7.32 Class H Weak FET Drive Control
+ - cirrus,classh-wk-fet-disable
+ - cirrus,classh-wk-fet-delay
+ - cirrus,classh-wk-fet-thld
+
+ Section 7.34 Class H VP Control
+ - cirrus,classh-vpch-auto
+ - cirrus,classh-vpch-rate
+ - cirrus,classh-vpch-man
+
+Optional Monitor Signal Format sub-node:
+
+ The cs35l35 node can have a single "cirrus,monitor-signal-format" sub-node
+ for adjusting the Depth, Location and Frame of the Monitoring Signals
+ for Algorithms.
+
+ See Sections 4.8.2 through 4.8.4 Serial-Port Control in the Datasheet
+
+ -cirrus,monitor-signal-format : Sub-node for the Monitor Signaling Formating
+ on the I2S Port. Each of the 3 8 bit values in the array contain the settings
+ for depth, location, and frame.
+
+ If not used, the defaults for the 6 monitor signals is used.
+
+ Sections 7.44 - 7.53 lists values for the depth, location, and frame
+ for each monitoring signal.
+
+ - cirrus,imon : 3 8 bit values to set the depth, location, and frame
+ of the IMON monitor signal.
+
+ - cirrus,vmon : 3 8 bit values to set the depth, location, and frame
+ of the VMON monitor signal.
+
+ - cirrus,vpmon : 3 8 bit values to set the depth, location, and frame
+ of the VPMON monitor signal.
+
+ - cirrus,vbstmon : 3 8 bit values to set the depth, location, and frame
+ of the VBSTMON monitor signal
+
+ - cirrus,vpbrstat : 3 8 bit values to set the depth, location, and frame
+ of the VPBRSTAT monitor signal
+
+ - cirrus,zerofill : 3 8 bit values to set the depth, location, and frame\
+ of the ZEROFILL packet in the monitor signal
+
+Example:
+
+cs35l35: audio-codec@40 {
+ compatible = "cirrus,cs35l35";
+ reg = <0x20>;
+ VA-supply = <&dummy_vreg>;
+ VP-supply = <&dummy_vreg>;
+ reset-gpios = <&axi_gpio 54 1>;
+ interrupt-parent = <&gpio8>;
+ interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+ cirrus,boost-ctl = <0x41>;
+
+ cirrus,stereo-config {
+ cirrus,audio-channel = <0x00>;
+ cirrus,advisory-channel = <0x01>;
+ cirrus,shared-boost;
+ };
+
+ cirrus,classh-internal-algo {
+ cirrus,classh-bst-overide;
+ cirrus,classh-bst-max-limit = <0x01>;
+ cirrus,classh-mem-depth = <0x01>;
+ cirrus,classh-release-rate = <0x08>;
+ cirrus,classh-headroom-millivolt = <0x0B>;
+ cirrus,classh-wk-fet-disable = <0x01>;
+ cirrus,classh-wk-fet-delay = <0x04>;
+ cirrus,classh-wk-fet-thld = <0x01>;
+ cirrus,classh-vpch-auto = <0x01>;
+ cirrus,classh-vpch-rate = <0x02>;
+ cirrus,classh-vpch-man = <0x05>;
+ };
+
+ /* Depth, Location, Frame */
+ cirrus,monitor-signal-format {
+ cirrus,imon = /bits/ 8 <0x03 0x00 0x01>;
+ cirrus,vmon = /bits/ 8 <0x03 0x00 0x00>;
+ cirrus,vpmon = /bits/ 8 <0x03 0x04 0x00>;
+ cirrus,vbstmon = /bits/ 8 <0x03 0x04 0x01>;
+ cirrus,vpbrstat = /bits/ 8 <0x00 0x04 0x00>;
+ cirrus,zerofill = /bits/ 8 <0x00 0x00 0x00>;
+ };
+
+};
--
1.9.1
1
0
[alsa-devel] [PATCH v2 1/2] ASoC: cs35l35: Add support for Cirrus CS35L35 Amplifier
by Li Xu 13 Dec '16
by Li Xu 13 Dec '16
13 Dec '16
Add driver support for Cirrus Logic CS35L35 boosted
speaker amplifier
Signed-off-by: Li Xu <li.xu(a)cirrus.com>
---
include/sound/cs35l35.h | 104 ++++
sound/soc/codecs/Kconfig | 5 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/cs35l35.c | 1357 ++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/cs35l35.h | 285 ++++++++++
5 files changed, 1753 insertions(+)
create mode 100644 include/sound/cs35l35.h
create mode 100644 sound/soc/codecs/cs35l35.c
create mode 100644 sound/soc/codecs/cs35l35.h
diff --git a/include/sound/cs35l35.h b/include/sound/cs35l35.h
new file mode 100644
index 0000000..005a813
--- /dev/null
+++ b/include/sound/cs35l35.h
@@ -0,0 +1,104 @@
+/*
+ * linux/sound/cs35l35.h -- Platform data for CS35l35
+ *
+ * Copyright (c) 2016 Cirrus Logic Inc.
+ *
+ * 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 __CS35L35_H
+#define __CS35L35_H
+
+struct classh_cfg {
+ /*
+ * Class H Algorithm Control Variables
+ * You can either have it done
+ * automatically or you can adjust
+ * these variables for tuning
+ *
+ * if you do not enable the internal algorithm
+ * you will get a set of mixer controls for
+ * Class H tuning
+ *
+ * Section 4.3 of the datasheet
+ */
+ /* Internal ClassH Algorithm */
+ bool classh_bst_override;
+ bool classh_algo_enable;
+ int classh_bst_max_limit;
+ int classh_mem_depth;
+ int classh_release_rate;
+ int classh_headroom;
+ int classh_wk_fet_disable;
+ int classh_wk_fet_delay;
+ int classh_wk_fet_thld;
+ int classh_vpch_auto;
+ int classh_vpch_rate;
+ int classh_vpch_man;
+};
+
+struct monitor_cfg {
+ /*
+ * Signal Monitor Data
+ * highly configurable signal monitoring
+ * data positioning and different types of
+ * monitoring data.
+ *
+ * Section 4.8.2 - 4.8.4 of the datasheet
+ */
+ bool is_present;
+ bool imon_specs;
+ bool vmon_specs;
+ bool vpmon_specs;
+ bool vbstmon_specs;
+ bool vpbrstat_specs;
+ bool zerofill_specs;
+ u8 imon_dpth;
+ u8 imon_loc;
+ u8 imon_frm;
+ u8 vmon_dpth;
+ u8 vmon_loc;
+ u8 vmon_frm;
+ u8 vpmon_dpth;
+ u8 vpmon_loc;
+ u8 vpmon_frm;
+ u8 vbstmon_dpth;
+ u8 vbstmon_loc;
+ u8 vbstmon_frm;
+ u8 vpbrstat_dpth;
+ u8 vpbrstat_loc;
+ u8 vpbrstat_frm;
+ u8 zerofill_dpth;
+ u8 zerofill_loc;
+ u8 zerofill_frm;
+};
+
+struct cs35l35_platform_data {
+
+ /* Stereo (2 Device) */
+ bool stereo;
+ /* serial port drive strength */
+ int sp_drv_str;
+ /* Boost Power Down with FET */
+ bool bst_pdn_fet_on;
+ /* Boost Voltage : used if ClassH Algo Enabled */
+ int bst_vctl;
+ /* Boost Converter Peak Current CTRL */
+ int bst_ipk;
+ /* Amp Gain Zero Cross */
+ bool gain_zc;
+ /* Audio Input Location */
+ int aud_channel;
+ /* Advisory Input Location */
+ int adv_channel;
+ /* Shared Boost for stereo */
+ bool shared_bst;
+ /* ClassH Algorithm */
+ struct classh_cfg classh_algo;
+ /* Monitor Config */
+ struct monitor_cfg mon_cfg;
+};
+
+#endif /* __CS35L35_H */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 9e1718a..3fd0a08 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -49,6 +49,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS35L32 if I2C
select SND_SOC_CS35L33 if I2C
select SND_SOC_CS35L34 if I2C
+ select SND_SOC_CS35L35 if I2C
select SND_SOC_CS42L42 if I2C
select SND_SOC_CS42L51_I2C if I2C
select SND_SOC_CS42L52 if I2C && INPUT
@@ -407,6 +408,10 @@ config SND_SOC_CS35L34
tristate "Cirrus Logic CS35L34 CODEC"
depends on I2C
+config SND_SOC_CS35L35
+ tristate "Cirrus Logic CS35L35 CODEC"
+ depends on I2C
+
config SND_SOC_CS42L42
tristate "Cirrus Logic CS42L42 CODEC"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7e1dad7..a5622f6 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -39,6 +39,7 @@ snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs35l32-objs := cs35l32.o
snd-soc-cs35l33-objs := cs35l33.o
snd-soc-cs35l34-objs := cs35l34.o
+snd-soc-cs35l35-objs := cs35l35.o
snd-soc-cs42l42-objs := cs42l42.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
@@ -268,6 +269,7 @@ obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o
obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o
+obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o
obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
new file mode 100644
index 0000000..ea84430
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.c
@@ -0,0 +1,1357 @@
+/*
+ * cs35l35.c -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin(a)cirrus.com>
+ * Li Xu <li.xu(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/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l35.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+
+#include "cs35l35.h"
+
+static const struct reg_default cs35l35_reg[] = {
+ {CS35L35_PWRCTL1, 0x01},
+ {CS35L35_PWRCTL2, 0x11},
+ {CS35L35_PWRCTL3, 0x00},
+ {CS35L35_CLK_CTL1, 0x04},
+ {CS35L35_CLK_CTL2, 0x10},
+ {CS35L35_CLK_CTL3, 0xCF},
+ {CS35L35_SP_FMT_CTL1, 0x20},
+ {CS35L35_SP_FMT_CTL2, 0x00},
+ {CS35L35_SP_FMT_CTL3, 0x02},
+ {CS35L35_MAG_COMP_CTL, 0x00},
+ {CS35L35_AMP_INP_DRV_CTL, 0x01},
+ {CS35L35_AMP_DIG_VOL_CTL, 0x12},
+ {CS35L35_AMP_DIG_VOL, 0x00},
+ {CS35L35_ADV_DIG_VOL, 0x00},
+ {CS35L35_PROTECT_CTL, 0x06},
+ {CS35L35_AMP_GAIN_AUD_CTL, 0x13},
+ {CS35L35_AMP_GAIN_PDM_CTL, 0x00},
+ {CS35L35_AMP_GAIN_ADV_CTL, 0x00},
+ {CS35L35_GPI_CTL, 0x00},
+ {CS35L35_BST_CVTR_V_CTL, 0x00},
+ {CS35L35_BST_PEAK_I, 0x07},
+ {CS35L35_BST_RAMP_CTL, 0x85},
+ {CS35L35_BST_CONV_COEF_1, 0x20},
+ {CS35L35_BST_CONV_COEF_2, 0x20},
+ {CS35L35_BST_CONV_SLOPE_COMP, 0x47},
+ {CS35L35_BST_CONV_SW_FREQ, 0x04},
+ {CS35L35_CLASS_H_CTL, 0x0B},
+ {CS35L35_CLASS_H_HEADRM_CTL, 0x0B},
+ {CS35L35_CLASS_H_RELEASE_RATE, 0x08},
+ {CS35L35_CLASS_H_FET_DRIVE_CTL, 0x41},
+ {CS35L35_CLASS_H_VP_CTL, 0xC5},
+ {CS35L35_VPBR_CTL, 0x0A},
+ {CS35L35_VPBR_VOL_CTL, 0x09},
+ {CS35L35_VPBR_TIMING_CTL, 0x6A},
+ {CS35L35_VPBR_MODE_VOL_CTL, 0x00},
+ {CS35L35_SPKR_MON_CTL, 0xC0},
+ {CS35L35_IMON_SCALE_CTL, 0x30},
+ {CS35L35_AUDIN_RXLOC_CTL, 0x00},
+ {CS35L35_ADVIN_RXLOC_CTL, 0x80},
+ {CS35L35_VMON_TXLOC_CTL, 0x00},
+ {CS35L35_IMON_TXLOC_CTL, 0x80},
+ {CS35L35_VPMON_TXLOC_CTL, 0x04},
+ {CS35L35_VBSTMON_TXLOC_CTL, 0x84},
+ {CS35L35_VPBR_STATUS_TXLOC_CTL, 0x04},
+ {CS35L35_ZERO_FILL_LOC_CTL, 0x00},
+ {CS35L35_AUDIN_DEPTH_CTL, 0x0F},
+ {CS35L35_SPKMON_DEPTH_CTL, 0x0F},
+ {CS35L35_SUPMON_DEPTH_CTL, 0x0F},
+ {CS35L35_ZEROFILL_DEPTH_CTL, 0x00},
+ {CS35L35_MULT_DEV_SYNCH1, 0x02},
+ {CS35L35_MULT_DEV_SYNCH2, 0x80},
+ {CS35L35_PROT_RELEASE_CTL, 0x00},
+ {CS35L35_DIAG_MODE_REG_LOCK, 0x00},
+ {CS35L35_DIAG_MODE_CTL_1, 0x40},
+ {CS35L35_DIAG_MODE_CTL_2, 0x00},
+ {CS35L35_INT_MASK_1, 0xFF},
+ {CS35L35_INT_MASK_2, 0xFF},
+ {CS35L35_INT_MASK_3, 0xFF},
+ {CS35L35_INT_MASK_4, 0xFF},
+
+};
+
+static bool cs35l35_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_DEVID_AB ... CS35L35_REV_ID:
+ case CS35L35_INT_STATUS_1:
+ case CS35L35_INT_STATUS_2:
+ case CS35L35_INT_STATUS_3:
+ case CS35L35_INT_STATUS_4:
+ case CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l35_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_DEVID_AB ... CS35L35_PWRCTL3:
+ case CS35L35_CLK_CTL1 ... CS35L35_SP_FMT_CTL3:
+ case CS35L35_MAG_COMP_CTL ... CS35L35_AMP_GAIN_AUD_CTL:
+ case CS35L35_AMP_GAIN_PDM_CTL ... CS35L35_BST_PEAK_I:
+ case CS35L35_BST_RAMP_CTL ... CS35L35_BST_CONV_SW_FREQ:
+ case CS35L35_CLASS_H_CTL ... CS35L35_CLASS_H_VP_CTL:
+ case CS35L35_CLASS_H_STATUS:
+ case CS35L35_VPBR_CTL ... CS35L35_VPBR_MODE_VOL_CTL:
+ case CS35L35_VPBR_ATTEN_STATUS:
+ case CS35L35_SPKR_MON_CTL:
+ case CS35L35_IMON_SCALE_CTL ... CS35L35_ZEROFILL_DEPTH_CTL:
+ case CS35L35_MULT_DEV_SYNCH1 ... CS35L35_PROT_RELEASE_CTL:
+ case CS35L35_DIAG_MODE_REG_LOCK ... CS35L35_DIAG_MODE_CTL_2:
+ case CS35L35_INT_MASK_1 ... CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l35_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_INT_STATUS_1:
+ case CS35L35_INT_STATUS_2:
+ case CS35L35_INT_STATUS_3:
+ case CS35L35_INT_STATUS_4:
+ case CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int cs35l35_sdin_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK, 0 << CS35L35_MCLK_DIS_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_DISCHG_FILT_MASK, 0 << CS35L35_DISCHG_FILT_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 1);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_DISCHG_FILT_MASK, 1 << CS35L35_DISCHG_FILT_SHIFT);
+
+ ret = wait_for_completion_timeout(&cs35l35->pdn_done,
+ msecs_to_jiffies(100));
+ if (ret == 0) {
+ pr_err("TIMEOUT PDN_DONE did not complete in 100ms\n");
+ ret = -ETIMEDOUT;
+ }
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK, 1 << CS35L35_MCLK_DIS_SHIFT);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int cs35l35_main_amp_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg[4];
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 0 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 0 << CS35L35_PDN_BST_FETOFF_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 0 << CS35L35_AMP_MUTE_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ /* If PDM mode we must use VP
+ * for Voltage control
+ */
+ if (cs35l35->pdm_mode)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_BST_CVTR_V_CTL, CS35L35_BST_CTL_MASK,
+ 0 << CS35L35_BST_CTL_SHIFT);
+ for (i = 0; i < 2; i++)
+ regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1,
+ ®, ARRAY_SIZE(reg));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ /* If PDM mode we should switch back to pdata value
+ * for Voltage control when we go down
+ */
+ if (cs35l35->pdm_mode)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_BST_CVTR_V_CTL, CS35L35_BST_CTL_MASK,
+ cs35l35->pdata.bst_vctl << CS35L35_BST_CTL_SHIFT);
+
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l35_aud_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Audio Volume", CS35L35_AMP_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("AMP Audio Gain", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0,
+ amp_gain_tlv),
+ SOC_SINGLE_TLV("AMP PDM Gain", CS35L35_AMP_GAIN_PDM_CTL, 0, 19, 0,
+ amp_gain_tlv),
+};
+
+static const struct snd_kcontrol_new cs35l35_adv_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Advisory Volume", CS35L35_ADV_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("AMP Advisory Gain", CS35L35_AMP_GAIN_ADV_CTL, 0, 19, 0,
+ amp_gain_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs35l35_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L35_PWRCTL3, 1, 1,
+ cs35l35_sdin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L35_PWRCTL3, 2, 1),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VBST"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L35_PWRCTL2, 7, 1),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L35_PWRCTL2, 6, 1),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L35_PWRCTL3, 3, 1),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L35_PWRCTL3, 4, 1),
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L35_PWRCTL2, 5, 1),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L35_PWRCTL2, 0, 1, NULL, 0,
+ cs35l35_main_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l35_audio_map[] = {
+ {"VPMON ADC", NULL, "VP"},
+ {"VBSTMON ADC", NULL, "VBST"},
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VMON ADC", NULL, "VSENSE"},
+ {"SDOUT", NULL, "IMON ADC"},
+ {"SDOUT", NULL, "VMON ADC"},
+ {"SDOUT", NULL, "VBSTMON ADC"},
+ {"SDOUT", NULL, "VPMON ADC"},
+ {"AMP Capture", NULL, "SDOUT"},
+
+ {"SDIN", NULL, "AMP Playback"},
+ {"CLASS H", NULL, "SDIN"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"SPK", NULL, "Main AMP"},
+};
+
+static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
+ cs35l35->slave_mode = false;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
+ cs35l35->slave_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs35l35->i2s_mode = true;
+ cs35l35->pdm_mode = false;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ cs35l35->pdm_mode = true;
+ cs35l35->i2s_mode = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct cs35l35_sysclk_config {
+ int sysclk;
+ int srate;
+ u8 clk_cfg;
+};
+
+static struct cs35l35_sysclk_config cs35l35_clk_ctl[] = {
+
+ /* SYSCLK, Sample Rate, Serial Port Cfg */
+ {5644800, 44100, 0x00},
+ {5644800, 88200, 0x40},
+ {6144000, 48000, 0x10},
+ {6144000, 96000, 0x50},
+ {11289600, 44100, 0x01},
+ {11289600, 88200, 0x41},
+ {11289600, 176400, 0x81},
+ {12000000, 44100, 0x03},
+ {12000000, 48000, 0x13},
+ {12000000, 88200, 0x43},
+ {12000000, 96000, 0x53},
+ {12000000, 176400, 0x83},
+ {12000000, 192000, 0x93},
+ {12288000, 48000, 0x11},
+ {12288000, 96000, 0x51},
+ {12288000, 192000, 0x91},
+ {13000000, 44100, 0x07},
+ {13000000, 48000, 0x17},
+ {13000000, 88200, 0x47},
+ {13000000, 96000, 0x57},
+ {13000000, 176400, 0x87},
+ {13000000, 192000, 0x97},
+ {22579200, 44100, 0x02},
+ {22579200, 88200, 0x42},
+ {22579200, 176400, 0x82},
+ {24000000, 44100, 0x0B},
+ {24000000, 48000, 0x1B},
+ {24000000, 88200, 0x4B},
+ {24000000, 96000, 0x5B},
+ {24000000, 176400, 0x8B},
+ {24000000, 192000, 0x9B},
+ {24576000, 48000, 0x12},
+ {24576000, 96000, 0x52},
+ {24576000, 192000, 0x92},
+ {26000000, 44100, 0x0F},
+ {26000000, 48000, 0x1F},
+ {26000000, 88200, 0x4F},
+ {26000000, 96000, 0x5F},
+ {26000000, 176400, 0x8F},
+ {26000000, 192000, 0x9F},
+};
+
+static int cs35l35_get_clk_config(int sysclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) {
+ if (cs35l35_clk_ctl[i].sysclk == sysclk &&
+ cs35l35_clk_ctl[i].srate == srate)
+ return cs35l35_clk_ctl[i].clk_cfg;
+ }
+ return -EINVAL;
+}
+
+static int cs35l35_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+ int srate = params_rate(params);
+ int ret = 0;
+ u8 sp_sclks;
+ int audin_format;
+ int errata_chk;
+
+ int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate);
+
+ if (clk_ctl < 0) {
+ dev_err(codec->dev, "Invalid CLK:Rate %d:%d\n",
+ cs35l35->sysclk, srate);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2,
+ CS35L35_CLK_CTL2_MASK, clk_ctl);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set port config %d\n", ret);
+ return ret;
+ }
+
+ /* Rev A0 Errata
+ *
+ * When configured for the weak-drive detection path (CH_WKFET_DIS = 0)
+ * the Class H algorithm does not enable weak-drive operation for
+ * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10
+ *
+ */
+ errata_chk = clk_ctl & CS35L35_SP_RATE_MASK;
+
+ if (classh->classh_wk_fet_disable == 0x00 &&
+ (errata_chk == 0x01 || errata_chk == 0x03)) {
+ ret = regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DEL_MASK,
+ 0 << CS35L35_CH_WKFET_DEL_SHIFT);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set fet config %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+/*
+ * You can pull more Monitor data from the SDOUT pin than going to SDIN
+ * Just make sure your SCLK is fast enough to fill the frame
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (params_width(params)) {
+ case 8:
+ audin_format = CS35L35_SDIN_DEPTH_8;
+ break;
+ case 16:
+ audin_format = CS35L35_SDIN_DEPTH_16;
+ break;
+ case 24:
+ audin_format = CS35L35_SDIN_DEPTH_24;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported Width %d\n",
+ params_width(params));
+ }
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_DEPTH_CTL, CS35L35_AUDIN_DEPTH_MASK,
+ audin_format << CS35L35_AUDIN_DEPTH_SHIFT);
+ if (cs35l35->pdata.stereo) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_DEPTH_CTL, CS35L35_ADVIN_DEPTH_MASK,
+ audin_format << CS35L35_ADVIN_DEPTH_SHIFT);
+ }
+ }
+/* We have to take the SCLK to derive num sclks
+ * to configure the CLOCK_CTL3 register correctly
+ */
+ if ((cs35l35->sclk / srate) % 4) {
+ dev_err(codec->dev, "Unsupported sclk/fs ratio %d:%d\n",
+ cs35l35->sclk, srate);
+ return -EINVAL;
+ }
+ sp_sclks = ((cs35l35->sclk / srate) / 4) - 1;
+
+ if (cs35l35->i2s_mode) {
+ /* Only certain ratios are supported in I2S Slave Mode */
+ if (cs35l35->slave_mode) {
+ switch (sp_sclks) {
+ case CS35L35_SP_SCLKS_32FS:
+ case CS35L35_SP_SCLKS_48FS:
+ case CS35L35_SP_SCLKS_64FS:
+ break;
+ default:
+ dev_err(codec->dev, "ratio not supported\n");
+ return -EINVAL;
+ };
+ } else {
+ /* Only certain ratios supported in I2S MASTER Mode */
+ switch (sp_sclks) {
+ case CS35L35_SP_SCLKS_32FS:
+ case CS35L35_SP_SCLKS_64FS:
+ break;
+ default:
+ dev_err(codec->dev, "ratio not supported\n");
+ return -EINVAL;
+ };
+ }
+ ret = regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLK_CTL3, CS35L35_SP_SCLKS_MASK,
+ sp_sclks << CS35L35_SP_SCLKS_SHIFT);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set fsclk %d\n", ret);
+ return ret;
+ }
+ }
+ if (cs35l35->pdm_mode) {
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK, 1 << CS35L35_PDM_MODE_SHIFT);
+ } else {
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK, 0 << CS35L35_PDM_MODE_SHIFT);
+ }
+ return ret;
+}
+
+static const unsigned int cs35l35_src_rates[] = {
+ 44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_constraints = {
+ .count = ARRAY_SIZE(cs35l35_src_rates),
+ .list = cs35l35_src_rates,
+};
+
+static int cs35l35_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, &cs35l35_constraints);
+ return 0;
+}
+
+static const unsigned int cs35l35_pdm_rates[] = {
+ 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = {
+ .count = ARRAY_SIZE(cs35l35_pdm_rates),
+ .list = cs35l35_pdm_rates,
+};
+
+static int cs35l35_pdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l35_pdm_constraints);
+ return 0;
+}
+
+static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+ /* Need the SCLK Frequency */
+ cs35l35->sclk = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l35_ops = {
+ .startup = cs35l35_pcm_startup,
+ .set_fmt = cs35l35_set_dai_fmt,
+ .hw_params = cs35l35_pcm_hw_params,
+ .set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops cs35l35_pdm_ops = {
+ .startup = cs35l35_pdm_startup,
+ .set_fmt = cs35l35_set_dai_fmt,
+ .hw_params = cs35l35_pcm_hw_params,
+ .set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs35l35_dai[] = {
+ {
+ .name = "cs35l35-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .ops = &cs35l35_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "cs35l35-pdm",
+ .id = 1,
+ .playback = {
+ .stream_name = "PDM Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .ops = &cs35l35_pdm_ops,
+ },
+};
+
+static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq,
+ int dir)
+{
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ int clksrc;
+ int ret = 0;
+
+ switch (clk_id) {
+ case 0:
+ clksrc = CS35L35_CLK_SOURCE_MCLK;
+ break;
+ case 1:
+ clksrc = CS35L35_CLK_SOURCE_SCLK;
+ break;
+ case 2:
+ clksrc = CS35L35_CLK_SOURCE_PDM;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid CLK Source\n");
+ return -EINVAL;
+ };
+
+ switch (freq) {
+ case 5644800:
+ case 6144000:
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 22579200:
+ case 24000000:
+ case 24576000:
+ case 26000000:
+ cs35l35->sysclk = freq;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid CLK Frequency\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_CLK_SOURCE_MASK, clksrc << CS35L35_CLK_SOURCE_SHIFT);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set sysclk %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cs35l35_codec_probe(struct snd_soc_codec *codec)
+{
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+ struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg;
+ int ret;
+
+ /* Set Platform Data */
+ if (cs35l35->pdata.bst_vctl)
+ regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK, cs35l35->pdata.bst_vctl);
+
+ if (cs35l35->pdata.bst_ipk)
+ regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I,
+ CS35L35_BST_IPK_MASK,
+ cs35l35->pdata.bst_ipk << CS35L35_BST_IPK_SHIFT);
+
+ if (cs35l35->pdata.gain_zc)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_GAIN_ZC_MASK,
+ cs35l35->pdata.gain_zc << CS35L35_AMP_GAIN_ZC_SHIFT);
+
+ if (cs35l35->pdata.aud_channel)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_RXLOC_CTL,
+ CS35L35_AUD_IN_LR_MASK,
+ cs35l35->pdata.aud_channel << CS35L35_AUD_IN_LR_SHIFT);
+
+ if (cs35l35->pdata.stereo) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ADVIN_RXLOC_CTL, CS35L35_ADV_IN_LR_MASK,
+ cs35l35->pdata.adv_channel << CS35L35_ADV_IN_LR_SHIFT);
+ if (cs35l35->pdata.shared_bst)
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL,
+ CS35L35_CH_STEREO_MASK, 1 << CS35L35_CH_STEREO_SHIFT);
+ ret = snd_soc_add_codec_controls(codec, cs35l35_adv_controls,
+ ARRAY_SIZE(cs35l35_adv_controls));
+ if (ret)
+ return ret;
+ }
+
+ if (cs35l35->pdata.sp_drv_str)
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_SP_DRV_MASK,
+ cs35l35->pdata.sp_drv_str << CS35L35_SP_DRV_SHIFT);
+
+ if (classh->classh_algo_enable) {
+ if (classh->classh_bst_override)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL, CS35L35_CH_BST_OVR_MASK,
+ classh->classh_bst_override << CS35L35_CH_BST_OVR_SHIFT);
+ if (classh->classh_bst_max_limit)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL, CS35L35_CH_BST_LIM_MASK,
+ classh->classh_bst_max_limit << CS35L35_CH_BST_LIM_SHIFT);
+ if (classh->classh_mem_depth)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL, CS35L35_CH_MEM_DEPTH_MASK,
+ classh->classh_mem_depth << CS35L35_CH_MEM_DEPTH_SHIFT);
+ if (classh->classh_headroom)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_HEADRM_CTL, CS35L35_CH_HDRM_CTL_MASK,
+ classh->classh_headroom << CS35L35_CH_HDRM_CTL_SHIFT);
+ if (classh->classh_release_rate)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_RELEASE_RATE, CS35L35_CH_REL_RATE_MASK,
+ classh->classh_release_rate << CS35L35_CH_REL_RATE_SHIFT);
+ if (classh->classh_wk_fet_disable)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DIS_MASK,
+ classh->classh_wk_fet_disable << CS35L35_CH_WKFET_DIS_SHIFT);
+ if (classh->classh_wk_fet_delay)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DEL_MASK,
+ classh->classh_wk_fet_delay << CS35L35_CH_WKFET_DEL_SHIFT);
+ if (classh->classh_wk_fet_thld)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_THLD_MASK,
+ classh->classh_wk_fet_thld << CS35L35_CH_WKFET_THLD_SHIFT);
+ if (classh->classh_vpch_auto)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_AUTO_MASK,
+ classh->classh_vpch_auto << CS35L35_CH_VP_AUTO_SHIFT);
+ if (classh->classh_vpch_rate)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_RATE_MASK,
+ classh->classh_vpch_rate << CS35L35_CH_VP_RATE_SHIFT);
+ if (classh->classh_vpch_man)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_MAN_MASK,
+ classh->classh_vpch_man << CS35L35_CH_VP_MAN_SHIFT);
+ }
+
+ if (monitor_config->is_present) {
+ if (monitor_config->vmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SPKMON_DEPTH_CTL, CS35L35_VMON_DEPTH_MASK,
+ monitor_config->vmon_dpth << CS35L35_VMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->vmon_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->vmon_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->imon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SPKMON_DEPTH_CTL, CS35L35_IMON_DEPTH_MASK,
+ monitor_config->imon_dpth << CS35L35_IMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->imon_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->imon_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vpmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL, CS35L35_VPMON_DEPTH_MASK,
+ monitor_config->vpmon_dpth << CS35L35_VPMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->vpmon_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->vpmon_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vbstmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL, CS35L35_VBSTMON_DEPTH_MASK,
+ monitor_config->vpmon_dpth << CS35L35_VBSTMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VBSTMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->vbstmon_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VBSTMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->vbstmon_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vpbrstat_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL, CS35L35_VPBRSTAT_DEPTH_MASK,
+ monitor_config->vpbrstat_dpth << CS35L35_VPBRSTAT_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPBR_STATUS_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->vpbrstat_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPBR_STATUS_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->vpbrstat_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->zerofill_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL, CS35L35_ZEROFILL_DEPTH_MASK,
+ monitor_config->zerofill_dpth << CS35L35_ZEROFILL_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ZERO_FILL_LOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->zerofill_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ZERO_FILL_LOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->zerofill_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ }
+
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l35 = {
+ .probe = cs35l35_codec_probe,
+ .set_sysclk = cs35l35_codec_set_sysclk,
+ .component_driver = {
+ .controls = cs35l35_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l35_aud_controls),
+ .dapm_widgets = cs35l35_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l35_dapm_widgets),
+
+ .dapm_routes = cs35l35_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l35_audio_map),
+ },
+};
+
+static struct regmap_config cs35l35_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L35_MAX_REGISTER,
+ .reg_defaults = cs35l35_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l35_reg),
+ .volatile_reg = cs35l35_volatile_register,
+ .readable_reg = cs35l35_readable_register,
+ .precious_reg = cs35l35_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static irqreturn_t cs35l35_irq(int irq, void *data)
+{
+ struct cs35l35_private *cs35l35 = data;
+ unsigned int sticky1, sticky2, sticky3, sticky4;
+ unsigned int mask1, mask2, mask3, mask4, current1;
+
+ /* ack the irq by reading all status registers */
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_4, &sticky4);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_3, &sticky3);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_2, &sticky2);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &sticky1);
+
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_4, &mask4);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_3, &mask3);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_2, &mask2);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_1, &mask1);
+
+ /* Check to see if unmasked bits are active */
+ if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+ && !(sticky4 & ~mask4))
+ return IRQ_NONE;
+
+ if (sticky2 & CS35L35_PDN_DONE)
+ complete(&cs35l35->pdn_done);
+
+ /* read the current values */
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, ¤t1);
+
+ /* handle the interrupts */
+ if (sticky1 & CS35L35_CAL_ERR) {
+ pr_err("%s : Calibration Error\n", __func__);
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_CAL_ERR)) {
+ pr_debug("%s : Cal error release\n", __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS,
+ CS35L35_CAL_ERR_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_AMP_SHORT) {
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_AMP_SHORT)) {
+ pr_debug("%s :Amp short error release\n", __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS,
+ CS35L35_SHORT_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_OTW) {
+ pr_err("%s : Over temperature warning\n", __func__);
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTW)) {
+ pr_debug("%s : Over temperature warning release\n",
+ __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS,
+ CS35L35_OTW_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_OTE) {
+ pr_crit("%s : Over temperature error\n", __func__);
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTE)) {
+ pr_debug("%s : Over temperature error release\n",
+ __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS,
+ CS35L35_OTE_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky3 & CS35L35_BST_HIGH) {
+ pr_crit("%s : VBST error: powering off!\n", __func__);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+ }
+
+ if (sticky3 & CS35L35_LBST_SHORT) {
+ pr_crit("%s : LBST error: powering off!\n", __func__);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+ }
+
+ if (sticky2 & CS35L35_VPBR_ERR)
+ pr_err("%s : Error: Reactive Brownout\n", __func__);
+
+ if (sticky4 & CS35L35_VMON_OVFL)
+ pr_err("%s : Error: VMON overflow\n", __func__);
+
+ if (sticky4 & CS35L35_IMON_OVFL)
+ pr_err("%s : Error: IMON overflow\n", __func__);
+
+ return IRQ_HANDLED;
+}
+
+
+static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l35_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ struct device_node *classh, *signal_format;
+ struct classh_cfg *classh_config = &pdata->classh_algo;
+ struct monitor_cfg *monitor_config = &pdata->mon_cfg;
+ unsigned int val32 = 0;
+ u8 monitor_array[3];
+ int ret = 0;
+
+ if (!np)
+ return 0;
+
+ pdata->bst_pdn_fet_on = of_property_read_bool(np,
+ "cirrus,boost-pdn-fet-on");
+
+ if (of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32) >= 0)
+ pdata->bst_vctl = val32;
+
+ if (of_property_read_u32(np, "cirrus,boost-ipk-milliamp", &val32) >= 0)
+ pdata->bst_ipk = val32;
+
+ if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0)
+ pdata->sp_drv_str = val32;
+
+ pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config");
+
+ if (pdata->stereo) {
+ if (of_property_read_u32(np, "cirrus,audio-channel", &val32) >= 0)
+ pdata->aud_channel = val32;
+ if (of_property_read_u32(np, "cirrus,advisory-channel",
+ &val32) >= 0)
+ pdata->adv_channel = val32;
+ pdata->shared_bst = of_property_read_bool(np,
+ "cirrus,shared-boost");
+ }
+
+ pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc");
+
+ classh = of_get_child_by_name(np, "cirrus,classh-internal-algo");
+ classh_config->classh_algo_enable = classh ? true : false;
+
+ if (classh_config->classh_algo_enable) {
+ classh_config->classh_bst_override =
+ of_property_read_bool(np, "cirrus,classh-bst-overide");
+
+ if (of_property_read_u32(classh, "cirrus,classh-bst-max-limit",
+ &val32) >= 0)
+ classh_config->classh_bst_max_limit = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-mem-depth",
+ &val32) >= 0)
+ classh_config->classh_mem_depth = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-release-rate",
+ &val32) >= 0)
+ classh_config->classh_release_rate = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-headroom",
+ &val32) >= 0)
+ classh_config->classh_headroom = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-wk-fet-disable",
+ &val32) >= 0)
+ classh_config->classh_wk_fet_disable = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-wk-fet-delay",
+ &val32) >= 0)
+ classh_config->classh_wk_fet_delay = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-wk-fet-thld",
+ &val32) >= 0)
+ classh_config->classh_wk_fet_thld = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-vpch-auto",
+ &val32) >= 0)
+ classh_config->classh_vpch_auto = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-vpch-rate",
+ &val32) >= 0)
+ classh_config->classh_vpch_rate = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-vpch-man",
+ &val32) >= 0)
+ classh_config->classh_vpch_man = val32;
+ }
+ of_node_put(classh);
+
+ /* frame depth location */
+ signal_format = of_get_child_by_name(np, "cirrus,monitor-signal-format");
+ monitor_config->is_present = signal_format ? true : false;
+ if (monitor_config->is_present) {
+ ret = of_property_read_u8_array(signal_format, "cirrus,imon",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->imon_specs = true;
+ monitor_config->imon_dpth = monitor_array[0];
+ monitor_config->imon_loc = monitor_array[1];
+ monitor_config->imon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vmon",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->vmon_specs = true;
+ monitor_config->vmon_dpth = monitor_array[0];
+ monitor_config->vmon_loc = monitor_array[1];
+ monitor_config->vmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vpmon",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->vpmon_specs = true;
+ monitor_config->vpmon_dpth = monitor_array[0];
+ monitor_config->vpmon_loc = monitor_array[1];
+ monitor_config->vpmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->vbstmon_specs = true;
+ monitor_config->vbstmon_dpth = monitor_array[0];
+ monitor_config->vbstmon_loc = monitor_array[1];
+ monitor_config->vbstmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->vpbrstat_specs = true;
+ monitor_config->vpbrstat_dpth = monitor_array[0];
+ monitor_config->vpbrstat_loc = monitor_array[1];
+ monitor_config->vpbrstat_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,zerofill",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->zerofill_specs = true;
+ monitor_config->zerofill_dpth = monitor_array[0];
+ monitor_config->zerofill_loc = monitor_array[1];
+ monitor_config->zerofill_frm = monitor_array[2];
+ }
+ }
+ of_node_put(signal_format);
+
+ return 0;
+}
+
+/* Errata Rev A0 */
+static const struct reg_sequence cs35l35_errata_patch[] = {
+
+ { 0x7F, 0x99 },
+ { 0x00, 0x99 },
+ { 0x52, 0x22 },
+ { 0x04, 0x14 },
+ { 0x6D, 0x44 },
+ { 0x24, 0x10 },
+ { 0x58, 0xC4 },
+ { 0x00, 0x98 },
+ { 0x18, 0x08 },
+ { 0x00, 0x00 },
+ { 0x7F, 0x00 },
+};
+
+static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs35l35_private *cs35l35;
+ struct cs35l35_platform_data *pdata =
+ dev_get_platdata(&i2c_client->dev);
+ int i;
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs35l35 = devm_kzalloc(&i2c_client->dev,
+ sizeof(struct cs35l35_private),
+ GFP_KERNEL);
+ if (!cs35l35) {
+ dev_err(&i2c_client->dev, "could not allocate codec\n");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(i2c_client, cs35l35);
+ cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap);
+ if (IS_ERR(cs35l35->regmap)) {
+ ret = PTR_ERR(cs35l35->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++)
+ cs35l35->supplies[i].supply = cs35l35_supplies[i];
+ cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies);
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l35->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev,
+ sizeof(struct cs35l35_platform_data),
+ GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&i2c_client->dev,
+ "could not allocate pdata\n");
+ return -ENOMEM;
+ }
+ if (i2c_client->dev.of_node) {
+ ret = cs35l35_handle_of_data(i2c_client, pdata);
+ if (ret != 0)
+ return ret;
+
+ }
+ cs35l35->pdata = *pdata;
+ }
+
+ ret = regulator_bulk_enable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* returning NULL can be an option if in stereo mode */
+ cs35l35->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l35->reset_gpio))
+ return PTR_ERR(cs35l35->reset_gpio);
+
+ if (cs35l35->reset_gpio)
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 1);
+
+ init_completion(&cs35l35->pdn_done);
+
+ ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch,
+ ARRAY_SIZE(cs35l35_errata_patch));
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+ cs35l35_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l35", cs35l35);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+ /* initialize codec */
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, ®);
+
+ devid = (reg & 0xFF) << 12;
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, ®);
+ devid |= (reg & 0xFF) << 4;
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, ®);
+ devid |= (reg & 0xF0) >> 4;
+
+ if (devid != CS35L35_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS35L35 Device ID (%X). Expected ID %X\n",
+ devid, CS35L35_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, ®);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS35L35 (%x), Revision: %02X\n", devid,
+ ret & 0xFF);
+
+ /* Set the INT Masks for critical errors */
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1, CS35L35_INT1_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2, CS35L35_INT2_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3, CS35L35_INT3_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4, CS35L35_INT4_CRIT_MASK);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PWR2_PDN_MASK, CS35L35_PWR2_PDN_MASK);
+
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3,
+ CS35L35_PWR3_PDN_MASK, CS35L35_PWR3_PDN_MASK);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_dev_cs35l35, cs35l35_dai,
+ ARRAY_SIZE(cs35l35_dai));
+ if (ret < 0) {
+ dev_err(&i2c_client->dev,
+ "%s: Register codec failed\n", __func__);
+ goto err;
+ }
+
+err:
+ regulator_bulk_disable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ return ret;
+}
+
+static int cs35l35_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct of_device_id cs35l35_of_match[] = {
+ {.compatible = "cirrus,cs35l35"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l35_of_match);
+
+static const struct i2c_device_id cs35l35_id[] = {
+ {"cs35l35", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l35_id);
+
+static struct i2c_driver cs35l35_i2c_driver = {
+ .driver = {
+ .name = "cs35l35",
+ .of_match_table = cs35l35_of_match,
+ },
+ .id_table = cs35l35_id,
+ .probe = cs35l35_i2c_probe,
+ .remove = cs35l35_i2c_remove,
+};
+
+module_i2c_driver(cs35l35_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L35 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin(a)cirrus.com>");
+MODULE_AUTHOR("Li Xu, Cirrus Logic Inc, <li.xu(a)cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
new file mode 100644
index 0000000..767227e
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.h
@@ -0,0 +1,285 @@
+/*
+ * cs35l35.h -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin(a)cirrus.com>
+ * Li Xu <li.xu(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 __CS35L35_H__
+#define __CS35L35_H__
+
+#define CS35L35_FIRSTREG 0x01
+#define CS35L35_LASTREG 0x7E
+#define CS35L35_CHIP_ID 0x00035A35
+#define CS35L35_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L35_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L35_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L35_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L35_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L35_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L35_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L35_PWRCTL3 0x08 /* Power Ctl 3 */
+#define CS35L35_CLK_CTL1 0x0A /* Clocking Ctl 1 */
+#define CS35L35_CLK_CTL2 0x0B /* Clocking Ctl 2 */
+#define CS35L35_CLK_CTL3 0x0C /* Clocking Ctl 3 */
+#define CS35L35_SP_FMT_CTL1 0x0D /* Serial Port Format CTL1 */
+#define CS35L35_SP_FMT_CTL2 0x0E /* Serial Port Format CTL2 */
+#define CS35L35_SP_FMT_CTL3 0x0F /* Serial Port Format CTL3 */
+#define CS35L35_MAG_COMP_CTL 0x13 /* Magnitude Comp CTL */
+#define CS35L35_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */
+#define CS35L35_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */
+#define CS35L35_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */
+#define CS35L35_ADV_DIG_VOL 0x17 /* Advisory Digital Volume */
+#define CS35L35_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */
+#define CS35L35_AMP_GAIN_AUD_CTL 0x19 /* Amp Serial Port Gain Ctl */
+#define CS35L35_AMP_GAIN_PDM_CTL 0x1A /* Amplifier Gain PDM Ctl */
+#define CS35L35_AMP_GAIN_ADV_CTL 0x1B /* Amplifier Gain Ctl */
+#define CS35L35_GPI_CTL 0x1C /* GPI Ctl */
+#define CS35L35_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */
+#define CS35L35_BST_PEAK_I 0x1E /* Boost Conv Peak Current */
+#define CS35L35_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */
+#define CS35L35_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */
+#define CS35L35_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */
+#define CS35L35_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */
+#define CS35L35_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */
+#define CS35L35_CLASS_H_CTL 0x30 /* CLS H Control */
+#define CS35L35_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */
+#define CS35L35_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */
+#define CS35L35_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */
+#define CS35L35_CLASS_H_VP_CTL 0x34 /* CLS H VP Ctl */
+#define CS35L35_CLASS_H_STATUS 0x38 /* CLS H Status */
+#define CS35L35_VPBR_CTL 0x3A /* VPBR Ctl */
+#define CS35L35_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */
+#define CS35L35_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */
+#define CS35L35_VPBR_MODE_VOL_CTL 0x3D /* VPBR Mode/Attack Vol Ctl */
+#define CS35L35_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */
+#define CS35L35_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */
+#define CS35L35_IMON_SCALE_CTL 0x51 /* IMON Scale Ctl */
+#define CS35L35_AUDIN_RXLOC_CTL 0x52 /* Audio Input RX Loc Ctl */
+#define CS35L35_ADVIN_RXLOC_CTL 0x53 /* Advisory Input RX Loc Ctl */
+#define CS35L35_VMON_TXLOC_CTL 0x54 /* VMON TX Loc Ctl */
+#define CS35L35_IMON_TXLOC_CTL 0x55 /* IMON TX Loc Ctl */
+#define CS35L35_VPMON_TXLOC_CTL 0x56 /* VPMON TX Loc Ctl */
+#define CS35L35_VBSTMON_TXLOC_CTL 0x57 /* VBSTMON TX Loc Ctl */
+#define CS35L35_VPBR_STATUS_TXLOC_CTL 0x58 /* VPBR Status TX Loc Ctl */
+#define CS35L35_ZERO_FILL_LOC_CTL 0x59 /* Zero Fill Loc Ctl */
+#define CS35L35_AUDIN_DEPTH_CTL 0x5A /* Audio Input Depth Ctl */
+#define CS35L35_SPKMON_DEPTH_CTL 0x5B /* SPK Mon Output Depth Ctl */
+#define CS35L35_SUPMON_DEPTH_CTL 0x5C /* Supply Mon Out Depth Ctl */
+#define CS35L35_ZEROFILL_DEPTH_CTL 0x5D /* Zero Fill Mon Output Ctl */
+#define CS35L35_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */
+#define CS35L35_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */
+#define CS35L35_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */
+#define CS35L35_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */
+#define CS35L35_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */
+#define CS35L35_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */
+#define CS35L35_INT_MASK_1 0x70 /* Interrupt Mask 1 */
+#define CS35L35_INT_MASK_2 0x71 /* Interrupt Mask 2 */
+#define CS35L35_INT_MASK_3 0x72 /* Interrupt Mask 3 */
+#define CS35L35_INT_MASK_4 0x73 /* Interrupt Mask 4 */
+#define CS35L35_INT_STATUS_1 0x74 /* Interrupt Status 1 */
+#define CS35L35_INT_STATUS_2 0x75 /* Interrupt Status 2 */
+#define CS35L35_INT_STATUS_3 0x76 /* Interrupt Status 3 */
+#define CS35L35_INT_STATUS_4 0x77 /* Interrupt Status 4 */
+#define CS35L35_PLL_STATUS 0x78 /* PLL Status */
+#define CS35L35_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */
+
+#define CS35L35_MAX_REGISTER 0x7F
+
+/* CS35L35_PWRCTL1 */
+#define CS35L35_SFT_RST 0x80
+#define CS35L35_DISCHG_FLT 0x02
+#define CS35L35_PDN_ALL 0x01
+
+/* CS35L35_PWRCTL2 */
+#define CS35L35_PDN_VMON 0x80
+#define CS35L35_PDN_IMON 0x40
+#define CS35L35_PDN_CLASSH 0x20
+#define CS35L35_PDN_VPBR 0x10
+#define CS35L35_PDN_BST 0x04
+#define CS35L35_PDN_AMP 0x01
+
+/* CS35L35_PWRCTL3 */
+#define CS35L35_PDN_VBSTMON_OUT 0x10
+#define CS35L35_PDN_VMON_OUT 0x08
+
+#define CS35L35_AUDIN_DEPTH_MASK 0x03
+#define CS35L35_AUDIN_DEPTH_SHIFT 0
+#define CS35L35_ADVIN_DEPTH_MASK 0x12
+#define CS35L35_ADVIN_DEPTH_SHIFT 2
+#define CS35L35_SDIN_DEPTH_8 0x01
+#define CS35L35_SDIN_DEPTH_16 0x02
+#define CS35L35_SDIN_DEPTH_24 0x03
+
+#define CS35L35_SDOUT_DEPTH_8 0x01
+#define CS35L35_SDOUT_DEPTH_12 0x02
+#define CS35L35_SDOUT_DEPTH_16 0x03
+
+#define CS35L35_AUD_IN_LR_MASK 0x80
+#define CS35L35_AUD_IN_LR_SHIFT 7
+#define CS35L35_ADV_IN_LR_MASK 0x80
+#define CS35L35_ADV_IN_LR_SHIFT 7
+#define CS35L35_AUD_IN_LOC_MASK 0x0F
+#define CS35L35_AUD_IN_LOC_SHIFT 0
+#define CS35L35_ADV_IN_LOC_MASK 0x0F
+#define CS35L35_ADV_IN_LOC_SHIFT 0
+
+#define CS35L35_IMON_DEPTH_MASK 0x03
+#define CS35L35_IMON_DEPTH_SHIFT 0
+#define CS35L35_VMON_DEPTH_MASK 0x0C
+#define CS35L35_VMON_DEPTH_SHIFT 2
+#define CS35L35_VBSTMON_DEPTH_MASK 0x03
+#define CS35L35_VBSTMON_DEPTH_SHIFT 0
+#define CS35L35_VPMON_DEPTH_MASK 0x0C
+#define CS35L35_VPMON_DEPTH_SHIFT 2
+#define CS35L35_VPBRSTAT_DEPTH_MASK 0x18
+#define CS35L35_VPBRSTAT_DEPTH_SHIFT 4
+#define CS35L35_ZEROFILL_DEPTH_MASK 0x03
+#define CS35L35_ZEROFILL_DEPTH_SHIFT 0x00
+
+#define CS35L35_MON_TXLOC_MASK 0x3F
+#define CS35L35_MON_TXLOC_SHIFT 0
+#define CS35L35_MON_FRM_MASK 0x80
+#define CS35L35_MON_FRM_SHIFT 7
+
+#define CS35L35_MS_MASK 0x80
+#define CS35L35_MS_SHIFT 7
+#define CS35L35_SPMODE_MASK 0x40
+#define CS35L35_SP_DRV_MASK 0x10
+#define CS35L35_SP_DRV_SHIFT 4
+#define CS35L35_CLK_CTL2_MASK 0xFF
+#define CS35L35_PDM_MODE_MASK 0x40
+#define CS35L35_PDM_MODE_SHIFT 6
+#define CS35L35_CLK_SOURCE_MASK 0x03
+#define CS35L35_CLK_SOURCE_SHIFT 0
+#define CS35L35_CLK_SOURCE_MCLK 0
+#define CS35L35_CLK_SOURCE_SCLK 1
+#define CS35L35_CLK_SOURCE_PDM 2
+
+#define CS35L35_SP_SCLKS_MASK 0x0F
+#define CS35L35_SP_SCLKS_SHIFT 0x00
+#define CS35L35_SP_SCLKS_16FS 0x03
+#define CS35L35_SP_SCLKS_32FS 0x07
+#define CS35L35_SP_SCLKS_48FS 0x0B
+#define CS35L35_SP_SCLKS_64FS 0x0F
+#define CS35L35_SP_RATE_MASK 0xC0
+
+#define CS35L35_PDN_BST_MASK 0x06
+#define CS35L35_PDN_BST_FETON_SHIFT 1
+#define CS35L35_PDN_BST_FETOFF_SHIFT 2
+#define CS35L35_PWR2_PDN_MASK 0xE0
+#define CS35L35_PWR3_PDN_MASK 0x1E
+#define CS35L35_PDN_ALL_MASK 0x01
+#define CS35L35_DISCHG_FILT_MASK 0x02
+#define CS35L35_DISCHG_FILT_SHIFT 1
+#define CS35L35_MCLK_DIS_MASK 0x04
+#define CS35L35_MCLK_DIS_SHIFT 2
+
+#define CS35L35_BST_CTL_MASK 0x7F
+#define CS35L35_BST_CTL_SHIFT 0
+#define CS35L35_BST_IPK_MASK 0x1F
+#define CS35L35_BST_IPK_SHIFT 0
+#define CS35L35_AMP_MUTE_MASK 0x20
+#define CS35L35_AMP_MUTE_SHIFT 5
+#define CS35L35_AMP_GAIN_ZC_MASK 0x10
+#define CS35L35_AMP_GAIN_ZC_SHIFT 4
+
+/* Class H Algorithm Control */
+#define CS35L35_CH_STEREO_MASK 0x40
+#define CS35L35_CH_STEREO_SHIFT 6
+#define CS35L35_CH_BST_OVR_MASK 0x04
+#define CS35L35_CH_BST_OVR_SHIFT 2
+#define CS35L35_CH_BST_LIM_MASK 0x08
+#define CS35L35_CH_BST_LIM_SHIFT 3
+#define CS35L35_CH_MEM_DEPTH_MASK 0x01
+#define CS35L35_CH_MEM_DEPTH_SHIFT 0
+#define CS35L35_CH_HDRM_CTL_MASK 0x3F
+#define CS35L35_CH_HDRM_CTL_SHIFT 0
+#define CS35L35_CH_REL_RATE_MASK 0xFF
+#define CS35L35_CH_REL_RATE_SHIFT 0
+#define CS35L35_CH_WKFET_DIS_MASK 0x80
+#define CS35L35_CH_WKFET_DIS_SHIFT 7
+#define CS35L35_CH_WKFET_DEL_MASK 0x70
+#define CS35L35_CH_WKFET_DEL_SHIFT 4
+#define CS35L35_CH_WKFET_THLD_MASK 0x0F
+#define CS35L35_CH_WKFET_THLD_SHIFT 0
+#define CS35L35_CH_VP_AUTO_MASK 0x80
+#define CS35L35_CH_VP_AUTO_SHIFT 7
+#define CS35L35_CH_VP_RATE_MASK 0x60
+#define CS35L35_CH_VP_RATE_SHIFT 5
+#define CS35L35_CH_VP_MAN_MASK 0x1F
+#define CS35L35_CH_VP_MAN_SHIFT 0
+
+/* CS35L35_PROT_RELEASE_CTL */
+#define CS35L35_CAL_ERR_RLS 0x80
+#define CS35L35_SHORT_RLS 0x04
+#define CS35L35_OTW_RLS 0x02
+#define CS35L35_OTE_RLS 0x01
+
+/* INT Mask Registers */
+#define CS35L35_INT1_CRIT_MASK 0x38
+#define CS35L35_INT2_CRIT_MASK 0xEF
+#define CS35L35_INT3_CRIT_MASK 0xEE
+#define CS35L35_INT4_CRIT_MASK 0xFF
+
+/* PDN DONE Masks */
+#define CS35L35_M_PDN_DONE_SHIFT 4
+#define CS35L35_M_PDN_DONE_MASK 0x10
+
+/* CS35L35_INT_1 */
+#define CS35L35_CAL_ERR 0x80
+#define CS35L35_OTP_ERR 0x40
+#define CS35L35_LRCLK_ERR 0x20
+#define CS35L35_SPCLK_ERR 0x10
+#define CS35L35_MCLK_ERR 0x08
+#define CS35L35_AMP_SHORT 0x04
+#define CS35L35_OTW 0x02
+#define CS35L35_OTE 0x01
+
+/* CS35L35_INT_2 */
+#define CS35L35_PDN_DONE 0x10
+#define CS35L35_VPBR_ERR 0x02
+#define CS35L35_VPBR_CLR 0x01
+
+/* CS35L35_INT_3 */
+#define CS35L35_BST_HIGH 0x10
+#define CS35L35_BST_HIGH_FLAG 0x08
+#define CS35L35_BST_IPK_FLAG 0x04
+#define CS35L35_LBST_SHORT 0x01
+
+/* CS35L35_INT_4 */
+#define CS35L35_VMON_OVFL 0x08
+#define CS35L35_IMON_OVFL 0x04
+
+#define CS35L35_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct cs35l35_private {
+ struct snd_soc_codec *codec;
+ struct cs35l35_platform_data pdata;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[2];
+ int num_supplies;
+ int sysclk;
+ int sclk;
+ bool pdm_mode;
+ bool i2s_mode;
+ bool slave_mode;
+ /* GPIO for /RST */
+ struct gpio_desc *reset_gpio;
+ struct completion pdn_done;
+};
+
+static const char * const cs35l35_supplies[] = {
+ "VA",
+ "VP",
+};
+
+#endif
--
1.9.1
1
0
[alsa-devel] [PATCH 2/2] ASoC: cs35l35: Add device tree documentation for CS35L35
by Li Xu 13 Dec '16
by Li Xu 13 Dec '16
13 Dec '16
Add device tree documentation for Cirrus Logic CS35L35
speaker amplifier
Signed-off-by: Li Xu <li.xu(a)cirrus.com>
---
.../devicetree/bindings/sound/cs35l35.txt | 172 +++++++++++++++++++++
1 file changed, 172 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/cs35l35.txt
diff --git a/Documentation/devicetree/bindings/sound/cs35l35.txt b/Documentation/devicetree/bindings/sound/cs35l35.txt
new file mode 100644
index 0000000..8b13f67
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cs35l35.txt
@@ -0,0 +1,172 @@
+CS35L35 Speaker Amplifier
+
+Required properties:
+
+ - compatible : "cirrus,cs35l35"
+
+ - reg : the I2C address of the device for I2C
+
+ - VA-supply, VP-supply : power supplies for the device,
+ as covered in
+ Documentation/devicetree/bindings/regulator/regulator.txt.
+
+ - interrupt-parent : Specifies the phandle of the interrupt controller to
+ which the IRQs from CS35L35 are delivered to.
+ - interrupts : IRQ line info CS35L35.
+ (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+ for further information relating to interrupt properties)
+
+Optional properties:
+ - cirrus,reset-gpios : Active low GPIO used to reset the amplifier
+
+ - cirrus,stereo-config : Boolean to determine if there are 2 AMPs for a
+ Stereo configuration
+
+ - cirrus,audio-channel : Set Location of Audio Signal on Serial Port
+ 0 = Data Packet received on Left I2S Channel
+ 1 = Data Packet received on Right I2S Channel
+
+ - cirrus,advisory-channel : Set Location of Advisory Signal on Serial Port
+ 0 = Data Packet received on Left I2S Channel
+ 1 = Data Packet received on Right I2S Channel
+
+ - cirrus,shared-boost : Boolean to enable ClassH tracking of Advisory Signal
+ if 2 Devices share Boost BST_CTL
+
+ - cirrus,sp-drv-strength : Value for setting the Serial Port drive strength
+ Table 3-10 of the datasheet lists drive-strength specifications
+ 0 = 1x (Default)
+ 1 = .5x
+
+ - cirrus,bst-pdn-fet-on : Boolean to determine if the Boost PDN control
+ powers down with a rectification FET On or Off. If VSPK is supplied
+ externally then FET is off.
+
+ - cirrus,boost-ctl-millivolt : Boost Converter control word. Step Size of 100mV
+ 0x00 = (Default) VP
+ 0x01 = 2600mV
+ 0x41 = 9000mV
+
+ - cirrus,boost-ipk-milliamp : Boost-converter peak current limit.
+ Configures the peak current by monitoring the current through the boost FET.
+ Step size: 112mA
+ 0x00 = 1680mA
+ 0x19 = 4480mA
+
+ - cirrus,amp-gain-zc : Boolean to determine if to use Amplifier gain-change
+ zero-cross
+
+Optional H/G Algorithm sub-node:
+
+ The cs35l35 node can have a single "cirrus,classh-internal-algo" sub-node
+ that will disable automatic control of the internal H/G Algorithm.
+
+ It is strongly recommended that the Datasheet be referenced when adjusting
+ or using these Class H Algorithm controls over the internal Algorithm.
+ Serious damage can occur to the Device and surrounding components.
+
+ - cirrus,classh-internal-algo : Sub-node for the Internal Class H Algorithm
+ See Section 4.3 Internal Class H Algorithm in the Datasheet.
+ If not used, the device manages the ClassH Algorithm internally.
+
+Optional properties for the "cirrus,classh-internal-algo" Sub-node
+
+ Section 7.29 Class H Control
+ - cirrus,classh-bst-overide : Boolean
+ - cirrus,classh-bst-max-limit
+ - cirrus,classh-mem-depth
+
+ Section 7.30 Class H Headroom Control
+ - cirrus,classh-headroom
+
+ Section 7.31 Class H Release Rate
+ - cirrus,classh-release-rate
+
+ Section 7.32 Class H Weak FET Drive Control
+ - cirrus,classh-wk-fet-disable
+ - cirrus,classh-wk-fet-delay
+ - cirrus,classh-wk-fet-thld
+
+ Section 7.34 Class H VP Control
+ - cirrus,classh-vpch-auto
+ - cirrus,classh-vpch-rate
+ - cirrus,classh-vpch-man
+
+Optional Monitor Signal Format sub-node:
+
+ The cs35l35 node can have a single "cirrus,monitor-signal-format" sub-node
+ for adjusting the Depth, Location and Frame of the Monitoring Signals
+ for Algorithms.
+
+ See Sections 4.8.2 through 4.8.4 Serial-Port Control in the Datasheet
+
+ -cirrus,monitor-signal-format : Sub-node for the Monitor Signaling Formating
+ on the I2S Port. Each of the 3 8 bit values in the array contain the settings
+ for depth, location, and frame.
+
+ If not used, the defaults for the 6 monitor signals is used.
+
+ Sections 7.44 - 7.53 lists values for the depth, location, and frame
+ for each monitoring signal.
+
+ - cirrus,imon : 3 8 bit values to set the depth, location, and frame
+ of the IMON monitor signal.
+
+ - cirrus,vmon : 3 8 bit values to set the depth, location, and frame
+ of the VMON monitor signal.
+
+ - cirrus,vpmon : 3 8 bit values to set the depth, location, and frame
+ of the VPMON monitor signal.
+
+ - cirrus,vbstmon : 3 8 bit values to set the depth, location, and frame
+ of the VBSTMON monitor signal
+
+ - cirrus,vpbrstat : 3 8 bit values to set the depth, location, and frame
+ of the VPBRSTAT monitor signal
+
+ - cirrus,zerofill : 3 8 bit values to set the depth, location, and frame\
+ of the ZEROFILL packet in the monitor signal
+
+Example:
+
+cs35l35: audio-codec@20 {
+ compatible = "cirrus,cs35l35";
+ reg = <0x20>;
+ VA-supply = <&dummy_vreg>;
+ VP-supply = <&dummy_vreg>;
+ reset-gpios = <&axi_gpio 54 1>;
+ interrupt-parent = <&gpio8>;
+ interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+ cirrus,boost-ctl = <0x41>;
+
+ cirrus,stereo-config {
+ cirrus,audio-channel = <0x00>;
+ cirrus,advisory-channel = <0x01>;
+ cirrus,shared-boost;
+ };
+
+ cirrus,classh-internal-algo {
+ cirrus,classh-bst-overide;
+ cirrus,classh-bst-max-limit = <0x01>;
+ cirrus,classh-mem-depth = <0x01>;
+ cirrus,classh-release-rate = <0x08>;
+ cirrus,classh-headroom-millivolt = <0x0B>;
+ cirrus,classh-wk-fet-disable = <0x01>;
+ cirrus,classh-wk-fet-delay = <0x04>;
+ cirrus,classh-wk-fet-thld = <0x01>;
+ cirrus,classh-vpch-auto = <0x01>;
+ cirrus,classh-vpch-rate = <0x02>;
+ cirrus,classh-vpch-man = <0x05>;
+ };
+
+ /* Depth, Location, Frame */
+ cirrus,monitor-signal-format {
+ cirrus,imon = /bits/ 8 <0x03 0x00 0x01>;
+ cirrus,vmon = /bits/ 8 <0x03 0x00 0x00>;
+ cirrus,vpmon = /bits/ 8 <0x03 0x04 0x00>;
+ cirrus,vbstmon = /bits/ 8 <0x03 0x04 0x01>;
+ cirrus,vpbrstat = /bits/ 8 <0x00 0x04 0x00>;
+ cirrus,zerofill = /bits/ 8 <0x00 0x00 0x00>;
+ };
+
+};
--
1.9.1
1
0
[alsa-devel] [PATCH 1/2] ASoC: cs35l35: Add support for Cirrus CS35L35 Amplifier
by Li Xu 13 Dec '16
by Li Xu 13 Dec '16
13 Dec '16
Add driver support for Cirrus Logic CS35L35 boosted
speaker amplifier
Signed-off-by: Li Xu <li.xu(a)cirrus.com>
---
include/sound/cs35l35.h | 104 ++++
sound/soc/codecs/Kconfig | 5 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/cs35l35.c | 1355 ++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/cs35l35.h | 284 ++++++++++
5 files changed, 1750 insertions(+)
create mode 100644 include/sound/cs35l35.h
create mode 100644 sound/soc/codecs/cs35l35.c
create mode 100644 sound/soc/codecs/cs35l35.h
diff --git a/include/sound/cs35l35.h b/include/sound/cs35l35.h
new file mode 100644
index 0000000..005a813
--- /dev/null
+++ b/include/sound/cs35l35.h
@@ -0,0 +1,104 @@
+/*
+ * linux/sound/cs35l35.h -- Platform data for CS35l35
+ *
+ * Copyright (c) 2016 Cirrus Logic Inc.
+ *
+ * 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 __CS35L35_H
+#define __CS35L35_H
+
+struct classh_cfg {
+ /*
+ * Class H Algorithm Control Variables
+ * You can either have it done
+ * automatically or you can adjust
+ * these variables for tuning
+ *
+ * if you do not enable the internal algorithm
+ * you will get a set of mixer controls for
+ * Class H tuning
+ *
+ * Section 4.3 of the datasheet
+ */
+ /* Internal ClassH Algorithm */
+ bool classh_bst_override;
+ bool classh_algo_enable;
+ int classh_bst_max_limit;
+ int classh_mem_depth;
+ int classh_release_rate;
+ int classh_headroom;
+ int classh_wk_fet_disable;
+ int classh_wk_fet_delay;
+ int classh_wk_fet_thld;
+ int classh_vpch_auto;
+ int classh_vpch_rate;
+ int classh_vpch_man;
+};
+
+struct monitor_cfg {
+ /*
+ * Signal Monitor Data
+ * highly configurable signal monitoring
+ * data positioning and different types of
+ * monitoring data.
+ *
+ * Section 4.8.2 - 4.8.4 of the datasheet
+ */
+ bool is_present;
+ bool imon_specs;
+ bool vmon_specs;
+ bool vpmon_specs;
+ bool vbstmon_specs;
+ bool vpbrstat_specs;
+ bool zerofill_specs;
+ u8 imon_dpth;
+ u8 imon_loc;
+ u8 imon_frm;
+ u8 vmon_dpth;
+ u8 vmon_loc;
+ u8 vmon_frm;
+ u8 vpmon_dpth;
+ u8 vpmon_loc;
+ u8 vpmon_frm;
+ u8 vbstmon_dpth;
+ u8 vbstmon_loc;
+ u8 vbstmon_frm;
+ u8 vpbrstat_dpth;
+ u8 vpbrstat_loc;
+ u8 vpbrstat_frm;
+ u8 zerofill_dpth;
+ u8 zerofill_loc;
+ u8 zerofill_frm;
+};
+
+struct cs35l35_platform_data {
+
+ /* Stereo (2 Device) */
+ bool stereo;
+ /* serial port drive strength */
+ int sp_drv_str;
+ /* Boost Power Down with FET */
+ bool bst_pdn_fet_on;
+ /* Boost Voltage : used if ClassH Algo Enabled */
+ int bst_vctl;
+ /* Boost Converter Peak Current CTRL */
+ int bst_ipk;
+ /* Amp Gain Zero Cross */
+ bool gain_zc;
+ /* Audio Input Location */
+ int aud_channel;
+ /* Advisory Input Location */
+ int adv_channel;
+ /* Shared Boost for stereo */
+ bool shared_bst;
+ /* ClassH Algorithm */
+ struct classh_cfg classh_algo;
+ /* Monitor Config */
+ struct monitor_cfg mon_cfg;
+};
+
+#endif /* __CS35L35_H */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 9e1718a..3fd0a08 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -49,6 +49,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS35L32 if I2C
select SND_SOC_CS35L33 if I2C
select SND_SOC_CS35L34 if I2C
+ select SND_SOC_CS35L35 if I2C
select SND_SOC_CS42L42 if I2C
select SND_SOC_CS42L51_I2C if I2C
select SND_SOC_CS42L52 if I2C && INPUT
@@ -407,6 +408,10 @@ config SND_SOC_CS35L34
tristate "Cirrus Logic CS35L34 CODEC"
depends on I2C
+config SND_SOC_CS35L35
+ tristate "Cirrus Logic CS35L35 CODEC"
+ depends on I2C
+
config SND_SOC_CS42L42
tristate "Cirrus Logic CS42L42 CODEC"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7e1dad7..a5622f6 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -39,6 +39,7 @@ snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs35l32-objs := cs35l32.o
snd-soc-cs35l33-objs := cs35l33.o
snd-soc-cs35l34-objs := cs35l34.o
+snd-soc-cs35l35-objs := cs35l35.o
snd-soc-cs42l42-objs := cs42l42.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
@@ -268,6 +269,7 @@ obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o
obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o
+obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o
obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
new file mode 100644
index 0000000..b05e900
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.c
@@ -0,0 +1,1355 @@
+/*
+ * cs35l35.c -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Li Xu <li.xu(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/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l35.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+
+#include "cs35l35.h"
+
+static const struct reg_default cs35l35_reg[] = {
+ {CS35L35_PWRCTL1, 0x01},
+ {CS35L35_PWRCTL2, 0x11},
+ {CS35L35_PWRCTL3, 0x00},
+ {CS35L35_CLK_CTL1, 0x04},
+ {CS35L35_CLK_CTL2, 0x10},
+ {CS35L35_CLK_CTL3, 0xCF},
+ {CS35L35_SP_FMT_CTL1, 0x20},
+ {CS35L35_SP_FMT_CTL2, 0x00},
+ {CS35L35_SP_FMT_CTL3, 0x02},
+ {CS35L35_MAG_COMP_CTL, 0x00},
+ {CS35L35_AMP_INP_DRV_CTL, 0x01},
+ {CS35L35_AMP_DIG_VOL_CTL, 0x12},
+ {CS35L35_AMP_DIG_VOL, 0x00},
+ {CS35L35_ADV_DIG_VOL, 0x00},
+ {CS35L35_PROTECT_CTL, 0x06},
+ {CS35L35_AMP_GAIN_AUD_CTL, 0x13},
+ {CS35L35_AMP_GAIN_PDM_CTL, 0x00},
+ {CS35L35_AMP_GAIN_ADV_CTL, 0x00},
+ {CS35L35_GPI_CTL, 0x00},
+ {CS35L35_BST_CVTR_V_CTL, 0x00},
+ {CS35L35_BST_PEAK_I, 0x07},
+ {CS35L35_BST_RAMP_CTL, 0x85},
+ {CS35L35_BST_CONV_COEF_1, 0x20},
+ {CS35L35_BST_CONV_COEF_2, 0x20},
+ {CS35L35_BST_CONV_SLOPE_COMP, 0x47},
+ {CS35L35_BST_CONV_SW_FREQ, 0x04},
+ {CS35L35_CLASS_H_CTL, 0x0B},
+ {CS35L35_CLASS_H_HEADRM_CTL, 0x0B},
+ {CS35L35_CLASS_H_RELEASE_RATE, 0x08},
+ {CS35L35_CLASS_H_FET_DRIVE_CTL, 0x41},
+ {CS35L35_CLASS_H_VP_CTL, 0xC5},
+ {CS35L35_VPBR_CTL, 0x0A},
+ {CS35L35_VPBR_VOL_CTL, 0x09},
+ {CS35L35_VPBR_TIMING_CTL, 0x6A},
+ {CS35L35_VPBR_MODE_VOL_CTL, 0x00},
+ {CS35L35_SPKR_MON_CTL, 0xC0},
+ {CS35L35_IMON_SCALE_CTL, 0x30},
+ {CS35L35_AUDIN_RXLOC_CTL, 0x00},
+ {CS35L35_ADVIN_RXLOC_CTL, 0x80},
+ {CS35L35_VMON_TXLOC_CTL, 0x00},
+ {CS35L35_IMON_TXLOC_CTL, 0x80},
+ {CS35L35_VPMON_TXLOC_CTL, 0x04},
+ {CS35L35_VBSTMON_TXLOC_CTL, 0x84},
+ {CS35L35_VPBR_STATUS_TXLOC_CTL, 0x04},
+ {CS35L35_ZERO_FILL_LOC_CTL, 0x00},
+ {CS35L35_AUDIN_DEPTH_CTL, 0x0F},
+ {CS35L35_SPKMON_DEPTH_CTL, 0x0F},
+ {CS35L35_SUPMON_DEPTH_CTL, 0x0F},
+ {CS35L35_ZEROFILL_DEPTH_CTL, 0x00},
+ {CS35L35_MULT_DEV_SYNCH1, 0x02},
+ {CS35L35_MULT_DEV_SYNCH2, 0x80},
+ {CS35L35_PROT_RELEASE_CTL, 0x00},
+ {CS35L35_DIAG_MODE_REG_LOCK, 0x00},
+ {CS35L35_DIAG_MODE_CTL_1, 0x40},
+ {CS35L35_DIAG_MODE_CTL_2, 0x00},
+ {CS35L35_INT_MASK_1, 0xFF},
+ {CS35L35_INT_MASK_2, 0xFF},
+ {CS35L35_INT_MASK_3, 0xFF},
+ {CS35L35_INT_MASK_4, 0xFF},
+
+};
+
+static bool cs35l35_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_DEVID_AB ... CS35L35_REV_ID:
+ case CS35L35_INT_STATUS_1:
+ case CS35L35_INT_STATUS_2:
+ case CS35L35_INT_STATUS_3:
+ case CS35L35_INT_STATUS_4:
+ case CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l35_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_DEVID_AB ... CS35L35_PWRCTL3:
+ case CS35L35_CLK_CTL1 ... CS35L35_SP_FMT_CTL3:
+ case CS35L35_MAG_COMP_CTL ... CS35L35_AMP_GAIN_AUD_CTL:
+ case CS35L35_AMP_GAIN_PDM_CTL ... CS35L35_BST_PEAK_I:
+ case CS35L35_BST_RAMP_CTL ... CS35L35_BST_CONV_SW_FREQ:
+ case CS35L35_CLASS_H_CTL ... CS35L35_CLASS_H_VP_CTL:
+ case CS35L35_CLASS_H_STATUS:
+ case CS35L35_VPBR_CTL ... CS35L35_VPBR_MODE_VOL_CTL:
+ case CS35L35_VPBR_ATTEN_STATUS:
+ case CS35L35_SPKR_MON_CTL:
+ case CS35L35_IMON_SCALE_CTL ... CS35L35_ZEROFILL_DEPTH_CTL:
+ case CS35L35_MULT_DEV_SYNCH1 ... CS35L35_PROT_RELEASE_CTL:
+ case CS35L35_DIAG_MODE_REG_LOCK ... CS35L35_DIAG_MODE_CTL_2:
+ case CS35L35_INT_MASK_1 ... CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l35_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_INT_STATUS_1:
+ case CS35L35_INT_STATUS_2:
+ case CS35L35_INT_STATUS_3:
+ case CS35L35_INT_STATUS_4:
+ case CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int cs35l35_sdin_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK, 0 << CS35L35_MCLK_DIS_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_DISCHG_FILT_MASK, 0 << CS35L35_DISCHG_FILT_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 1);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_DISCHG_FILT_MASK, 1 << CS35L35_DISCHG_FILT_SHIFT);
+
+ ret = wait_for_completion_timeout(&cs35l35->pdn_done,
+ msecs_to_jiffies(100));
+ if (ret == 0) {
+ pr_err("TIMEOUT PDN_DONE did not complete in 100ms\n");
+ ret = -ETIMEDOUT;
+ }
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK, 1 << CS35L35_MCLK_DIS_SHIFT);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int cs35l35_main_amp_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg[4];
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 0 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 0 << CS35L35_PDN_BST_FETOFF_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 0 << CS35L35_AMP_MUTE_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ /* If PDM mode we must use VP
+ * for Voltage control
+ */
+ if (cs35l35->pdm_mode)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_BST_CVTR_V_CTL, CS35L35_BST_CTL_MASK,
+ 0 << CS35L35_BST_CTL_SHIFT);
+ for (i = 0; i < 2; i++)
+ regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1,
+ ®, ARRAY_SIZE(reg));
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ /* If PDM mode we should switch back to pdata value
+ * for Voltage control when we go down
+ */
+ if (cs35l35->pdm_mode)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_BST_CVTR_V_CTL, CS35L35_BST_CTL_MASK,
+ cs35l35->pdata.bst_vctl << CS35L35_BST_CTL_SHIFT);
+
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l35_aud_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Audio Volume", CS35L35_AMP_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("AMP Audio Gain", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0,
+ amp_gain_tlv),
+ SOC_SINGLE_TLV("AMP PDM Gain", CS35L35_AMP_GAIN_PDM_CTL, 0, 19, 0,
+ amp_gain_tlv),
+};
+
+static const struct snd_kcontrol_new cs35l35_adv_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Advisory Volume", CS35L35_ADV_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("AMP Advisory Gain", CS35L35_AMP_GAIN_ADV_CTL, 0, 19, 0,
+ amp_gain_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs35l35_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L35_PWRCTL3, 1, 1,
+ cs35l35_sdin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L35_PWRCTL3, 2, 1),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VBST"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L35_PWRCTL2, 7, 1),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L35_PWRCTL2, 6, 1),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L35_PWRCTL3, 3, 1),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L35_PWRCTL3, 4, 1),
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L35_PWRCTL2, 5, 1),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L35_PWRCTL2, 0, 1, NULL, 0,
+ cs35l35_main_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l35_audio_map[] = {
+ {"VPMON ADC", NULL, "VP"},
+ {"VBSTMON ADC", NULL, "VBST"},
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VMON ADC", NULL, "VSENSE"},
+ {"SDOUT", NULL, "IMON ADC"},
+ {"SDOUT", NULL, "VMON ADC"},
+ {"SDOUT", NULL, "VBSTMON ADC"},
+ {"SDOUT", NULL, "VPMON ADC"},
+ {"AMP Capture", NULL, "SDOUT"},
+
+ {"SDIN", NULL, "AMP Playback"},
+ {"CLASS H", NULL, "SDIN"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"SPK", NULL, "Main AMP"},
+};
+
+static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
+ cs35l35->slave_mode = false;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
+ cs35l35->slave_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs35l35->i2s_mode = true;
+ cs35l35->pdm_mode = false;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ cs35l35->pdm_mode = true;
+ cs35l35->i2s_mode = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct cs35l35_sysclk_config {
+ int sysclk;
+ int srate;
+ u8 clk_cfg;
+};
+
+static struct cs35l35_sysclk_config cs35l35_clk_ctl[] = {
+
+ /* SYSCLK, Sample Rate, Serial Port Cfg */
+ {5644800, 44100, 0x00},
+ {5644800, 88200, 0x40},
+ {6144000, 48000, 0x10},
+ {6144000, 96000, 0x50},
+ {11289600, 44100, 0x01},
+ {11289600, 88200, 0x41},
+ {11289600, 176400, 0x81},
+ {12000000, 44100, 0x03},
+ {12000000, 48000, 0x13},
+ {12000000, 88200, 0x43},
+ {12000000, 96000, 0x53},
+ {12000000, 176400, 0x83},
+ {12000000, 192000, 0x93},
+ {12288000, 48000, 0x11},
+ {12288000, 96000, 0x51},
+ {12288000, 192000, 0x91},
+ {13000000, 44100, 0x07},
+ {13000000, 48000, 0x17},
+ {13000000, 88200, 0x47},
+ {13000000, 96000, 0x57},
+ {13000000, 176400, 0x87},
+ {13000000, 192000, 0x97},
+ {22579200, 44100, 0x02},
+ {22579200, 88200, 0x42},
+ {22579200, 176400, 0x82},
+ {24000000, 44100, 0x0B},
+ {24000000, 48000, 0x1B},
+ {24000000, 88200, 0x4B},
+ {24000000, 96000, 0x5B},
+ {24000000, 176400, 0x8B},
+ {24000000, 192000, 0x9B},
+ {24576000, 48000, 0x12},
+ {24576000, 96000, 0x52},
+ {24576000, 192000, 0x92},
+ {26000000, 44100, 0x0F},
+ {26000000, 48000, 0x1F},
+ {26000000, 88200, 0x4F},
+ {26000000, 96000, 0x5F},
+ {26000000, 176400, 0x8F},
+ {26000000, 192000, 0x9F},
+};
+
+static int cs35l35_get_clk_config(int sysclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) {
+ if (cs35l35_clk_ctl[i].sysclk == sysclk &&
+ cs35l35_clk_ctl[i].srate == srate)
+ return cs35l35_clk_ctl[i].clk_cfg;
+ }
+ return -EINVAL;
+}
+
+static int cs35l35_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+ int srate = params_rate(params);
+ int ret = 0;
+ u8 sp_sclks;
+ int audin_format;
+ int errata_chk;
+
+ int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate);
+
+ if (clk_ctl < 0) {
+ dev_err(codec->dev, "Invalid CLK:Rate %d:%d\n",
+ cs35l35->sysclk, srate);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2,
+ CS35L35_CLK_CTL2_MASK, clk_ctl);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set port config %d\n", ret);
+ return ret;
+ }
+
+ /* Rev A0 Errata
+ *
+ * When configured for the weak-drive detection path (CH_WKFET_DIS = 0)
+ * the Class H algorithm does not enable weak-drive operation for
+ * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10
+ *
+ */
+ errata_chk = clk_ctl & CS35L35_SP_RATE_MASK;
+
+ if (classh->classh_wk_fet_disable == 0x00 &&
+ (errata_chk == 0x01 || errata_chk == 0x03)) {
+ ret = regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DEL_MASK,
+ 0 << CS35L35_CH_WKFET_DEL_SHIFT);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set fet config %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+/*
+ * You can pull more Monitor data from the SDOUT pin than going to SDIN
+ * Just make sure your SCLK is fast enough to fill the frame
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (params_width(params)) {
+ case 8:
+ audin_format = CS35L35_SDIN_DEPTH_8;
+ break;
+ case 16:
+ audin_format = CS35L35_SDIN_DEPTH_16;
+ break;
+ case 24:
+ audin_format = CS35L35_SDIN_DEPTH_24;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported Width %d\n",
+ params_width(params));
+ }
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_DEPTH_CTL, CS35L35_AUDIN_DEPTH_MASK,
+ audin_format << CS35L35_AUDIN_DEPTH_SHIFT);
+ if (cs35l35->pdata.stereo) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_DEPTH_CTL, CS35L35_ADVIN_DEPTH_MASK,
+ audin_format << CS35L35_ADVIN_DEPTH_SHIFT);
+ }
+ }
+/* We have to take the SCLK to derive num sclks
+ * to configure the CLOCK_CTL3 register correctly
+ */
+ if ((cs35l35->sclk / srate) % 4) {
+ dev_err(codec->dev, "Unsupported sclk/fs ratio %d:%d\n",
+ cs35l35->sclk, srate);
+ return -EINVAL;
+ }
+ sp_sclks = ((cs35l35->sclk / srate) / 4) - 1;
+
+ if (cs35l35->i2s_mode) {
+ /* Only certain ratios are supported in I2S Slave Mode */
+ if (cs35l35->slave_mode) {
+ switch (sp_sclks) {
+ case CS35L35_SP_SCLKS_32FS:
+ case CS35L35_SP_SCLKS_48FS:
+ case CS35L35_SP_SCLKS_64FS:
+ break;
+ default:
+ dev_err(codec->dev, "ratio not supported\n");
+ return -EINVAL;
+ };
+ } else {
+ /* Only certain ratios supported in I2S MASTER Mode */
+ switch (sp_sclks) {
+ case CS35L35_SP_SCLKS_32FS:
+ case CS35L35_SP_SCLKS_64FS:
+ break;
+ default:
+ dev_err(codec->dev, "ratio not supported\n");
+ return -EINVAL;
+ };
+ }
+ ret = regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLK_CTL3, CS35L35_SP_SCLKS_MASK,
+ sp_sclks << CS35L35_SP_SCLKS_SHIFT);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set fsclk %d\n", ret);
+ return ret;
+ }
+ }
+ if (cs35l35->pdm_mode) {
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK, 1 << CS35L35_PDM_MODE_SHIFT);
+ } else {
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK, 0 << CS35L35_PDM_MODE_SHIFT);
+ }
+ return ret;
+}
+
+static const unsigned int cs35l35_src_rates[] = {
+ 44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_constraints = {
+ .count = ARRAY_SIZE(cs35l35_src_rates),
+ .list = cs35l35_src_rates,
+};
+
+static int cs35l35_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, &cs35l35_constraints);
+ return 0;
+}
+
+static const unsigned int cs35l35_pdm_rates[] = {
+ 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = {
+ .count = ARRAY_SIZE(cs35l35_pdm_rates),
+ .list = cs35l35_pdm_rates,
+};
+
+static int cs35l35_pdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l35_pdm_constraints);
+ return 0;
+}
+
+static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+ /* Need the SCLK Frequency */
+ cs35l35->sclk = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l35_ops = {
+ .startup = cs35l35_pcm_startup,
+ .set_fmt = cs35l35_set_dai_fmt,
+ .hw_params = cs35l35_pcm_hw_params,
+ .set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops cs35l35_pdm_ops = {
+ .startup = cs35l35_pdm_startup,
+ .set_fmt = cs35l35_set_dai_fmt,
+ .hw_params = cs35l35_pcm_hw_params,
+ .set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs35l35_dai[] = {
+ {
+ .name = "cs35l35-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .ops = &cs35l35_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "cs35l35-pdm",
+ .id = 1,
+ .playback = {
+ .stream_name = "PDM Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .ops = &cs35l35_pdm_ops,
+ },
+};
+
+static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq,
+ int dir)
+{
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ int clksrc;
+ int ret = 0;
+
+ switch (clk_id) {
+ case 0:
+ clksrc = CS35L35_CLK_SOURCE_MCLK;
+ break;
+ case 1:
+ clksrc = CS35L35_CLK_SOURCE_SCLK;
+ break;
+ case 2:
+ clksrc = CS35L35_CLK_SOURCE_PDM;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid CLK Source\n");
+ return -EINVAL;
+ };
+
+ switch (freq) {
+ case 5644800:
+ case 6144000:
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 22579200:
+ case 24000000:
+ case 24576000:
+ case 26000000:
+ cs35l35->sysclk = freq;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid CLK Frequency\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_CLK_SOURCE_MASK, clksrc << CS35L35_CLK_SOURCE_SHIFT);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set sysclk %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cs35l35_codec_probe(struct snd_soc_codec *codec)
+{
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+ struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg;
+ int ret;
+
+ /* Set Platform Data */
+ if (cs35l35->pdata.bst_vctl)
+ regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK, cs35l35->pdata.bst_vctl);
+
+ if (cs35l35->pdata.bst_ipk)
+ regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I,
+ CS35L35_BST_IPK_MASK,
+ cs35l35->pdata.bst_ipk << CS35L35_BST_IPK_SHIFT);
+
+ if (cs35l35->pdata.gain_zc)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_GAIN_ZC_MASK,
+ cs35l35->pdata.gain_zc << CS35L35_AMP_GAIN_ZC_SHIFT);
+
+ if (cs35l35->pdata.aud_channel)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_RXLOC_CTL,
+ CS35L35_AUD_IN_LR_MASK,
+ cs35l35->pdata.aud_channel << CS35L35_AUD_IN_LR_SHIFT);
+
+ if (cs35l35->pdata.stereo) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ADVIN_RXLOC_CTL, CS35L35_ADV_IN_LR_MASK,
+ cs35l35->pdata.adv_channel << CS35L35_ADV_IN_LR_SHIFT);
+ if (cs35l35->pdata.shared_bst)
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL,
+ CS35L35_CH_STEREO_MASK, 1 << CS35L35_CH_STEREO_SHIFT);
+ ret = snd_soc_add_codec_controls(codec, cs35l35_adv_controls,
+ ARRAY_SIZE(cs35l35_adv_controls));
+ if (ret)
+ return ret;
+ }
+
+ if (cs35l35->pdata.sp_drv_str)
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_SP_DRV_MASK,
+ cs35l35->pdata.sp_drv_str << CS35L35_SP_DRV_SHIFT);
+
+ if (classh->classh_algo_enable) {
+ if (classh->classh_bst_override)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL, CS35L35_CH_BST_OVR_MASK,
+ classh->classh_bst_override << CS35L35_CH_BST_OVR_SHIFT);
+ if (classh->classh_bst_max_limit)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL, CS35L35_CH_BST_LIM_MASK,
+ classh->classh_bst_max_limit << CS35L35_CH_BST_LIM_SHIFT);
+ if (classh->classh_mem_depth)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL, CS35L35_CH_MEM_DEPTH_MASK,
+ classh->classh_mem_depth << CS35L35_CH_MEM_DEPTH_SHIFT);
+ if (classh->classh_headroom)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_HEADRM_CTL, CS35L35_CH_HDRM_CTL_MASK,
+ classh->classh_headroom << CS35L35_CH_HDRM_CTL_SHIFT);
+ if (classh->classh_release_rate)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_RELEASE_RATE, CS35L35_CH_REL_RATE_MASK,
+ classh->classh_release_rate << CS35L35_CH_REL_RATE_SHIFT);
+ if (classh->classh_wk_fet_disable)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DIS_MASK,
+ classh->classh_wk_fet_disable << CS35L35_CH_WKFET_DIS_SHIFT);
+ if (classh->classh_wk_fet_delay)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DEL_MASK,
+ classh->classh_wk_fet_delay << CS35L35_CH_WKFET_DEL_SHIFT);
+ if (classh->classh_wk_fet_thld)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_THLD_MASK,
+ classh->classh_wk_fet_thld << CS35L35_CH_WKFET_THLD_SHIFT);
+ if (classh->classh_vpch_auto)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_AUTO_MASK,
+ classh->classh_vpch_auto << CS35L35_CH_VP_AUTO_SHIFT);
+ if (classh->classh_vpch_rate)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_RATE_MASK,
+ classh->classh_vpch_rate << CS35L35_CH_VP_RATE_SHIFT);
+ if (classh->classh_vpch_man)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_MAN_MASK,
+ classh->classh_vpch_man << CS35L35_CH_VP_MAN_SHIFT);
+ }
+
+ if (monitor_config->is_present) {
+ if (monitor_config->vmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SPKMON_DEPTH_CTL, CS35L35_VMON_DEPTH_MASK,
+ monitor_config->vmon_dpth << CS35L35_VMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->vmon_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->vmon_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->imon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SPKMON_DEPTH_CTL, CS35L35_IMON_DEPTH_MASK,
+ monitor_config->imon_dpth << CS35L35_IMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->imon_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->imon_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vpmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL, CS35L35_VPMON_DEPTH_MASK,
+ monitor_config->vpmon_dpth << CS35L35_VPMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->vpmon_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->vpmon_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vbstmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL, CS35L35_VBSTMON_DEPTH_MASK,
+ monitor_config->vpmon_dpth << CS35L35_VBSTMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VBSTMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->vbstmon_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VBSTMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->vbstmon_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vpbrstat_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL, CS35L35_VPBRSTAT_DEPTH_MASK,
+ monitor_config->vpbrstat_dpth << CS35L35_VPBRSTAT_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPBR_STATUS_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->vpbrstat_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPBR_STATUS_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->vpbrstat_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->zerofill_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL, CS35L35_ZEROFILL_DEPTH_MASK,
+ monitor_config->zerofill_dpth << CS35L35_ZEROFILL_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ZERO_FILL_LOC_CTL, CS35L35_MON_TXLOC_MASK,
+ monitor_config->zerofill_loc << CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ZERO_FILL_LOC_CTL, CS35L35_MON_FRM_MASK,
+ monitor_config->zerofill_frm << CS35L35_MON_FRM_SHIFT);
+ }
+ }
+
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l35 = {
+ .probe = cs35l35_codec_probe,
+ .set_sysclk = cs35l35_codec_set_sysclk,
+ .component_driver = {
+ .controls = cs35l35_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l35_aud_controls),
+ .dapm_widgets = cs35l35_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l35_dapm_widgets),
+
+ .dapm_routes = cs35l35_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l35_audio_map),
+ },
+};
+
+static struct regmap_config cs35l35_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L35_MAX_REGISTER,
+ .reg_defaults = cs35l35_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l35_reg),
+ .volatile_reg = cs35l35_volatile_register,
+ .readable_reg = cs35l35_readable_register,
+ .precious_reg = cs35l35_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static irqreturn_t cs35l35_irq(int irq, void *data)
+{
+ struct cs35l35_private *cs35l35 = data;
+ unsigned int sticky1, sticky2, sticky3, sticky4;
+ unsigned int mask1, mask2, mask3, mask4, current1;
+
+ /* ack the irq by reading all status registers */
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_4, &sticky4);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_3, &sticky3);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_2, &sticky2);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &sticky1);
+
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_4, &mask4);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_3, &mask3);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_2, &mask2);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_1, &mask1);
+
+ /* Check to see if unmasked bits are active */
+ if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+ && !(sticky4 & ~mask4))
+ return IRQ_NONE;
+
+ if (sticky2 & CS35L35_PDN_DONE)
+ complete(&cs35l35->pdn_done);
+
+ /* read the current values */
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, ¤t1);
+
+ /* handle the interrupts */
+ if (sticky1 & CS35L35_CAL_ERR) {
+ pr_err("%s : Calibration Error\n", __func__);
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_CAL_ERR)) {
+ pr_debug("%s : Cal error release\n", __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS,
+ CS35L35_CAL_ERR_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_AMP_SHORT) {
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_AMP_SHORT)) {
+ pr_debug("%s :Amp short error release\n", __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS,
+ CS35L35_SHORT_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_OTW) {
+ pr_err("%s : Over temperature warning\n", __func__);
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTW)) {
+ pr_debug("%s : Over temperature warning release\n",
+ __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS,
+ CS35L35_OTW_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_OTE) {
+ pr_crit("%s : Over temperature error\n", __func__);
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTE)) {
+ pr_debug("%s : Over temperature error release\n",
+ __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS,
+ CS35L35_OTE_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky3 & CS35L35_BST_HIGH) {
+ pr_crit("%s : VBST error: powering off!\n", __func__);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+ }
+
+ if (sticky3 & CS35L35_LBST_SHORT) {
+ pr_crit("%s : LBST error: powering off!\n", __func__);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+ }
+
+ if (sticky2 & CS35L35_VPBR_ERR)
+ pr_err("%s : Error: Reactive Brownout\n", __func__);
+
+ if (sticky4 & CS35L35_VMON_OVFL)
+ pr_err("%s : Error: VMON overflow\n", __func__);
+
+ if (sticky4 & CS35L35_IMON_OVFL)
+ pr_err("%s : Error: IMON overflow\n", __func__);
+
+ return IRQ_HANDLED;
+}
+
+
+static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l35_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ struct device_node *classh, *signal_format;
+ struct classh_cfg *classh_config = &pdata->classh_algo;
+ struct monitor_cfg *monitor_config = &pdata->mon_cfg;
+ unsigned int val32 = 0;
+ u8 monitor_array[3];
+ int ret = 0;
+
+ if (!np)
+ return 0;
+
+ pdata->bst_pdn_fet_on = of_property_read_bool(np,
+ "cirrus,boost-pdn-fet-on");
+
+ if (of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32) >= 0)
+ pdata->bst_vctl = val32;
+
+ if (of_property_read_u32(np, "cirrus,boost-ipk-milliamp", &val32) >= 0)
+ pdata->bst_ipk = val32;
+
+ if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0)
+ pdata->sp_drv_str = val32;
+
+ pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config");
+
+ if (pdata->stereo) {
+ if (of_property_read_u32(np, "cirrus,audio-channel", &val32) >= 0)
+ pdata->aud_channel = val32;
+ if (of_property_read_u32(np, "cirrus,advisory-channel",
+ &val32) >= 0)
+ pdata->adv_channel = val32;
+ pdata->shared_bst = of_property_read_bool(np,
+ "cirrus,shared-boost");
+ }
+
+ pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc");
+
+ classh = of_get_child_by_name(np, "cirrus,classh-internal-algo");
+ classh_config->classh_algo_enable = classh ? true : false;
+
+ if (classh_config->classh_algo_enable) {
+ classh_config->classh_bst_override =
+ of_property_read_bool(np, "cirrus,classh-bst-overide");
+
+ if (of_property_read_u32(classh, "cirrus,classh-bst-max-limit",
+ &val32) >= 0)
+ classh_config->classh_bst_max_limit = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-mem-depth",
+ &val32) >= 0)
+ classh_config->classh_mem_depth = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-release-rate",
+ &val32) >= 0)
+ classh_config->classh_release_rate = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-headroom",
+ &val32) >= 0)
+ classh_config->classh_headroom = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-wk-fet-disable",
+ &val32) >= 0)
+ classh_config->classh_wk_fet_disable = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-wk-fet-delay",
+ &val32) >= 0)
+ classh_config->classh_wk_fet_delay = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-wk-fet-thld",
+ &val32) >= 0)
+ classh_config->classh_wk_fet_thld = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-vpch-auto",
+ &val32) >= 0)
+ classh_config->classh_vpch_auto = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-vpch-rate",
+ &val32) >= 0)
+ classh_config->classh_vpch_rate = val32;
+ if (of_property_read_u32(classh, "cirrus,classh-vpch-man",
+ &val32) >= 0)
+ classh_config->classh_vpch_man = val32;
+ }
+ of_node_put(classh);
+
+ /* frame depth location */
+ signal_format = of_get_child_by_name(np, "cirrus,monitor-signal-format");
+ monitor_config->is_present = signal_format ? true : false;
+ if (monitor_config->is_present) {
+ ret = of_property_read_u8_array(signal_format, "cirrus,imon",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->imon_specs = true;
+ monitor_config->imon_dpth = monitor_array[0];
+ monitor_config->imon_loc = monitor_array[1];
+ monitor_config->imon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vmon",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->vmon_specs = true;
+ monitor_config->vmon_dpth = monitor_array[0];
+ monitor_config->vmon_loc = monitor_array[1];
+ monitor_config->vmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vpmon",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->vpmon_specs = true;
+ monitor_config->vpmon_dpth = monitor_array[0];
+ monitor_config->vpmon_loc = monitor_array[1];
+ monitor_config->vpmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->vbstmon_specs = true;
+ monitor_config->vbstmon_dpth = monitor_array[0];
+ monitor_config->vbstmon_loc = monitor_array[1];
+ monitor_config->vbstmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->vpbrstat_specs = true;
+ monitor_config->vpbrstat_dpth = monitor_array[0];
+ monitor_config->vpbrstat_loc = monitor_array[1];
+ monitor_config->vpbrstat_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,zerofill",
+ monitor_array, ARRAY_SIZE(monitor_array));
+ if (!ret) {
+ monitor_config->zerofill_specs = true;
+ monitor_config->zerofill_dpth = monitor_array[0];
+ monitor_config->zerofill_loc = monitor_array[1];
+ monitor_config->zerofill_frm = monitor_array[2];
+ }
+ }
+ of_node_put(signal_format);
+
+ return 0;
+}
+
+/* Errata Rev A0 */
+static const struct reg_sequence cs35l35_errata_patch[] = {
+
+ { 0x7F, 0x99 },
+ { 0x00, 0x99 },
+ { 0x52, 0x22 },
+ { 0x04, 0x14 },
+ { 0x6D, 0x44 },
+ { 0x24, 0x10 },
+ { 0x58, 0xC4 },
+ { 0x00, 0x98 },
+ { 0x18, 0x08 },
+ { 0x00, 0x00 },
+ { 0x7F, 0x00 },
+};
+
+static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs35l35_private *cs35l35;
+ struct cs35l35_platform_data *pdata =
+ dev_get_platdata(&i2c_client->dev);
+ int i;
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs35l35 = devm_kzalloc(&i2c_client->dev,
+ sizeof(struct cs35l35_private),
+ GFP_KERNEL);
+ if (!cs35l35) {
+ dev_err(&i2c_client->dev, "could not allocate codec\n");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(i2c_client, cs35l35);
+ cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap);
+ if (IS_ERR(cs35l35->regmap)) {
+ ret = PTR_ERR(cs35l35->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++)
+ cs35l35->supplies[i].supply = cs35l35_supplies[i];
+ cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies);
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l35->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev,
+ sizeof(struct cs35l35_platform_data),
+ GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&i2c_client->dev,
+ "could not allocate pdata\n");
+ return -ENOMEM;
+ }
+ if (i2c_client->dev.of_node) {
+ ret = cs35l35_handle_of_data(i2c_client, pdata);
+ if (ret != 0)
+ return ret;
+
+ }
+ cs35l35->pdata = *pdata;
+ }
+
+ ret = regulator_bulk_enable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* returning NULL can be an option if in stereo mode */
+ cs35l35->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l35->reset_gpio))
+ return PTR_ERR(cs35l35->reset_gpio);
+
+ if (cs35l35->reset_gpio)
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 1);
+
+ init_completion(&cs35l35->pdn_done);
+
+ ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch,
+ ARRAY_SIZE(cs35l35_errata_patch));
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+ cs35l35_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l35", cs35l35);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+ /* initialize codec */
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, ®);
+
+ devid = (reg & 0xFF) << 12;
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, ®);
+ devid |= (reg & 0xFF) << 4;
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, ®);
+ devid |= (reg & 0xF0) >> 4;
+
+ if (devid != CS35L35_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS35L35 Device ID (%X). Expected ID %X\n",
+ devid, CS35L35_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, ®);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS35L35 (%x), Revision: %02X\n", devid,
+ ret & 0xFF);
+
+ /* Set the INT Masks for critical errors */
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1, CS35L35_INT1_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2, CS35L35_INT2_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3, CS35L35_INT3_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4, CS35L35_INT4_CRIT_MASK);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PWR2_PDN_MASK, CS35L35_PWR2_PDN_MASK);
+
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3,
+ CS35L35_PWR3_PDN_MASK, CS35L35_PWR3_PDN_MASK);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_dev_cs35l35, cs35l35_dai,
+ ARRAY_SIZE(cs35l35_dai));
+ if (ret < 0) {
+ dev_err(&i2c_client->dev,
+ "%s: Register codec failed\n", __func__);
+ goto err;
+ }
+
+err:
+ regulator_bulk_disable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ return ret;
+}
+
+static int cs35l35_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct of_device_id cs35l35_of_match[] = {
+ {.compatible = "cirrus,cs35l35"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l35_of_match);
+
+static const struct i2c_device_id cs35l35_id[] = {
+ {"cs35l35", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l35_id);
+
+static struct i2c_driver cs35l35_i2c_driver = {
+ .driver = {
+ .name = "cs35l35",
+ .of_match_table = cs35l35_of_match,
+ },
+ .id_table = cs35l35_id,
+ .probe = cs35l35_i2c_probe,
+ .remove = cs35l35_i2c_remove,
+};
+
+module_i2c_driver(cs35l35_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L35 driver");
+MODULE_AUTHOR("Li Xu, Cirrus Logic Inc, <li.xu(a)cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
new file mode 100644
index 0000000..fb02785
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.h
@@ -0,0 +1,284 @@
+/*
+ * cs35l35.h -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Li Xu <li.xu(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 __CS35L35_H__
+#define __CS35L35_H__
+
+#define CS35L35_FIRSTREG 0x01
+#define CS35L35_LASTREG 0x7E
+#define CS35L35_CHIP_ID 0x00035A35
+#define CS35L35_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L35_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L35_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L35_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L35_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L35_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L35_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L35_PWRCTL3 0x08 /* Power Ctl 3 */
+#define CS35L35_CLK_CTL1 0x0A /* Clocking Ctl 1 */
+#define CS35L35_CLK_CTL2 0x0B /* Clocking Ctl 2 */
+#define CS35L35_CLK_CTL3 0x0C /* Clocking Ctl 3 */
+#define CS35L35_SP_FMT_CTL1 0x0D /* Serial Port Format CTL1 */
+#define CS35L35_SP_FMT_CTL2 0x0E /* Serial Port Format CTL2 */
+#define CS35L35_SP_FMT_CTL3 0x0F /* Serial Port Format CTL3 */
+#define CS35L35_MAG_COMP_CTL 0x13 /* Magnitude Comp CTL */
+#define CS35L35_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */
+#define CS35L35_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */
+#define CS35L35_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */
+#define CS35L35_ADV_DIG_VOL 0x17 /* Advisory Digital Volume */
+#define CS35L35_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */
+#define CS35L35_AMP_GAIN_AUD_CTL 0x19 /* Amp Serial Port Gain Ctl */
+#define CS35L35_AMP_GAIN_PDM_CTL 0x1A /* Amplifier Gain PDM Ctl */
+#define CS35L35_AMP_GAIN_ADV_CTL 0x1B /* Amplifier Gain Ctl */
+#define CS35L35_GPI_CTL 0x1C /* GPI Ctl */
+#define CS35L35_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */
+#define CS35L35_BST_PEAK_I 0x1E /* Boost Conv Peak Current */
+#define CS35L35_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */
+#define CS35L35_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */
+#define CS35L35_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */
+#define CS35L35_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */
+#define CS35L35_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */
+#define CS35L35_CLASS_H_CTL 0x30 /* CLS H Control */
+#define CS35L35_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */
+#define CS35L35_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */
+#define CS35L35_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */
+#define CS35L35_CLASS_H_VP_CTL 0x34 /* CLS H VP Ctl */
+#define CS35L35_CLASS_H_STATUS 0x38 /* CLS H Status */
+#define CS35L35_VPBR_CTL 0x3A /* VPBR Ctl */
+#define CS35L35_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */
+#define CS35L35_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */
+#define CS35L35_VPBR_MODE_VOL_CTL 0x3D /* VPBR Mode/Attack Vol Ctl */
+#define CS35L35_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */
+#define CS35L35_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */
+#define CS35L35_IMON_SCALE_CTL 0x51 /* IMON Scale Ctl */
+#define CS35L35_AUDIN_RXLOC_CTL 0x52 /* Audio Input RX Loc Ctl */
+#define CS35L35_ADVIN_RXLOC_CTL 0x53 /* Advisory Input RX Loc Ctl */
+#define CS35L35_VMON_TXLOC_CTL 0x54 /* VMON TX Loc Ctl */
+#define CS35L35_IMON_TXLOC_CTL 0x55 /* IMON TX Loc Ctl */
+#define CS35L35_VPMON_TXLOC_CTL 0x56 /* VPMON TX Loc Ctl */
+#define CS35L35_VBSTMON_TXLOC_CTL 0x57 /* VBSTMON TX Loc Ctl */
+#define CS35L35_VPBR_STATUS_TXLOC_CTL 0x58 /* VPBR Status TX Loc Ctl */
+#define CS35L35_ZERO_FILL_LOC_CTL 0x59 /* Zero Fill Loc Ctl */
+#define CS35L35_AUDIN_DEPTH_CTL 0x5A /* Audio Input Depth Ctl */
+#define CS35L35_SPKMON_DEPTH_CTL 0x5B /* SPK Mon Output Depth Ctl */
+#define CS35L35_SUPMON_DEPTH_CTL 0x5C /* Supply Mon Out Depth Ctl */
+#define CS35L35_ZEROFILL_DEPTH_CTL 0x5D /* Zero Fill Mon Output Ctl */
+#define CS35L35_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */
+#define CS35L35_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */
+#define CS35L35_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */
+#define CS35L35_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */
+#define CS35L35_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */
+#define CS35L35_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */
+#define CS35L35_INT_MASK_1 0x70 /* Interrupt Mask 1 */
+#define CS35L35_INT_MASK_2 0x71 /* Interrupt Mask 2 */
+#define CS35L35_INT_MASK_3 0x72 /* Interrupt Mask 3 */
+#define CS35L35_INT_MASK_4 0x73 /* Interrupt Mask 4 */
+#define CS35L35_INT_STATUS_1 0x74 /* Interrupt Status 1 */
+#define CS35L35_INT_STATUS_2 0x75 /* Interrupt Status 2 */
+#define CS35L35_INT_STATUS_3 0x76 /* Interrupt Status 3 */
+#define CS35L35_INT_STATUS_4 0x77 /* Interrupt Status 4 */
+#define CS35L35_PLL_STATUS 0x78 /* PLL Status */
+#define CS35L35_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */
+
+#define CS35L35_MAX_REGISTER 0x7F
+
+/* CS35L35_PWRCTL1 */
+#define CS35L35_SFT_RST 0x80
+#define CS35L35_DISCHG_FLT 0x02
+#define CS35L35_PDN_ALL 0x01
+
+/* CS35L35_PWRCTL2 */
+#define CS35L35_PDN_VMON 0x80
+#define CS35L35_PDN_IMON 0x40
+#define CS35L35_PDN_CLASSH 0x20
+#define CS35L35_PDN_VPBR 0x10
+#define CS35L35_PDN_BST 0x04
+#define CS35L35_PDN_AMP 0x01
+
+/* CS35L35_PWRCTL3 */
+#define CS35L35_PDN_VBSTMON_OUT 0x10
+#define CS35L35_PDN_VMON_OUT 0x08
+
+#define CS35L35_AUDIN_DEPTH_MASK 0x03
+#define CS35L35_AUDIN_DEPTH_SHIFT 0
+#define CS35L35_ADVIN_DEPTH_MASK 0x12
+#define CS35L35_ADVIN_DEPTH_SHIFT 2
+#define CS35L35_SDIN_DEPTH_8 0x01
+#define CS35L35_SDIN_DEPTH_16 0x02
+#define CS35L35_SDIN_DEPTH_24 0x03
+
+#define CS35L35_SDOUT_DEPTH_8 0x01
+#define CS35L35_SDOUT_DEPTH_12 0x02
+#define CS35L35_SDOUT_DEPTH_16 0x03
+
+#define CS35L35_AUD_IN_LR_MASK 0x80
+#define CS35L35_AUD_IN_LR_SHIFT 7
+#define CS35L35_ADV_IN_LR_MASK 0x80
+#define CS35L35_ADV_IN_LR_SHIFT 7
+#define CS35L35_AUD_IN_LOC_MASK 0x0F
+#define CS35L35_AUD_IN_LOC_SHIFT 0
+#define CS35L35_ADV_IN_LOC_MASK 0x0F
+#define CS35L35_ADV_IN_LOC_SHIFT 0
+
+#define CS35L35_IMON_DEPTH_MASK 0x03
+#define CS35L35_IMON_DEPTH_SHIFT 0
+#define CS35L35_VMON_DEPTH_MASK 0x0C
+#define CS35L35_VMON_DEPTH_SHIFT 2
+#define CS35L35_VBSTMON_DEPTH_MASK 0x03
+#define CS35L35_VBSTMON_DEPTH_SHIFT 0
+#define CS35L35_VPMON_DEPTH_MASK 0x0C
+#define CS35L35_VPMON_DEPTH_SHIFT 2
+#define CS35L35_VPBRSTAT_DEPTH_MASK 0x18
+#define CS35L35_VPBRSTAT_DEPTH_SHIFT 4
+#define CS35L35_ZEROFILL_DEPTH_MASK 0x03
+#define CS35L35_ZEROFILL_DEPTH_SHIFT 0x00
+
+#define CS35L35_MON_TXLOC_MASK 0x3F
+#define CS35L35_MON_TXLOC_SHIFT 0
+#define CS35L35_MON_FRM_MASK 0x80
+#define CS35L35_MON_FRM_SHIFT 7
+
+#define CS35L35_MS_MASK 0x80
+#define CS35L35_MS_SHIFT 7
+#define CS35L35_SPMODE_MASK 0x40
+#define CS35L35_SP_DRV_MASK 0x10
+#define CS35L35_SP_DRV_SHIFT 4
+#define CS35L35_CLK_CTL2_MASK 0xFF
+#define CS35L35_PDM_MODE_MASK 0x40
+#define CS35L35_PDM_MODE_SHIFT 6
+#define CS35L35_CLK_SOURCE_MASK 0x03
+#define CS35L35_CLK_SOURCE_SHIFT 0
+#define CS35L35_CLK_SOURCE_MCLK 0
+#define CS35L35_CLK_SOURCE_SCLK 1
+#define CS35L35_CLK_SOURCE_PDM 2
+
+#define CS35L35_SP_SCLKS_MASK 0x0F
+#define CS35L35_SP_SCLKS_SHIFT 0x00
+#define CS35L35_SP_SCLKS_16FS 0x03
+#define CS35L35_SP_SCLKS_32FS 0x07
+#define CS35L35_SP_SCLKS_48FS 0x0B
+#define CS35L35_SP_SCLKS_64FS 0x0F
+#define CS35L35_SP_RATE_MASK 0xC0
+
+#define CS35L35_PDN_BST_MASK 0x06
+#define CS35L35_PDN_BST_FETON_SHIFT 1
+#define CS35L35_PDN_BST_FETOFF_SHIFT 2
+#define CS35L35_PWR2_PDN_MASK 0xE0
+#define CS35L35_PWR3_PDN_MASK 0x1E
+#define CS35L35_PDN_ALL_MASK 0x01
+#define CS35L35_DISCHG_FILT_MASK 0x02
+#define CS35L35_DISCHG_FILT_SHIFT 1
+#define CS35L35_MCLK_DIS_MASK 0x04
+#define CS35L35_MCLK_DIS_SHIFT 2
+
+#define CS35L35_BST_CTL_MASK 0x7F
+#define CS35L35_BST_CTL_SHIFT 0
+#define CS35L35_BST_IPK_MASK 0x1F
+#define CS35L35_BST_IPK_SHIFT 0
+#define CS35L35_AMP_MUTE_MASK 0x20
+#define CS35L35_AMP_MUTE_SHIFT 5
+#define CS35L35_AMP_GAIN_ZC_MASK 0x10
+#define CS35L35_AMP_GAIN_ZC_SHIFT 4
+
+/* Class H Algorithm Control */
+#define CS35L35_CH_STEREO_MASK 0x40
+#define CS35L35_CH_STEREO_SHIFT 6
+#define CS35L35_CH_BST_OVR_MASK 0x04
+#define CS35L35_CH_BST_OVR_SHIFT 2
+#define CS35L35_CH_BST_LIM_MASK 0x08
+#define CS35L35_CH_BST_LIM_SHIFT 3
+#define CS35L35_CH_MEM_DEPTH_MASK 0x01
+#define CS35L35_CH_MEM_DEPTH_SHIFT 0
+#define CS35L35_CH_HDRM_CTL_MASK 0x3F
+#define CS35L35_CH_HDRM_CTL_SHIFT 0
+#define CS35L35_CH_REL_RATE_MASK 0xFF
+#define CS35L35_CH_REL_RATE_SHIFT 0
+#define CS35L35_CH_WKFET_DIS_MASK 0x80
+#define CS35L35_CH_WKFET_DIS_SHIFT 7
+#define CS35L35_CH_WKFET_DEL_MASK 0x70
+#define CS35L35_CH_WKFET_DEL_SHIFT 4
+#define CS35L35_CH_WKFET_THLD_MASK 0x0F
+#define CS35L35_CH_WKFET_THLD_SHIFT 0
+#define CS35L35_CH_VP_AUTO_MASK 0x80
+#define CS35L35_CH_VP_AUTO_SHIFT 7
+#define CS35L35_CH_VP_RATE_MASK 0x60
+#define CS35L35_CH_VP_RATE_SHIFT 5
+#define CS35L35_CH_VP_MAN_MASK 0x1F
+#define CS35L35_CH_VP_MAN_SHIFT 0
+
+/* CS35L35_PROT_RELEASE_CTL */
+#define CS35L35_CAL_ERR_RLS 0x80
+#define CS35L35_SHORT_RLS 0x04
+#define CS35L35_OTW_RLS 0x02
+#define CS35L35_OTE_RLS 0x01
+
+/* INT Mask Registers */
+#define CS35L35_INT1_CRIT_MASK 0x38
+#define CS35L35_INT2_CRIT_MASK 0xEF
+#define CS35L35_INT3_CRIT_MASK 0xEE
+#define CS35L35_INT4_CRIT_MASK 0xFF
+
+/* PDN DONE Masks */
+#define CS35L35_M_PDN_DONE_SHIFT 4
+#define CS35L35_M_PDN_DONE_MASK 0x10
+
+/* CS35L35_INT_1 */
+#define CS35L35_CAL_ERR 0x80
+#define CS35L35_OTP_ERR 0x40
+#define CS35L35_LRCLK_ERR 0x20
+#define CS35L35_SPCLK_ERR 0x10
+#define CS35L35_MCLK_ERR 0x08
+#define CS35L35_AMP_SHORT 0x04
+#define CS35L35_OTW 0x02
+#define CS35L35_OTE 0x01
+
+/* CS35L35_INT_2 */
+#define CS35L35_PDN_DONE 0x10
+#define CS35L35_VPBR_ERR 0x02
+#define CS35L35_VPBR_CLR 0x01
+
+/* CS35L35_INT_3 */
+#define CS35L35_BST_HIGH 0x10
+#define CS35L35_BST_HIGH_FLAG 0x08
+#define CS35L35_BST_IPK_FLAG 0x04
+#define CS35L35_LBST_SHORT 0x01
+
+/* CS35L35_INT_4 */
+#define CS35L35_VMON_OVFL 0x08
+#define CS35L35_IMON_OVFL 0x04
+
+#define CS35L35_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct cs35l35_private {
+ struct snd_soc_codec *codec;
+ struct cs35l35_platform_data pdata;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[2];
+ int num_supplies;
+ int sysclk;
+ int sclk;
+ bool pdm_mode;
+ bool i2s_mode;
+ bool slave_mode;
+ /* GPIO for /RST */
+ struct gpio_desc *reset_gpio;
+ struct completion pdn_done;
+};
+
+static const char * const cs35l35_supplies[] = {
+ "VA",
+ "VP",
+};
+
+#endif
--
1.9.1
1
0
[alsa-devel] [alsa-lib][PATCH] pcm: fix wrong document references to PCM APIs which perform direct memory access with frame copying
by Takashi Sakamoto 13 Dec '16
by Takashi Sakamoto 13 Dec '16
13 Dec '16
In a design of ALSA PCM interface, for PCM frame transmission to/from
kernel space, applications can select from two options; direct memory access
or ioctl(2). Available options are decided depending on device capacity and
machine architecture. Applications can get available options by the first
entry of 'struct snd_pcm_hw_params.masks'.
When the mask includes 'SNDRV_PCM_ACCESS_MMAP_xxx', applications can use
direct memory access. For this use case, userspace library has two types
of PCM API. One is to expose a pointer over the memory to start
reading/writing PCM frames. Another is to copy PCM frames between the
memory and a given buffer.
Current documentation includes wrong references to these APIs to describe
their advantages/disadvantages. This confuses application developers
because the references indicate PCM APIs to execute ioctl(2) operation to
read/write PCM frames.
This commit fixes the bug.
Signed-off-by: Takashi Sakamoto <o-takashi(a)sakamocchi.jp>
---
src/pcm/pcm.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index f2ca02b..0cf740f 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -260,8 +260,9 @@ If you like to use the compatibility functions in mmap mode, there are
read / write routines equaling to standard read / write transfers. Using
these functions discards the benefits of direct access to memory region.
See the #snd_pcm_mmap_readi(),
-#snd_pcm_writei(), #snd_pcm_readn()
-and #snd_pcm_writen() functions.
+#snd_pcm_mmap_writei(), #snd_pcm_mmap_readn()
+and #snd_pcm_mmap_writen() functions. These functions use
+#snd_pcm_areas_copy() internally.
\section pcm_errors Error codes
--
2.9.3
2
6
When attempting to change sample rate for the interface after first
set-play cycle, the change is not reflected to the hardware, while from
user space everything seems to go fine.
This patch fixes the issue and the driver now behaves the same way as
for example the USB Audio Class driver.
3
5
[alsa-devel] [PATCH] ALSA: usb-audio: Eliminate noise at the start of DSD playback.
by Nobutaka Okabe 12 Dec '16
by Nobutaka Okabe 12 Dec '16
12 Dec '16
[Problem]
In some USB DACs, a terrible pop noise comes to be heard
at the start of DSD playback (in the following situations).
- play first DSD track
- change from PCM track to DSD track
- change from DSD64 track to DSD128 track (and etc...)
- seek DSD track
- Fast-Forward/Rewind DSD track
[Cause]
At the start of playback, there is a little silence.
The silence bit pattern "0x69" is required on DSD mode,
but it is not like that.
[Solution]
This patch adds DSD silence pattern to the endpoint settings.
Signed-off-by: Nobutaka Okabe <nob77413(a)gmail.com>
---
sound/usb/endpoint.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 57b0d99..a2cdf33 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -638,7 +638,21 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
ep->datainterval = fmt->datainterval;
ep->stride = frame_bits >> 3;
- ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
+
+ switch (pcm_format) {
+ case SNDRV_PCM_FORMAT_U8:
+ ep->silence_value = 0x80;
+ break;
+ case SNDRV_PCM_FORMAT_DSD_U8:
+ case SNDRV_PCM_FORMAT_DSD_U16_LE:
+ case SNDRV_PCM_FORMAT_DSD_U32_LE:
+ case SNDRV_PCM_FORMAT_DSD_U16_BE:
+ case SNDRV_PCM_FORMAT_DSD_U32_BE:
+ ep->silence_value = 0x69;
+ break;
+ default:
+ ep->silence_value = 0;
+ }
/* assume max. frequency is 50% higher than nominal */
ep->freqmax = ep->freqn + (ep->freqn >> 1);
--
2.7.4
2
1
[alsa-devel] [PATCH] ALSA: usb-audio: Add native DSD support for TEAC 501/503 DAC
by Nobutaka Okabe 12 Dec '16
by Nobutaka Okabe 12 Dec '16
12 Dec '16
This patch adds native DSD support for the following devices.
- TEAC NT-503
- TEAC UD-503
- TEAC UD-501
(1)Add quirks for native DSD support for TEAC devices.
(2)A specific vendor command is needed to switch between PCM/DOP and DSD mode, same as Denon/Marantz devices.
Signed-off-by: Nobutaka Okabe <nob77413(a)gmail.com>
---
sound/usb/quirks.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 2782155..b3fd238 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1165,6 +1165,18 @@ static bool is_marantz_denon_dac(unsigned int id)
return false;
}
+/* TEAC UD-501/UD-503/NT-503 USB DACs need a vendor cmd to switch
+ * between PCM/DOP and native DSD mode
+ */
+static bool is_teac_50X_dac(unsigned int id)
+{
+ switch (id) {
+ case USB_ID(0x0644, 0x8043): /* TEAC UD-501/UD-503/NT-503 */
+ return true;
+ }
+ return false;
+}
+
int snd_usb_select_mode_quirk(struct snd_usb_substream *subs,
struct audioformat *fmt)
{
@@ -1192,6 +1204,26 @@ int snd_usb_select_mode_quirk(struct snd_usb_substream *subs,
break;
}
mdelay(20);
+ } else if (is_teac_50X_dac(subs->stream->chip->usb_id)) {
+ /* Vendor mode switch cmd is required. */
+ switch (fmt->altsetting) {
+ case 3: /* DSD mode (DSD_U32) requested */
+ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
+ USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+ 1, 1, NULL, 0);
+ if (err < 0)
+ return err;
+ break;
+
+ case 2: /* PCM or DOP mode (S32) requested */
+ case 1: /* PCM mode (S16) requested */
+ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
+ USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+ 0, 1, NULL, 0);
+ if (err < 0)
+ return err;
+ break;
+ }
}
return 0;
}
@@ -1337,5 +1369,11 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
}
+ /* TEAC devices with USB DAC functionality */
+ if (is_teac_50X_dac(chip->usb_id)) {
+ if (fp->altsetting == 3)
+ return SNDRV_PCM_FMTBIT_DSD_U32_BE;
+ }
+
return 0;
}
--
2.7.4
2
1
The following changes since commit 69973b830859bc6529a7a0468ba0d80ee5117826:
Linux 4.9 (2016-12-11 11:17:54 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git tags/asoc-v4.10
for you to fetch changes up to a5de5b74a50113564a1e0850e2da96c37c35e55d:
Merge remote-tracking branches 'asoc/topic/wm9712', 'asoc/topic/wm9713' and 'asoc/topic/zte' into asoc-next (2016-12-12 15:53:32 +0000)
----------------------------------------------------------------
ASoC: Updates for v4.10
There's been a few bits of framework work this time around and quite a
lot of cleanups and improvements to existing code:
- Support for stereo DAPM controls from Chen-yu Tsai.
- Some initial work on the of-graph sound card from Morimoto-san, the
main bulk of this is currently in binding review.
- Lots of Renesas cleanups from Morimoto-san and sunxi work from
Chen-yu Tsai.
- regmap conversions of the remaining AC'97 drivers from Lars-Peter
Clausen.
- A new version of the topology ABI from Mengdong Lin.
- New drivers for Cirrus Logic CS42L42, Qualcomm MSM8916-WCD, and Realtek
RT5665.
----------------------------------------------------------------
Adam Thomson (1):
ASoC: da7219: Improve pop/click performance for sensitive HPs
Alexey Khoroshilov (1):
SoC: mxs-saif: check validity of ids in mxs_saif_probe()
Arnaud Pouliquen (5):
ASoC: sti: fix errors management
ASoC: sti: reset refactoring
ASoC: sti: clean unused include
ASoC: sti-sas: clean legacy in sti-sas
ASoC: sti-sas: add missing return in messages strings
Arnd Bergmann (2):
ASoC: lpass-platform: initialize dma channel number
ASoC: topology: avoid uninitialized kcontrol_type
Axel Lin (6):
regulator: rk808: Use rdev_get_id() to access id of regulator
ASoC: cs35l34: Remove CS35L34_CHIP_ID from cs35l34_readable_register
ASoC: msm8916-wcd-analog: Update correct register setting for MIC BIAS Internal1
ASoC: rt5665: Fix missing mutex_unlock in rt5665_calibrate
ASoC: rt5665: Use devm_gpio_request_one()
ASoC: cs35l34: Simplify the logic to set CS35L34_MCLK_CTL setting
Bard Liao (10):
ASoC: rt5663: rename rt5668 as rt5663 v2
ASoC: rt5660: enable MCLK detection
ASoC: rt5640: enable MCLK detection
ASoC: rt5670: Enable MCLK detection
ASoC: rt5670: increse LDO power
ASoC: rt5640: add Mono ADC Capture Switch control
ASoC: rt286: remove unnecessary selection in Kconfig
ASoC: add rt5665 codec driver
ASoC: rl6231: add 19.2M to 4.096M pll preset table
ASoC: rt298: disable IRQ when jack is NULL
Charles Keepax (18):
ASoC: arizona: Add gating for clock when used for direct MCLK
ASoC: arizona: Add gating for source clocks of the FLLs
ASoC: samsung: i2s: Fixup last IRQ unsafe spin lock call
ASoC: cs42xx8: Mark chip ID as volatile and remove cache bypass
ASoC: cs42l56: Make ID registers volatile and remove cache bypass
ASoC: cs42l73: Remove cache bypass for read of ID registers
ASoC: arizona: Move request of speaker IRQs into bus probe
ASoC: arizona: Move request of DSP IRQ into bus probe
ASoC: cs47l24: Fixup missing variable typo
ASoC: arizona: Access driver data through platform from compressed ops
ASoC: core: If a platform doesn't have an of_node use parent's node
ASoC: wm2200: Correct types of mixer texts and values
ASoC: arizona: Move notifier functions to header and make inline
ASoC: arizona: Call arizona_init_notifiers for all CODECs
ASoC: wm_adsp: Only write shutdown controls for active firmwares
ASoC: wm_adsp: Check return value from wm_adsp_buffer_init
ASoC: arizona: Remove redundant extern declarations
ASoC: wm_adsp: Remove redundant extern declarations
Chen-Yu Tsai (19):
ASoC: dapm: Support second register for DAPM control updates
ASoC: dapm: Implement stereo mixer control support
ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type
ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type
ASoC: sun4i-codec: Move data structures to add create_card call to quirks
ASoC: sun4i-codec: Revise comments for register definition macros
ASoC: sun4i-codec: Expand quirks to handle register offsets and card creation
ASoC: sun4i-codec: Increase DMA max burst to 8
ASoC: sun4i-codec: Add support for A31 playback through headphone output
ASoC: sun4i-codec: Add support for A31 Line In playback
ASoC: sun4i-codec: Add support for A31 Line Out playback
ASoC: sun4i-codec: Add support for A31 analog microphone inputs
ASoC: sun4i-codec: Add support for A31 board level audio routing
ASoC: sun4i-codec: Add support for A31 ADC capture path
ASoC: sun4i-codec: Add support for optional reset control to quirks
ASoC: sunxi: Add bindings for A23/A33/H3 codec's analog path controls
ASoC: sunxi: Add support for A23/A33/H3 codec's analog path controls
ASoC: sun4i-codec: Add support for A23 codec
ASoC: sun4i-codec: Add support for H3 codec
Colin Ian King (2):
ASoC: sst-bxt-da7219_max98357a: fix obsoleted initializer for array
ASoC: mioa701_wm9713: add missing white space in dev_err message
Dan Carpenter (2):
ASoC: Intel: Skylake: Fix a shift wrapping bug
ASoC: sunxi: Uninitialized variable in probe()
Dharageswari R (2):
ASoC: Intel: Skylake: Use DPIB to update position for Playback stream
ASoC: Intel: Skylake: Use DPIB to update position for Playback stream
Florian Vaussard (1):
ASoC: cs42l56: Fix misuse of regmap_update_bits
Geert Uytterhoeven (1):
ASoC: davinci-mcbsp: DT fix s/interrupts-names/interrupt-names/
GuruprasadX Pawse (2):
ASoC: Intel: Skylake: Don't use dma I2S config structure in kernel
ASoC: Intel: Skylake: Removed the unused I2S blob structure
Jack Yu (1):
ASoC: Add jd function for rt5663.
James Schulman (2):
ASoC: Add support for CS42L42 codec
ASoC: cs42l42: Add devicetree bindings for CS42L42
Jarkko Nikula (2):
ASoC: rl6347a: Use dev_err for I2C communication error prints
ASoC: rl6347a: Use dev_err for I2C communication error prints
Jayachandran B (3):
ASoC: Intel: Skylake: Add D0iX callbacks
ASoC: Intel: Skylake: remove pci device enabling calls on suspend
ASoC: Intel: Skylake: Flush pending D0i3 request on suspend
John Hsu (5):
ASoC: nau8825: Disable short Frame Sync detection logic
ASoC: nau8825: AD/DA over sampling rate configuration
ASoC: nau8825: FLL parameters finetune
ASoC: nau8825: lock longer to avoid playback pop upon resume
ASoC: nau8825: disable sinc filter for high THD of ADC
Julia Lawall (7):
ASoC: intel: broadwell: constify snd_soc_ops structures
ASoC: atmel_wm8904: constify snd_soc_ops structures
ASoC: rockchip: constify snd_soc_ops structures
ASoC: qcom: storm: constify snd_soc_ops structures
ASoC: tegra: constify snd_soc_ops structures
ASoC: constify snd_soc_ops structures
ASoC: constify snd_soc_ops structures
Krzysztof Kozlowski (4):
ASoC: samsung: Remove non-existing MACH dependencies
ASoC: samsung: smdk_wm8580: Remove old platforms and drop mach-types usage
ASoC: samsung: Enable COMPILE_TEST for entire Samsung ASoc
ASoC: samsung: Enable COMPILE_TEST for SmartQ and WM8580
Kuninori Morimoto (47):
ASoC: soc.h: use bit field for playback/capture_only
ASoC: bunch up bit field for snd_soc_pcm_runtime
ASoC: bunch up bit field for snd_soc_dai
ASoC: remove component from snd_soc_pcm_runtime
ASoC: rsnd: remove duplicate define of rsnd_dvc_of_node()
ASoC: rsnd: amend .probe/.remove call for DPCM
ASoC: rsnd: add rsnd_mod_next() for for_each_rsnd_mod_xxx()
ASoC: rsnd: use for_each_rsnd_mod_xxx() on rsnd_dai_call()
ASoC: rsnd: use for_each_rsnd_mod_xxx() on rsnd_rdai_continuance_probe()
ASoC: rsnd: add rsnd_parse_of_node() and integrate rsnd_xxx_of_node
ASoC: rsnd: remove non DT support for DMA
ASoC: rsnd: don't use devm_request_irq() for SSI
ASoC: rsnd: remove rsnd_dma_detach()
ASoC: rsnd: don't call unneeded of_node_put() on dma.c
ASoC: rsnd: add nolock_start/stop callback
ASoC: rsnd: depends on OF
ASoC: rsnd: enable COMPILE_TEST
ASoC: rsnd: remove "Gen2 only" comment
ASoC: rsnd: rsnd_reg cleanup for SSIU
ASoC: rsnd: fixup SCU_SYS_STATUSx access
ASoC: rsnd: clear SSI_SYS_STATUSx every time
ASoC: rsnd: enable SRC sync even FIN = FOUT
ASoC: max98504: Add missing MAX98504 on SND_SOC_ALL_CODECS
ASoC: rsnd: use BRGCKR instead of SSICKR
ASoC: simple-scu-card: code sync: follow to simple family style
ASoC: simple-scu-card: code sync: rename asoc_simple_card_priv
ASoC: simple-scu-card: code sync: tidyup props/link naming
ASoC: simple-card-utils: remove unnecessary cpu/codec pointer check
ASoC: soc-core: adjust for graph on snd_soc_of_parse_audio_routing
ASoC: soc-core: snd_soc_get_dai_name() become non static
ASoC: soc-core: adjust for graph on snd_soc_of_parse_audio_prefix
ASoC: soc-core: adjust for graph on snd_soc_of_parse_card_name
ASoC: soc-core: adjust for graph on snd_soc_of_parse_audio_simple_widgets
ASoC: uda1380: Remove #if IS_ENABLED(CONFIG_I2C)
ASoC: wm9081: Remove #if IS_ENABLED(CONFIG_I2C)
ASoC: wm8523: Remove #if IS_ENABLED(CONFIG_I2C)
ASoC: wm8580: Remove #if IS_ENABLED(CONFIG_I2C)
ASoC: rsnd: Request/Release DMA channel each time
ASoC: rsnd: use dma_sync_single_for_xxx() for IOMMU
ASoC: core: replace codec_dev_list to component_dev_list on Card
ASoC: core: replace aux_comp_list to component_dev_list
ASoC: add Component level suspend/resume
ASoC: rsnd: rsnd_get_dalign() needs to care SSIU, not SSI
ASoC: simple_card_utils: tidyup file comment/define
ASoC: rsnd: tidyup ssi->usrcnt counter check in hw_params
ASoC: rsnd: enable/disable ADG when suspend/resume timing
ASoC: rsnd: setup BRGCKR/BRRA/BRRB when starting
Lars-Peter Clausen (27):
ASoC: wm9712: Convert to regmap
ASoC: wm9712: Remove ac97_read/ac97_write wrappers
ASoC: wm9705: Convert to regmap
ASoC: wm9705: Remove ac97_read/ac97_write wrappers
ASoC: wm9712: Remove unused DAI ID defines
ASoC: wm9713: Remove unused DAI ID defines
ASoC: wm8753: Remove unused DAI ID defines
ASoC: wm9705: Remove unused DAI ID defines
ASoC: pxa2xx-i2s: Remove unused DAI ID defines
ASoC: pxa-ssp: Remove unused DAI ID defines
ASoC: es8328: Remove some unused defines
ASoC: ak4641: Remove unused DAI ID defines
ASoC: ak4641: Remove unused AK4641_CACHEREGNUM define
ASoC: ak4641: Move register defines to main source
ASoC: stac9766: Remove unused DAI ID defines
ASoC: stac9766: Remove register paging support
ASoC: stac9766: Move register defines to main source file
ASoC: stac9766: Convert to regmap
ASoC: stac9766: Remove ac97_read/ac97_write wrappers
ASoC: samsung: Remove unselectable smdk_wm8580pcm
ASoC: pxa: Make static string arrays 'const 'char * const []'
ASoC: rt5616: Don't use rtd->codec
ASoC: intel: mfld: Make static string arrays 'const 'char * const []'
ASoC: Make return type of dpcm_state_string() const char *
ASoC: uda1380: Remove unused DAI ID defines
ASoC: uda1380: Request GPIOs at bus probe time
ASoC: ab8500: Remove explicit initialization of driver callbacks to NULL
Mark Brown (31):
Merge branch 'topic/dmaengine' of git://git.kernel.org/.../broonie/sound into asoc-samsung
Merge remote-tracking branch 'asoc/topic/rl6347a' into asoc-rl6347a
Merge branch 'fix/samsung' of git://git.kernel.org/.../broonie/sound into asoc-samsung
Merge branch 'fix/qcom' of git://git.kernel.org/.../broonie/sound into asoc-qcom
Merge branch 'fix/sunxi' of git://git.kernel.org/.../broonie/sound into asoc-sunxi
Merge branch 'topic/dapm' of git://git.kernel.org/.../broonie/sound into asoc-sunxi
Merge branch 'topic/dapm-pin' of git://git.kernel.org/.../broonie/sound into asoc-arizona
Merge branch 'topic/component' of git://git.kernel.org/.../broonie/sound into asoc-intel
Merge remote-tracking branch 'asoc/fix/intel' into asoc-linus
Merge remote-tracking branches 'asoc/fix/da7219-pops' and 'asoc/fix/qcom' into asoc-linus
Merge remote-tracking branch 'asoc/topic/component' into asoc-next
Merge remote-tracking branch 'asoc/topic/core' into asoc-next
Merge remote-tracking branch 'asoc/topic/dapm' into asoc-next
Merge remote-tracking branch 'asoc/topic/dapm-pin' into asoc-next
Merge remote-tracking branch 'asoc/topic/dmaengine' into asoc-next
Merge remote-tracking branch 'asoc/topic/intel' into asoc-next
Merge remote-tracking branch 'asoc/topic/rcar' into asoc-next
Merge remote-tracking branches 'asoc/topic/ab8500', 'asoc/topic/arizona', 'asoc/topic/atmel', 'asoc/topic/bcm' and 'asoc/topic/bitfield' into asoc-next
Merge remote-tracking branches 'asoc/topic/compress', 'asoc/topic/const' and 'asoc/topic/cs35l34' into asoc-next
Merge remote-tracking branches 'asoc/topic/cs42l42', 'asoc/topic/cs42l56', 'asoc/topic/cs42l73' and 'asoc/topic/cs42xx8' into asoc-next
Merge remote-tracking branches 'asoc/topic/davinci' and 'asoc/topic/doc' into asoc-next
Merge remote-tracking branches 'asoc/topic/dpcm', 'asoc/topic/es8328', 'asoc/topic/extcon' and 'asoc/topic/fsl' into asoc-next
Merge remote-tracking branches 'asoc/topic/inntel', 'asoc/topic/input', 'asoc/topic/max98504' and 'asoc/topic/nau8825' into asoc-next
Merge remote-tracking branches 'asoc/topic/of-graph', 'asoc/topic/pxa', 'asoc/topic/qcom' and 'asoc/topic/rk808' into asoc-next
Merge remote-tracking branches 'asoc/topic/rl6231', 'asoc/topic/rl6347a', 'asoc/topic/rockchip', 'asoc/topic/rt286' and 'asoc/topic/rt298' into asoc-next
Merge remote-tracking branches 'asoc/topic/rt5514', 'asoc/topic/rt5616', 'asoc/topic/rt5640', 'asoc/topic/rt5660' and 'asoc/topic/rt5663' into asoc-next
Merge remote-tracking branches 'asoc/topic/rt5665', 'asoc/topic/rt5670', 'asoc/topic/rt5677', 'asoc/topic/samsung' and 'asoc/topic/simple' into asoc-next
Merge remote-tracking branches 'asoc/topic/stac9766', 'asoc/topic/sti', 'asoc/topic/sti-codec', 'asoc/topic/sunxi' and 'asoc/topic/tegra' into asoc-next
Merge remote-tracking branches 'asoc/topic/tlv320aic31xx', 'asoc/topic/topology', 'asoc/topic/uda1380', 'asoc/topic/wm2200' and 'asoc/topic/wm8523' into asoc-next
Merge remote-tracking branches 'asoc/topic/wm8580', 'asoc/topic/wm8753', 'asoc/topic/wm8978', 'asoc/topic/wm9081' and 'asoc/topic/wm9705' into asoc-next
Merge remote-tracking branches 'asoc/topic/wm9712', 'asoc/topic/wm9713' and 'asoc/topic/zte' into asoc-next
Matt Flax (1):
ASoC: wm8580: Add the wm8581 codec to the driver
Maxime Ripard (3):
ASoC: sun4i-i2s: Implement capture support
ASoC: sunxi: i2s: Implement set_sysclk
ASoC: wm8978: Adjust clock indices so that simple card works
Mengdong Lin (17):
ASoC: topology: ABI - Define DAI physical PCM data formats
ASoC: topology: Make manifest backward compatible from ABI v4
ASoC: topology: Make PCM backward compatible from ABI v4
ASoC: topology: Only use valid names of PCM for the kernel DAI & DAI link
ASoC: topology: Support topology file of ABI v4
ASoC: topology: ABI - Add flags and private data to PCM
ASoC: topology: ABI - Update physical DAI link configuration for version 5
ASoC: Define API to find a dai link
ASoC: topology: Add support to configure existing physical DAI links
ASoC: topology: Rename the function to create a FE link
ASoC: topology: ABI - Rename struct and type for physical DAIs
ASoC: topology: Rename functions & variables for physical DAIs
ASoC: topology: ABI - Add voice wake up flag for DAI links
ASoC: topology: Check name strings of physical DAI links
ASoC: topology: ABI - Rename be_dai_elems to dai_elems in manifest
ASoC: topology: Allow a widget to have multiple enum controls
ASoC: topology: Only free TLV for volume mixers of a widget
Oder Chiou (1):
ASoC: rt5514: Add the DMIC initial delay to wait it ready.
Pardha Saradhi K (2):
ASoC: Intel: Skylake: Add D0iX IPCs
ASoC: Intel: Skylake: Add support for programming D0i3C
Paul Handrigan (2):
ASoC: cs35l34: Add device tree bindings file for cs35l34
ASoC: cs35l34: Initial commit of the cs35l34 CODEC driver.
Peter Rosin (3):
ASoC: atmel_ssc_dai: if not provided, default to sensible dividers
ASoC: tse850: document axentia,tse850-pcm5142 bindings
ASoC: atmel: tse850: add ASoC driver for the Axentia TSE-850
Peter Ujfalusi (2):
ASoC: tlv320aic31xx: Add missing of_device_id for dac3100
ASoC: tlv320aic31xx: Add support for tlv320dac3101
Pierre-Louis Bossart (3):
ASoC: Intel: common: add ACPI package extraction utility
ASoC: Intel: detect audio routing with CHAN package
ASoC: Intel: bytct_rt5640: change default capture settings
Randy Dunlap (1):
ASoC: fsl: fix fsl_spdif.c build errors
Richard Fitzgerald (12):
ASoC: wm_adsp: Remove duplicate set of kcontrol->iface
ASoC: wm_adsp: Signal firmware shutdown through event control
ASoC: wm_adsp: factor out getting region name from type
ASoC: wm_adsp: Support acknowledged controls
ASoC: wm_adsp: factor out getting base register for a control
ASoC: wm_adsp: Firmware controls should be added as codec controls
ASoC: wm_adsp: Remove unused wm_coeff_ctl.kcontrol
ASoC: core: Add component pin control functions
ASoC: arizona: Use component pin control functions
extcon: arizona: Use SoC component pin control functions
Input: arizona-haptics - Use SoC component pin control functions
ASoC: wm_adsp: wm_adsp_buf_alloc should use kfree in error path
Sathyanarayana Nujella (2):
ASoC: Intel: report JACK_LINEOUT event
ASoC: Intel: update bxt_da7219_max98357a to support quad ch dmic capture
Scott Branden (1):
ASoC: bcm: add depends on HAS_DMA
Sebastien Guiriec (4):
ASoC: Intel: atom: Add debug information related to FW version
ASoC: Intel: atom: Add sysfs entry in order to store FW version
ASoC: Intel: atom: save FW version
ASoC: Intel: Add ASoC Intel SST Atom sysfs description
Shawn Guo (2):
ASoC: zte: spdif and i2s drivers are not zx296702 specific
ASoC: zte: spdif: correct ZX_SPDIF_CLK_RAT define
Srinivas Kandagatla (7):
ASoC: codecs: Add msm8916-wcd analog codec
ASoC: codecs: Add msm8916-wcd digital codec
ASoC: apq8016-sbc: dt bindings: remove incorrect property
ASoC: qcom: apq8016-sbc: Add support to multi codec.
ASoC: lpass-platform: use dma_ch instead of rdma_ch/wrdma_ch
ASoC: codecs: msm8916-wcd-analog: clean parse_dt()
ASoC: codecs: msm8916-wcd-digital: rename parse_dt to get_clks
Stuart Henderson (1):
ASoC: wm_adsp: Add support for SYSTEM firmware controls
Sylwester Nawrocki (11):
ASoC: s3c24xx_uda134x: Move global variables to driver's data structure
ASoC: samsung: Remove unneeded initialization of chan_name
ASoC: Drop SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME flag
ASoC: samsung: s3c24xx-i2s: Don't use platform_data for DMA parameters
ASoC: samsung: s3c24xx-i2s: Debug/error trace cleanup
ASoC: samsung: pcm: Conversion to use devm_ioremap_resource()
ASoC: samsung: s2c24xx-i2s: remove redundant error message
ASoC: samsung: Drop AC97 drivers
ASoC: samsung: s3c24xx-i2s.c merge fixup
ASoC: samsung: Add DT bindings documentation for TM2 sound subsystem
ASoC: samsung: Add machine driver for Exynos5433 based TM2 board
Takashi Iwai (10):
ASoC: rt5514: Remove superfluous linux/kthread.h inclusion
ASoC: rt5677: Remove superfluous linux/kthread.h inclusion
ASoC: cht_bsw_rt5645: Fix leftover kmalloc
ASoC: Intel: atom: Make some messages to debug level
ASoC: Intel: Add missing 10EC5672 ACPI ID matching for Cherry Trail
ASoC: rt5670: Enable Braswell platform workaround for Dell Wyse 3040
ASoC: rt5670: Add missing 10EC5072 ACPI ID
ASoC: intel: Replace kthread with work
ASoC: intel: Fix crash at suspend/resume without card registration
ASoC: cht_bsw_rt5672: Use HID translation unit
Valentin Rothberg (1):
ASoC: samsung: Makefile cleanup
Vinod Koul (6):
ASoC: Intel: Add check_dsp_lp_on callback on IPC
ASoC: Intel: Add sst_ipc_tx_message_nopm
ASoC: Intel: Skylake: Add support for LPMode
ASoC: Intel: Skylake: Add support for specifying D0i3 configuration
ASoC: Intel: Skylake: Add D0i3 mode ref counting
ASoC: compress: Add support for compress dai ops
Wei Yongjun (2):
ASoC: rk3399_gru_sound: Fix non static symbol warning
ASoC: wm8580: Fix non static symbol warnings
anish kumar (1):
ASoC: Codec to codec dai link description
kbuild test robot (2):
ASoC: sun4i-codec: fix semicolon.cocci warnings
ASoC: fix platform_no_drv_owner.cocci warnings
Documentation/ABI/testing/sysfs-platform-sst-atom | 17 +
.../bindings/sound/axentia,tse850-pcm5142.txt | 88 +
.../devicetree/bindings/sound/cs35l34.txt | 64 +
.../devicetree/bindings/sound/cs42l42.txt | 110 +
.../devicetree/bindings/sound/davinci-mcbsp.txt | 2 +-
.../devicetree/bindings/sound/qcom,apq8016-sbc.txt | 5 +-
.../bindings/sound/qcom,msm8916-wcd-analog.txt | 85 +
.../bindings/sound/qcom,msm8916-wcd-digital.txt | 20 +
Documentation/devicetree/bindings/sound/rt5514.txt | 3 +
Documentation/devicetree/bindings/sound/rt5663.txt | 6 +-
Documentation/devicetree/bindings/sound/rt5665.txt | 68 +
.../bindings/sound/samsung,tm2-audio.txt | 38 +
.../devicetree/bindings/sound/sun4i-codec.txt | 65 +-
.../bindings/sound/sun8i-codec-analog.txt | 16 +
.../devicetree/bindings/sound/tlv320aic31xx.txt | 1 +
Documentation/devicetree/bindings/sound/wm8580.txt | 4 +-
Documentation/sound/alsa/soc/codec_to_codec.txt | 103 +
MAINTAINERS | 7 +
drivers/extcon/extcon-arizona.c | 8 +-
drivers/input/misc/arizona-haptics.c | 13 +-
drivers/regulator/rk808-regulator.c | 9 +-
include/dt-bindings/sound/cs42l42.h | 73 +
include/sound/cs35l34.h | 35 +
include/sound/dmaengine_pcm.h | 6 -
include/sound/rt5514.h | 20 +
include/sound/rt5665.h | 47 +
include/sound/simple_card_utils.h | 8 +-
include/sound/soc-dai.h | 43 +-
include/sound/soc-dapm.h | 14 +
include/sound/soc-topology.h | 2 +-
include/sound/soc.h | 87 +-
include/uapi/sound/asoc.h | 90 +-
include/uapi/sound/snd_sst_tokens.h | 8 +-
sound/soc/atmel/Kconfig | 10 +
sound/soc/atmel/Makefile | 2 +
sound/soc/atmel/atmel_ssc_dai.c | 83 +-
sound/soc/atmel/atmel_ssc_dai.h | 1 +
sound/soc/atmel/atmel_wm8904.c | 2 +-
sound/soc/atmel/tse850-pcm5142.c | 472 ++
sound/soc/bcm/Kconfig | 1 +
sound/soc/codecs/Kconfig | 31 +-
sound/soc/codecs/Makefile | 11 +-
sound/soc/codecs/ab8500-codec.c | 2 -
sound/soc/codecs/adau17x1.c | 2 +-
sound/soc/codecs/ak4641.c | 22 +-
sound/soc/codecs/ak4641.h | 47 -
sound/soc/codecs/arizona.c | 153 +-
sound/soc/codecs/arizona.h | 109 +-
sound/soc/codecs/cs35l34.c | 1251 +++++
sound/soc/codecs/cs35l34.h | 269 ++
sound/soc/codecs/cs42l42.c | 1986 ++++++++
sound/soc/codecs/cs42l42.h | 776 ++++
sound/soc/codecs/cs42l56.c | 18 +-
sound/soc/codecs/cs42l73.c | 4 -
sound/soc/codecs/cs42xx8.c | 10 -
sound/soc/codecs/cs47l24.c | 55 +-
sound/soc/codecs/da7219-aad.c | 18 +-
sound/soc/codecs/da7219.c | 139 +-
sound/soc/codecs/da7219.h | 5 +
sound/soc/codecs/es8328.h | 37 -
sound/soc/codecs/msm8916-wcd-analog.c | 890 ++++
sound/soc/codecs/msm8916-wcd-digital.c | 923 ++++
sound/soc/codecs/nau8825.c | 142 +-
sound/soc/codecs/nau8825.h | 16 +
sound/soc/codecs/rl6231.c | 1 +
sound/soc/codecs/rl6347a.c | 2 +-
sound/soc/codecs/rt298.c | 24 +-
sound/soc/codecs/rt5514-spi.c | 1 -
sound/soc/codecs/rt5514.c | 17 +
sound/soc/codecs/rt5514.h | 2 +
sound/soc/codecs/rt5616.c | 3 +-
sound/soc/codecs/rt5640.c | 5 +
sound/soc/codecs/rt5640.h | 6 +
sound/soc/codecs/rt5660.c | 4 +
sound/soc/codecs/rt5660.h | 3 +
sound/soc/codecs/rt5663.c | 1137 ++---
sound/soc/codecs/rt5663.h | 1162 ++---
sound/soc/codecs/rt5665.c | 4874 ++++++++++++++++++++
sound/soc/codecs/rt5665.h | 1990 ++++++++
sound/soc/codecs/rt5670.c | 16 +-
sound/soc/codecs/rt5670.h | 1 +
sound/soc/codecs/rt5677-spi.c | 1 -
sound/soc/codecs/stac9766.c | 162 +-
sound/soc/codecs/stac9766.h | 17 -
sound/soc/codecs/sti-sas.c | 179 +-
sound/soc/codecs/tlv320aic31xx.c | 3 +
sound/soc/codecs/tlv320aic31xx.h | 1 +
sound/soc/codecs/tlv320aic3x.c | 2 +-
sound/soc/codecs/uda1380.c | 77 +-
sound/soc/codecs/uda1380.h | 4 -
sound/soc/codecs/wm2200.c | 4 +-
sound/soc/codecs/wm5102.c | 59 +-
sound/soc/codecs/wm5110.c | 61 +-
sound/soc/codecs/wm8523.c | 24 +-
sound/soc/codecs/wm8580.c | 123 +-
sound/soc/codecs/wm8753.h | 3 -
sound/soc/codecs/wm8978.h | 2 +-
sound/soc/codecs/wm8997.c | 39 +-
sound/soc/codecs/wm8998.c | 38 +-
sound/soc/codecs/wm9081.c | 2 -
sound/soc/codecs/wm9705.c | 138 +-
sound/soc/codecs/wm9705.h | 11 -
sound/soc/codecs/wm9712.c | 177 +-
sound/soc/codecs/wm9712.h | 11 -
sound/soc/codecs/wm9713.c | 2 +-
sound/soc/codecs/wm9713.h | 4 -
sound/soc/codecs/wm_adsp.c | 354 +-
sound/soc/codecs/wm_adsp.h | 27 +-
sound/soc/codecs/wmfw.h | 4 +
sound/soc/fsl/Kconfig | 1 +
sound/soc/fsl/efika-audio-fabric.c | 1 -
sound/soc/fsl/fsl-asoc-card.c | 2 +-
sound/soc/fsl/imx-wm8962.c | 2 +-
sound/soc/generic/simple-card-utils.c | 5 +-
sound/soc/generic/simple-card.c | 2 +-
sound/soc/generic/simple-scu-card.c | 115 +-
sound/soc/intel/atom/sst-atom-controls.c | 2 +-
sound/soc/intel/atom/sst-mfld-platform-pcm.c | 6 +
sound/soc/intel/atom/sst/sst.c | 39 +
sound/soc/intel/atom/sst/sst.h | 1 +
sound/soc/intel/atom/sst/sst_acpi.c | 2 +
sound/soc/intel/atom/sst/sst_ipc.c | 11 +
sound/soc/intel/atom/sst/sst_stream.c | 4 +-
sound/soc/intel/baytrail/sst-baytrail-ipc.c | 3 +-
sound/soc/intel/boards/bdw-rt5677.c | 2 +-
sound/soc/intel/boards/broadwell.c | 18 +-
sound/soc/intel/boards/bxt_da7219_max98357a.c | 26 +-
sound/soc/intel/boards/bxt_rt298.c | 4 +-
sound/soc/intel/boards/bytcr_rt5640.c | 68 +-
sound/soc/intel/boards/bytcr_rt5651.c | 4 +-
sound/soc/intel/boards/cht_bsw_max98090_ti.c | 4 +-
sound/soc/intel/boards/cht_bsw_rt5645.c | 10 +-
sound/soc/intel/boards/cht_bsw_rt5672.c | 46 +-
sound/soc/intel/boards/haswell.c | 2 +-
sound/soc/intel/boards/mfld_machine.c | 4 +-
sound/soc/intel/boards/skl_nau88l25_max98357a.c | 6 +-
sound/soc/intel/boards/skl_nau88l25_ssm4567.c | 6 +-
sound/soc/intel/boards/skl_rt286.c | 4 +-
sound/soc/intel/common/sst-acpi.h | 17 +-
sound/soc/intel/common/sst-ipc.c | 85 +-
sound/soc/intel/common/sst-ipc.h | 8 +-
sound/soc/intel/common/sst-match-acpi.c | 57 +
sound/soc/intel/haswell/sst-haswell-ipc.c | 3 +-
sound/soc/intel/skylake/bxt-sst.c | 145 +
sound/soc/intel/skylake/skl-messages.c | 39 +-
sound/soc/intel/skylake/skl-pcm.c | 28 +-
sound/soc/intel/skylake/skl-sst-cldma.c | 1 -
sound/soc/intel/skylake/skl-sst-dsp.h | 12 +
sound/soc/intel/skylake/skl-sst-ipc.c | 71 +-
sound/soc/intel/skylake/skl-sst-ipc.h | 37 +-
sound/soc/intel/skylake/skl-sst-utils.c | 2 +-
sound/soc/intel/skylake/skl-topology.c | 47 +
sound/soc/intel/skylake/skl-topology.h | 28 +-
sound/soc/intel/skylake/skl.c | 59 +-
sound/soc/intel/skylake/skl.h | 6 +
sound/soc/kirkwood/armada-370-db.c | 2 +-
sound/soc/mxs/mxs-saif.c | 13 +-
sound/soc/mxs/mxs-sgtl5000.c | 2 +-
sound/soc/pxa/corgi.c | 6 +-
sound/soc/pxa/e740_wm9705.c | 1 -
sound/soc/pxa/e750_wm9705.c | 1 -
sound/soc/pxa/e800_wm9712.c | 1 -
sound/soc/pxa/em-x270.c | 1 -
sound/soc/pxa/hx4700.c | 2 -
sound/soc/pxa/magician.c | 2 +-
sound/soc/pxa/mioa701_wm9713.c | 2 +-
sound/soc/pxa/palm27x.c | 1 -
sound/soc/pxa/poodle.c | 4 +-
sound/soc/pxa/pxa-ssp.h | 6 -
sound/soc/pxa/pxa2xx-i2s.h | 3 -
sound/soc/pxa/spitz.c | 6 +-
sound/soc/pxa/tosa.c | 7 +-
sound/soc/qcom/apq8016_sbc.c | 11 +-
sound/soc/qcom/lpass-platform.c | 49 +-
sound/soc/qcom/storm.c | 2 +-
sound/soc/rockchip/rk3399_gru_sound.c | 8 +-
sound/soc/rockchip/rockchip_max98090.c | 2 +-
sound/soc/rockchip/rockchip_rt5645.c | 2 +-
sound/soc/samsung/Kconfig | 58 +-
sound/soc/samsung/Makefile | 9 +-
sound/soc/samsung/ac97.c | 437 --
sound/soc/samsung/dmaengine.c | 8 +-
sound/soc/samsung/i2s.c | 8 +-
sound/soc/samsung/ln2440sbc_alc650.c | 72 -
sound/soc/samsung/pcm.c | 60 +-
sound/soc/samsung/regs-ac97.h | 66 -
sound/soc/samsung/s3c2412-i2s.c | 2 -
sound/soc/samsung/s3c24xx-i2s.c | 51 +-
sound/soc/samsung/s3c24xx_uda134x.c | 79 +-
sound/soc/samsung/smdk2443_wm9710.c | 68 -
sound/soc/samsung/smdk_wm8580.c | 30 +-
sound/soc/samsung/smdk_wm8580pcm.c | 175 -
sound/soc/samsung/smdk_wm9713.c | 108 -
sound/soc/samsung/tm2_wm5110.c | 552 +++
sound/soc/sh/Kconfig | 3 +-
sound/soc/sh/rcar/adg.c | 61 +-
sound/soc/sh/rcar/core.c | 175 +-
sound/soc/sh/rcar/dma.c | 295 +-
sound/soc/sh/rcar/dvc.c | 2 -
sound/soc/sh/rcar/gen.c | 12 +-
sound/soc/sh/rcar/rsnd.h | 156 +-
sound/soc/sh/rcar/src.c | 13 +-
sound/soc/sh/rcar/ssi.c | 28 +-
sound/soc/sh/rcar/ssiu.c | 20 +
sound/soc/soc-compress.c | 98 +-
sound/soc/soc-core.c | 181 +-
sound/soc/soc-dapm.c | 154 +-
sound/soc/soc-generic-dmaengine-pcm.c | 13 +-
sound/soc/soc-pcm.c | 2 +-
sound/soc/soc-topology.c | 751 ++-
sound/soc/soc-utils.c | 199 +
sound/soc/sti/sti_uniperif.c | 43 +-
sound/soc/sti/uniperif.h | 2 +
sound/soc/sti/uniperif_player.c | 91 +-
sound/soc/sti/uniperif_reader.c | 41 +-
sound/soc/sunxi/Kconfig | 8 +
sound/soc/sunxi/Makefile | 1 +
sound/soc/sunxi/sun4i-codec.c | 867 +++-
sound/soc/sunxi/sun4i-i2s.c | 105 +-
sound/soc/sunxi/sun8i-codec-analog.c | 665 +++
sound/soc/tegra/tegra_alc5632.c | 2 +-
sound/soc/tegra/tegra_max98090.c | 2 +-
sound/soc/tegra/tegra_rt5640.c | 2 +-
sound/soc/tegra/tegra_rt5677.c | 2 +-
sound/soc/tegra/tegra_sgtl5000.c | 2 +-
sound/soc/tegra/tegra_wm8753.c | 2 +-
sound/soc/tegra/tegra_wm8903.c | 2 +-
sound/soc/tegra/trimslice.c | 2 +-
sound/soc/zte/Kconfig | 16 +-
sound/soc/zte/Makefile | 4 +-
sound/soc/zte/{zx296702-i2s.c => zx-i2s.c} | 0
sound/soc/zte/{zx296702-spdif.c => zx-spdif.c} | 2 +-
232 files changed, 22048 insertions(+), 4367 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-platform-sst-atom
create mode 100644 Documentation/devicetree/bindings/sound/axentia,tse850-pcm5142.txt
create mode 100644 Documentation/devicetree/bindings/sound/cs35l34.txt
create mode 100644 Documentation/devicetree/bindings/sound/cs42l42.txt
create mode 100644 Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
create mode 100644 Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt
create mode 100755 Documentation/devicetree/bindings/sound/rt5665.txt
create mode 100644 Documentation/devicetree/bindings/sound/samsung,tm2-audio.txt
create mode 100644 Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt
create mode 100644 Documentation/sound/alsa/soc/codec_to_codec.txt
create mode 100644 include/dt-bindings/sound/cs42l42.h
create mode 100644 include/sound/cs35l34.h
create mode 100644 include/sound/rt5514.h
create mode 100755 include/sound/rt5665.h
create mode 100644 sound/soc/atmel/tse850-pcm5142.c
delete mode 100644 sound/soc/codecs/ak4641.h
create mode 100644 sound/soc/codecs/cs35l34.c
create mode 100644 sound/soc/codecs/cs35l34.h
create mode 100644 sound/soc/codecs/cs42l42.c
create mode 100644 sound/soc/codecs/cs42l42.h
create mode 100644 sound/soc/codecs/msm8916-wcd-analog.c
create mode 100644 sound/soc/codecs/msm8916-wcd-digital.c
create mode 100644 sound/soc/codecs/rt5665.c
create mode 100644 sound/soc/codecs/rt5665.h
delete mode 100644 sound/soc/codecs/stac9766.h
delete mode 100644 sound/soc/codecs/wm9705.h
delete mode 100644 sound/soc/codecs/wm9712.h
delete mode 100644 sound/soc/samsung/ac97.c
delete mode 100644 sound/soc/samsung/ln2440sbc_alc650.c
delete mode 100644 sound/soc/samsung/regs-ac97.h
delete mode 100644 sound/soc/samsung/smdk2443_wm9710.c
delete mode 100644 sound/soc/samsung/smdk_wm8580pcm.c
delete mode 100644 sound/soc/samsung/smdk_wm9713.c
create mode 100644 sound/soc/samsung/tm2_wm5110.c
create mode 100644 sound/soc/sunxi/sun8i-codec-analog.c
rename sound/soc/zte/{zx296702-i2s.c => zx-i2s.c} (100%)
rename sound/soc/zte/{zx296702-spdif.c => zx-spdif.c} (99%)
2
1
12 Dec '16
Add devicetree bindings documentation file for Cirrus
Logic CS43130 codec.
Signed-off-by: Li Xu <li.xu(a)cirrus.com>
---
.../devicetree/bindings/sound/cs43130.txt | 41 ++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/cs43130.txt
diff --git a/Documentation/devicetree/bindings/sound/cs43130.txt b/Documentation/devicetree/bindings/sound/cs43130.txt
new file mode 100644
index 0000000..9a2a22a
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cs43130.txt
@@ -0,0 +1,41 @@
+CS43130 DAC
+
+Required properties:
+
+ - compatible : "cirrus,cs43130"
+
+ - reg : the I2C address of the device for I2C
+
+ - VA-supply, VP-supply, VL-supply, VCP-supply, VD-supply:
+ power supplies for the device, as covered in
+ Documentation/devicetree/bindings/regulator/regulator.txt.
+
+
+Optional properties:
+
+ - reset-gpios : gpio used to reset the Device
+
+ - cirrus,xtal-ibias:
+ When external MCLK is generated by external crystal
+ oscillator, CS43130 can be used to provide bias current
+ for external crystal. Amount of bias current sent is
+ set as:
+ 1 = 7.5uA
+ 2 = 12.5uA
+ 3 = 15uA
+
+Example:
+
+cs43130: cs43130@30 {
+ compatible = "cirrus,cs43130";
+ reg = <0x30>;
+ reset-gpios = <&axi_gpio 54 0>;
+ VA-supply = <&dummy_vreg>;
+ VP-supply = <&dummy_vreg>;
+ VL-supply = <&dummy_vreg>;
+ VCP-supply = <&dummy_vreg>;
+ VD-supply = <&dummy_vreg>;
+ cirrus,xtal-ibias = <2>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <55 8>;
+};
--
1.9.1
2
2
Add support for Cirrus Logic CS43130 codec.
Support I2C control and I2S audio playback.
Signed-off-by: Li Xu <li.xu(a)cirrus.com>
---
sound/soc/codecs/Kconfig | 6 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/cs43130.c | 1164 ++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/cs43130.h | 268 ++++++++++
4 files changed, 1440 insertions(+)
create mode 100644 sound/soc/codecs/cs43130.c
create mode 100644 sound/soc/codecs/cs43130.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 9e1718a..d6ede2b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -59,6 +59,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS4271_I2C if I2C
select SND_SOC_CS4271_SPI if SPI_MASTER
select SND_SOC_CS42XX8_I2C if I2C
+ select SND_SOC_CS43130 if I2C
select SND_SOC_CS4349 if I2C
select SND_SOC_CS47L24 if MFD_CS47L24
select SND_SOC_CS53L30 if I2C
@@ -473,6 +474,11 @@ config SND_SOC_CS42XX8_I2C
select SND_SOC_CS42XX8
select REGMAP_I2C
+# Cirrus Logic CS43130 HiFi DAC
+config SND_SOC_CS43130
+ tristate "Cirrus Logic CS43130 CODEC"
+ depends on I2C
+
# Cirrus Logic CS4349 HiFi DAC
config SND_SOC_CS4349
tristate "Cirrus Logic CS4349 CODEC"
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7e1dad7..2f15228 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -52,6 +52,7 @@ snd-soc-cs4271-i2c-objs := cs4271-i2c.o
snd-soc-cs4271-spi-objs := cs4271-spi.o
snd-soc-cs42xx8-objs := cs42xx8.o
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
+snd-soc-cs43130-objs := cs43130.o
snd-soc-cs4349-objs := cs4349.o
snd-soc-cs47l24-objs := cs47l24.o
snd-soc-cs53l30-objs := cs53l30.o
@@ -281,6 +282,7 @@ obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o
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_CS43130) += snd-soc-cs43130.o
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
new file mode 100644
index 0000000..79e4d2a
--- /dev/null
+++ b/sound/soc/codecs/cs43130.c
@@ -0,0 +1,1164 @@
+/*
+ * cs43130.c -- CS43130 ALSA Soc Audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Authors: Li Xu <li.xu(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/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 <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_irq.h>
+
+#include "cs43130.h"
+
+
+static const struct reg_default cs43130_reg_defaults[] = {
+ { CS43130_SYS_CLK_CTL_1, 0x06 },
+ { CS43130_SP_SRATE, 0x01 },
+ { CS43130_SP_BITSIZE, 0x05 },
+ { CS43130_PAD_INT_CFG, 0x03 },
+ { CS43130_PWDN_CTL, 0xFE },
+ { CS43130_CRYSTAL_SET, 0x04 },
+ { CS43130_PLL_SET_1, 0x00 },
+ { CS43130_PLL_SET_2, 0x00 },
+ { CS43130_PLL_SET_3, 0x00 },
+ { CS43130_PLL_SET_4, 0x00 },
+ { CS43130_PLL_SET_5, 0x40 },
+ { CS43130_PLL_SET_6, 0x10 },
+ { CS43130_PLL_SET_7, 0x80 },
+ { CS43130_PLL_SET_8, 0x03 },
+ { CS43130_PLL_SET_9, 0x02 },
+ { CS43130_PLL_SET_10, 0x02 },
+ { CS43130_CLKOUT_CTL, 0x00 },
+ { CS43130_ASP_NUM_1, 0x01 },
+ { CS43130_ASP_NUM_2, 0x00 },
+ { CS43130_ASP_DENOM_1, 0x08 },
+ { CS43130_ASP_DENOM_2, 0x00 },
+ { CS43130_ASP_LRCK_HI_TIME_1, 0x1F },
+ { CS43130_ASP_LRCK_HI_TIME_2, 0x00 },
+ { CS43130_ASP_LRCK_PERIOD_1, 0x3F },
+ { CS43130_ASP_LRCK_PERIOD_2, 0x00 },
+ { CS43130_ASP_CLOCK_CONF, 0x0C },
+ { CS43130_ASP_FRAME_CONF, 0x0A },
+ { CS43130_XSP_NUM_1, 0x01 },
+ { CS43130_XSP_NUM_2, 0x00 },
+ { CS43130_XSP_DENOM_1, 0x02 },
+ { CS43130_XSP_DENOM_2, 0x00 },
+ { CS43130_XSP_LRCK_HI_TIME_1, 0x1F },
+ { CS43130_XSP_LRCK_HI_TIME_2, 0x00 },
+ { CS43130_XSP_LRCK_PERIOD_1, 0x3F },
+ { CS43130_XSP_LRCK_PERIOD_2, 0x00 },
+ { CS43130_XSP_CLOCK_CONF, 0x0C },
+ { CS43130_XSP_FRAME_CONF, 0x0A },
+ { CS43130_ASP_CH_1_LOC, 0x00 },
+ { CS43130_ASP_CH_2_LOC, 0x00 },
+ { CS43130_ASP_CH_1_SZ_EN, 0x06 },
+ { CS43130_ASP_CH_2_SZ_EN, 0x0E },
+ { CS43130_XSP_CH_1_LOC, 0x00 },
+ { CS43130_XSP_CH_2_LOC, 0x00 },
+ { CS43130_XSP_CH_1_SZ_EN, 0x06 },
+ { CS43130_XSP_CH_2_SZ_EN, 0x0E },
+ { CS43130_DSD_VOL_B, 0x78 },
+ { CS43130_DSD_VOL_A, 0x78 },
+ { CS43130_DSD_PATH_CTL_1, 0xA8 },
+ { CS43130_DSD_INT_CFG, 0x00 },
+ { CS43130_DSD_PATH_CTL_2, 0x00 },
+ { CS43130_DSD_PCM_MIX_CTL, 0x00 },
+ { CS43130_DSD_PATH_CTL_3, 0x40 },
+ { CS43130_HP_OUT_CTL_1, 0x30 },
+ { CS43130_PCM_FILT_OPT, 0x02 },
+ { CS43130_PCM_VOL_B, 0x78 },
+ { CS43130_PCM_VOL_A, 0x78 },
+ { CS43130_PCM_PATH_CTL_1, 0xA8 },
+ { CS43130_PCM_PATH_CTL_2, 0x00 },
+ { CS43130_CLASS_H_CTL, 0x1E },
+ { CS43130_HP_DETECT, 0x04 },
+ { CS43130_HP_LOAD_1, 0x00 },
+ { CS43130_HP_MEAS_LOAD_1, 0x00 },
+ { CS43130_HP_MEAS_LOAD_2, 0x00 },
+ { CS43130_INT_MASK_1, 0xFF },
+ { CS43130_INT_MASK_2, 0xFF },
+ { CS43130_INT_MASK_3, 0xFF },
+ { CS43130_INT_MASK_4, 0xFF },
+ { CS43130_INT_MASK_5, 0xFF },
+};
+
+static bool cs43130_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs43130_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_DEVID_AB ... CS43130_SYS_CLK_CTL_1:
+ case CS43130_SP_SRATE ... CS43130_PAD_INT_CFG:
+ case CS43130_DXD1:
+ case CS43130_PWDN_CTL:
+ case CS43130_DXD2:
+ case CS43130_CRYSTAL_SET:
+ case CS43130_PLL_SET_1 ... CS43130_PLL_SET_5:
+ case CS43130_PLL_SET_6:
+ case CS43130_PLL_SET_7:
+ case CS43130_PLL_SET_8:
+ case CS43130_PLL_SET_9:
+ case CS43130_PLL_SET_10:
+ case CS43130_CLKOUT_CTL:
+ case CS43130_ASP_NUM_1 ... CS43130_ASP_FRAME_CONF:
+ case CS43130_XSP_NUM_1 ... CS43130_XSP_FRAME_CONF:
+ case CS43130_ASP_CH_1_LOC:
+ case CS43130_ASP_CH_2_LOC:
+ case CS43130_ASP_CH_1_SZ_EN:
+ case CS43130_ASP_CH_2_SZ_EN:
+ case CS43130_XSP_CH_1_LOC:
+ case CS43130_XSP_CH_2_LOC:
+ case CS43130_XSP_CH_1_SZ_EN:
+ case CS43130_XSP_CH_2_SZ_EN:
+ case CS43130_DSD_VOL_B ... CS43130_DSD_PATH_CTL_3:
+ case CS43130_HP_OUT_CTL_1:
+ case CS43130_PCM_FILT_OPT ... CS43130_PCM_PATH_CTL_2:
+ case CS43130_CLASS_H_CTL:
+ case CS43130_HP_DETECT:
+ case CS43130_HP_STATUS:
+ case CS43130_HP_LOAD_1:
+ case CS43130_HP_MEAS_LOAD_1:
+ case CS43130_HP_MEAS_LOAD_2:
+ case CS43130_HP_DC_STAT_1:
+ case CS43130_HP_DC_STAT_2:
+ case CS43130_HP_AC_STAT_1:
+ case CS43130_HP_AC_STAT_2:
+ case CS43130_HP_LOAD_STAT:
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ case CS43130_INT_MASK_1 ... CS43130_INT_MASK_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs43130_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+struct cs43130_pll_params {
+ u32 pll_in;
+ u8 mclk_int;
+ u8 sclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ u32 pll_out;
+ u8 pll_cal_ratio;
+};
+
+static const struct cs43130_pll_params pll_ratio_table[] = {
+ { 9600000, 1, 0x02, 0x49, 0x800000, 0x00, 0x08, 22579200, 151 },
+ { 9600000, 0, 0x02, 0x50, 0x000000, 0x00, 0x08, 24576000, 164 },
+
+ { 11289600, 1, 0x02, 0X40, 0, 0x01, 0x08, 22579200, 128 },
+ { 11289600, 0, 0x02, 0x44, 0x06F700, 0x0, 0x08, 24576000, 139 },
+
+ { 12000000, 1, 0x02, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120 },
+ { 12000000, 0, 0x02, 0x40, 0x000000, 0x00, 0x08, 24576000, 131 },
+
+ { 12288000, 1, 0x02, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118 },
+ { 12288000, 0, 0x02, 0x40, 0x000000, 0x01, 0x08, 24576000, 128 },
+
+ { 13000000, 1, 0x02, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118 },
+ { 13000000, 0, 0x02, 0x40, 0x000000, 0x01, 0x08, 24576000, 128 },
+
+ { 19200000, 1, 0x02, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111 },
+ { 19200000, 0, 0x02, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121 },
+
+ { 22579200, 1, 0, 0, 0, 0, 0, 22579200, 0 },
+ { 22579200, 0, 0x03, 0x44, 0x06F700, 0x00, 0x08, 24576000, 139 },
+
+ { 24000000, 1, 0x03, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120 },
+ { 24000000, 0, 0x03, 0x40, 0x000000, 0x00, 0x08, 24576000, 131 },
+
+ { 24576000, 1, 0x03, 0x49, 0x800000, 0x01, 0x0A, 22579200, 128 },
+ { 24576000, 0, 0, 0, 0, 0, 0, 24576000, 0 },
+
+ { 26000000, 1, 0x03, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111 },
+ { 26000000, 0, 0x03, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121 },
+};
+
+static int cs43130_pll_config(struct snd_soc_codec *codec)
+{
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ dev_dbg(codec->dev, "%s: cs43130->mclk = %d, cs43130->pll_out = %d",
+ __func__, cs43130->mclk, cs43130->pll_out);
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].pll_in == cs43130->mclk &&
+ pll_ratio_table[i].pll_out == cs43130->pll_out) {
+
+ cs43130->mclk_int = pll_ratio_table[i].mclk_int;
+
+ if (pll_ratio_table[i].pll_cal_ratio == 0) {
+ if (cs43130->xtal_ibias > 0) {
+ usleep_range(1000, 1050);
+ /*PDN_XTAL = 0,enable*/
+ regmap_update_bits(cs43130->regmap,
+ CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK,
+ 0 << CS43130_PDN_XTAL_SHIFT);
+ }
+
+ /* PLL_START = 0, disable PLL_START */
+ regmap_update_bits(cs43130->regmap,
+ CS43130_PLL_SET_1,
+ CS43130_PLL_START_MASK,
+ 0 << CS43130_PLL_START_MASK);
+
+ cs43130->pll_bypass = true;
+ return 0;
+ }
+
+ /* PDN_PLL= 0,enable */
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK,
+ 0 << CS43130_PDN_PLL_SHIFT);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_9,
+ CS43130_PLL_REF_PREDIV_MASK,
+ pll_ratio_table[i].sclk_prediv);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_5,
+ pll_ratio_table[i].pll_div_int);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_2,
+ pll_ratio_table[i].pll_div_frac &
+ CS43130_7_0_MASK);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_3,
+ (pll_ratio_table[i].pll_div_frac &
+ CS43130_15_8_MASK) >> 8);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_4,
+ (pll_ratio_table[i].pll_div_frac &
+ CS43130_23_16_MASK) >> 16);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_8,
+ CS43130_PLL_MODE_MASK,
+ pll_ratio_table[i].pll_mode
+ << CS43130_PLL_MODE_SHIFT);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_6,
+ pll_ratio_table[i].pll_divout);
+
+ regmap_write(cs43130->regmap, CS43130_PLL_SET_7,
+ pll_ratio_table[i].pll_cal_ratio);
+
+ /* PLL_START = 1, enable PLL_START */
+ regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_1,
+ CS43130_PLL_START_MASK, CS43130_PLL_START_MASK);
+ cs43130->pll_bypass = false;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int cs43130_format_config(struct snd_soc_codec *codec)
+{
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ int period_size = 0;
+ int pulse_width = 0;
+ int asp_fsd;
+ int asp_stp;
+ int bick_inv;
+ int asp_m = 0;
+ int asp_sprate = 0;
+ int ret = 0;
+ unsigned int bitwidth_sclk = (cs43130->sclk / cs43130->fs) / 2;
+ unsigned int bitwidth_dai = (cs43130->dai_bit + 1) * 8;
+
+ if (cs43130->fs) {
+ if (bitwidth_sclk < bitwidth_dai) {
+ dev_err(codec->dev, "Format not supported\n");
+ return -EINVAL;
+ }
+ period_size = cs43130->sclk / cs43130->fs;
+ pulse_width = period_size/2;
+
+ if (cs43130->sclk != 0)
+ asp_m = cs43130->pll_out / cs43130->sclk;
+ }
+ dev_dbg(codec->dev, "%s: cs43130->sclk = %d, cs43130->fs = %d, cs43130->dai_bit = %d",
+ __func__, cs43130->sclk, cs43130->fs, cs43130->dai_bit);
+ dev_dbg(codec->dev, "%s: period_size = %d, pulse_width = %d, asp_m = %d",
+ __func__, period_size, pulse_width, asp_m);
+
+ if (cs43130->dai_format) {
+ /*MSB*/
+ bick_inv = 0;
+ asp_fsd = 0;
+ asp_stp = 1;
+
+ } else {
+ /*I2S*/
+ bick_inv = 1;
+ asp_fsd = 2; /* one bick delay */
+ asp_stp = 0;
+ }
+ dev_dbg(codec->dev,
+ "%s: cs43130->dai_format = %d, bick_inv = %d, asp_fsd = %d, asp_stp = %d",
+ __func__, cs43130->dai_format, bick_inv, asp_fsd, asp_stp);
+
+ switch (cs43130->fs) {
+ case 32000:
+ asp_sprate = CS43130_ASP_SPRATE_32K;
+ break;
+ case 44100:
+ asp_sprate = CS43130_ASP_SPRATE_44_1K;
+ break;
+ case 48000:
+ asp_sprate = CS43130_ASP_SPRATE_48K;
+ break;
+ case 88200:
+ asp_sprate = CS43130_ASP_SPRATE_88_2K;
+ break;
+ case 96000:
+ asp_sprate = CS43130_ASP_SPRATE_96K;
+ break;
+ case 176400:
+ asp_sprate = CS43130_ASP_SPRATE_176_4K;
+ break;
+ case 192000:
+ asp_sprate = CS43130_ASP_SPRATE_192K;
+ break;
+ case 352800:
+ asp_sprate = CS43130_ASP_SPRATE_352_8K;
+ break;
+ case 384000:
+ asp_sprate = CS43130_ASP_SPRATE_384K;
+ break;
+ default:
+ dev_err(codec->dev, "sample rate(%d) not supported\n",
+ cs43130->fs);
+ return -EINVAL;
+ }
+ dev_dbg(codec->dev, "%s: asp_sprate = %d, cs43130->asp_size = %d",
+ __func__, asp_sprate, cs43130->asp_size);
+
+ /* ASP_SPRATE = fs*/
+ regmap_write(cs43130->regmap, CS43130_SP_SRATE, asp_sprate);
+ /*ASP_SPSIZE*/
+ regmap_update_bits(cs43130->regmap, CS43130_SP_BITSIZE,
+ CS43130_SP_BITSIZE_ASP_MASK, cs43130->asp_size);
+
+
+ /* Set up slave mode */
+ /*BICK = ASP_N/ASP_M * PLL_OUT */
+ /* ASP_N = 1 */
+ regmap_write(cs43130->regmap, CS43130_ASP_NUM_1, 1);
+ regmap_write(cs43130->regmap, CS43130_ASP_NUM_2, 0);
+
+ /*ASP_M*/
+ regmap_write(cs43130->regmap, CS43130_ASP_DENOM_1, asp_m & 0xff);
+ regmap_write(cs43130->regmap, CS43130_ASP_DENOM_2, (asp_m >> 8) & 0x3f);
+
+
+ /* H / L ratio of LRCK*/
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_1,
+ (pulse_width-1) & 0xff);
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_2,
+ ((pulse_width-1) >> 8) & 0xff);
+
+ /* the period of LRCK*/
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_1,
+ (period_size-1) & 0xff);
+ regmap_write(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_2,
+ ((period_size-1) >> 8) & 0xff);
+
+ /*resolution*/
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_1_SZ_EN,
+ CS43130_SP_BITSIZE_ASP_MASK, cs43130->dai_bit);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_2_SZ_EN,
+ CS43130_SP_BITSIZE_ASP_MASK, cs43130->dai_bit);
+
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_FRAME_CONF,
+ CS43130_ASP_FSD_MASK, asp_fsd << CS43130_ASP_FSD_SHIFT);
+
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_FRAME_CONF,
+ CS43130_ASP_STP_MASK, asp_stp << CS43130_ASP_STP_SHIFT);
+
+ /* set clk master/slave */
+ dev_dbg(codec->dev, "%s: cs43130->dai_mode = %d",
+ __func__, cs43130->dai_mode);
+ regmap_update_bits(cs43130->regmap, CS43130_ASP_CLOCK_CONF,
+ CS43130_ASP_MODE_MASK, cs43130->dai_mode << CS43130_ASP_MODE_SHIFT);
+
+ return ret;
+}
+
+static int cs43130_change_clksrc(struct snd_soc_codec *codec,
+ enum cs43130_mclk_src_sel src)
+{
+ int ret = 0;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_SRC_SEL_MASK, src << CS43130_MCLK_SRC_SEL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+ CS43130_MCLK_INT_MASK, cs43130->mclk_int << CS43130_MCLK_INT_SHIFT);
+
+ usleep_range(150, 200);
+
+ return ret;
+}
+
+static int cs43130_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ unsigned int bitwidth;
+ int ret = 0;
+
+ cs43130->fs = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_8;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_32;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_16;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_24;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_24;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ cs43130->dai_bit = CS43130_SP_BIT_SIZE_32;
+ cs43130->asp_size = CS43130_SP_BIT_SIZE_8;
+ break;
+ default:
+ dev_err(codec->dev, "Format(%d) not supported",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ bitwidth = (cs43130->dai_bit+1)*8;
+ dev_dbg(codec->dev, "(data bit)%d: (rate)%d",
+ bitwidth, cs43130->fs);
+
+ ret = cs43130_format_config(codec);
+ return ret;
+}
+
+static const DECLARE_TLV_DB_SCALE(pcm_vol_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new cs43130_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Master Playback Volume",
+ CS43130_PCM_VOL_A, CS43130_PCM_VOL_B, 0, 0xFF, 1,
+ pcm_vol_tlv),
+ SOC_SINGLE("Swap L/R", CS43130_PCM_PATH_CTL_2, 1, 1, 0),
+ SOC_SINGLE("Copy L/R", CS43130_PCM_PATH_CTL_2, 0, 1, 0),
+};
+
+static int cs43130_aspin_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs43130->pll_bypass)
+ cs43130_change_clksrc(codec, CS43130_MCLK_SRC_XTAL);
+ else
+ cs43130_change_clksrc(codec, CS43130_MCLK_SRC_PLL);
+
+ usleep_range(10000, 10050);
+ /* ASP_3ST = 0 in master mode */
+ if (cs43130->dai_mode)
+ regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG,
+ 0x01, 0x00);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_CLKOUT_MASK, 0
+ << CS43130_PDN_CLKOUT_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+ default:
+ dev_err(codec->dev, "Invalid ASPOUT event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_dac_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ cs43130_change_clksrc(codec, CS43130_MCLK_SRC_RCO);
+
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_XTAL_MASK, 1
+ << CS43130_PDN_XTAL_SHIFT);
+ regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+ CS43130_PDN_PLL_MASK, 1
+ << CS43130_PDN_PLL_SHIFT);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAC event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_hpin_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 cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x99);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_OUT_CTL_1,
+ CS43130_HP_IN_EN_MASK, 0 << CS43130_HP_IN_EN_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_DXD2, 0x00);
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x00);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x99);
+ regmap_write(cs43130->regmap, CS43130_DXD2, 0x01);
+ regmap_update_bits(cs43130->regmap, CS43130_HP_OUT_CTL_1,
+ CS43130_HP_IN_EN_MASK, 1 << CS43130_HP_IN_EN_SHIFT);
+ regmap_write(cs43130->regmap, CS43130_DXD1, 0x00);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid HPIN event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs43130_dapm_widgets[] = {
+
+ SND_SOC_DAPM_OUTPUT("HPOUTA"),
+ SND_SOC_DAPM_OUTPUT("HPOUTB"),
+
+ SND_SOC_DAPM_AIF_IN_E("ASPIN", NULL, 0, CS43130_PWDN_CTL,
+ CS43130_PDN_ASP_SHIFT, 1, cs43130_aspin_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)),
+
+ SND_SOC_DAPM_DAC_E("HiFi DAC",
+ NULL, CS43130_PWDN_CTL, CS43130_PDN_HP_SHIFT, 1,
+ cs43130_dac_event,
+ (SND_SOC_DAPM_PRE_PMD)
+ ),
+
+ SND_SOC_DAPM_LINE("Analog Playback", cs43130_hpin_event),
+};
+
+static const struct snd_soc_dapm_route cs43130_routes[] = {
+ {"ASPIN", NULL, "DAC Playback"},
+ {"HiFi DAC", NULL, "ASPIN"},
+
+ {"HPOUTA", NULL, "HiFi DAC"},
+ {"HPOUTB", NULL, "HiFi DAC"},
+ {"HPOUTA", NULL, "Analog Playback"},
+ {"HPOUTB", NULL, "Analog Playback"},
+};
+
+static const unsigned int cs43130_src_rates[] = {
+ 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000
+};
+
+static const struct snd_pcm_hw_constraint_list cs43130_constraints = {
+ .count = ARRAY_SIZE(cs43130_src_rates),
+ .list = cs43130_src_rates,
+};
+
+static int cs43130_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, &cs43130_constraints);
+ return 0;
+}
+
+static int cs43130_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs43130->dai_mode = CS43130_SLAVE_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs43130->dai_mode = CS43130_MASTER_MODE;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported i2s master mode\n");
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs43130->dai_format = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cs43130->dai_format = 1;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported audio format except I2S and MSB\n");
+ return -EINVAL;
+ }
+
+ /* BICK/LRCK pority */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ cs43130->bick_invert = false;
+ cs43130->lrck_invert = false;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cs43130->bick_invert = true;
+ cs43130->lrck_invert = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ cs43130->bick_invert = false;
+ cs43130->lrck_invert = true;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ cs43130->bick_invert = true;
+ cs43130->lrck_invert = true;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported audio polarity\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int cs43130_set_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ unsigned int reg;
+ u8 mute_reg;
+
+ regmap_read(cs43130->regmap, CS43130_PCM_PATH_CTL_1, ®);
+ mute_reg = reg & 0xfc;
+ if (mute)
+ regmap_write(cs43130->regmap, CS43130_PCM_PATH_CTL_1,
+ mute_reg | 0x03);
+ else
+ regmap_write(cs43130->regmap, CS43130_PCM_PATH_CTL_1, mute_reg);
+
+ return ret;
+}
+
+static int cs43130_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+
+ switch (div_id) {
+ case CS43130_AIF_BICK_RATE:
+ cs43130->bick = div;
+ break;
+ default:
+ dev_err(codec->dev,
+ "Unsupported divide value: div_id = %d", div_id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs43130_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ int ret = 0;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ if (freq_in < 9600000 || freq_in > 26000000) {
+ dev_err(codec->dev,
+ "unsupported pll input reference clock:%d\n", freq_in);
+ return -EINVAL;
+ }
+
+ switch (freq_in) {
+ case 9600000:
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 19200000:
+ case 22579200:
+ case 24000000:
+ case 24576000:
+ case 26000000:
+ cs43130->mclk = freq_in;
+ break;
+ default:
+ dev_err(codec->dev,
+ "unsupported pll input reference clock:%d\n", freq_in);
+ return -EINVAL;
+ }
+
+ switch (freq_out) {
+ case 22579200:
+ cs43130->pll_out = freq_out;
+ cs43130->mclk_int = 1;
+ break;
+ case 24576000:
+ cs43130->pll_out = freq_out;
+ cs43130->mclk_int = 0;
+ break;
+ default:
+ dev_err(codec->dev,
+ "unsupported pll output reference clock:%d\n",
+ freq_out);
+ return -EINVAL;
+ }
+
+ ret = cs43130_pll_config(codec);
+ dev_dbg(codec->dev, "%s: cs43130->pll_bypass = %d",
+ __func__, cs43130->pll_bypass);
+ return ret;
+}
+
+static int cs43130_dai_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: clk_id = %d, freq = %d, dir = %d",
+ __func__, clk_id, freq, dir);
+ cs43130->sclk = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs43130_dai_ops = {
+ .startup = cs43130_pcm_startup,
+ .hw_params = cs43130_pcm_hw_params,
+ .set_sysclk = cs43130_dai_set_sysclk,
+ .set_fmt = cs43130_set_dai_fmt,
+ .digital_mute = cs43130_set_mute,
+ .set_clkdiv = cs43130_set_clkdiv,
+};
+
+static struct snd_soc_dai_driver cs43130_dai[] = {
+ {
+ .name = "cs43130_hifi",
+ .id = 0,
+ .playback = {
+ .stream_name = "DAC Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_ASP_FORMATS,
+ },
+ .ops = &cs43130_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "cs43130-xsp",
+ .id = 1,
+ .playback = {
+ .stream_name = "XSP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS43130_XSP_FORMATS,
+ },
+ .symmetric_rates = 1,
+ },
+};
+
+static int cs43130_codec_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ /* 24576000 is not supported */
+ unsigned int mclk_int_freq = 22579200;
+
+ dev_dbg(codec->dev, "%s: clk_id = %d, source = %d, freq = %d, dir = %d\n",
+ __func__, clk_id, source, freq, dir);
+ /*
+ * freq is external mclk freq
+ * if freq == mclk_int_freq, pll is bypassed
+ * modify mclk_int_freq as needed for application
+ */
+ cs43130_set_pll(codec, 0, 0, freq, mclk_int_freq);
+ return 0;
+}
+
+static irqreturn_t cs43130_irq_thread(int irq, void *data)
+{
+ struct cs43130_private *cs43130 =
+ (struct cs43130_private *)data;
+ struct snd_soc_codec *codec = cs43130->codec;
+ unsigned int stickies[CS43130_NUM_INT];
+ unsigned int masks[CS43130_NUM_INT];
+ unsigned int i;
+
+ /* Read all INT status and mask reg */
+ regmap_bulk_read(cs43130->regmap, CS43130_INT_STATUS_1,
+ stickies, CS43130_NUM_INT * sizeof(unsigned int));
+ regmap_bulk_read(cs43130->regmap, CS43130_INT_MASK_1,
+ masks, CS43130_NUM_INT * sizeof(unsigned int));
+
+ for (i = 0; i < ARRAY_SIZE(stickies); i++)
+ stickies[i] = stickies[i] & (~masks[i]);
+
+ if (stickies[0] & CS43130_XTAL_RDY_INT)
+ dev_dbg(codec->dev, "%s: Crystal ready", __func__);
+
+ if (stickies[0] & CS43130_XTAL_ERR_INT)
+ dev_err(codec->dev, "%s: Crystal err", __func__);
+
+ if (stickies[0] & CS43130_HP_PLUG_INT)
+ dev_dbg(codec->dev, "%s: HP plugged", __func__);
+
+ if (stickies[0] & CS43130_HP_UNPLUG_INT)
+ dev_dbg(codec->dev, "%s: HP unplugged", __func__);
+
+ return IRQ_HANDLED;
+}
+
+static int cs43130_probe(struct snd_soc_codec *codec)
+{
+ struct cs43130_private *cs43130 = snd_soc_codec_get_drvdata(codec);
+
+ cs43130->codec = codec;
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs43130 = {
+ .probe = cs43130_probe,
+ .component_driver = {
+ .controls = cs43130_snd_controls,
+ .num_controls = ARRAY_SIZE(cs43130_snd_controls),
+ .dapm_widgets = cs43130_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs43130_dapm_widgets),
+ .dapm_routes = cs43130_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs43130_routes),
+ },
+ .set_sysclk = cs43130_codec_set_sysclk,
+ .set_pll = cs43130_set_pll,
+};
+
+static const struct regmap_config cs43130_regmap = {
+ .reg_bits = 24,
+ .pad_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS43130_LASTREG,
+ .reg_defaults = cs43130_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs43130_reg_defaults),
+ .readable_reg = cs43130_readable_register,
+ .precious_reg = cs43130_precious_register,
+ .volatile_reg = cs43130_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs43130_handle_device_data(
+ struct i2c_client *i2c_client, struct cs43130_private *cs43130)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ unsigned int val;
+ int ret = 0;
+
+ of_property_read_u32(np, "cirrus,xtal-ibias", &val);
+ switch (val) {
+ case 1:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_7_5UA;
+ break;
+ case 2:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_12_5UA;
+ break;
+ case 3:
+ cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA;
+ break;
+ default:
+ dev_info(&i2c_client->dev,
+ "cirrus,xtal-ibias value or xtal unused %d",
+ val);
+ }
+ return ret;
+}
+
+static int cs43130_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cs43130_private *cs43130;
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+ int i;
+
+ cs43130 = devm_kzalloc(&client->dev, sizeof(*cs43130), GFP_KERNEL);
+ if (cs43130 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, cs43130);
+
+ cs43130->regmap = devm_regmap_init_i2c(client, &cs43130_regmap);
+ if (IS_ERR(cs43130->regmap)) {
+ ret = PTR_ERR(cs43130->regmap);
+ return ret;
+ }
+
+ if (client->dev.of_node) {
+ ret = cs43130_handle_device_data(client, cs43130);
+ if (ret != 0)
+ return ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(cs43130->supplies); i++)
+ cs43130->supplies[i].supply = cs43130_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(&client->dev,
+ "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(&client->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ cs43130->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs43130->reset_gpio))
+ return PTR_ERR(cs43130->reset_gpio);
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 1);
+
+ usleep_range(2000, 2050);
+
+ /* initialize codec */
+ ret = regmap_read(cs43130->regmap, CS43130_DEVID_AB, ®);
+
+ devid = (reg & 0xFF) << 12;
+ ret = regmap_read(cs43130->regmap, CS43130_DEVID_CD, ®);
+ devid |= (reg & 0xFF) << 4;
+ ret = regmap_read(cs43130->regmap, CS43130_DEVID_E, ®);
+ devid |= (reg & 0xF0) >> 4;
+
+ switch (devid) {
+ case CS43130_CHIP_ID:
+ break;
+ case CS4399_CHIP_ID:
+ break;
+ default:
+ dev_err(&client->dev,
+ "CS43130 Device ID (%X). Expected ID %X or %X\n",
+ devid, CS43130_CHIP_ID, CS4399_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ cs43130->dev_id = devid;
+ ret = regmap_read(cs43130->regmap, CS43130_REV_ID, ®);
+ if (ret < 0) {
+ dev_err(&client->dev, "Get Revision ID failed\n");
+ goto err;
+ }
+
+ dev_info(&client->dev,
+ "Cirrus Logic CS43130 (%x), Revision: %02X\n", devid,
+ reg & 0xFF);
+
+ /* Enable interrupt handler */
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq,
+ NULL, cs43130_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs43130", cs43130);
+ if (ret != 0) {
+ dev_err(&client->dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+
+ /* Unmask INT */
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT | CS43130_XTAL_ERR_INT |
+ CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT, 0);
+
+ /* Enable HP detect */
+ regmap_update_bits(cs43130->regmap, CS43130_HP_DETECT,
+ CS43130_HP_DETECT_CTRL_MASK, CS43130_HP_DETECT_CTRL_MASK);
+
+ regmap_write(cs43130->regmap,
+ CS43130_CRYSTAL_SET, cs43130->xtal_ibias);
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_dev_cs43130, cs43130_dai,
+ ARRAY_SIZE(cs43130_dai));
+
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: snd_soc_register_codec failed with ret = %d\n",
+ __func__, ret);
+ goto err;
+ }
+ return 0;
+err:
+ return ret;
+
+}
+
+static int cs43130_i2c_remove(struct i2c_client *client)
+{
+ struct cs43130_private *cs43130 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+
+ if (cs43130->reset_gpio)
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+
+ pm_runtime_disable(&client->dev);
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES,
+ cs43130->supplies);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs43130_runtime_suspend(struct device *dev)
+{
+ struct cs43130_private *cs43130 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs43130->regmap, true);
+ regcache_mark_dirty(cs43130->regmap);
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES,
+ cs43130->supplies);
+ return 0;
+}
+
+static int cs43130_runtime_resume(struct device *dev)
+{
+ struct cs43130_private *cs43130 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(CS43130_NUM_SUPPLIES,
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs43130->regmap, false);
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 1);
+
+ usleep_range(2000, 2050);
+
+ ret = regcache_sync(cs43130->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+ return 0;
+err:
+ regcache_cache_only(cs43130->regmap, true);
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES,
+ cs43130->supplies);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops cs43130_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs43130_of_match[] = {
+ { .compatible = "cirrus,cs43130", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs43130_of_match);
+
+static const struct i2c_device_id cs43130_i2c_id[] = {
+ {"cs43130", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id);
+
+static struct i2c_driver cs43130_i2c_driver = {
+ .driver = {
+ .name = "cs43130",
+ .of_match_table = cs43130_of_match,
+ },
+ .id_table = cs43130_i2c_id,
+ .probe = cs43130_i2c_probe,
+ .remove = cs43130_i2c_remove,
+};
+
+module_i2c_driver(cs43130_i2c_driver);
+
+MODULE_AUTHOR("Li Xu <li.xu(a)cirrus.com>");
+MODULE_DESCRIPTION("Cirrus Logic CS43130 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h
new file mode 100644
index 0000000..bceae76
--- /dev/null
+++ b/sound/soc/codecs/cs43130.h
@@ -0,0 +1,268 @@
+/*
+ * ALSA SoC CS43130 codec driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Li Xu <li.xu(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.
+ *
+ * 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 __CS43130_H__
+#define __CS43130_H__
+
+/* CS43130 registers addresses */
+/* all reg address is shifted by a byte for control byte to be LSB */
+#define CS43130_FIRSTREG 0x010000
+#define CS43130_LASTREG 0x0F0014
+#define CS43130_CHIP_ID 0x00043130
+#define CS4399_CHIP_ID 0x00043990
+#define CS43130_DEVID_AB 0x010000 /*Device ID A & B [RO]*/
+#define CS43130_DEVID_CD 0x010001 /*Device ID C & D [RO]*/
+#define CS43130_DEVID_E 0x010002 /*Device ID E [RO]*/
+#define CS43130_FAB_ID 0x010003 /*Fab ID [RO]*/
+#define CS43130_REV_ID 0x010004 /*Revision ID [RO]*/
+#define CS43130_SUBREV_ID 0x010005 /*Subrevision ID*/
+#define CS43130_SYS_CLK_CTL_1 0x010006 /*System Clocking Ctl 1*/
+#define CS43130_SP_SRATE 0x01000B /*Serial Port Sample Rate*/
+#define CS43130_SP_BITSIZE 0x01000C /*Serial Port Bit Size*/
+#define CS43130_PAD_INT_CFG 0x01000D /*Pad Interface Config*/
+#define CS43130_DXD1 0x010010 /*DXD1*/
+#define CS43130_PWDN_CTL 0x020000 /*Power Down Ctl*/
+#define CS43130_DXD2 0x020019 /*DXD2*/
+#define CS43130_CRYSTAL_SET 0x020052 /*Crystal Setting*/
+#define CS43130_PLL_SET_1 0x030001 /*PLL Setting 1*/
+#define CS43130_PLL_SET_2 0x030002 /*PLL Setting 2*/
+#define CS43130_PLL_SET_3 0x030003 /*PLL Setting 3*/
+#define CS43130_PLL_SET_4 0x030004 /*PLL Setting 4*/
+#define CS43130_PLL_SET_5 0x030005 /*PLL Setting 5*/
+#define CS43130_PLL_SET_6 0x030008 /*PLL Setting 6*/
+#define CS43130_PLL_SET_7 0x03000A /*PLL Setting 7*/
+#define CS43130_PLL_SET_8 0x03001B /*PLL Setting 8*/
+#define CS43130_PLL_SET_9 0x040002 /*PLL Setting 9*/
+#define CS43130_PLL_SET_10 0x040003 /*PLL Setting 10*/
+#define CS43130_CLKOUT_CTL 0x040004 /*CLKOUT Ctl*/
+#define CS43130_ASP_NUM_1 0x040010 /*ASP Numerator 1*/
+#define CS43130_ASP_NUM_2 0x040011 /*ASP Numerator 2*/
+#define CS43130_ASP_DENOM_1 0x040012 /*ASP Denominator 1*/
+#define CS43130_ASP_DENOM_2 0x040013 /*ASP Denominator 2*/
+#define CS43130_ASP_LRCK_HI_TIME_1 0x040014 /*ASP LRCK High Time 1*/
+#define CS43130_ASP_LRCK_HI_TIME_2 0x040015 /*ASP LRCK High Time 2*/
+#define CS43130_ASP_LRCK_PERIOD_1 0x040016 /*ASP LRCK Period 1*/
+#define CS43130_ASP_LRCK_PERIOD_2 0x040017 /*ASP LRCK Period 2*/
+#define CS43130_ASP_CLOCK_CONF 0x040018 /*ASP Clock Config*/
+#define CS43130_ASP_FRAME_CONF 0x040019 /*ASP Frame Config*/
+#define CS43130_XSP_NUM_1 0x040020 /*XSP Numerator 1*/
+#define CS43130_XSP_NUM_2 0x040021 /*XSP Numerator 2*/
+#define CS43130_XSP_DENOM_1 0x040022 /*XSP Denominator 1*/
+#define CS43130_XSP_DENOM_2 0x040023 /*XSP Denominator 2*/
+#define CS43130_XSP_LRCK_HI_TIME_1 0x040024 /*XSP LRCK High Time 1*/
+#define CS43130_XSP_LRCK_HI_TIME_2 0x040025 /*XSP LRCK High Time 2*/
+#define CS43130_XSP_LRCK_PERIOD_1 0x040026 /*XSP LRCK Period 1*/
+#define CS43130_XSP_LRCK_PERIOD_2 0x040027 /*XSP LRCK Period 2*/
+#define CS43130_XSP_CLOCK_CONF 0x040028 /*XSP Clock Config*/
+#define CS43130_XSP_FRAME_CONF 0x040029 /*XSP Frame Config*/
+#define CS43130_ASP_CH_1_LOC 0x050000 /*ASP Chan 1 Location*/
+#define CS43130_ASP_CH_2_LOC 0x050001 /*ASP Chan 2 Location*/
+#define CS43130_ASP_CH_1_SZ_EN 0x05000A /*ASP Chan 1 Size, Enable*/
+#define CS43130_ASP_CH_2_SZ_EN 0x05000B /*ASP Chan 2 Size, Enable*/
+#define CS43130_XSP_CH_1_LOC 0x060000 /*XSP Chan 1 Location*/
+#define CS43130_XSP_CH_2_LOC 0x060001 /*XSP Chan 2 Location*/
+#define CS43130_XSP_CH_1_SZ_EN 0x06000A /*XSP Chan 1 Size, Enable*/
+#define CS43130_XSP_CH_2_SZ_EN 0x06000B /*XSP Chan 2 Size, Enable*/
+#define CS43130_DSD_VOL_B 0x070000 /*DSD Volume B*/
+#define CS43130_DSD_VOL_A 0x070001 /*DSD Volume A*/
+#define CS43130_DSD_PATH_CTL_1 0x070002 /*DSD Proc Path Sig Ctl 1*/
+#define CS43130_DSD_INT_CFG 0x070003 /*DSD Interface Config*/
+#define CS43130_DSD_PATH_CTL_2 0x070004 /*DSD Proc Path Sig Ctl 2*/
+#define CS43130_DSD_PCM_MIX_CTL 0x070005 /*DSD and PCM Mixing Ctl*/
+#define CS43130_DSD_PATH_CTL_3 0x070006 /*DSD Proc Path Sig Ctl 3*/
+#define CS43130_HP_OUT_CTL_1 0x080000 /*HP Output Ctl 1*/
+#define CS43130_PCM_FILT_OPT 0x090000 /*PCM Filter Option*/
+#define CS43130_PCM_VOL_B 0x090001 /*PCM Volume B*/
+#define CS43130_PCM_VOL_A 0x090002 /*PCM Volume A*/
+#define CS43130_PCM_PATH_CTL_1 0x090003 /*PCM Path Signal Ctl 1*/
+#define CS43130_PCM_PATH_CTL_2 0x090004 /*PCM Path Signal Ctl 2*/
+#define CS43130_CLASS_H_CTL 0x0B0000 /*Class H Ctl*/
+#define CS43130_HP_DETECT 0x0D0000 /*HP Detect*/
+#define CS43130_HP_STATUS 0x0D0001 /*HP Status [RO]*/
+#define CS43130_HP_LOAD_1 0x0E0000 /*HP Load 1*/
+#define CS43130_HP_MEAS_LOAD_1 0x0E0003 /*HP Load Measurement 1*/
+#define CS43130_HP_MEAS_LOAD_2 0x0E0004 /*HP Load Measurement 2*/
+#define CS43130_HP_DC_STAT_1 0x0E000D /*HP DC Load Status 0 [RO]*/
+#define CS43130_HP_DC_STAT_2 0x0E000E /*HP DC Load Status 1 [RO]*/
+#define CS43130_HP_AC_STAT_1 0x0E0010 /*HP AC Load Status 0 [RO]*/
+#define CS43130_HP_AC_STAT_2 0x0E0011 /*HP AC Load Status 1 [RO]*/
+#define CS43130_HP_LOAD_STAT 0x0E001A /*HP Load Status [RO]*/
+#define CS43130_INT_STATUS_1 0x0F0000 /*Interrupt Status 1*/
+#define CS43130_INT_STATUS_2 0x0F0001 /*Interrupt Status 2*/
+#define CS43130_INT_STATUS_3 0x0F0002 /*Interrupt Status 3*/
+#define CS43130_INT_STATUS_4 0x0F0003 /*Interrupt Status 4*/
+#define CS43130_INT_STATUS_5 0x0F0004 /*Interrupt Status 5*/
+#define CS43130_INT_MASK_1 0x0F0010 /*Interrupt Mask 1*/
+#define CS43130_INT_MASK_2 0x0F0011 /*Interrupt Mask 2*/
+#define CS43130_INT_MASK_3 0x0F0012 /*Interrupt Mask 3*/
+#define CS43130_INT_MASK_4 0x0F0013 /*Interrupt Mask 4*/
+#define CS43130_INT_MASK_5 0x0F0014 /*Interrupt Mask 5*/
+
+#define CS43130_MCLK_SRC_SEL_MASK 0x03
+#define CS43130_MCLK_SRC_SEL_SHIFT 0
+#define CS43130_MCLK_INT_MASK 0x04
+#define CS43130_MCLK_INT_SHIFT 2
+#define CS43130_SP_SRATE_MASK 0x0F
+#define CS43130_SP_SRATE_SHIFT 0
+#define CS43130_SP_BITSIZE_ASP_MASK 0x03
+#define CS43130_SP_BITSIZE_ASP_SHIFT 0
+#define CS43130_HP_DETECT_CTRL_SHIFT 6
+#define CS43130_HP_DETECT_CTRL_MASK (0x03 << CS43130_HP_DETECT_CTRL_SHIFT)
+#define CS43130_HP_DETECT_INV_SHIFT 5
+#define CS43130_HP_DETECT_INV_MASK (1 << CS43130_HP_DETECT_INV_SHIFT)
+
+/* CS43130_INT_MASK_1 */
+#define CS43130_HP_PLUG_INT_SHIFT 6
+#define CS43130_HP_PLUG_INT (1 << CS43130_HP_PLUG_INT_SHIFT)
+#define CS43130_HP_UNPLUG_INT_SHIFT 5
+#define CS43130_HP_UNPLUG_INT (1 << CS43130_HP_UNPLUG_INT_SHIFT)
+#define CS43130_XTAL_RDY_INT_SHIFT 4
+#define CS43130_XTAL_RDY_INT (1 << CS43130_XTAL_RDY_INT_SHIFT)
+#define CS43130_XTAL_ERR_INT_SHIFT 3
+#define CS43130_XTAL_ERR_INT (1 << CS43130_XTAL_ERR_INT_SHIFT)
+
+/*Reg CS43130_SP_BITSIZE*/
+#define CS43130_SP_BIT_SIZE_8 0x00
+#define CS43130_SP_BIT_SIZE_16 0x01
+#define CS43130_SP_BIT_SIZE_24 0x02
+#define CS43130_SP_BIT_SIZE_32 0x03
+
+/*PLL*/
+#define CS43130_PLL_START_MASK (0x1<<0)
+#define CS43130_PLL_MODE_MASK 0x02
+#define CS43130_PLL_MODE_SHIFT 1
+
+#define CS43130_PLL_REF_PREDIV_MASK 0x3
+
+#define CS43130_ASP_STP_MASK 0x10
+#define CS43130_ASP_STP_SHIFT 4
+#define CS43130_ASP_5050_MASK 0x08
+#define CS43130_ASP_5050_SHIFT 3
+#define CS43130_ASP_FSD_MASK 0x07
+#define CS43130_ASP_FSD_SHIFT 0
+
+#define CS43130_ASP_MODE_MASK 0x10
+#define CS43130_ASP_MODE_SHIFT 4
+#define CS43130_ASP_SCPOL_OUT_MASK 0x08
+#define CS43130_ASP_SCPOL_OUT_SHIFT 3
+#define CS43130_ASP_SCPOL_IN_MASK 0x04
+#define CS43130_ASP_SCPOL_IN_SHIFT 2
+#define CS43130_ASP_LCPOL_OUT_MASK 0x02
+#define CS43130_ASP_LCPOL_OUT_SHIFT 1
+#define CS43130_ASP_LCPOL_IN_MASK 0x01
+#define CS43130_ASP_LCPOL_IN_SHIFT 0
+
+/*Reg CS43130_PWDN_CTL*/
+#define CS43130_PDN_XSP_MASK 0x80
+#define CS43130_PDN_XSP_SHIFT 7
+#define CS43130_PDN_ASP_MASK 0x40
+#define CS43130_PDN_ASP_SHIFT 6
+#define CS43130_PDN_DSPIF_MASK 0x20
+#define CS43130_PDN_DSDIF_SHIFT 5
+#define CS43130_PDN_HP_MASK 0x10
+#define CS43130_PDN_HP_SHIFT 4
+#define CS43130_PDN_XTAL_MASK 0x08
+#define CS43130_PDN_XTAL_SHIFT 3
+#define CS43130_PDN_PLL_MASK 0x04
+#define CS43130_PDN_PLL_SHIFT 2
+#define CS43130_PDN_CLKOUT_MASK 0x02
+#define CS43130_PDN_CLKOUT_SHIFT 1
+
+#define CS43130_7_0_MASK 0xFF
+#define CS43130_15_8_MASK 0xFF00
+#define CS43130_23_16_MASK 0xFF0000
+
+/* Reg CS43130_HP_OUT_CTL_1 */
+#define CS43130_HP_IN_EN_SHIFT 3
+#define CS43130_HP_IN_EN_MASK 0x08
+
+#define CS43130_ASP_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS43130_XSP_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+enum cs43130_asp_rate {
+ CS43130_ASP_SPRATE_32K = 0,
+ CS43130_ASP_SPRATE_44_1K,
+ CS43130_ASP_SPRATE_48K,
+ CS43130_ASP_SPRATE_88_2K,
+ CS43130_ASP_SPRATE_96K,
+ CS43130_ASP_SPRATE_176_4K,
+ CS43130_ASP_SPRATE_192K,
+ CS43130_ASP_SPRATE_352_8K,
+ CS43130_ASP_SPRATE_384K,
+};
+
+enum cs43130_mclk_src_sel {
+ CS43130_MCLK_SRC_XTAL = 0,
+ CS43130_MCLK_SRC_PLL,
+ CS43130_MCLK_SRC_RCO
+};
+
+enum cs43130_mode {
+ CS43130_SLAVE_MODE = 0,
+ CS43130_MASTER_MODE
+};
+
+enum cs43130_xtal_ibias {
+ CS43130_XTAL_IBIAS_15UA = 2,
+ CS43130_XTAL_IBIAS_12_5UA = 4,
+ CS43130_XTAL_IBIAS_7_5UA = 6,
+};
+
+#define CS43130_AIF_BICK_RATE 1
+#define CS43130_SYSCLK_MCLK 1
+#define CS43130_NUM_SUPPLIES 5
+static const char *const cs43130_supply_names[CS43130_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+ "VCP",
+ "VD",
+ "VL",
+};
+
+#define CS43130_NUM_INT 5 /* number of interrupt status reg */
+
+struct cs43130_private {
+ struct snd_soc_codec *codec;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[CS43130_NUM_SUPPLIES];
+ /* codec device ID */
+ unsigned int dev_id;
+ int mclk;
+ int sclk;
+ int xtal_ibias;
+
+ bool pll_bypass;
+ int pll_out;
+ int mclk_int;
+ int dai_format;
+ int dai_mode;
+ int dai_bit;
+ int asp_size;
+ int fs;
+ bool bick_invert;
+ bool lrck_invert;
+ int bick;
+ struct gpio_desc *reset_gpio;
+};
+
+#endif /* __CS43130_H__ */
--
1.9.1
1
0
12 Dec '16
When num_kcontrols is zero, widget->dobj.widget.kcontrol_type
gets set to an uninitialized local variable:
sound/soc/soc-topology.c: In function 'soc_tplg_dapm_widget_create':
sound/soc/soc-topology.c:1566:36: error: 'kcontrol_type' may be used uninitialized in this function [-Werror=maybe-uninitialized]
I could not figure out which of the valid types would be appropriate
here, so this sets it to '0', which is invalid but at least well-defined
here. There is probably a better way to address the issue.
Fixes: eea3dd4f1247 ("ASoC: topology: Only free TLV for volume mixers of a widget")
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
sound/soc/soc-topology.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 11feb19e9730..65670b2b408c 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1485,6 +1485,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
tplg->pos +=
(sizeof(struct snd_soc_tplg_dapm_widget) + w->priv.size);
if (w->num_kcontrols == 0) {
+ kcontrol_type = 0;
template.num_kcontrols = 0;
goto widget;
}
--
2.9.0
3
2
Re: [alsa-devel] [PATCH 0/7] Move dell-led to drivers/platform/x86
by Mario.Limonciello@dell.com 11 Dec '16
by Mario.Limonciello@dell.com 11 Dec '16
11 Dec '16
add Anthony Wong @ Canonical.
I'm about to go on break and won't have access to hardware for a while.
Anthony,
Your team has this hardware more readily available than I do. Would you be able to have someone on the team verify this series?
1
0
[alsa-devel] [asoc:topic/cq93vc 1/1] include/linux/mfd/davinci_voicecodec.h:31:27: fatal error: mach/hardware.h: No such file or directory
by kbuild test robot 11 Dec '16
by kbuild test robot 11 Dec '16
11 Dec '16
tree: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git topic/cq93vc
head: 4de429888c5b534f43a3d693e3627021b17e900c
commit: 4de429888c5b534f43a3d693e3627021b17e900c [1/1] ASoC: cq93vc: remove MFD_DAVINCI_VOICECODEC dependency for CQ0093VC
config: xtensa-allmodconfig (attached as .config)
compiler: xtensa-linux-gcc (GCC) 4.9.0
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/m… -O ~/bin/make.cross
chmod +x ~/bin/make.cross
git checkout 4de429888c5b534f43a3d693e3627021b17e900c
# save the attached .config to linux build tree
make.cross ARCH=xtensa
All errors (new ones prefixed by >>):
In file included from sound/soc/codecs/cq93vc.c:32:0:
>> include/linux/mfd/davinci_voicecodec.h:31:27: fatal error: mach/hardware.h: No such file or directory
#include <mach/hardware.h>
^
compilation terminated.
vim +31 include/linux/mfd/davinci_voicecodec.h
ca26308c Miguel Aguilar 2010-03-11 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
ca26308c Miguel Aguilar 2010-03-11 16 * GNU General Public License for more details.
ca26308c Miguel Aguilar 2010-03-11 17 *
ca26308c Miguel Aguilar 2010-03-11 18 * You should have received a copy of the GNU General Public License
ca26308c Miguel Aguilar 2010-03-11 19 * along with this program; if not, write to the Free Software
ca26308c Miguel Aguilar 2010-03-11 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
ca26308c Miguel Aguilar 2010-03-11 21 */
ca26308c Miguel Aguilar 2010-03-11 22
ca26308c Miguel Aguilar 2010-03-11 23 #ifndef __LINUX_MFD_DAVINCI_VOICECODEC_H_
c593aca4 Rasmus Villemoes 2014-08-22 24 #define __LINUX_MFD_DAVINCI_VOICECODEC_H_
ca26308c Miguel Aguilar 2010-03-11 25
ca26308c Miguel Aguilar 2010-03-11 26 #include <linux/kernel.h>
ca26308c Miguel Aguilar 2010-03-11 27 #include <linux/platform_device.h>
ca26308c Miguel Aguilar 2010-03-11 28 #include <linux/mfd/core.h>
3ad7a42d Matt Porter 2013-03-06 29 #include <linux/platform_data/edma.h>
ca26308c Miguel Aguilar 2010-03-11 30
99717470 Sachin Kamat 2013-06-18 @31 #include <mach/hardware.h>
ca26308c Miguel Aguilar 2010-03-11 32
921a2c87 Mark Brown 2013-08-31 33 struct regmap;
921a2c87 Mark Brown 2013-08-31 34
ca26308c Miguel Aguilar 2010-03-11 35 /*
ca26308c Miguel Aguilar 2010-03-11 36 * Register values.
ca26308c Miguel Aguilar 2010-03-11 37 */
ca26308c Miguel Aguilar 2010-03-11 38 #define DAVINCI_VC_PID 0x00
ca26308c Miguel Aguilar 2010-03-11 39 #define DAVINCI_VC_CTRL 0x04
:::::: The code at line 31 was first introduced by commit
:::::: 997174705458d2abdbc31ba1594bf2a4503cb41a mfd: davinci_voicecodec: Fix build breakage
:::::: TO: Sachin Kamat <sachin.kamat(a)linaro.org>
:::::: CC: Samuel Ortiz <sameo(a)linux.intel.com>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
1
0
[alsa-devel] [PATCH SND/USB]: Add QuickCam Communicate Deluxe/S7500 to volume_control_quirks.
by Con Kolivas 09 Dec '16
by Con Kolivas 09 Dec '16
09 Dec '16
The Logitech QuickCam Communicate Deluxe/S7500 microphone fails with the
following warning.
[ 6.778995] usb 2-1.2.2.2: Warning! Unlikely big volume range (=3072),
cval->res is probably wrong.
[ 6.778996] usb 2-1.2.2.2: [5] FU [Mic Capture Volume] ch = 1, val =
4608/7680/1
Adding it to the list of devices in volume_control_quirks makes it work
properly, fixing related typo.
Signed-off-by: Con Kolivas <kernel(a)kolivas.org>
---
sound/usb/mixer.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 2f8c388..4703cae 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -932,9 +932,10 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */
case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */
case USB_ID(0x046d, 0x0991):
+ case USB_ID(0x046d, 0x09a2): /* QuickCam Communicate Deluxe/S7500 */
/* Most audio usb devices lie about volume resolution.
* Most Logitech webcams have res = 384.
- * Proboly there is some logitech magic behind this number --fishor
+ * Probably there is some logitech magic behind this number --fishor
*/
if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
usb_audio_info(chip,
--
2.7.4
3
4
09 Dec '16
Both SND_SOC_SMARTQ and SND_SOC_SAMSUNG_TM2_WM5110
use gpio/consumer.h
This patch adds GPIOLIB || COMPILE_TEST to Kconfig entries
to fix runtime dependency.
See commit 638f958baeaf
("extcon: Allow compile test of GPIO consumers if !GPIOLIB")
for similar problem and explanations.
Reviewed-by: Krzysztof Kozlowski <krzk(a)kernel.org>
Reported-by: Krzysztof Kozlowski <krzk(a)kernel.org>
Signed-off-by: Fabian Frederick <fabf(a)skynet.be>
---
V3:
-Fix commit message (suggested by Krzysztof Kozlowski)
-Add reviewed-by
sound/soc/samsung/Kconfig | 2 ++
1 file changed, 2 insertions(+)
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 7c42315..f1f1d79 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -111,6 +111,7 @@ config SND_SOC_SAMSUNG_RX1950_UDA1380
config SND_SOC_SMARTQ
tristate "SoC I2S Audio support for SmartQ board"
depends on MACH_SMARTQ || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
depends on I2C
select SND_SAMSUNG_I2S
select SND_SOC_WM8750
@@ -193,6 +194,7 @@ config SND_SOC_ARNDALE_RT5631_ALC5631
config SND_SOC_SAMSUNG_TM2_WM5110
tristate "SoC I2S Audio support for WM5110 on TM2 board"
depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER
+ depends on GPIOLIB || COMPILE_TEST
select SND_SOC_MAX98504
select SND_SOC_WM5110
select SND_SAMSUNG_I2S
--
2.7.4
1
0