summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig11
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/ak4613.c10
-rw-r--r--sound/soc/codecs/cs35l35.c1580
-rw-r--r--sound/soc/codecs/cs35l35.h294
-rw-r--r--sound/soc/codecs/cs4271.c2
-rw-r--r--sound/soc/codecs/cs53l30.c1
-rw-r--r--sound/soc/codecs/da7213.c13
-rw-r--r--sound/soc/codecs/hdac_hdmi.c20
-rw-r--r--sound/soc/codecs/rt5665.c232
-rw-r--r--sound/soc/codecs/rt5665.h4
-rw-r--r--sound/soc/codecs/rt5670.c21
-rw-r--r--sound/soc/codecs/wm_adsp.c333
-rw-r--r--sound/soc/codecs/wm_adsp.h24
14 files changed, 2401 insertions, 146 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 5e5fcb660ca3..e3e6f39d14e8 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
@@ -304,12 +305,14 @@ config SND_SOC_ADAU1761
select SND_SOC_ADAU17X1
config SND_SOC_ADAU1761_I2C
- tristate
+ tristate "Analog Devices AU1761 CODEC - I2C"
+ depends on I2C
select SND_SOC_ADAU1761
select REGMAP_I2C
config SND_SOC_ADAU1761_SPI
- tristate
+ tristate "Analog Devices AU1761 CODEC - SPI"
+ depends on SPI
select SND_SOC_ADAU1761
select REGMAP_SPI
@@ -409,6 +412,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 04894fa8891b..2c596d84fa3e 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
@@ -270,6 +271,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/ak4613.c b/sound/soc/codecs/ak4613.c
index e819dd8c82fd..b2dfddead227 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -75,6 +75,12 @@
#define DFS_DOUBLE_SPEED (1 << 2)
#define DFS_QUAD_SPEED (2 << 2)
+/* ICTRL */
+#define ICTRL_MASK (0x3)
+
+/* OCTRL */
+#define OCTRL_MASK (0x3F)
+
struct ak4613_formats {
unsigned int width;
unsigned int fmt;
@@ -365,8 +371,8 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2);
- snd_soc_write(codec, ICTRL, priv->ic);
- snd_soc_write(codec, OCTRL, priv->oc);
+ snd_soc_update_bits(codec, ICTRL, ICTRL_MASK, priv->ic);
+ snd_soc_update_bits(codec, OCTRL, OCTRL_MASK, priv->oc);
hw_params_end:
if (ret < 0)
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
new file mode 100644
index 000000000000..f8aef5869b03
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.c
@@ -0,0 +1,1580 @@
+/*
+ * cs35l35.c -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@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/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"
+
+/*
+ * Some fields take zero as a valid value so use a high bit flag that won't
+ * get written to the device to mark those.
+ */
+#define CS35L35_VALID_PDATA 0x80000000
+
+static const struct reg_default cs35l35_reg[] = {
+ {CS35L35_PWRCTL1, 0x01},
+ {CS35L35_PWRCTL2, 0x11},
+ {CS35L35_PWRCTL3, 0x00},
+ {CS35L35_CLK_CTL1, 0x04},
+ {CS35L35_CLK_CTL2, 0x12},
+ {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, 0x24},
+ {CS35L35_BST_CONV_COEF_2, 0x24},
+ {CS35L35_BST_CONV_SLOPE_COMP, 0x4E},
+ {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, 0x90},
+ {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_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_wait_for_pdn(struct cs35l35_private *cs35l35)
+{
+ int ret;
+
+ if (cs35l35->pdata.ext_bst) {
+ usleep_range(5000, 5500);
+ return 0;
+ }
+
+ reinit_completion(&cs35l35->pdn_done);
+
+ ret = wait_for_completion_timeout(&cs35l35->pdn_done,
+ msecs_to_jiffies(100));
+ if (ret == 0) {
+ dev_err(cs35l35->dev, "PDN_DONE did not complete\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+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_DISCHG_FILT_MASK,
+ 1 << CS35L35_DISCHG_FILT_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 1);
+
+ /* Already muted, so disable volume ramp for faster shutdown */
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL,
+ CS35L35_AMP_DIGSFT_MASK, 0);
+
+ ret = cs35l35_wait_for_pdn(cs35l35);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK,
+ 1 << CS35L35_MCLK_DIS_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL,
+ CS35L35_AMP_DIGSFT_MASK,
+ 1 << CS35L35_AMP_DIGSFT_SHIFT);
+ break;
+ default:
+ dev_err(codec->dev, "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);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ /* If in 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);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 0);
+
+ for (i = 0; i < 2; i++)
+ regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1,
+ &reg, 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:
+ dev_err(codec->dev, "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("Analog Audio Volume", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0,
+ amp_gain_tlv),
+ SOC_SINGLE_TLV("PDM Volume", 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("Analog Advisory Volume", 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_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));
+ return -EINVAL;
+ }
+ 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);
+ }
+ }
+
+ if (cs35l35->i2s_mode) {
+ /* 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;
+
+ /* 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;
+ }
+ }
+
+ 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)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+ if (!substream->runtime)
+ return 0;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &cs35l35_constraints);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK,
+ 0 << CS35L35_PDM_MODE_SHIFT);
+
+ 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)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+ if (!substream->runtime)
+ return 0;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l35_pdm_constraints);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK,
+ 1 << CS35L35_PDM_MODE_SHIFT);
+
+ 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 regardless of sysclk source for I2S */
+ 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_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_hw_params,
+};
+
+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 Input : %d\n", freq);
+ 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 (cs35l35->pdata.sp_drv_unused)
+ regmap_update_bits(cs35l35->regmap, CS35L35_SP_FMT_CTL3,
+ CS35L35_SP_I2S_DRV_MASK,
+ cs35l35->pdata.sp_drv_unused <<
+ CS35L35_SP_I2S_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);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_SCALE_CTL,
+ CS35L35_IMON_SCALE_MASK,
+ monitor_config->imon_scale <<
+ CS35L35_IMON_SCALE_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 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l35 = {
+ .probe = cs35l35_codec_probe,
+ .set_sysclk = cs35l35_codec_set_sysclk,
+ .component_driver = {
+ .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),
+
+ .controls = cs35l35_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l35_aud_controls),
+ },
+
+};
+
+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, &current1);
+
+ /* handle the interrupts */
+ if (sticky1 & CS35L35_CAL_ERR) {
+ dev_crit(cs35l35->dev, "Calibration Error\n");
+
+ /* 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) {
+ dev_crit(cs35l35->dev, "AMP Short Error\n");
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_AMP_SHORT)) {
+ dev_dbg(cs35l35->dev, "Amp short error release\n");
+ 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) {
+ dev_warn(cs35l35->dev, "Over temperature warning\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTW)) {
+ dev_dbg(cs35l35->dev, "Over temperature warn release\n");
+ 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) {
+ dev_crit(cs35l35->dev, "Over temperature error\n");
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTE)) {
+ dev_dbg(cs35l35->dev, "Over temperature error release\n");
+ 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) {
+ dev_crit(cs35l35->dev, "VBST error: powering off!\n");
+ 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) {
+ dev_crit(cs35l35->dev, "LBST error: powering off!\n");
+ 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)
+ dev_dbg(cs35l35->dev, "Error: Reactive Brownout\n");
+
+ if (sticky4 & CS35L35_VMON_OVFL)
+ dev_dbg(cs35l35->dev, "Error: VMON overflow\n");
+
+ if (sticky4 & CS35L35_IMON_OVFL)
+ dev_dbg(cs35l35->dev, "Error: IMON overflow\n");
+
+ 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[4];
+ const int imon_array_size = ARRAY_SIZE(monitor_array);
+ const int mon_array_size = imon_array_size - 1;
+ int ret = 0;
+
+ if (!np)
+ return 0;
+
+ pdata->bst_pdn_fet_on = of_property_read_bool(np,
+ "cirrus,boost-pdn-fet-on");
+
+ ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32);
+ if (ret >= 0) {
+ if (val32 < 2600 || val32 > 9000) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Voltage %d mV\n", val32);
+ return -EINVAL;
+ }
+ pdata->bst_vctl = ((val32 - 2600) / 100) + 1;
+ }
+
+ ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val32);
+ if (ret >= 0) {
+ if (val32 < 1680 || val32 > 4480) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Peak Current %u mA\n", val32);
+ return -EINVAL;
+ }
+
+ pdata->bst_ipk = (val32 - 1680) / 110;
+ }
+
+ if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0)
+ pdata->sp_drv_str = val32;
+ if (of_property_read_u32(np, "cirrus,sp-drv-unused", &val32) >= 0)
+ pdata->sp_drv_unused = val32 | CS35L35_VALID_PDATA;
+
+ pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config");
+
+ if (pdata->stereo) {
+ ret = of_property_read_u32(np, "cirrus,audio-channel", &val32);
+ if (ret >= 0)
+ pdata->aud_channel = val32;
+
+ ret = of_property_read_u32(np, "cirrus,advisory-channel",
+ &val32);
+ if (ret >= 0)
+ pdata->adv_channel = val32;
+
+ pdata->shared_bst = of_property_read_bool(np,
+ "cirrus,shared-boost");
+ }
+
+ pdata->ext_bst = of_property_read_bool(np, "cirrus,external-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");
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-bst-max-limit",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_bst_max_limit = val32;
+ }
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-bst-max-limit",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_bst_max_limit = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-mem-depth",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_mem_depth = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-release-rate",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_release_rate = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-headroom",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_headroom = val32;
+ }
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-wk-fet-disable",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_wk_fet_disable = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-delay",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_wk_fet_delay = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-thld",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_wk_fet_thld = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-auto",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_vpch_auto = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-rate",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_vpch_rate = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-man",
+ &val32);
+ if (ret >= 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, imon_array_size);
+ 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];
+ monitor_config->imon_scale = monitor_array[3];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vmon",
+ monitor_array, mon_array_size);
+ 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, mon_array_size);
+ 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, mon_array_size);
+ 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, mon_array_size);
+ 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, mon_array_size);
+ 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 device *dev = &i2c_client->dev;
+ struct cs35l35_platform_data *pdata = dev_get_platdata(dev);
+ int i;
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs35l35 = devm_kzalloc(dev, sizeof(struct cs35l35_private), GFP_KERNEL);
+ if (!cs35l35)
+ return -ENOMEM;
+
+ cs35l35->dev = dev;
+
+ 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(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(dev, cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l35->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(dev, sizeof(struct cs35l35_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ 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(dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* returning NULL can be valid if in stereo mode */
+ cs35l35->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l35->reset_gpio)) {
+ ret = PTR_ERR(cs35l35->reset_gpio);
+ cs35l35->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(dev,
+ "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
+ goto err;
+ }
+ }
+
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 1);
+
+ init_completion(&cs35l35->pdn_done);
+
+ ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l35_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW |
+ IRQF_SHARED, "cs35l35", cs35l35);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+ /* initialize codec */
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, &reg);
+
+ devid = (reg & 0xFF) << 12;
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, &reg);
+ devid |= (reg & 0xFF) << 4;
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, &reg);
+ devid |= (reg & 0xF0) >> 4;
+
+ if (devid != CS35L35_CHIP_ID) {
+ dev_err(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, &reg);
+ if (ret < 0) {
+ dev_err(dev, "Get Revision ID failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch,
+ ARRAY_SIZE(cs35l35_errata_patch));
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply errata patch: %d\n", ret);
+ goto err;
+ }
+
+ dev_info(dev, "Cirrus Logic CS35L35 (%x), Revision: %02X\n",
+ devid, reg & 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(dev, &soc_codec_dev_cs35l35, cs35l35_dai,
+ ARRAY_SIZE(cs35l35_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regulator_bulk_disable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 0);
+
+ return ret;
+}
+
+static int cs35l35_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ 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@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
new file mode 100644
index 000000000000..5a6e43a87c4d
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.h
@@ -0,0 +1,294 @@
+/*
+ * cs35l35.h -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@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 0x0C
+#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 0x30
+#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_IMON_SCALE_MASK 0xF8
+#define CS35L35_IMON_SCALE_SHIFT 3
+
+#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
+
+#define CS35L35_AMP_DIGSFT_MASK 0x02
+#define CS35L35_AMP_DIGSFT_SHIFT 1
+
+/* CS35L35_SP_FMT_CTL3 */
+#define CS35L35_SP_I2S_DRV_MASK 0x03
+#define CS35L35_SP_I2S_DRV_SHIFT 0
+
+/* 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 device *dev;
+ 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
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 8c0f3b89b5bc..e78b5f055f25 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -498,7 +498,7 @@ static int cs4271_reset(struct snd_soc_codec *codec)
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
if (gpio_is_valid(cs4271->gpio_nreset)) {
- gpio_set_value(cs4271->gpio_nreset, 0);
+ gpio_direction_output(cs4271->gpio_nreset, 0);
mdelay(1);
gpio_set_value(cs4271->gpio_nreset, 1);
mdelay(1);
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
index cb47fb595ff4..1e0d5973b758 100644
--- a/sound/soc/codecs/cs53l30.c
+++ b/sound/soc/codecs/cs53l30.c
@@ -1130,6 +1130,7 @@ MODULE_DEVICE_TABLE(i2c, cs53l30_id);
static struct i2c_driver cs53l30_i2c_driver = {
.driver = {
.name = "cs53l30",
+ .of_match_table = cs53l30_of_match,
.pm = &cs53l30_runtime_pm,
},
.id_table = cs53l30_id,
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 12da55882c06..6dd7578f0bb8 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -12,6 +12,7 @@
* option) any later version.
*/
+#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -1528,12 +1529,23 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
+#if defined(CONFIG_OF)
/* DT */
static const struct of_device_id da7213_of_match[] = {
{ .compatible = "dlg,da7213", },
{ }
};
MODULE_DEVICE_TABLE(of, da7213_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id da7213_acpi_match[] = {
+ { "DLGS7212", 0},
+ { "DLGS7213", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, da7213_acpi_match);
+#endif
static enum da7213_micbias_voltage
da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
@@ -1844,6 +1856,7 @@ static struct i2c_driver da7213_i2c_driver = {
.driver = {
.name = "da7213",
.of_match_table = of_match_ptr(da7213_of_match),
+ .acpi_match_table = ACPI_PTR(da7213_acpi_match),
},
.probe = da7213_i2c_probe,
.remove = da7213_remove,
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 78fca8acd3ec..bc2e74ff3b2d 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -469,7 +469,7 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
format = snd_hdac_calc_stream_format(params_rate(hparams),
params_channels(hparams), params_format(hparams),
- 24, 0);
+ dai->driver->playback.sig_bits, 0);
pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
if (!pcm)
@@ -1419,8 +1419,8 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac,
hdmi_dais[i].playback.rate_min = rate_min;
hdmi_dais[i].playback.channels_min = 2;
hdmi_dais[i].playback.channels_max = 2;
+ hdmi_dais[i].playback.sig_bits = bps;
hdmi_dais[i].ops = &hdmi_dai_ops;
-
i++;
}
@@ -1534,21 +1534,20 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
pin->mst_capable = false;
/* if not MST, default is port[0] */
hport = &pin->ports[0];
- goto out;
} else {
for (i = 0; i < pin->num_ports; i++) {
pin->mst_capable = true;
if (pin->ports[i].id == pipe) {
hport = &pin->ports[i];
- goto out;
+ break;
}
}
}
+
+ if (hport)
+ hdac_hdmi_present_sense(pin, hport);
}
-out:
- if (pin && hport)
- hdac_hdmi_present_sense(pin, hport);
}
static struct i915_audio_component_audio_ops aops = {
@@ -1998,7 +1997,7 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
struct hdac_hdmi_pin *pin, *pin_next;
struct hdac_hdmi_cvt *cvt, *cvt_next;
struct hdac_hdmi_pcm *pcm, *pcm_next;
- struct hdac_hdmi_port *port;
+ struct hdac_hdmi_port *port, *port_next;
int i;
snd_soc_unregister_codec(&edev->hdac.dev);
@@ -2008,8 +2007,9 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
if (list_empty(&pcm->port_list))
continue;
- list_for_each_entry(port, &pcm->port_list, head)
- port = NULL;
+ list_for_each_entry_safe(port, port_next,
+ &pcm->port_list, head)
+ list_del(&port->head);
list_del(&pcm->head);
kfree(pcm);
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index 324461e985b3..8cd22307f5b6 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -1139,7 +1139,8 @@ static void rt5665_enable_push_button_irq(struct snd_soc_codec *codec,
bool enable)
{
if (enable) {
- snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, 0x000b);
+ snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, 0x0003);
+ snd_soc_update_bits(codec, RT5665_SAR_IL_CMD_9, 0x1, 0x1);
snd_soc_write(codec, RT5665_IL_CMD_1, 0x0048);
snd_soc_update_bits(codec, RT5665_4BTN_IL_CMD_2,
RT5665_4BTN_IL_MASK | RT5665_4BTN_IL_RST_MASK,
@@ -1192,10 +1193,13 @@ static int rt5665_headset_detect(struct snd_soc_codec *codec, int jack_insert)
}
regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1,
- 0x180, 0x180);
+ 0x1a0, 0x120);
regmap_write(rt5665->regmap, RT5665_EJD_CTRL_3, 0x3424);
+ regmap_write(rt5665->regmap, RT5665_IL_CMD_1, 0x0048);
regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, 0xa291);
+ usleep_range(10000, 15000);
+
rt5665->sar_adc_value = snd_soc_read(rt5665->codec,
RT5665_SAR_IL_CMD_4) & 0x7ff;
@@ -1241,7 +1245,7 @@ static irqreturn_t rt5665_irq(int irq, void *data)
static void rt5665_jd_check_handler(struct work_struct *work)
{
struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv,
- calibrate_work.work);
+ jd_check_work.work);
if (snd_soc_read(rt5665->codec, RT5665_AJD1_CTRL) & 0x0010) {
/* jack out */
@@ -1256,8 +1260,8 @@ static void rt5665_jd_check_handler(struct work_struct *work)
}
}
-int rt5665_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hs_jack)
+static int rt5665_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *hs_jack, void *data)
{
struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
@@ -1284,7 +1288,6 @@ int rt5665_set_jack_detect(struct snd_soc_codec *codec,
return 0;
}
-EXPORT_SYMBOL_GPL(rt5665_set_jack_detect);
static void rt5665_jack_detect_handler(struct work_struct *work)
{
@@ -2252,7 +2255,7 @@ static const char * const rt5665_if2_1_adc_in_src[] = {
static const SOC_ENUM_SINGLE_DECL(
rt5665_if2_1_adc_in_enum, RT5665_DIG_INF2_DATA,
- RT5665_IF3_ADC_IN_SFT, rt5665_if2_1_adc_in_src);
+ RT5665_IF2_1_ADC_IN_SFT, rt5665_if2_1_adc_in_src);
static const struct snd_kcontrol_new rt5665_if2_1_adc_in_mux =
SOC_DAPM_ENUM("IF2_1 ADC IN Source", rt5665_if2_1_adc_in_enum);
@@ -2600,6 +2603,55 @@ static int rt5655_set_verf(struct snd_soc_dapm_widget *w,
return 0;
}
+static int rt5665_i2s_pin_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);
+ unsigned int val1, val2, mask1, mask2 = 0;
+
+ switch (w->shift) {
+ case RT5665_PWR_I2S2_1_BIT:
+ mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK |
+ RT5665_GP4_PIN_MASK | RT5665_GP5_PIN_MASK;
+ val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 |
+ RT5665_GP4_PIN_DACDAT2_1 | RT5665_GP5_PIN_ADCDAT2_1;
+ break;
+ case RT5665_PWR_I2S2_2_BIT:
+ mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK |
+ RT5665_GP8_PIN_MASK;
+ val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 |
+ RT5665_GP8_PIN_DACDAT2_2;
+ mask2 = RT5665_GP9_PIN_MASK;
+ val2 = RT5665_GP9_PIN_ADCDAT2_2;
+ break;
+ case RT5665_PWR_I2S3_BIT:
+ mask1 = RT5665_GP6_PIN_MASK | RT5665_GP7_PIN_MASK |
+ RT5665_GP8_PIN_MASK;
+ val1 = RT5665_GP6_PIN_BCLK3 | RT5665_GP7_PIN_LRCK3 |
+ RT5665_GP8_PIN_DACDAT3;
+ mask2 = RT5665_GP9_PIN_MASK;
+ val2 = RT5665_GP9_PIN_ADCDAT3;
+ break;
+ }
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, val1);
+ if (mask2)
+ snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2,
+ mask2, val2);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, 0);
+ if (mask2)
+ snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2,
+ mask2, 0);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("LDO2", RT5665_PWR_ANLG_3, RT5665_PWR_LDO2_BIT, 0,
@@ -2852,11 +2904,14 @@ static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("I2S1_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S1_2_BIT,
0, NULL, 0),
SND_SOC_DAPM_SUPPLY("I2S2_1", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_1_BIT,
- 0, NULL, 0),
+ 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("I2S2_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_2_BIT,
- 0, NULL, 0),
+ 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("I2S3", RT5665_PWR_DIG_1, RT5665_PWR_I2S3_BIT,
- 0, NULL, 0),
+ 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -3178,6 +3233,9 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = {
{"DAC Mono Right Filter", NULL, "DAC Mono R ASRC", is_using_asrc},
{"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc},
{"DAC Stereo2 Filter", NULL, "DAC STO2 ASRC", is_using_asrc},
+ {"I2S1 ASRC", NULL, "CLKDET"},
+ {"I2S2 ASRC", NULL, "CLKDET"},
+ {"I2S3 ASRC", NULL, "CLKDET"},
/*Vref*/
{"Mic Det Power", NULL, "Vref2"},
@@ -3912,6 +3970,7 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = {
{"Mono MIX", "MONOVOL Switch", "MONOVOL"},
{"Mono Amp", NULL, "Mono MIX"},
{"Mono Amp", NULL, "Vref2"},
+ {"Mono Amp", NULL, "Vref3"},
{"Mono Amp", NULL, "CLKDET SYS"},
{"Mono Amp", NULL, "CLKDET MONO"},
{"Mono Playback", "Switch", "Mono Amp"},
@@ -3959,12 +4018,68 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = {
{"PDMR", NULL, "PDM R Playback"},
};
+static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val = 0;
+
+ if (rx_mask || tx_mask)
+ val |= RT5665_I2S1_MODE_TDM;
+
+ switch (slots) {
+ case 4:
+ val |= RT5665_TDM_IN_CH_4;
+ val |= RT5665_TDM_OUT_CH_4;
+ break;
+ case 6:
+ val |= RT5665_TDM_IN_CH_6;
+ val |= RT5665_TDM_OUT_CH_6;
+ break;
+ case 8:
+ val |= RT5665_TDM_IN_CH_8;
+ val |= RT5665_TDM_OUT_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= RT5665_TDM_IN_LEN_20;
+ val |= RT5665_TDM_OUT_LEN_20;
+ break;
+ case 24:
+ val |= RT5665_TDM_IN_LEN_24;
+ val |= RT5665_TDM_OUT_LEN_24;
+ break;
+ case 32:
+ val |= RT5665_TDM_IN_LEN_32;
+ val |= RT5665_TDM_OUT_LEN_32;
+ break;
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, RT5665_TDM_CTRL_1,
+ RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK |
+ RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK |
+ RT5665_TDM_OUT_LEN_MASK, val);
+
+ return 0;
+}
+
+
static int rt5665_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 rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
- unsigned int val_len = 0, val_clk, mask_clk, val_bits = 0x0100;
+ unsigned int val_len = 0, val_clk, reg_clk, mask_clk, val_bits = 0x0100;
int pre_div, frame_size;
rt5665->lrck[dai->id] = params_rate(params);
@@ -4005,6 +4120,10 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
switch (dai->id) {
case RT5665_AIF1_1:
case RT5665_AIF1_2:
+ if (params_channels(params) > 2)
+ rt5665_set_tdm_slot(dai, 0xf, 0xf,
+ params_channels(params), params_width(params));
+ reg_clk = RT5665_ADDA_CLK_1;
mask_clk = RT5665_I2S_PD1_MASK;
val_clk = pre_div << RT5665_I2S_PD1_SFT;
snd_soc_update_bits(codec, RT5665_I2S1_SDP,
@@ -4012,12 +4131,14 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
break;
case RT5665_AIF2_1:
case RT5665_AIF2_2:
+ reg_clk = RT5665_ADDA_CLK_2;
mask_clk = RT5665_I2S_PD2_MASK;
val_clk = pre_div << RT5665_I2S_PD2_SFT;
snd_soc_update_bits(codec, RT5665_I2S2_SDP,
RT5665_I2S_DL_MASK, val_len);
break;
case RT5665_AIF3:
+ reg_clk = RT5665_ADDA_CLK_2;
mask_clk = RT5665_I2S_PD3_MASK;
val_clk = pre_div << RT5665_I2S_PD3_SFT;
snd_soc_update_bits(codec, RT5665_I2S3_SDP,
@@ -4028,7 +4149,7 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, mask_clk, val_clk);
+ snd_soc_update_bits(codec, reg_clk, mask_clk, val_clk);
snd_soc_update_bits(codec, RT5665_STO1_DAC_SIL_DET, 0x3700, val_bits);
switch (rt5665->lrck[dai->id]) {
@@ -4121,10 +4242,9 @@ static int rt5665_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
-static int rt5665_set_dai_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
+static int rt5665_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
unsigned int reg_val = 0;
@@ -4150,20 +4270,20 @@ static int rt5665_set_dai_sysclk(struct snd_soc_dai *dai,
rt5665->sysclk = freq;
rt5665->sysclk_src = clk_id;
- dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
return 0;
}
-static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
- unsigned int freq_in, unsigned int freq_out)
+static int rt5665_set_codec_pll(struct snd_soc_codec *codec, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
struct rl6231_pll_code pll_code;
int ret;
- if (Source == rt5665->pll_src && freq_in == rt5665->pll_in &&
+ if (source == rt5665->pll_src && freq_in == rt5665->pll_in &&
freq_out == rt5665->pll_out)
return 0;
@@ -4177,7 +4297,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
return 0;
}
- switch (Source) {
+ switch (source) {
case RT5665_PLL1_S_MCLK:
snd_soc_update_bits(codec, RT5665_GLB_CLK,
RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_MCLK);
@@ -4195,7 +4315,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK3);
break;
default:
- dev_err(codec->dev, "Unknown PLL Source %d\n", Source);
+ dev_err(codec->dev, "Unknown PLL Source %d\n", source);
return -EINVAL;
}
@@ -4217,62 +4337,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
rt5665->pll_in = freq_in;
rt5665->pll_out = freq_out;
- rt5665->pll_src = Source;
-
- return 0;
-}
-
-static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
- unsigned int rx_mask, int slots, int slot_width)
-{
- struct snd_soc_codec *codec = dai->codec;
- unsigned int val = 0;
-
- if (rx_mask || tx_mask)
- val |= RT5665_I2S1_MODE_TDM;
-
- switch (slots) {
- case 4:
- val |= RT5665_TDM_IN_CH_4;
- val |= RT5665_TDM_OUT_CH_4;
- break;
- case 6:
- val |= RT5665_TDM_IN_CH_6;
- val |= RT5665_TDM_OUT_CH_6;
- break;
- case 8:
- val |= RT5665_TDM_IN_CH_8;
- val |= RT5665_TDM_OUT_CH_8;
- break;
- case 2:
- break;
- default:
- return -EINVAL;
- }
-
- switch (slot_width) {
- case 20:
- val |= RT5665_TDM_IN_LEN_20;
- val |= RT5665_TDM_OUT_LEN_20;
- break;
- case 24:
- val |= RT5665_TDM_IN_LEN_24;
- val |= RT5665_TDM_OUT_LEN_24;
- break;
- case 32:
- val |= RT5665_TDM_IN_LEN_32;
- val |= RT5665_TDM_OUT_LEN_32;
- break;
- case 16:
- break;
- default:
- return -EINVAL;
- }
-
- snd_soc_update_bits(codec, RT5665_TDM_CTRL_1,
- RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK |
- RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK |
- RT5665_TDM_OUT_LEN_MASK, val);
+ rt5665->pll_src = source;
return 0;
}
@@ -4389,9 +4454,7 @@ static int rt5665_resume(struct snd_soc_codec *codec)
static const struct snd_soc_dai_ops rt5665_aif_dai_ops = {
.hw_params = rt5665_hw_params,
.set_fmt = rt5665_set_dai_fmt,
- .set_sysclk = rt5665_set_dai_sysclk,
.set_tdm_slot = rt5665_set_tdm_slot,
- .set_pll = rt5665_set_dai_pll,
.set_bclk_ratio = rt5665_set_bclk_ratio,
};
@@ -4500,7 +4563,10 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5665 = {
.num_dapm_widgets = ARRAY_SIZE(rt5665_dapm_widgets),
.dapm_routes = rt5665_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt5665_dapm_routes),
- }
+ },
+ .set_sysclk = rt5665_set_codec_sysclk,
+ .set_pll = rt5665_set_codec_pll,
+ .set_jack = rt5665_set_jack_detect,
};
@@ -4779,7 +4845,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c,
regmap_write(rt5665->regmap, RT5665_HP_LOGIC_CTRL_2, 0x0002);
regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1,
- 0xf000 | RT5665_VREF_POW_MASK, 0xd000 | RT5665_VREF_POW_REG);
+ 0xf000 | RT5665_VREF_POW_MASK, 0xe000 | RT5665_VREF_POW_REG);
/* Work around for pow_pump */
regmap_update_bits(rt5665->regmap, RT5665_STO1_DAC_SIL_DET,
RT5665_DEB_STO_DAC_MASK, RT5665_DEB_80_MS);
@@ -4798,7 +4864,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c,
/* Enhance performance*/
regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1,
RT5665_HP_DRIVER_MASK | RT5665_LDO1_DVO_MASK,
- RT5665_HP_DRIVER_5X | RT5665_LDO1_DVO_09);
+ RT5665_HP_DRIVER_5X | RT5665_LDO1_DVO_12);
INIT_DELAYED_WORK(&rt5665->jack_detect_work,
rt5665_jack_detect_handler);
diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h
index 12f7080a0d3c..1db5c6a62a8e 100644
--- a/sound/soc/codecs/rt5665.h
+++ b/sound/soc/codecs/rt5665.h
@@ -1106,7 +1106,7 @@
#define RT5665_HP_DRIVER_MASK (0x3 << 2)
#define RT5665_HP_DRIVER_1X (0x0 << 2)
#define RT5665_HP_DRIVER_3X (0x1 << 2)
-#define RT5665_HP_DRIVER_5X (0x2 << 2)
+#define RT5665_HP_DRIVER_5X (0x3 << 2)
#define RT5665_LDO1_DVO_MASK (0x3)
#define RT5665_LDO1_DVO_09 (0x0)
#define RT5665_LDO1_DVO_10 (0x1)
@@ -1984,7 +1984,5 @@ enum {
int rt5665_sel_asrc_clk_src(struct snd_soc_codec *codec,
unsigned int filter_mask, unsigned int clk_src);
-int rt5665_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hs_jack);
#endif /* __RT5665_H__ */
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 17d20b99f041..e27c5a4a0a15 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -2835,6 +2835,27 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Wyse 3040"),
},
},
+ {
+ .ident = "Lenovo Thinkpad Tablet 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"),
+ },
+ },
+ {
+ .ident = "Lenovo Thinkpad Tablet 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"),
+ },
+ },
+ {
+ .ident = "Lenovo Thinkpad Tablet 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
+ },
+ },
{}
};
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index d151224ffcca..20695b691aff 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -112,17 +112,22 @@
#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
-#define ADSP2_CONTROL 0x0
-#define ADSP2_CLOCKING 0x1
-#define ADSP2_STATUS1 0x4
-#define ADSP2_WDMA_CONFIG_1 0x30
-#define ADSP2_WDMA_CONFIG_2 0x31
-#define ADSP2_RDMA_CONFIG_1 0x34
-
-#define ADSP2_SCRATCH0 0x40
-#define ADSP2_SCRATCH1 0x41
-#define ADSP2_SCRATCH2 0x42
-#define ADSP2_SCRATCH3 0x43
+#define ADSP2_CONTROL 0x0
+#define ADSP2_CLOCKING 0x1
+#define ADSP2V2_CLOCKING 0x2
+#define ADSP2_STATUS1 0x4
+#define ADSP2_WDMA_CONFIG_1 0x30
+#define ADSP2_WDMA_CONFIG_2 0x31
+#define ADSP2V2_WDMA_CONFIG_2 0x32
+#define ADSP2_RDMA_CONFIG_1 0x34
+
+#define ADSP2_SCRATCH0 0x40
+#define ADSP2_SCRATCH1 0x41
+#define ADSP2_SCRATCH2 0x42
+#define ADSP2_SCRATCH3 0x43
+
+#define ADSP2V2_SCRATCH0_1 0x40
+#define ADSP2V2_SCRATCH2_3 0x42
/*
* ADSP2 Control
@@ -153,6 +158,17 @@
#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
/*
+ * ADSP2V2 clocking
+ */
+#define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */
+#define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */
+#define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
+
+#define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */
+#define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */
+#define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */
+
+/*
* ADSP2 Status 1
*/
#define ADSP2_RAM_RDY 0x0001
@@ -160,6 +176,37 @@
#define ADSP2_RAM_RDY_SHIFT 0
#define ADSP2_RAM_RDY_WIDTH 1
+/*
+ * ADSP2 Lock support
+ */
+#define ADSP2_LOCK_CODE_0 0x5555
+#define ADSP2_LOCK_CODE_1 0xAAAA
+
+#define ADSP2_WATCHDOG 0x0A
+#define ADSP2_BUS_ERR_ADDR 0x52
+#define ADSP2_REGION_LOCK_STATUS 0x64
+#define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66
+#define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68
+#define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A
+#define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C
+#define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E
+#define ADSP2_LOCK_REGION_CTRL 0x7A
+#define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C
+
+#define ADSP2_REGION_LOCK_ERR_MASK 0x8000
+#define ADSP2_SLAVE_ERR_MASK 0x4000
+#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000
+#define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002
+#define ADSP2_CTRL_ERR_EINT 0x0001
+
+#define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF
+#define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF
+#define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000
+#define ADSP2_PMEM_ERR_ADDR_SHIFT 16
+#define ADSP2_WDT_ENA_MASK 0xFFFFFFFD
+
+#define ADSP2_LOCK_REGION_SHIFT 16
+
#define ADSP_MAX_STD_CTRL_SIZE 512
#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
@@ -683,6 +730,9 @@ static const struct soc_enum wm_adsp_fw_enum[] = {
SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
};
const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
@@ -694,6 +744,12 @@ const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
wm_adsp_fw_get, wm_adsp_fw_put),
SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP5 Firmware", wm_adsp_fw_enum[4],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP6 Firmware", wm_adsp_fw_enum[5],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP7 Firmware", wm_adsp_fw_enum[6],
+ wm_adsp_fw_get, wm_adsp_fw_put),
};
EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
@@ -750,6 +806,29 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
be16_to_cpu(scratch[3]));
}
+static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp)
+{
+ u32 scratch[2];
+ int ret;
+
+ ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1,
+ scratch, sizeof(scratch));
+
+ if (ret) {
+ adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
+ return;
+ }
+
+ scratch[0] = be32_to_cpu(scratch[0]);
+ scratch[1] = be32_to_cpu(scratch[1]);
+
+ adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
+ scratch[0] & 0xFFFF,
+ scratch[0] >> 16,
+ scratch[1] & 0xFFFF,
+ scratch[1] >> 16);
+}
+
static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
{
return container_of(ext, struct wm_coeff_ctl, bytes_ext);
@@ -899,7 +978,10 @@ static int wm_coeff_put(struct snd_kcontrol *kctl,
mutex_lock(&ctl->dsp->pwr_lock);
- memcpy(ctl->cache, p, ctl->len);
+ if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+ ret = -EPERM;
+ else
+ memcpy(ctl->cache, p, ctl->len);
ctl->set = 1;
if (ctl->enabled && ctl->dsp->running)
@@ -926,6 +1008,8 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
ctl->set = 1;
if (ctl->enabled && ctl->dsp->running)
ret = wm_coeff_write_control(ctl, ctl->cache, size);
+ else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+ ret = -EPERM;
}
mutex_unlock(&ctl->dsp->pwr_lock);
@@ -947,7 +1031,7 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
mutex_lock(&ctl->dsp->pwr_lock);
- if (ctl->enabled)
+ if (ctl->enabled && ctl->dsp->running)
ret = wm_coeff_write_acked_control(ctl, val);
else
ret = -EPERM;
@@ -2430,10 +2514,17 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
unsigned int val;
int ret, count;
- ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, ADSP2_SYS_ENA);
- if (ret != 0)
- return ret;
+ switch (dsp->rev) {
+ case 0:
+ ret = regmap_update_bits_async(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, ADSP2_SYS_ENA);
+ if (ret != 0)
+ return ret;
+ break;
+ default:
+ break;
+ }
/* Wait for the RAM to start, should be near instantaneous */
for (count = 0; count < 10; ++count) {
@@ -2492,11 +2583,17 @@ static void wm_adsp2_boot_work(struct work_struct *work)
if (ret != 0)
goto err_ena;
- /* Turn DSP back off until we are ready to run */
- ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, 0);
- if (ret != 0)
- goto err_ena;
+ switch (dsp->rev) {
+ case 0:
+ /* Turn DSP back off until we are ready to run */
+ ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, 0);
+ if (ret != 0)
+ goto err_ena;
+ break;
+ default:
+ break;
+ }
dsp->booted = true;
@@ -2518,12 +2615,21 @@ static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
{
int ret;
- ret = regmap_update_bits_async(dsp->regmap,
- dsp->base + ADSP2_CLOCKING,
- ADSP2_CLK_SEL_MASK,
- freq << ADSP2_CLK_SEL_SHIFT);
- if (ret != 0)
- adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+ switch (dsp->rev) {
+ case 0:
+ ret = regmap_update_bits_async(dsp->regmap,
+ dsp->base + ADSP2_CLOCKING,
+ ADSP2_CLK_SEL_MASK,
+ freq << ADSP2_CLK_SEL_SHIFT);
+ if (ret) {
+ adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+ return;
+ }
+ break;
+ default:
+ /* clock is handled by parent codec driver */
+ break;
+ }
}
int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
@@ -2563,6 +2669,18 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
+static void wm_adsp_stop_watchdog(struct wm_adsp *dsp)
+{
+ switch (dsp->rev) {
+ case 0:
+ case 1:
+ return;
+ default:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG,
+ ADSP2_WDT_ENA_MASK, 0);
+ }
+}
+
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event,
unsigned int freq)
@@ -2635,6 +2753,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
+ wm_adsp2_lock(dsp, dsp->lock_regions);
+
ret = regmap_update_bits(dsp->regmap,
dsp->base + ADSP2_CONTROL,
ADSP2_CORE_ENA | ADSP2_START,
@@ -2658,23 +2778,49 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
/* Tell the firmware to cleanup */
wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
+ wm_adsp_stop_watchdog(dsp);
+
/* Log firmware state, it can be useful for analysis */
- wm_adsp2_show_fw_status(dsp);
+ switch (dsp->rev) {
+ case 0:
+ wm_adsp2_show_fw_status(dsp);
+ break;
+ default:
+ wm_adsp2v2_show_fw_status(dsp);
+ break;
+ }
mutex_lock(&dsp->pwr_lock);
dsp->running = false;
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ regmap_update_bits(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
ADSP2_CORE_ENA | ADSP2_START, 0);
/* Make sure DMAs are quiesced */
- regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
- regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
- regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
-
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, 0);
+ switch (dsp->rev) {
+ case 0:
+ regmap_write(dsp->regmap,
+ dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+ regmap_write(dsp->regmap,
+ dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+ regmap_write(dsp->regmap,
+ dsp->base + ADSP2_WDMA_CONFIG_2, 0);
+
+ regmap_update_bits(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, 0);
+ break;
+ default:
+ regmap_write(dsp->regmap,
+ dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+ regmap_write(dsp->regmap,
+ dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+ regmap_write(dsp->regmap,
+ dsp->base + ADSP2V2_WDMA_CONFIG_2, 0);
+ break;
+ }
if (wm_adsp_fw[dsp->fw].num_caps != 0)
wm_adsp_buffer_free(dsp);
@@ -2727,15 +2873,22 @@ int wm_adsp2_init(struct wm_adsp *dsp)
{
int ret;
- /*
- * Disable the DSP memory by default when in reset for a small
- * power saving.
- */
- ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_MEM_ENA, 0);
- if (ret != 0) {
- adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
- return ret;
+ switch (dsp->rev) {
+ case 0:
+ /*
+ * Disable the DSP memory by default when in reset for a small
+ * power saving.
+ */
+ ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_MEM_ENA, 0);
+ if (ret) {
+ adsp_err(dsp,
+ "Failed to clear memory retention: %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ break;
}
INIT_LIST_HEAD(&dsp->alg_regions);
@@ -3518,4 +3671,94 @@ int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
+int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions)
+{
+ struct regmap *regmap = dsp->regmap;
+ unsigned int code0, code1, lock_reg;
+
+ if (!(lock_regions & WM_ADSP2_REGION_ALL))
+ return 0;
+
+ lock_regions &= WM_ADSP2_REGION_ALL;
+ lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0;
+
+ while (lock_regions) {
+ code0 = code1 = 0;
+ if (lock_regions & BIT(0)) {
+ code0 = ADSP2_LOCK_CODE_0;
+ code1 = ADSP2_LOCK_CODE_1;
+ }
+ if (lock_regions & BIT(1)) {
+ code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT;
+ code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT;
+ }
+ regmap_write(regmap, lock_reg, code0);
+ regmap_write(regmap, lock_reg, code1);
+ lock_regions >>= 2;
+ lock_reg += 2;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_lock);
+
+irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp)
+{
+ unsigned int val;
+ struct regmap *regmap = dsp->regmap;
+ int ret = 0;
+
+ ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val);
+ if (ret) {
+ adsp_err(dsp,
+ "Failed to read Region Lock Ctrl register: %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ if (val & ADSP2_WDT_TIMEOUT_STS_MASK) {
+ adsp_err(dsp, "watchdog timeout error\n");
+ wm_adsp_stop_watchdog(dsp);
+ }
+
+ if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) {
+ if (val & ADSP2_SLAVE_ERR_MASK)
+ adsp_err(dsp, "bus error: slave error\n");
+ else
+ adsp_err(dsp, "bus error: region lock error\n");
+
+ ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val);
+ if (ret) {
+ adsp_err(dsp,
+ "Failed to read Bus Err Addr register: %d\n",
+ ret);
+ return IRQ_HANDLED;
+ }
+
+ adsp_err(dsp, "bus error address = 0x%x\n",
+ val & ADSP2_BUS_ERR_ADDR_MASK);
+
+ ret = regmap_read(regmap,
+ dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR,
+ &val);
+ if (ret) {
+ adsp_err(dsp,
+ "Failed to read Pmem Xmem Err Addr register: %d\n",
+ ret);
+ return IRQ_HANDLED;
+ }
+
+ adsp_err(dsp, "xmem error address = 0x%x\n",
+ val & ADSP2_XMEM_ERR_ADDR_MASK);
+ adsp_err(dsp, "pmem error address = 0x%x\n",
+ (val & ADSP2_PMEM_ERR_ADDR_MASK) >>
+ ADSP2_PMEM_ERR_ADDR_SHIFT);
+ }
+
+ regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL,
+ ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
+
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 3706b11053a3..41cc11c19b83 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -23,6 +23,23 @@
#define WM_ADSP_COMPR_OK 0
#define WM_ADSP_COMPR_VOICE_TRIGGER 1
+#define WM_ADSP2_REGION_0 BIT(0)
+#define WM_ADSP2_REGION_1 BIT(1)
+#define WM_ADSP2_REGION_2 BIT(2)
+#define WM_ADSP2_REGION_3 BIT(3)
+#define WM_ADSP2_REGION_4 BIT(4)
+#define WM_ADSP2_REGION_5 BIT(5)
+#define WM_ADSP2_REGION_6 BIT(6)
+#define WM_ADSP2_REGION_7 BIT(7)
+#define WM_ADSP2_REGION_8 BIT(8)
+#define WM_ADSP2_REGION_9 BIT(9)
+#define WM_ADSP2_REGION_1_9 (WM_ADSP2_REGION_1 | \
+ WM_ADSP2_REGION_2 | WM_ADSP2_REGION_3 | \
+ WM_ADSP2_REGION_4 | WM_ADSP2_REGION_5 | \
+ WM_ADSP2_REGION_6 | WM_ADSP2_REGION_7 | \
+ WM_ADSP2_REGION_8 | WM_ADSP2_REGION_9)
+#define WM_ADSP2_REGION_ALL (WM_ADSP2_REGION_0 | WM_ADSP2_REGION_1_9)
+
struct wm_adsp_region {
int type;
unsigned int base;
@@ -40,6 +57,7 @@ struct wm_adsp_compr_buf;
struct wm_adsp {
const char *part;
+ int rev;
int num;
int type;
struct device *dev;
@@ -75,6 +93,8 @@ struct wm_adsp {
struct mutex pwr_lock;
+ unsigned int lock_regions;
+
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
char *wmfw_file_name;
@@ -113,6 +133,10 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event,
unsigned int freq);
+
+int wm_adsp2_lock(struct wm_adsp *adsp, unsigned int regions);
+irqreturn_t wm_adsp2_bus_error(struct wm_adsp *adsp);
+
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
OpenPOWER on IntegriCloud