diff options
Diffstat (limited to 'sound/soc/codecs')
29 files changed, 2997 insertions, 430 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e6ce18c21b98..419114edfd57 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -55,6 +55,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS35L33 if I2C select SND_SOC_CS35L34 if I2C select SND_SOC_CS35L35 if I2C + select SND_SOC_CS35L36 if I2C select SND_SOC_CS42L42 if I2C select SND_SOC_CS42L51_I2C if I2C select SND_SOC_CS42L52 if I2C && INPUT @@ -484,6 +485,10 @@ config SND_SOC_CS35L35 tristate "Cirrus Logic CS35L35 CODEC" depends on I2C +config SND_SOC_CS35L36 + tristate "Cirrus Logic CS35L36 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 b07dfb5fa700..aab2ad95a137 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -47,6 +47,7 @@ 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-cs35l36-objs := cs35l36.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o @@ -319,6 +320,7 @@ 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_CS35L36) += snd-soc-cs35l36.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/ad193x.c b/sound/soc/codecs/ad193x.c index 4b60ebee491d..96d7cb2e4a56 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -37,6 +37,13 @@ static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1, static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0); +static const unsigned int ad193x_sb[] = {32}; + +static struct snd_pcm_hw_constraint_list constr = { + .list = ad193x_sb, + .count = ARRAY_SIZE(ad193x_sb), +}; + static const struct snd_kcontrol_new ad193x_snd_controls[] = { /* DAC volume control */ SOC_DOUBLE_R_TLV("DAC1 Volume", AD193X_DAC_L1_VOL, @@ -93,6 +100,15 @@ static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = { SND_SOC_DAPM_INPUT("ADC2IN"), }; +static int ad193x_check_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + + return !!ad193x->sysclk; +} + static const struct snd_soc_dapm_route audio_paths[] = { { "DAC", NULL, "SYSCLK" }, { "DAC Output", NULL, "DAC" }, @@ -101,7 +117,7 @@ static const struct snd_soc_dapm_route audio_paths[] = { { "DAC2OUT", NULL, "DAC Output" }, { "DAC3OUT", NULL, "DAC Output" }, { "DAC4OUT", NULL, "DAC Output" }, - { "SYSCLK", NULL, "PLL_PWR" }, + { "SYSCLK", NULL, "PLL_PWR", &ad193x_check_pll }, }; static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = { @@ -181,23 +197,26 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, { struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(codec_dai->component); unsigned int adc_serfmt = 0; + unsigned int dac_serfmt = 0; unsigned int adc_fmt = 0; unsigned int dac_fmt = 0; /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S - * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) + * with TDM), ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) and DAC I2S mode + * (SND_SOC_DAIFMT_I2S) */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: adc_serfmt |= AD193X_ADC_SERFMT_TDM; + dac_serfmt |= AD193X_DAC_SERFMT_STEREO; break; case SND_SOC_DAIFMT_DSP_A: adc_serfmt |= AD193X_ADC_SERFMT_AUX; + dac_serfmt |= AD193X_DAC_SERFMT_TDM; break; default: if (ad193x_has_adc(ad193x)) return -EINVAL; - break; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -221,6 +240,12 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } + /* For DSP_*, LRCLK's polarity must be inverted */ + if (fmt & SND_SOC_DAIFMT_DSP_A) { + change_bit(ffs(AD193X_DAC_LEFT_HIGH) - 1, + (unsigned long *)&dac_fmt); + } + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ adc_fmt |= AD193X_ADC_LCR_MASTER; @@ -248,6 +273,8 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, AD193X_ADC_FMT_MASK, adc_fmt); } + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, + AD193X_DAC_SERFMT_MASK, dac_serfmt); regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, AD193X_DAC_FMT_MASK, dac_fmt); @@ -258,7 +285,22 @@ static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + + if (clk_id == AD193X_SYSCLK_MCLK) { + /* MCLK must be 512 x fs */ + if (dir == SND_SOC_CLOCK_OUT || freq != 24576000) + return -EINVAL; + + regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL1, + AD193X_PLL_SRC_MASK, + AD193X_PLL_DAC_SRC_MCLK | + AD193X_PLL_CLK_SRC_MCLK); + + snd_soc_dapm_sync(dapm); + return 0; + } switch (freq) { case 12288000: case 18432000: @@ -321,7 +363,16 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, return 0; } +static int ad193x_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &constr); +} + static const struct snd_soc_dai_ops ad193x_dai_ops = { + .startup = ad193x_startup, .hw_params = ad193x_hw_params, .digital_mute = ad193x_mute, .set_tdm_slot = ad193x_set_tdm_slot, @@ -351,6 +402,20 @@ static struct snd_soc_dai_driver ad193x_dai = { .ops = &ad193x_dai_ops, }; +/* codec DAI instance for DAC only */ +static struct snd_soc_dai_driver ad193x_no_adc_dai = { + .name = "ad193x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ad193x_dai_ops, +}; + static int ad193x_component_probe(struct snd_soc_component *component) { struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); @@ -444,8 +509,11 @@ int ad193x_probe(struct device *dev, struct regmap *regmap, dev_set_drvdata(dev, ad193x); + if (ad193x_has_adc(ad193x)) + return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, + &ad193x_dai, 1); return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, - &ad193x_dai, 1); + &ad193x_no_adc_dai, 1); } EXPORT_SYMBOL_GPL(ad193x_probe); diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h index 8b1e65f928d2..27d6afbd7dfb 100644 --- a/sound/soc/codecs/ad193x.h +++ b/sound/soc/codecs/ad193x.h @@ -31,6 +31,11 @@ int ad193x_probe(struct device *dev, struct regmap *regmap, #define AD193X_PLL_INPUT_512 (2 << 1) #define AD193X_PLL_INPUT_768 (3 << 1) #define AD193X_PLL_CLK_CTRL1 0x01 +#define AD193X_PLL_SRC_MASK 0x03 +#define AD193X_PLL_DAC_SRC_PLL 0 +#define AD193X_PLL_DAC_SRC_MCLK 1 +#define AD193X_PLL_CLK_SRC_PLL (0 << 1) +#define AD193X_PLL_CLK_SRC_MCLK (1 << 1) #define AD193X_DAC_CTRL0 0x02 #define AD193X_DAC_POWERDOWN 0x01 #define AD193X_DAC_SERFMT_MASK 0xC0 @@ -96,4 +101,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap, #define AD193X_NUM_REGS 17 +#define AD193X_SYSCLK_PLL 0 +#define AD193X_SYSCLK_MCLK 1 + #endif diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index 116af6a9ce3b..11c53bcb71dd 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -885,13 +885,15 @@ static int adau1977_setup_micbias(struct adau1977 *adau1977) struct adau1977_platform_data *pdata = adau1977->dev->platform_data; unsigned int micbias; - if (pdata) { + if (pdata) micbias = pdata->micbias; - if (micbias > ADAU1977_MICBIAS_9V0) - return -EINVAL; - - } else { + else if (device_property_read_u32(adau1977->dev, "adi,micbias", + &micbias)) micbias = ADAU1977_MICBIAS_8V5; + + if (micbias > ADAU1977_MICBIAS_9V0) { + dev_err(adau1977->dev, "Invalid value for 'adi,micbias'\n"); + return -EINVAL; } return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MICBIAS, diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index b14100b6a939..99a3af8a15ff 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -206,7 +206,7 @@ static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops = { .set_fmt = cros_ec_i2s_set_dai_fmt, }; -struct snd_soc_dai_driver cros_ec_dai[] = { +static struct snd_soc_dai_driver cros_ec_dai[] = { { .name = "cros_ec_codec I2S", .id = 0, diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c new file mode 100644 index 000000000000..e9b5f76f27a8 --- /dev/null +++ b/sound/soc/codecs/cs35l36.c @@ -0,0 +1,1957 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l36.c -- CS35L36 ALSA SoC audio driver +// +// Copyright 2018 Cirrus Logic, Inc. +// +// Author: James Schulman <james.schulman@cirrus.com> + +#include <linux/module.h> +#include <linux/moduleparam.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/cs35l36.h> +#include <linux/of_irq.h> +#include <linux/completion.h> + +#include "cs35l36.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 CS35L36_VALID_PDATA 0x80000000 + +static const char * const cs35l36_supplies[] = { + "VA", + "VP", +}; + +struct cs35l36_private { + struct device *dev; + struct cs35l36_platform_data pdata; + struct regmap *regmap; + struct regulator_bulk_data supplies[2]; + int num_supplies; + int clksrc; + int chip_version; + int rev_id; + int ldm_mode_sel; + struct gpio_desc *reset_gpio; +}; + +struct cs35l36_pll_config { + int freq; + int clk_cfg; + int fll_igain; +}; + +static const struct cs35l36_pll_config cs35l36_pll_sysclk[] = { + {32768, 0x00, 0x05}, + {8000, 0x01, 0x03}, + {11025, 0x02, 0x03}, + {12000, 0x03, 0x03}, + {16000, 0x04, 0x04}, + {22050, 0x05, 0x04}, + {24000, 0x06, 0x04}, + {32000, 0x07, 0x05}, + {44100, 0x08, 0x05}, + {48000, 0x09, 0x05}, + {88200, 0x0A, 0x06}, + {96000, 0x0B, 0x06}, + {128000, 0x0C, 0x07}, + {176400, 0x0D, 0x07}, + {192000, 0x0E, 0x07}, + {256000, 0x0F, 0x08}, + {352800, 0x10, 0x08}, + {384000, 0x11, 0x08}, + {512000, 0x12, 0x09}, + {705600, 0x13, 0x09}, + {750000, 0x14, 0x09}, + {768000, 0x15, 0x09}, + {1000000, 0x16, 0x0A}, + {1024000, 0x17, 0x0A}, + {1200000, 0x18, 0x0A}, + {1411200, 0x19, 0x0A}, + {1500000, 0x1A, 0x0A}, + {1536000, 0x1B, 0x0A}, + {2000000, 0x1C, 0x0A}, + {2048000, 0x1D, 0x0A}, + {2400000, 0x1E, 0x0A}, + {2822400, 0x1F, 0x0A}, + {3000000, 0x20, 0x0A}, + {3072000, 0x21, 0x0A}, + {3200000, 0x22, 0x0A}, + {4000000, 0x23, 0x0A}, + {4096000, 0x24, 0x0A}, + {4800000, 0x25, 0x0A}, + {5644800, 0x26, 0x0A}, + {6000000, 0x27, 0x0A}, + {6144000, 0x28, 0x0A}, + {6250000, 0x29, 0x08}, + {6400000, 0x2A, 0x0A}, + {6500000, 0x2B, 0x08}, + {6750000, 0x2C, 0x09}, + {7526400, 0x2D, 0x0A}, + {8000000, 0x2E, 0x0A}, + {8192000, 0x2F, 0x0A}, + {9600000, 0x30, 0x0A}, + {11289600, 0x31, 0x0A}, + {12000000, 0x32, 0x0A}, + {12288000, 0x33, 0x0A}, + {12500000, 0x34, 0x08}, + {12800000, 0x35, 0x0A}, + {13000000, 0x36, 0x0A}, + {13500000, 0x37, 0x0A}, + {19200000, 0x38, 0x0A}, + {22579200, 0x39, 0x0A}, + {24000000, 0x3A, 0x0A}, + {24576000, 0x3B, 0x0A}, + {25000000, 0x3C, 0x0A}, + {25600000, 0x3D, 0x0A}, + {26000000, 0x3E, 0x0A}, + {27000000, 0x3F, 0x0A}, +}; + +static struct reg_default cs35l36_reg[] = { + {CS35L36_TESTKEY_CTRL, 0x00000000}, + {CS35L36_USERKEY_CTL, 0x00000000}, + {CS35L36_OTP_CTRL1, 0x00002460}, + {CS35L36_OTP_CTRL2, 0x00000000}, + {CS35L36_OTP_CTRL3, 0x00000000}, + {CS35L36_OTP_CTRL4, 0x00000000}, + {CS35L36_OTP_CTRL5, 0x00000000}, + {CS35L36_PAC_CTL1, 0x00000004}, + {CS35L36_PAC_CTL2, 0x00000000}, + {CS35L36_PAC_CTL3, 0x00000000}, + {CS35L36_PWR_CTRL1, 0x00000000}, + {CS35L36_PWR_CTRL2, 0x00003321}, + {CS35L36_PWR_CTRL3, 0x01000010}, + {CS35L36_CTRL_OVRRIDE, 0x00000002}, + {CS35L36_AMP_OUT_MUTE, 0x00000000}, + {CS35L36_OTP_TRIM_STATUS, 0x00000000}, + {CS35L36_DISCH_FILT, 0x00000000}, + {CS35L36_PROTECT_REL_ERR, 0x00000000}, + {CS35L36_PAD_INTERFACE, 0x00000038}, + {CS35L36_PLL_CLK_CTRL, 0x00000010}, + {CS35L36_GLOBAL_CLK_CTRL, 0x00000003}, + {CS35L36_ADC_CLK_CTRL, 0x00000000}, + {CS35L36_SWIRE_CLK_CTRL, 0x00000000}, + {CS35L36_SP_SCLK_CLK_CTRL, 0x00000000}, + {CS35L36_MDSYNC_EN, 0x00000000}, + {CS35L36_MDSYNC_TX_ID, 0x00000000}, + {CS35L36_MDSYNC_PWR_CTRL, 0x00000000}, + {CS35L36_MDSYNC_DATA_TX, 0x00000000}, + {CS35L36_MDSYNC_TX_STATUS, 0x00000002}, + {CS35L36_MDSYNC_RX_STATUS, 0x00000000}, + {CS35L36_MDSYNC_ERR_STATUS, 0x00000000}, + {CS35L36_BSTCVRT_VCTRL1, 0x00000000}, + {CS35L36_BSTCVRT_VCTRL2, 0x00000001}, + {CS35L36_BSTCVRT_PEAK_CUR, 0x0000004A}, + {CS35L36_BSTCVRT_SFT_RAMP, 0x00000003}, + {CS35L36_BSTCVRT_COEFF, 0x00002424}, + {CS35L36_BSTCVRT_SLOPE_LBST, 0x00005800}, + {CS35L36_BSTCVRT_SW_FREQ, 0x00010000}, + {CS35L36_BSTCVRT_DCM_CTRL, 0x00002001}, + {CS35L36_BSTCVRT_DCM_MODE_FORCE, 0x00000000}, + {CS35L36_BSTCVRT_OVERVOLT_CTRL, 0x00000130}, + {CS35L36_VPI_LIMIT_MODE, 0x00000000}, + {CS35L36_VPI_LIMIT_MINMAX, 0x00003000}, + {CS35L36_VPI_VP_THLD, 0x00101010}, + {CS35L36_VPI_TRACK_CTRL, 0x00000000}, + {CS35L36_VPI_TRIG_MODE_CTRL, 0x00000000}, + {CS35L36_VPI_TRIG_STEPS, 0x00000000}, + {CS35L36_VI_SPKMON_FILT, 0x00000003}, + {CS35L36_VI_SPKMON_GAIN, 0x00000909}, + {CS35L36_VI_SPKMON_IP_SEL, 0x00000000}, + {CS35L36_DTEMP_WARN_THLD, 0x00000002}, + {CS35L36_DTEMP_STATUS, 0x00000000}, + {CS35L36_VPVBST_FS_SEL, 0x00000001}, + {CS35L36_VPVBST_VP_CTRL, 0x000001C0}, + {CS35L36_VPVBST_VBST_CTRL, 0x000001C0}, + {CS35L36_ASP_TX_PIN_CTRL, 0x00000028}, + {CS35L36_ASP_RATE_CTRL, 0x00090000}, + {CS35L36_ASP_FORMAT, 0x00000002}, + {CS35L36_ASP_FRAME_CTRL, 0x00180018}, + {CS35L36_ASP_TX1_TX2_SLOT, 0x00010000}, + {CS35L36_ASP_TX3_TX4_SLOT, 0x00030002}, + {CS35L36_ASP_TX5_TX6_SLOT, 0x00050004}, + {CS35L36_ASP_TX7_TX8_SLOT, 0x00070006}, + {CS35L36_ASP_RX1_SLOT, 0x00000000}, + {CS35L36_ASP_RX_TX_EN, 0x00000000}, + {CS35L36_ASP_RX1_SEL, 0x00000008}, + {CS35L36_ASP_TX1_SEL, 0x00000018}, + {CS35L36_ASP_TX2_SEL, 0x00000019}, + {CS35L36_ASP_TX3_SEL, 0x00000028}, + {CS35L36_ASP_TX4_SEL, 0x00000029}, + {CS35L36_ASP_TX5_SEL, 0x00000020}, + {CS35L36_ASP_TX6_SEL, 0x00000000}, + {CS35L36_SWIRE_P1_TX1_SEL, 0x00000018}, + {CS35L36_SWIRE_P1_TX2_SEL, 0x00000019}, + {CS35L36_SWIRE_P2_TX1_SEL, 0x00000028}, + {CS35L36_SWIRE_P2_TX2_SEL, 0x00000029}, + {CS35L36_SWIRE_P2_TX3_SEL, 0x00000020}, + {CS35L36_SWIRE_DP1_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_DP2_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_DP3_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_PCM_RX_DATA, 0x00000000}, + {CS35L36_SWIRE_FS_SEL, 0x00000001}, + {CS35L36_AMP_DIG_VOL_CTRL, 0x00008000}, + {CS35L36_VPBR_CFG, 0x02AA1905}, + {CS35L36_VBBR_CFG, 0x02AA1905}, + {CS35L36_VPBR_STATUS, 0x00000000}, + {CS35L36_VBBR_STATUS, 0x00000000}, + {CS35L36_OVERTEMP_CFG, 0x00000001}, + {CS35L36_AMP_ERR_VOL, 0x00000000}, + {CS35L36_CLASSH_CFG, 0x000B0405}, + {CS35L36_CLASSH_FET_DRV_CFG, 0x00000111}, + {CS35L36_NG_CFG, 0x00000033}, + {CS35L36_AMP_GAIN_CTRL, 0x00000273}, + {CS35L36_PWM_MOD_IO_CTRL, 0x00000000}, + {CS35L36_PWM_MOD_STATUS, 0x00000000}, + {CS35L36_DAC_MSM_CFG, 0x00000000}, + {CS35L36_AMP_SLOPE_CTRL, 0x00000B00}, + {CS35L36_AMP_PDM_VOLUME, 0x00000000}, + {CS35L36_AMP_PDM_RATE_CTRL, 0x00000000}, + {CS35L36_PDM_CH_SEL, 0x00000000}, + {CS35L36_AMP_NG_CTRL, 0x0000212F}, + {CS35L36_PDM_HIGHFILT_CTRL, 0x00000000}, + {CS35L36_PAC_INT0_CTRL, 0x00000001}, + {CS35L36_PAC_INT1_CTRL, 0x00000001}, + {CS35L36_PAC_INT2_CTRL, 0x00000001}, + {CS35L36_PAC_INT3_CTRL, 0x00000001}, + {CS35L36_PAC_INT4_CTRL, 0x00000001}, + {CS35L36_PAC_INT5_CTRL, 0x00000001}, + {CS35L36_PAC_INT6_CTRL, 0x00000001}, + {CS35L36_PAC_INT7_CTRL, 0x00000001}, +}; + +static bool cs35l36_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_SW_RESET: + case CS35L36_SW_REV: + case CS35L36_HW_REV: + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_OTP_MEM30: + case CS35L36_OTP_CTRL1: + case CS35L36_OTP_CTRL2: + case CS35L36_OTP_CTRL3: + case CS35L36_OTP_CTRL4: + case CS35L36_OTP_CTRL5: + case CS35L36_PAC_CTL1: + case CS35L36_PAC_CTL2: + case CS35L36_PAC_CTL3: + case CS35L36_DEVICE_ID: + case CS35L36_FAB_ID: + case CS35L36_REV_ID: + case CS35L36_PWR_CTRL1: + case CS35L36_PWR_CTRL2: + case CS35L36_PWR_CTRL3: + case CS35L36_CTRL_OVRRIDE: + case CS35L36_AMP_OUT_MUTE: + case CS35L36_OTP_TRIM_STATUS: + case CS35L36_DISCH_FILT: + case CS35L36_PROTECT_REL_ERR: + case CS35L36_PAD_INTERFACE: + case CS35L36_PLL_CLK_CTRL: + case CS35L36_GLOBAL_CLK_CTRL: + case CS35L36_ADC_CLK_CTRL: + case CS35L36_SWIRE_CLK_CTRL: + case CS35L36_SP_SCLK_CLK_CTRL: + case CS35L36_TST_FS_MON0: + case CS35L36_MDSYNC_EN: + case CS35L36_MDSYNC_TX_ID: + case CS35L36_MDSYNC_PWR_CTRL: + case CS35L36_MDSYNC_DATA_TX: + case CS35L36_MDSYNC_TX_STATUS: + case CS35L36_MDSYNC_RX_STATUS: + case CS35L36_MDSYNC_ERR_STATUS: + case CS35L36_BSTCVRT_VCTRL1: + case CS35L36_BSTCVRT_VCTRL2: + case CS35L36_BSTCVRT_PEAK_CUR: + case CS35L36_BSTCVRT_SFT_RAMP: + case CS35L36_BSTCVRT_COEFF: + case CS35L36_BSTCVRT_SLOPE_LBST: + case CS35L36_BSTCVRT_SW_FREQ: + case CS35L36_BSTCVRT_DCM_CTRL: + case CS35L36_BSTCVRT_DCM_MODE_FORCE: + case CS35L36_BSTCVRT_OVERVOLT_CTRL: + case CS35L36_BST_TST_MANUAL: + case CS35L36_BST_ANA2_TEST: + case CS35L36_VPI_LIMIT_MODE: + case CS35L36_VPI_LIMIT_MINMAX: + case CS35L36_VPI_VP_THLD: + case CS35L36_VPI_TRACK_CTRL: + case CS35L36_VPI_TRIG_MODE_CTRL: + case CS35L36_VPI_TRIG_STEPS: + case CS35L36_VI_SPKMON_FILT: + case CS35L36_VI_SPKMON_GAIN: + case CS35L36_VI_SPKMON_IP_SEL: + case CS35L36_DTEMP_WARN_THLD: + case CS35L36_DTEMP_STATUS: + case CS35L36_VPVBST_FS_SEL: + case CS35L36_VPVBST_VP_CTRL: + case CS35L36_VPVBST_VBST_CTRL: + case CS35L36_ASP_TX_PIN_CTRL: + case CS35L36_ASP_RATE_CTRL: + case CS35L36_ASP_FORMAT: + case CS35L36_ASP_FRAME_CTRL: + case CS35L36_ASP_TX1_TX2_SLOT: + case CS35L36_ASP_TX3_TX4_SLOT: + case CS35L36_ASP_TX5_TX6_SLOT: + case CS35L36_ASP_TX7_TX8_SLOT: + case CS35L36_ASP_RX1_SLOT: + case CS35L36_ASP_RX_TX_EN: + case CS35L36_ASP_RX1_SEL: + case CS35L36_ASP_TX1_SEL: + case CS35L36_ASP_TX2_SEL: + case CS35L36_ASP_TX3_SEL: + case CS35L36_ASP_TX4_SEL: + case CS35L36_ASP_TX5_SEL: + case CS35L36_ASP_TX6_SEL: + case CS35L36_SWIRE_P1_TX1_SEL: + case CS35L36_SWIRE_P1_TX2_SEL: + case CS35L36_SWIRE_P2_TX1_SEL: + case CS35L36_SWIRE_P2_TX2_SEL: + case CS35L36_SWIRE_P2_TX3_SEL: + case CS35L36_SWIRE_DP1_FIFO_CFG: + case CS35L36_SWIRE_DP2_FIFO_CFG: + case CS35L36_SWIRE_DP3_FIFO_CFG: + case CS35L36_SWIRE_PCM_RX_DATA: + case CS35L36_SWIRE_FS_SEL: + case CS35L36_AMP_DIG_VOL_CTRL: + case CS35L36_VPBR_CFG: + case CS35L36_VBBR_CFG: + case CS35L36_VPBR_STATUS: + case CS35L36_VBBR_STATUS: + case CS35L36_OVERTEMP_CFG: + case CS35L36_AMP_ERR_VOL: + case CS35L36_CLASSH_CFG: + case CS35L36_CLASSH_FET_DRV_CFG: + case CS35L36_NG_CFG: + case CS35L36_AMP_GAIN_CTRL: + case CS35L36_PWM_MOD_IO_CTRL: + case CS35L36_PWM_MOD_STATUS: + case CS35L36_DAC_MSM_CFG: + case CS35L36_AMP_SLOPE_CTRL: + case CS35L36_AMP_PDM_VOLUME: + case CS35L36_AMP_PDM_RATE_CTRL: + case CS35L36_PDM_CH_SEL: + case CS35L36_AMP_NG_CTRL: + case CS35L36_PDM_HIGHFILT_CTRL: + case CS35L36_INT1_STATUS: + case CS35L36_INT2_STATUS: + case CS35L36_INT3_STATUS: + case CS35L36_INT4_STATUS: + case CS35L36_INT1_RAW_STATUS: + case CS35L36_INT2_RAW_STATUS: + case CS35L36_INT3_RAW_STATUS: + case CS35L36_INT4_RAW_STATUS: + case CS35L36_INT1_MASK: + case CS35L36_INT2_MASK: + case CS35L36_INT3_MASK: + case CS35L36_INT4_MASK: + case CS35L36_INT1_EDGE_LVL_CTRL: + case CS35L36_INT3_EDGE_LVL_CTRL: + case CS35L36_PAC_INT_STATUS: + case CS35L36_PAC_INT_RAW_STATUS: + case CS35L36_PAC_INT_FLUSH_CTRL: + case CS35L36_PAC_INT0_CTRL: + case CS35L36_PAC_INT1_CTRL: + case CS35L36_PAC_INT2_CTRL: + case CS35L36_PAC_INT3_CTRL: + case CS35L36_PAC_INT4_CTRL: + case CS35L36_PAC_INT5_CTRL: + case CS35L36_PAC_INT6_CTRL: + case CS35L36_PAC_INT7_CTRL: + return true; + default: + if (reg >= CS35L36_PAC_PMEM_WORD0 && + reg <= CS35L36_PAC_PMEM_WORD1023) + return true; + else + return false; + } +} + +static bool cs35l36_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_TST_FS_MON0: + return true; + default: + return false; + } +} + +static bool cs35l36_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_SW_RESET: + case CS35L36_SW_REV: + case CS35L36_HW_REV: + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_DEVICE_ID: + case CS35L36_FAB_ID: + case CS35L36_REV_ID: + case CS35L36_INT1_STATUS: + case CS35L36_INT2_STATUS: + case CS35L36_INT3_STATUS: + case CS35L36_INT4_STATUS: + case CS35L36_INT1_RAW_STATUS: + case CS35L36_INT2_RAW_STATUS: + case CS35L36_INT3_RAW_STATUS: + case CS35L36_INT4_RAW_STATUS: + case CS35L36_INT1_MASK: + case CS35L36_INT2_MASK: + case CS35L36_INT3_MASK: + case CS35L36_INT4_MASK: + case CS35L36_INT1_EDGE_LVL_CTRL: + case CS35L36_INT3_EDGE_LVL_CTRL: + case CS35L36_PAC_INT_STATUS: + case CS35L36_PAC_INT_RAW_STATUS: + case CS35L36_PAC_INT_FLUSH_CTRL: + return true; + default: + if (reg >= CS35L36_PAC_PMEM_WORD0 && + reg <= CS35L36_PAC_PMEM_WORD1023) + return true; + else + return false; + } +} + +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 25, 0); +static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1); + +static const char * const cs35l36_pcm_sftramp_text[] = { + "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"}; + +static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L36_AMP_DIG_VOL_CTRL, 0, + cs35l36_pcm_sftramp_text); + +static int cs35l36_ldm_sel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = cs35l36->ldm_mode_sel; + + return 0; +} + +static int cs35l36_ldm_sel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int val = (ucontrol->value.integer.value[0]) ? CS35L36_NG_AMP_EN_MASK : + 0; + + cs35l36->ldm_mode_sel = val; + + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_AMP_EN_MASK, val); + + return 0; +} + +static const struct snd_kcontrol_new cs35l36_aud_controls[] = { + SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L36_AMP_DIG_VOL_CTRL, + 3, 0x4D0, 0x390, dig_vol_tlv), + SOC_SINGLE_TLV("Analog PCM Volume", CS35L36_AMP_GAIN_CTRL, 5, 0x13, 0, + amp_gain_tlv), + SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp), + SOC_SINGLE("Amp Gain Zero-Cross Switch", CS35L36_AMP_GAIN_CTRL, + CS35L36_AMP_ZC_SHIFT, 1, 0), + SOC_SINGLE("PDM LDM Enter Ramp Switch", CS35L36_DAC_MSM_CFG, + CS35L36_PDM_LDM_ENTER_SHIFT, 1, 0), + SOC_SINGLE("PDM LDM Exit Ramp Switch", CS35L36_DAC_MSM_CFG, + CS35L36_PDM_LDM_EXIT_SHIFT, 1, 0), + SOC_SINGLE_BOOL_EXT("LDM Select Switch", 0, cs35l36_ldm_sel_get, + cs35l36_ldm_sel_put), +}; + +static int cs35l36_main_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + u32 reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1, + CS35L36_GLOBAL_EN_MASK, + 1 << CS35L36_GLOBAL_EN_SHIFT); + + usleep_range(2000, 2100); + + regmap_read(cs35l36->regmap, CS35L36_INT4_RAW_STATUS, ®); + + if (WARN_ON_ONCE(reg & CS35L36_PLL_UNLOCK_MASK)) + dev_crit(cs35l36->dev, "PLL Unlocked\n"); + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL, + CS35L36_PCM_RX_SEL_MASK, + CS35L36_PCM_RX_SEL_PCM); + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_MASK, + 0 << CS35L36_AMP_MUTE_SHIFT); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL, + CS35L36_PCM_RX_SEL_MASK, + CS35L36_PCM_RX_SEL_ZERO); + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_MASK, + 1 << CS35L36_AMP_MUTE_SHIFT); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1, + CS35L36_GLOBAL_EN_MASK, + 0 << CS35L36_GLOBAL_EN_SHIFT); + + usleep_range(2000, 2100); + break; + default: + dev_dbg(component->dev, "Invalid event = 0x%x\n", event); + return -EINVAL; + } + + return 0; +} + +static int cs35l36_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!cs35l36->pdata.extern_boost) + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2, + CS35L36_BST_EN_MASK, + CS35L36_BST_EN << + CS35L36_BST_EN_SHIFT); + break; + case SND_SOC_DAPM_POST_PMD: + if (!cs35l36->pdata.extern_boost) + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2, + CS35L36_BST_EN_MASK, + CS35L36_BST_DIS_VP << + CS35L36_BST_EN_SHIFT); + break; + default: + dev_dbg(component->dev, "Invalid event = 0x%x\n", event); + return -EINVAL; + } + + return 0; +} + +static const char * const cs35l36_chan_text[] = { + "RX1", + "RX2", +}; + +static SOC_ENUM_SINGLE_DECL(chansel_enum, CS35L36_ASP_RX1_SLOT, 0, + cs35l36_chan_text); + +static const struct snd_kcontrol_new cs35l36_chan_mux = + SOC_DAPM_ENUM("Input Mux", chansel_enum); + +static const struct snd_kcontrol_new amp_enable_ctrl = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_SHIFT, 1, 1); + +static const struct snd_kcontrol_new boost_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const char * const asp_tx_src_text[] = { + "Zero Fill", "ASPRX1", "VMON", "IMON", "ERRVOL", "VPMON", "VBSTMON" +}; + +static const unsigned int asp_tx_src_values[] = { + 0x00, 0x08, 0x18, 0x19, 0x20, 0x28, 0x29 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx1_src_enum, CS35L36_ASP_TX1_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx1_src = + SOC_DAPM_ENUM("ASPTX1SRC", asp_tx1_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx2_src_enum, CS35L36_ASP_TX2_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx2_src = + SOC_DAPM_ENUM("ASPTX2SRC", asp_tx2_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx3_src_enum, CS35L36_ASP_TX3_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx3_src = + SOC_DAPM_ENUM("ASPTX3SRC", asp_tx3_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx4_src_enum, CS35L36_ASP_TX4_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx4_src = + SOC_DAPM_ENUM("ASPTX4SRC", asp_tx4_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx5_src_enum, CS35L36_ASP_TX5_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx5_src = + SOC_DAPM_ENUM("ASPTX5SRC", asp_tx5_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx6_src_enum, CS35L36_ASP_TX6_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx6_src = + SOC_DAPM_ENUM("ASPTX6SRC", asp_tx6_src_enum); + +static const struct snd_soc_dapm_widget cs35l36_dapm_widgets[] = { + SND_SOC_DAPM_MUX("Channel Mux", SND_SOC_NOPM, 0, 0, &cs35l36_chan_mux), + SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS35L36_ASP_RX_TX_EN, 16, 0), + + SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L36_PWR_CTRL2, 0, 0, NULL, 0, + cs35l36_main_amp_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1, &_enable_ctrl), + SND_SOC_DAPM_MIXER("CLASS H", CS35L36_PWR_CTRL3, 4, 0, NULL, 0), + SND_SOC_DAPM_SWITCH_E("BOOST Enable", SND_SOC_NOPM, 0, 0, &boost_ctrl, + cs35l36_boost_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L36_ASP_RX_TX_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1, CS35L36_ASP_RX_TX_EN, 1, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2, CS35L36_ASP_RX_TX_EN, 2, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3, CS35L36_ASP_RX_TX_EN, 3, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4, CS35L36_ASP_RX_TX_EN, 4, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5, CS35L36_ASP_RX_TX_EN, 5, 0), + + SND_SOC_DAPM_MUX("ASPTX1SRC", SND_SOC_NOPM, 0, 0, &asp_tx1_src), + SND_SOC_DAPM_MUX("ASPTX2SRC", SND_SOC_NOPM, 0, 0, &asp_tx2_src), + SND_SOC_DAPM_MUX("ASPTX3SRC", SND_SOC_NOPM, 0, 0, &asp_tx3_src), + SND_SOC_DAPM_MUX("ASPTX4SRC", SND_SOC_NOPM, 0, 0, &asp_tx4_src), + SND_SOC_DAPM_MUX("ASPTX5SRC", SND_SOC_NOPM, 0, 0, &asp_tx5_src), + SND_SOC_DAPM_MUX("ASPTX6SRC", SND_SOC_NOPM, 0, 0, &asp_tx6_src), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L36_PWR_CTRL2, 12, 0), + SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L36_PWR_CTRL2, 13, 0), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L36_PWR_CTRL2, 8, 0), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L36_PWR_CTRL2, 9, 0), + + SND_SOC_DAPM_INPUT("VP"), + SND_SOC_DAPM_INPUT("VBST"), + SND_SOC_DAPM_INPUT("VSENSE"), +}; + +static const struct snd_soc_dapm_route cs35l36_audio_map[] = { + {"VPMON ADC", NULL, "VP"}, + {"VBSTMON ADC", NULL, "VBST"}, + {"IMON ADC", NULL, "VSENSE"}, + {"VMON ADC", NULL, "VSENSE"}, + + {"ASPTX1SRC", "IMON", "IMON ADC"}, + {"ASPTX1SRC", "VMON", "VMON ADC"}, + {"ASPTX1SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX1SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX2SRC", "IMON", "IMON ADC"}, + {"ASPTX2SRC", "VMON", "VMON ADC"}, + {"ASPTX2SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX2SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX3SRC", "IMON", "IMON ADC"}, + {"ASPTX3SRC", "VMON", "VMON ADC"}, + {"ASPTX3SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX3SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX4SRC", "IMON", "IMON ADC"}, + {"ASPTX4SRC", "VMON", "VMON ADC"}, + {"ASPTX4SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX4SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX5SRC", "IMON", "IMON ADC"}, + {"ASPTX5SRC", "VMON", "VMON ADC"}, + {"ASPTX5SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX5SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX6SRC", "IMON", "IMON ADC"}, + {"ASPTX6SRC", "VMON", "VMON ADC"}, + {"ASPTX6SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX6SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX1", NULL, "ASPTX1SRC"}, + {"ASPTX2", NULL, "ASPTX2SRC"}, + {"ASPTX3", NULL, "ASPTX3SRC"}, + {"ASPTX4", NULL, "ASPTX4SRC"}, + {"ASPTX5", NULL, "ASPTX5SRC"}, + {"ASPTX6", NULL, "ASPTX6SRC"}, + + {"AMP Capture", NULL, "ASPTX1"}, + {"AMP Capture", NULL, "ASPTX2"}, + {"AMP Capture", NULL, "ASPTX3"}, + {"AMP Capture", NULL, "ASPTX4"}, + {"AMP Capture", NULL, "ASPTX5"}, + {"AMP Capture", NULL, "ASPTX6"}, + + {"AMP Enable", "Switch", "AMP Playback"}, + {"SDIN", NULL, "AMP Enable"}, + {"Channel Mux", "RX1", "SDIN"}, + {"Channel Mux", "RX2", "SDIN"}, + {"BOOST Enable", "Switch", "Channel Mux"}, + {"CLASS H", NULL, "BOOST Enable"}, + {"Main AMP", NULL, "Channel Mux"}, + {"Main AMP", NULL, "CLASS H"}, + {"SPK", NULL, "Main AMP"}, +}; + +static int cs35l36_set_dai_fmt(struct snd_soc_dai *component_dai, + unsigned int fmt) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component_dai->component); + unsigned int asp_fmt, lrclk_fmt, sclk_fmt, slave_mode, clk_frc; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + slave_mode = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + slave_mode = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_MSTR_MASK, + slave_mode << CS35L36_SCLK_MSTR_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_MSTR_MASK, + slave_mode << CS35L36_LRCLK_MSTR_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: + clk_frc = 1; + break; + case SND_SOC_DAIFMT_GATED: + clk_frc = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_FRC_MASK, clk_frc << + CS35L36_SCLK_FRC_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_FRC_MASK, clk_frc << + CS35L36_LRCLK_FRC_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + asp_fmt = 0; + break; + case SND_SOC_DAIFMT_I2S: + asp_fmt = 2; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + lrclk_fmt = 1; + sclk_fmt = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + lrclk_fmt = 0; + sclk_fmt = 1; + break; + case SND_SOC_DAIFMT_IB_IF: + lrclk_fmt = 1; + sclk_fmt = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + lrclk_fmt = 0; + sclk_fmt = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_INV_MASK, + lrclk_fmt << CS35L36_LRCLK_INV_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_INV_MASK, + sclk_fmt << CS35L36_SCLK_INV_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FORMAT, + CS35L36_ASP_FMT_MASK, asp_fmt); + + return 0; +} + +struct cs35l36_global_fs_config { + int rate; + int fs_cfg; +}; + +static const struct cs35l36_global_fs_config cs35l36_fs_rates[] = { + {12000, 0x01}, + {24000, 0x02}, + {48000, 0x03}, + {96000, 0x04}, + {192000, 0x05}, + {384000, 0x06}, + {11025, 0x09}, + {22050, 0x0A}, + {44100, 0x0B}, + {88200, 0x0C}, + {176400, 0x0D}, + {8000, 0x11}, + {16000, 0x12}, + {32000, 0x13}, +}; + +static int cs35l36_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(dai->component); + unsigned int asp_width, global_fs = params_rate(params); + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l36_fs_rates); i++) { + if (global_fs == cs35l36_fs_rates[i].rate) + regmap_update_bits(cs35l36->regmap, + CS35L36_GLOBAL_CLK_CTRL, + CS35L36_GLOBAL_FS_MASK, + cs35l36_fs_rates[i].fs_cfg << + CS35L36_GLOBAL_FS_SHIFT); + } + + switch (params_width(params)) { + case 16: + asp_width = CS35L36_ASP_WIDTH_16; + break; + case 24: + asp_width = CS35L36_ASP_WIDTH_24; + break; + case 32: + asp_width = CS35L36_ASP_WIDTH_32; + break; + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL, + CS35L36_ASP_RX_WIDTH_MASK, + asp_width << CS35L36_ASP_RX_WIDTH_SHIFT); + } else { + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL, + CS35L36_ASP_TX_WIDTH_MASK, + asp_width << CS35L36_ASP_TX_WIDTH_SHIFT); + } + + return 0; +} + +static int cs35l36_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int fs1, fs2; + + if (freq > CS35L36_FS_NOM_6MHZ) { + fs1 = CS35L36_FS1_DEFAULT_VAL; + fs2 = CS35L36_FS2_DEFAULT_VAL; + } else { + fs1 = 3 * ((CS35L36_FS_NOM_6MHZ * 4 + freq - 1) / freq) + 4; + fs2 = 5 * ((CS35L36_FS_NOM_6MHZ * 4 + freq - 1) / freq) + 4; + } + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_TST_FS_MON0, + CS35L36_FS1_WINDOW_MASK | CS35L36_FS2_WINDOW_MASK, + fs1 | (fs2 << CS35L36_FS2_WINDOW_SHIFT)); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + return 0; +} + +static const struct cs35l36_pll_config *cs35l36_get_clk_config( + struct cs35l36_private *cs35l36, int freq) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l36_pll_sysclk); i++) { + if (cs35l36_pll_sysclk[i].freq == freq) + return &cs35l36_pll_sysclk[i]; + } + + return NULL; +} + +static const unsigned int cs35l36_src_rates[] = { + 8000, 12000, 11025, 16000, 22050, 24000, 32000, + 44100, 48000, 88200, 96000, 176400, 192000, 384000 +}; + +static const struct snd_pcm_hw_constraint_list cs35l36_constraints = { + .count = ARRAY_SIZE(cs35l36_src_rates), + .list = cs35l36_src_rates, +}; + +static int cs35l36_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, &cs35l36_constraints); + + return 0; +} + +static const struct snd_soc_dai_ops cs35l36_ops = { + .startup = cs35l36_pcm_startup, + .set_fmt = cs35l36_set_dai_fmt, + .hw_params = cs35l36_pcm_hw_params, + .set_sysclk = cs35l36_dai_set_sysclk, +}; + +static struct snd_soc_dai_driver cs35l36_dai[] = { + { + .name = "cs35l36-pcm", + .id = 0, + .playback = { + .stream_name = "AMP Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L36_RX_FORMATS, + }, + .capture = { + .stream_name = "AMP Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L36_TX_FORMATS, + }, + .ops = &cs35l36_ops, + .symmetric_rates = 1, + }, +}; + +static int cs35l36_component_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, + int dir) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + const struct cs35l36_pll_config *clk_cfg; + int prev_clksrc; + bool pdm_switch; + + prev_clksrc = cs35l36->clksrc; + + switch (clk_id) { + case 0: + cs35l36->clksrc = CS35L36_PLLSRC_SCLK; + break; + case 1: + cs35l36->clksrc = CS35L36_PLLSRC_LRCLK; + break; + case 2: + cs35l36->clksrc = CS35L36_PLLSRC_PDMCLK; + break; + case 3: + cs35l36->clksrc = CS35L36_PLLSRC_SELF; + break; + case 4: + cs35l36->clksrc = CS35L36_PLLSRC_MCLK; + break; + default: + return -EINVAL; + } + + clk_cfg = cs35l36_get_clk_config(cs35l36, freq); + if (clk_cfg == NULL) { + dev_err(component->dev, "Invalid CLK Config Freq: %d\n", freq); + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_OPENLOOP_MASK, + 1 << CS35L36_PLL_OPENLOOP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_REFCLK_FREQ_MASK, + clk_cfg->clk_cfg << CS35L36_REFCLK_FREQ_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_REFCLK_EN_MASK, + 0 << CS35L36_PLL_REFCLK_EN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_CLK_SEL_MASK, + cs35l36->clksrc); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_OPENLOOP_MASK, + 0 << CS35L36_PLL_OPENLOOP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_REFCLK_EN_MASK, + 1 << CS35L36_PLL_REFCLK_EN_SHIFT); + + if (cs35l36->rev_id == CS35L36_REV_A0) { + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_write(cs35l36->regmap, CS35L36_DCO_CTRL, 0x00036DA8); + regmap_write(cs35l36->regmap, CS35L36_MISC_CTRL, 0x0100EE0E); + + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS, + CS35L36_PLL_IGAIN_MASK, + CS35L36_PLL_IGAIN << + CS35L36_PLL_IGAIN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS, + CS35L36_PLL_FFL_IGAIN_MASK, + clk_cfg->fll_igain); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + if (cs35l36->clksrc == CS35L36_PLLSRC_PDMCLK) { + pdm_switch = cs35l36->ldm_mode_sel && + (prev_clksrc != CS35L36_PLLSRC_PDMCLK); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 0 << CS35L36_NG_DELAY_SHIFT); + + regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG, + CS35L36_PDM_MODE_MASK, + 1 << CS35L36_PDM_MODE_SHIFT); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 3 << CS35L36_NG_DELAY_SHIFT); + } else { + pdm_switch = cs35l36->ldm_mode_sel && + (prev_clksrc == CS35L36_PLLSRC_PDMCLK); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 0 << CS35L36_NG_DELAY_SHIFT); + + regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG, + CS35L36_PDM_MODE_MASK, + 0 << CS35L36_PDM_MODE_SHIFT); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 3 << CS35L36_NG_DELAY_SHIFT); + } + + return 0; +} + +static int cs35l36_boost_inductor(struct cs35l36_private *cs35l36, int inductor) +{ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF, + CS35L36_BSTCVRT_K1_MASK, 0x3C); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF, + CS35L36_BSTCVRT_K2_MASK, + 0x3C << CS35L36_BSTCVRT_K2_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SW_FREQ, + CS35L36_BSTCVRT_CCMFREQ_MASK, 0x00); + + switch (inductor) { + case 1000: /* 1 uH */ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_SLOPE_MASK, + 0x75 << CS35L36_BSTCVRT_SLOPE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_LBSTVAL_MASK, 0x00); + break; + case 1200: /* 1.2 uH */ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_SLOPE_MASK, + 0x6B << CS35L36_BSTCVRT_SLOPE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_LBSTVAL_MASK, 0x01); + break; + default: + dev_err(cs35l36->dev, "%s Invalid Inductor Value %d uH\n", + __func__, inductor); + return -EINVAL; + } + + return 0; +} + +static int cs35l36_component_probe(struct snd_soc_component *component) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int ret = 0; + + if ((cs35l36->rev_id == CS35L36_REV_A0) && cs35l36->pdata.dcm_mode) { + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_DCM_CTRL, + CS35L36_DCM_AUTO_MASK, + CS35L36_DCM_AUTO_MASK); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL, + CS35L36_BST_MAN_IPKCOMP_MASK, + 0 << CS35L36_BST_MAN_IPKCOMP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL, + CS35L36_BST_MAN_IPKCOMP_EN_MASK, + CS35L36_BST_MAN_IPKCOMP_EN_MASK); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + if (cs35l36->pdata.amp_pcm_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_DIG_VOL_CTRL, + CS35L36_AMP_PCM_INV_MASK, + CS35L36_AMP_PCM_INV_MASK); + + if (cs35l36->pdata.multi_amp_mode) + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_ASP_TX_HIZ_MASK, + CS35L36_ASP_TX_HIZ_MASK); + + if (cs35l36->pdata.imon_pol_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT, + CS35L36_IMON_POL_MASK, 0); + + if (cs35l36->pdata.vmon_pol_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT, + CS35L36_VMON_POL_MASK, 0); + + if (cs35l36->pdata.bst_vctl) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1, + CS35L35_BSTCVRT_CTL_MASK, + cs35l36->pdata.bst_vctl); + + if (cs35l36->pdata.bst_vctl_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2, + CS35L35_BSTCVRT_CTL_SEL_MASK, + cs35l36->pdata.bst_vctl_sel); + + if (cs35l36->pdata.bst_ipk) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_PEAK_CUR, + CS35L36_BST_IPK_MASK, + cs35l36->pdata.bst_ipk); + + if (cs35l36->pdata.boost_ind) { + ret = cs35l36_boost_inductor(cs35l36, cs35l36->pdata.boost_ind); + if (ret < 0) { + dev_err(cs35l36->dev, + "Boost inductor config failed(%d)\n", ret); + return ret; + } + } + + if (cs35l36->pdata.temp_warn_thld) + regmap_update_bits(cs35l36->regmap, CS35L36_DTEMP_WARN_THLD, + CS35L36_TEMP_THLD_MASK, + cs35l36->pdata.temp_warn_thld); + + if (cs35l36->pdata.irq_drv_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_DRV_SEL_MASK, + cs35l36->pdata.irq_drv_sel << + CS35L36_INT_DRV_SEL_SHIFT); + + if (cs35l36->pdata.irq_gpio_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_GPIO_SEL_MASK, + cs35l36->pdata.irq_gpio_sel << + CS35L36_INT_GPIO_SEL_SHIFT); + + /* + * Rev B0 has 2 versions + * L36 is 10V + * L37 is 12V + * If L36 we need to clamp some values for safety + * after probe has setup dt values. We want to make + * sure we dont miss any values set in probe + */ + if (cs35l36->chip_version == CS35L36_10V_L36) { + regmap_update_bits(cs35l36->regmap, + CS35L36_BSTCVRT_OVERVOLT_CTRL, + CS35L36_BST_OVP_THLD_MASK, + CS35L36_BST_OVP_THLD_11V); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_BST_ANA2_TEST, + CS35L36_BST_OVP_TRIM_MASK, + CS35L36_BST_OVP_TRIM_11V << + CS35L36_BST_OVP_TRIM_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2, + CS35L36_BST_CTRL_LIM_MASK, + 1 << CS35L36_BST_CTRL_LIM_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1, + CS35L35_BSTCVRT_CTL_MASK, + CS35L36_BST_CTRL_10V_CLAMP); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + /* + * RevA and B require the disabling of + * SYNC_GLOBAL_OVR when GLOBAL_EN = 0. + * Just turn it off from default + */ + regmap_update_bits(cs35l36->regmap, CS35L36_CTRL_OVRRIDE, + CS35L36_SYNC_GLOBAL_OVR_MASK, + 0 << CS35L36_SYNC_GLOBAL_OVR_SHIFT); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_cs35l36 = { + .probe = &cs35l36_component_probe, + .set_sysclk = cs35l36_component_set_sysclk, + .dapm_widgets = cs35l36_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l36_dapm_widgets), + .dapm_routes = cs35l36_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l36_audio_map), + .controls = cs35l36_aud_controls, + .num_controls = ARRAY_SIZE(cs35l36_aud_controls), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static struct regmap_config cs35l36_regmap = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = CS35L36_PAC_PMEM_WORD1023, + .reg_defaults = cs35l36_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l36_reg), + .precious_reg = cs35l36_precious_reg, + .volatile_reg = cs35l36_volatile_reg, + .readable_reg = cs35l36_readable_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static irqreturn_t cs35l36_irq(int irq, void *data) +{ + struct cs35l36_private *cs35l36 = data; + unsigned int status[4]; + unsigned int masks[4]; + int ret = IRQ_NONE; + + /* ack the irq by reading all status registers */ + regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_STATUS, status, + ARRAY_SIZE(status)); + + regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_MASK, masks, + ARRAY_SIZE(masks)); + + /* Check to see if unmasked bits are active */ + if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) && + !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) { + return IRQ_NONE; + } + + /* + * The following interrupts require a + * protection release cycle to get the + * speaker out of Safe-Mode. + */ + if (status[2] & CS35L36_AMP_SHORT_ERR) { + dev_crit(cs35l36->dev, "Amp short error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, + CS35L36_AMP_SHORT_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT3_STATUS, + CS35L36_AMP_SHORT_ERR, + CS35L36_AMP_SHORT_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_TEMP_WARN) { + dev_crit(cs35l36->dev, "Over temperature warning\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, + CS35L36_TEMP_WARN_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_TEMP_WARN, CS35L36_TEMP_WARN); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_TEMP_ERR) { + dev_crit(cs35l36->dev, "Over temperature error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_TEMP_ERR, CS35L36_TEMP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_OVP_ERR) { + dev_crit(cs35l36->dev, "VBST Over Voltage error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_OVP_ERR, CS35L36_BST_OVP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_DCM_UVP_ERR) { + dev_crit(cs35l36->dev, "DCM VBST Under Voltage Error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, + CS35L36_BST_UVP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_DCM_UVP_ERR, + CS35L36_BST_DCM_UVP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_SHORT_ERR) { + dev_crit(cs35l36->dev, "LBST SHORT error!\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, + CS35L36_BST_SHORT_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_SHORT_ERR, + CS35L36_BST_SHORT_ERR); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int cs35l36_handle_of_data(struct i2c_client *i2c_client, + struct cs35l36_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config; + struct device_node *vpbr_node; + unsigned int val; + int ret; + + if (!np) + return 0; + + ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val); + if (!ret) { + if (val < 2550 || val > 12000) { + dev_err(&i2c_client->dev, + "Invalid Boost Voltage %d mV\n", val); + return -EINVAL; + } + pdata->bst_vctl = (((val - 2550) / 100) + 1) << 1; + } else { + dev_err(&i2c_client->dev, + "Unable to find required parameter 'cirrus,boost-ctl-millivolt'"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "cirrus,boost-ctl-select", &val); + if (!ret) + pdata->bst_vctl_sel = val | CS35L36_VALID_PDATA; + + ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val); + if (!ret) { + if (val < 1600 || val > 4500) { + dev_err(&i2c_client->dev, + "Invalid Boost Peak Current %u mA\n", val); + return -EINVAL; + } + + pdata->bst_ipk = (val - 1600) / 50; + } else { + dev_err(&i2c_client->dev, + "Unable to find required parameter 'cirrus,boost-peak-milliamp'"); + return -EINVAL; + } + + pdata->multi_amp_mode = of_property_read_bool(np, + "cirrus,multi-amp-mode"); + + pdata->dcm_mode = of_property_read_bool(np, + "cirrus,dcm-mode-enable"); + + pdata->amp_pcm_inv = of_property_read_bool(np, + "cirrus,amp-pcm-inv"); + + pdata->imon_pol_inv = of_property_read_bool(np, + "cirrus,imon-pol-inv"); + + pdata->vmon_pol_inv = of_property_read_bool(np, + "cirrus,vmon-pol-inv"); + + if (of_property_read_u32(np, "cirrus,temp-warn-threshold", &val) >= 0) + pdata->temp_warn_thld = val | CS35L36_VALID_PDATA; + + if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) { + pdata->boost_ind = val; + } else { + dev_err(&i2c_client->dev, "Inductor not specified.\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "cirrus,irq-drive-select", &val) >= 0) + pdata->irq_drv_sel = val | CS35L36_VALID_PDATA; + + if (of_property_read_u32(np, "cirrus,irq-gpio-select", &val) >= 0) + pdata->irq_gpio_sel = val | CS35L36_VALID_PDATA; + + /* VPBR Config */ + vpbr_node = of_get_child_by_name(np, "cirrus,vpbr-config"); + vpbr_config->is_present = vpbr_node ? true : false; + if (vpbr_config->is_present) { + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-en", + &val) >= 0) + vpbr_config->vpbr_en = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-thld", + &val) >= 0) + vpbr_config->vpbr_thld = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-rate", + &val) >= 0) + vpbr_config->vpbr_atk_rate = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-vol", + &val) >= 0) + vpbr_config->vpbr_atk_vol = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-max-attn", + &val) >= 0) + vpbr_config->vpbr_max_attn = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-wait", + &val) >= 0) + vpbr_config->vpbr_wait = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-rel-rate", + &val) >= 0) + vpbr_config->vpbr_rel_rate = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-mute-en", + &val) >= 0) + vpbr_config->vpbr_mute_en = val; + } + of_node_put(vpbr_node); + + return 0; +} + +static int cs35l36_pac(struct cs35l36_private *cs35l36) +{ + int ret, count; + unsigned int val; + + if (cs35l36->rev_id != CS35L36_REV_B0) + return 0; + + /* + * Magic code for internal PAC + */ + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + usleep_range(9500, 10500); + + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_RESET); + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3, + CS35L36_PAC_MEM_ACCESS); + regmap_write(cs35l36->regmap, CS35L36_PAC_PMEM_WORD0, + CS35L36_B0_PAC_PATCH); + + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3, + CS35L36_PAC_MEM_ACCESS_CLR); + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_ENABLE_MASK); + + usleep_range(9500, 10500); + + ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS, &val); + if (ret < 0) { + dev_err(cs35l36->dev, "Failed to read int4_status %d\n", ret); + return ret; + } + + count = 0; + while (!(val & CS35L36_MCU_CONFIG_CLR)) { + usleep_range(100, 200); + count++; + + ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS, + &val); + if (ret < 0) { + dev_err(cs35l36->dev, "Failed to read int4_status %d\n", + ret); + return ret; + } + + if (count >= 100) + return -EINVAL; + } + + regmap_write(cs35l36->regmap, CS35L36_INT4_STATUS, + CS35L36_MCU_CONFIG_CLR); + regmap_update_bits(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_ENABLE_MASK, 0); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + + return 0; +} + +static void cs35l36_apply_vpbr_config(struct cs35l36_private *cs35l36) +{ + struct cs35l36_platform_data *pdata = &cs35l36->pdata; + struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config; + + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL3, + CS35L36_VPBR_EN_MASK, + vpbr_config->vpbr_en << + CS35L36_VPBR_EN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_THLD_MASK, + vpbr_config->vpbr_thld << + CS35L36_VPBR_THLD_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_MAX_ATTN_MASK, + vpbr_config->vpbr_max_attn << + CS35L36_VPBR_MAX_ATTN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_ATK_VOL_MASK, + vpbr_config->vpbr_atk_vol << + CS35L36_VPBR_ATK_VOL_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_ATK_RATE_MASK, + vpbr_config->vpbr_atk_rate << + CS35L36_VPBR_ATK_RATE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_WAIT_MASK, + vpbr_config->vpbr_wait << + CS35L36_VPBR_WAIT_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_REL_RATE_MASK, + vpbr_config->vpbr_rel_rate << + CS35L36_VPBR_REL_RATE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_MUTE_EN_MASK, + vpbr_config->vpbr_mute_en << + CS35L36_VPBR_MUTE_EN_SHIFT); +} + +static const struct reg_sequence cs35l36_reva0_errata_patch[] = { + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 }, + /* Errata Writes */ + { CS35L36_OTP_CTRL1, 0x00002060 }, + { CS35L36_OTP_CTRL2, 0x00000001 }, + { CS35L36_OTP_CTRL1, 0x00002460 }, + { CS35L36_OTP_CTRL2, 0x00000001 }, + { 0x00002088, 0x012A1838 }, + { 0x00003014, 0x0100EE0E }, + { 0x00003008, 0x0008184A }, + { 0x00007418, 0x509001C8 }, + { 0x00007064, 0x0929A800 }, + { 0x00002D10, 0x0002C01C }, + { 0x0000410C, 0x00000A11 }, + { 0x00006E08, 0x8B19140C }, + { 0x00006454, 0x0300000A }, + { CS35L36_AMP_NG_CTRL, 0x000020EF }, + { 0x00007E34, 0x0000000E }, + { 0x0000410C, 0x00000A11 }, + { 0x00007410, 0x20514B00 }, + /* PAC Config */ + { CS35L36_CTRL_OVRRIDE, 0x00000000 }, + { CS35L36_PAC_INT0_CTRL, 0x00860001 }, + { CS35L36_PAC_INT1_CTRL, 0x00860001 }, + { CS35L36_PAC_INT2_CTRL, 0x00860001 }, + { CS35L36_PAC_INT3_CTRL, 0x00860001 }, + { CS35L36_PAC_INT4_CTRL, 0x00860001 }, + { CS35L36_PAC_INT5_CTRL, 0x00860001 }, + { CS35L36_PAC_INT6_CTRL, 0x00860001 }, + { CS35L36_PAC_INT7_CTRL, 0x00860001 }, + { CS35L36_PAC_INT_FLUSH_CTRL, 0x000000FF }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 }, +}; + +static const struct reg_sequence cs35l36_revb0_errata_patch[] = { + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 }, + { 0x00007064, 0x0929A800 }, + { 0x00007850, 0x00002FA9 }, + { 0x00007854, 0x0003F1D5 }, + { 0x00007858, 0x0003F5E3 }, + { 0x0000785C, 0x00001137 }, + { 0x00007860, 0x0001A7A5 }, + { 0x00007864, 0x0002F16A }, + { 0x00007868, 0x00003E21 }, + { 0x00007848, 0x00000001 }, + { 0x00003854, 0x05180240 }, + { 0x00007418, 0x509001C8 }, + { 0x0000394C, 0x028764BD }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 }, +}; + +static int cs35l36_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct cs35l36_private *cs35l36; + struct device *dev = &i2c_client->dev; + struct cs35l36_platform_data *pdata = dev_get_platdata(dev); + struct irq_data *irq_d; + int ret, irq_pol, chip_irq_pol, i; + u32 reg_id, reg_revid, l37_id_reg; + + cs35l36 = devm_kzalloc(dev, sizeof(struct cs35l36_private), GFP_KERNEL); + if (!cs35l36) + return -ENOMEM; + + cs35l36->dev = dev; + + i2c_set_clientdata(i2c_client, cs35l36); + cs35l36->regmap = devm_regmap_init_i2c(i2c_client, &cs35l36_regmap); + if (IS_ERR(cs35l36->regmap)) { + ret = PTR_ERR(cs35l36->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + goto err; + } + + cs35l36->num_supplies = ARRAY_SIZE(cs35l36_supplies); + for (i = 0; i < ARRAY_SIZE(cs35l36_supplies); i++) + cs35l36->supplies[i].supply = cs35l36_supplies[i]; + + ret = devm_regulator_bulk_get(dev, cs35l36->num_supplies, + cs35l36->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request core supplies: %d\n", ret); + return ret; + } + + if (pdata) { + cs35l36->pdata = *pdata; + } else { + pdata = devm_kzalloc(dev, sizeof(struct cs35l36_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (i2c_client->dev.of_node) { + ret = cs35l36_handle_of_data(i2c_client, pdata); + if (ret != 0) + return ret; + + } + + cs35l36->pdata = *pdata; + } + + ret = regulator_bulk_enable(cs35l36->num_supplies, cs35l36->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", ret); + return ret; + } + + /* returning NULL can be an option if in stereo mode */ + cs35l36->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs35l36->reset_gpio)) { + ret = PTR_ERR(cs35l36->reset_gpio); + cs35l36->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_disable_regs; + } + } + + if (cs35l36->reset_gpio) + gpiod_set_value_cansleep(cs35l36->reset_gpio, 1); + + usleep_range(2000, 2100); + + /* initialize amplifier */ + ret = regmap_read(cs35l36->regmap, CS35L36_SW_RESET, ®_id); + if (ret < 0) { + dev_err(dev, "Get Device ID failed %d\n", ret); + goto err; + } + + if (reg_id != CS35L36_CHIP_ID) { + dev_err(dev, "Device ID (%X). Expected ID %X\n", reg_id, + CS35L36_CHIP_ID); + ret = -ENODEV; + goto err; + } + + ret = regmap_read(cs35l36->regmap, CS35L36_REV_ID, ®_revid); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed %d\n", ret); + goto err; + } + + cs35l36->rev_id = reg_revid >> 8; + + ret = regmap_read(cs35l36->regmap, CS35L36_OTP_MEM30, &l37_id_reg); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to read otp_id Register %d\n", + ret); + return ret; + } + + if ((l37_id_reg & CS35L36_OTP_REV_MASK) == CS35L36_OTP_REV_L37) + cs35l36->chip_version = CS35L36_12V_L37; + else + cs35l36->chip_version = CS35L36_10V_L36; + + switch (cs35l36->rev_id) { + case CS35L36_REV_A0: + ret = regmap_register_patch(cs35l36->regmap, + cs35l36_reva0_errata_patch, + ARRAY_SIZE(cs35l36_reva0_errata_patch)); + if (ret < 0) { + dev_err(dev, "Failed to apply A0 errata patch %d\n", + ret); + goto err; + } + break; + case CS35L36_REV_B0: + ret = cs35l36_pac(cs35l36); + if (ret < 0) { + dev_err(dev, "Failed to Trim OTP %d\n", ret); + goto err; + } + + ret = regmap_register_patch(cs35l36->regmap, + cs35l36_revb0_errata_patch, + ARRAY_SIZE(cs35l36_revb0_errata_patch)); + if (ret < 0) { + dev_err(dev, "Failed to apply B0 errata patch %d\n", + ret); + goto err; + } + break; + } + + if (pdata->vpbr_config.is_present) + cs35l36_apply_vpbr_config(cs35l36); + + irq_d = irq_get_irq_data(i2c_client->irq); + if (!irq_d) { + dev_err(&i2c_client->dev, "Invalid IRQ: %d\n", i2c_client->irq); + ret = -ENODEV; + goto err; + } + + irq_pol = irqd_get_trigger_type(irq_d); + + switch (irq_pol) { + case IRQF_TRIGGER_FALLING: + case IRQF_TRIGGER_LOW: + chip_irq_pol = 0; + break; + case IRQF_TRIGGER_RISING: + case IRQF_TRIGGER_HIGH: + chip_irq_pol = 1; + break; + default: + dev_err(cs35l36->dev, "Invalid IRQ polarity: %d\n", irq_pol); + ret = -EINVAL; + goto err; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_POL_SEL_MASK, + chip_irq_pol << CS35L36_INT_POL_SEL_SHIFT); + + ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l36_irq, + IRQF_ONESHOT | irq_pol, "cs35l36", + cs35l36); + if (ret != 0) { + dev_err(dev, "Failed to request IRQ: %d\n", ret); + goto err; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_OUTPUT_EN_MASK, 1); + + /* Set interrupt masks for critical errors */ + regmap_write(cs35l36->regmap, CS35L36_INT1_MASK, + CS35L36_INT1_MASK_DEFAULT); + regmap_write(cs35l36->regmap, CS35L36_INT3_MASK, + CS35L36_INT3_MASK_DEFAULT); + + dev_info(&i2c_client->dev, "Cirrus Logic CS35L%d, Revision: %02X\n", + cs35l36->chip_version, reg_revid >> 8); + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_cs35l36, + cs35l36_dai, + ARRAY_SIZE(cs35l36_dai)); + if (ret < 0) { + dev_err(dev, "%s: Register component failed %d\n", __func__, + ret); + goto err; + } + + return 0; + +err: + gpiod_set_value_cansleep(cs35l36->reset_gpio, 0); + +err_disable_regs: + regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies); + return ret; +} + +static int cs35l36_i2c_remove(struct i2c_client *client) +{ + struct cs35l36_private *cs35l36 = i2c_get_clientdata(client); + + /* Reset interrupt masks for device removal */ + regmap_write(cs35l36->regmap, CS35L36_INT1_MASK, + CS35L36_INT1_MASK_RESET); + regmap_write(cs35l36->regmap, CS35L36_INT3_MASK, + CS35L36_INT3_MASK_RESET); + + if (cs35l36->reset_gpio) + gpiod_set_value_cansleep(cs35l36->reset_gpio, 0); + + regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies); + + return 0; +} +static const struct of_device_id cs35l36_of_match[] = { + {.compatible = "cirrus,cs35l36"}, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l36_of_match); + +static const struct i2c_device_id cs35l36_id[] = { + {"cs35l36", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs35l36_id); + +static struct i2c_driver cs35l36_i2c_driver = { + .driver = { + .name = "cs35l36", + .of_match_table = cs35l36_of_match, + }, + .id_table = cs35l36_id, + .probe = cs35l36_i2c_probe, + .remove = cs35l36_i2c_remove, +}; +module_i2c_driver(cs35l36_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L36 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l36.h b/sound/soc/codecs/cs35l36.h new file mode 100644 index 000000000000..f6e38c633b93 --- /dev/null +++ b/sound/soc/codecs/cs35l36.h @@ -0,0 +1,446 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cs35l36.h -- CS35L36 ALSA SoC audio driver + * + * Copyright 2018 Cirrus Logic, Inc. + * + * Author: James Schulman <james.schulman@cirrus.com> + * + */ + +#ifndef __CS35L36_H__ +#define __CS35L36_H__ + +#include <linux/regmap.h> + +#define CS35L36_FIRSTREG 0x00000000 +#define CS35L36_LASTREG 0x00E037FC +#define CS35L36_SW_RESET 0x00000000 +#define CS35L36_SW_REV 0x00000004 +#define CS35L36_HW_REV 0x00000008 +#define CS35L36_TESTKEY_CTRL 0x00000020 +#define CS35L36_USERKEY_CTL 0x00000024 +#define CS35L36_OTP_MEM30 0x00000478 +#define CS35L36_OTP_CTRL1 0x00000500 +#define CS35L36_OTP_CTRL2 0x00000504 +#define CS35L36_OTP_CTRL3 0x00000508 +#define CS35L36_OTP_CTRL4 0x0000050C +#define CS35L36_OTP_CTRL5 0x00000510 +#define CS35L36_PAC_CTL1 0x00000C00 +#define CS35L36_PAC_CTL2 0x00000C04 +#define CS35L36_PAC_CTL3 0x00000C08 +#define CS35L36_DEVICE_ID 0x00002004 +#define CS35L36_FAB_ID 0x00002008 +#define CS35L36_REV_ID 0x0000200C +#define CS35L36_PWR_CTRL1 0x00002014 +#define CS35L36_PWR_CTRL2 0x00002018 +#define CS35L36_PWR_CTRL3 0x0000201C +#define CS35L36_CTRL_OVRRIDE 0x00002020 +#define CS35L36_AMP_OUT_MUTE 0x00002024 +#define CS35L36_OTP_TRIM_STATUS 0x00002028 +#define CS35L36_DISCH_FILT 0x0000202C +#define CS35L36_OSC_TRIM 0x00002030 +#define CS35L36_PROTECT_REL_ERR 0x00002034 +#define CS35L36_PAD_INTERFACE 0x00002400 +#define CS35L36_PLL_CLK_CTRL 0x00002C04 +#define CS35L36_GLOBAL_CLK_CTRL 0x00002C0C +#define CS35L36_ADC_CLK_CTRL 0x00002C10 +#define CS35L36_SWIRE_CLK_CTRL 0x00002C14 +#define CS35L36_SP_SCLK_CLK_CTRL 0x00002D00 +#define CS35L36_TST_FS_MON0 0x00002D10 +#define CS35L36_PLL_LOOP_PARAMS 0x00003008 +#define CS35L36_DCO_CTRL 0x00003010 +#define CS35L36_MISC_CTRL 0x00003014 +#define CS35L36_MDSYNC_EN 0x00003404 +#define CS35L36_MDSYNC_TX_ID 0x00003408 +#define CS35L36_MDSYNC_PWR_CTRL 0x0000340C +#define CS35L36_MDSYNC_DATA_TX 0x00003410 +#define CS35L36_MDSYNC_TX_STATUS 0x0000341C +#define CS35L36_MDSYNC_RX_STATUS 0x00003420 +#define CS35L36_MDSYNC_ERR_STATUS 0x00003424 +#define CS35L36_BSTCVRT_VCTRL1 0x00003800 +#define CS35L36_BSTCVRT_VCTRL2 0x00003804 +#define CS35L36_BSTCVRT_PEAK_CUR 0x00003808 +#define CS35L36_BSTCVRT_SFT_RAMP 0x0000380C +#define CS35L36_BSTCVRT_COEFF 0x00003810 +#define CS35L36_BSTCVRT_SLOPE_LBST 0x00003814 +#define CS35L36_BSTCVRT_SW_FREQ 0x00003818 +#define CS35L36_BSTCVRT_DCM_CTRL 0x0000381C +#define CS35L36_BSTCVRT_DCM_MODE_FORCE 0x00003820 +#define CS35L36_BSTCVRT_OVERVOLT_CTRL 0x00003830 +#define CS35L36_BST_TST_MANUAL 0x0000393C +#define CS35L36_BST_ANA2_TEST 0x0000394C +#define CS35L36_VPI_LIMIT_MODE 0x00003C04 +#define CS35L36_VPI_LIMIT_MINMAX 0x00003C08 +#define CS35L36_VPI_VP_THLD 0x00003C0C +#define CS35L36_VPI_TRACK_CTRL 0x00003C10 +#define CS35L36_VPI_TRIG_MODE_CTRL 0x00003C14 +#define CS35L36_VPI_TRIG_STEPS 0x00003C18 +#define CS35L36_VI_SPKMON_FILT 0x00004004 +#define CS35L36_VI_SPKMON_GAIN 0x00004008 +#define CS35L36_VI_SPKMON_IP_SEL 0x00004100 +#define CS35L36_DTEMP_WARN_THLD 0x00004220 +#define CS35L36_DTEMP_STATUS 0x00004300 +#define CS35L36_VPVBST_FS_SEL 0x00004400 +#define CS35L36_VPVBST_VP_CTRL 0x00004440 +#define CS35L36_VPVBST_VBST_CTRL 0x00004444 +#define CS35L36_ASP_TX_PIN_CTRL 0x00004800 +#define CS35L36_ASP_RATE_CTRL 0x00004804 +#define CS35L36_ASP_FORMAT 0x00004808 +#define CS35L36_ASP_FRAME_CTRL 0x00004818 +#define CS35L36_ASP_TX1_TX2_SLOT 0x0000481C +#define CS35L36_ASP_TX3_TX4_SLOT 0x00004820 +#define CS35L36_ASP_TX5_TX6_SLOT 0x00004824 +#define CS35L36_ASP_TX7_TX8_SLOT 0x00004828 +#define CS35L36_ASP_RX1_SLOT 0x0000482C +#define CS35L36_ASP_RX_TX_EN 0x0000483C +#define CS35L36_ASP_RX1_SEL 0x00004C00 +#define CS35L36_ASP_TX1_SEL 0x00004C20 +#define CS35L36_ASP_TX2_SEL 0x00004C24 +#define CS35L36_ASP_TX3_SEL 0x00004C28 +#define CS35L36_ASP_TX4_SEL 0x00004C2C +#define CS35L36_ASP_TX5_SEL 0x00004C30 +#define CS35L36_ASP_TX6_SEL 0x00004C34 +#define CS35L36_SWIRE_P1_TX1_SEL 0x00004C40 +#define CS35L36_SWIRE_P1_TX2_SEL 0x00004C44 +#define CS35L36_SWIRE_P2_TX1_SEL 0x00004C60 +#define CS35L36_SWIRE_P2_TX2_SEL 0x00004C64 +#define CS35L36_SWIRE_P2_TX3_SEL 0x00004C68 +#define CS35L36_SWIRE_DP1_FIFO_CFG 0x00005000 +#define CS35L36_SWIRE_DP2_FIFO_CFG 0x00005004 +#define CS35L36_SWIRE_DP3_FIFO_CFG 0x00005008 +#define CS35L36_SWIRE_PCM_RX_DATA 0x0000500C +#define CS35L36_SWIRE_FS_SEL 0x00005010 +#define CS35L36_SPARE_CP_BITS 0x00005C00 +#define CS35L36_AMP_DIG_VOL_CTRL 0x00006000 +#define CS35L36_VPBR_CFG 0x00006404 +#define CS35L36_VBBR_CFG 0x00006408 +#define CS35L36_VPBR_STATUS 0x0000640C +#define CS35L36_VBBR_STATUS 0x00006410 +#define CS35L36_OVERTEMP_CFG 0x00006414 +#define CS35L36_AMP_ERR_VOL 0x00006418 +#define CS35L36_CLASSH_CFG 0x00006800 +#define CS35L36_CLASSH_FET_DRV_CFG 0x00006804 +#define CS35L36_NG_CFG 0x00006808 +#define CS35L36_AMP_GAIN_CTRL 0x00006C04 +#define CS35L36_PWM_MOD_IO_CTRL 0x0000706C +#define CS35L36_PWM_MOD_STATUS 0x00007070 +#define CS35L36_DAC_MSM_CFG 0x00007400 +#define CS35L36_AMP_SLOPE_CTRL 0x00007410 +#define CS35L36_AMP_PDM_VOLUME 0x00007E04 +#define CS35L36_AMP_PDM_RATE_CTRL 0x00007E08 +#define CS35L36_PDM_CH_SEL 0x00007E10 +#define CS35L36_AMP_NG_CTRL 0x00007E14 +#define CS35L36_PDM_HIGHFILT_CTRL 0x00007E3C +#define CS35L36_INT1_STATUS 0x00D00000 +#define CS35L36_INT2_STATUS 0x00D00004 +#define CS35L36_INT3_STATUS 0x00D00008 +#define CS35L36_INT4_STATUS 0x00D0000C +#define CS35L36_INT1_RAW_STATUS 0x00D00020 +#define CS35L36_INT2_RAW_STATUS 0x00D00024 +#define CS35L36_INT3_RAW_STATUS 0x00D00028 +#define CS35L36_INT4_RAW_STATUS 0x00D0002C +#define CS35L36_INT1_MASK 0x00D00040 +#define CS35L36_INT2_MASK 0x00D00044 +#define CS35L36_INT3_MASK 0x00D00048 +#define CS35L36_INT4_MASK 0x00D0004C +#define CS35L36_INT1_EDGE_LVL_CTRL 0x00D00060 +#define CS35L36_INT3_EDGE_LVL_CTRL 0x00D00068 +#define CS35L36_PAC_INT_STATUS 0x00D00200 +#define CS35L36_PAC_INT_RAW_STATUS 0x00D00210 +#define CS35L36_PAC_INT_FLUSH_CTRL 0x00D00218 +#define CS35L36_PAC_INT0_CTRL 0x00D00220 +#define CS35L36_PAC_INT1_CTRL 0x00D00224 +#define CS35L36_PAC_INT2_CTRL 0x00D00228 +#define CS35L36_PAC_INT3_CTRL 0x00D0022C +#define CS35L36_PAC_INT4_CTRL 0x00D00230 +#define CS35L36_PAC_INT5_CTRL 0x00D00234 +#define CS35L36_PAC_INT6_CTRL 0x00D00238 +#define CS35L36_PAC_INT7_CTRL 0x00D0023C +#define CS35L36_PAC_PMEM_WORD0 0x00E02800 +#define CS35L36_PAC_PMEM_WORD1 0x00E02804 +#define CS35L36_PAC_PMEM_WORD1023 0x00E037FC + +#define CS35L36_INTPAC_REG_COUNT 25 +#define CS35L36_CHIP_ID 0x00035A36 + +#define CS35L36_INT_OUTPUT_EN_MASK 0x01 +#define CS35L36_INT_GPIO_SEL_MASK 0x02 +#define CS35L36_INT_GPIO_SEL_SHIFT 1 +#define CS35L36_INT_POL_SEL_MASK 0x04 +#define CS35L36_INT_POL_SEL_SHIFT 2 +#define CS35L36_INT_DRV_SEL_MASK 0x20 +#define CS35L36_INT_DRV_SEL_SHIFT 5 +#define CS35L36_IRQ_SRC_MASK 0x08 +#define CS35L36_IRQ_SRC_SHIFT 3 + +#define CS35L36_SCLK_MSTR_MASK 0x40 +#define CS35L36_SCLK_MSTR_SHIFT 6 +#define CS35L36_LRCLK_MSTR_MASK 0x01 +#define CS35L36_LRCLK_MSTR_SHIFT 0 +#define CS35L36_SCLK_INV_MASK 0x100 +#define CS35L36_SCLK_INV_SHIFT 8 +#define CS35L36_LRCLK_INV_MASK 0x04 +#define CS35L36_LRCLK_INV_SHIFT 2 +#define CS35L36_SCLK_FRC_MASK 0x80 +#define CS35L36_SCLK_FRC_SHIFT 7 +#define CS35L36_LRCLK_FRC_MASK 0x02 +#define CS35L36_LRCLK_FRC_SHIFT 1 + +#define CS35L36_PDM_MODE_MASK 0x01 +#define CS35L36_PDM_MODE_SHIFT 0 + +#define CS35L36_ASP_FMT_MASK 0x07 +#define CS35L36_ASP_FMT_SHIFT 0 + +#define CS35L36_ASP_RX_WIDTH_MASK 0xFF0000 +#define CS35L36_ASP_RX_WIDTH_SHIFT 16 +#define CS35L36_ASP_TX_WIDTH_MASK 0xFF +#define CS35L36_ASP_TX_WIDTH_SHIFT 0 +#define CS35L36_ASP_WIDTH_16 0x10 +#define CS35L36_ASP_WIDTH_24 0x18 +#define CS35L36_ASP_WIDTH_32 0x20 + +#define CS35L36_ASP_RX1_SLOT_MASK 0x3F +#define CS35L36_ASP_RX1_EN_MASK 0x00010000 +#define CS35L36_ASP_RX1_EN_SHIFT 16 + +#define CS35L36_ASP_TX1_SLOT_MASK 0x3F +#define CS35L36_ASP_TX2_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX2_SLOT_SHIFT 16 +#define CS35L36_ASP_TX3_SLOT_MASK 0x3F +#define CS35L36_ASP_TX4_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX4_SLOT_SHIFT 16 +#define CS35L36_ASP_TX5_SLOT_MASK 0x3F +#define CS35L36_ASP_TX6_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX6_SLOT_SHIFT 16 +#define CS35L36_ASP_TX7_SLOT_MASK 0x3F +#define CS35L36_ASP_TX8_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX8_SLOT_SHIFT 16 +#define CS35L36_ASP_TX_HIZ_MASK 0x200000 + +#define CS35L36_APS_TX_SEL_MASK 0x7F + +#define CS35L36_ASP_TX1_EN_MASK 0x01 +#define CS35L36_ASP_TX2_EN_MASK 0x02 +#define CS35L36_ASP_TX2_EN_SHIFT 1 +#define CS35L36_ASP_TX3_EN_MASK 0x04 +#define CS35L36_ASP_TX3_EN_SHIFT 2 +#define CS35L36_ASP_TX4_EN_MASK 0x08 +#define CS35L36_ASP_TX4_EN_SHIFT 3 +#define CS35L36_ASP_TX5_EN_MASK 0x10 +#define CS35L36_ASP_TX5_EN_SHIFT 4 +#define CS35L36_ASP_TX6_EN_MASK 0x20 +#define CS35L36_ASP_TX6_EN_SHIFT 5 +#define CS35L36_ASP_TX7_EN_MASK 0x40 +#define CS35L36_ASP_TX7_EN_SHIFT 6 +#define CS35L36_ASP_TX8_EN_MASK 0x80 +#define CS35L36_ASP_TX8_EN_SHIFT 7 + + +#define CS35L36_PLL_CLK_SEL_MASK 0x07 +#define CS35L36_PLL_CLK_SEL_SHIFT 0 +#define CS35L36_PLLSRC_SCLK 0 +#define CS35L36_PLLSRC_LRCLK 1 +#define CS35L36_PLLSRC_SELF 3 +#define CS35L36_PLLSRC_PDMCLK 4 +#define CS35L36_PLLSRC_MCLK 5 +#define CS35L36_PLLSRC_SWIRE 7 +#define CS35L36_REFCLK_FREQ_MASK 0x7E0 +#define CS35L36_REFCLK_FREQ_SHIFT 5 +#define CS35L36_PLL_OPENLOOP_MASK 0x800 +#define CS35L36_PLL_OPENLOOP_SHIFT 11 +#define CS35L36_PLL_REFCLK_EN_MASK 0x10 +#define CS35L36_PLL_REFCLK_EN_SHIFT 4 + + +#define CS35L36_GLOBAL_FS_MASK 0x1F +#define CS35L36_GLOBAL_FS_SHIFT 0 + +#define CS35L36_HPF_PCM_EN_MASK 0x800 +#define CS35L36_HPF_PCM_EN_SHIFT 15 +#define CS35L36_PCM_RX_SEL_MASK 0x7F +#define CS35L36_PCM_RX_SEL_SHIFT 0 + +#define CS35L36_PCM_RX_SEL_ZERO 0x00 +#define CS35L36_PCM_RX_SEL_PCM 0x08 +#define CS35L36_PCM_RX_SEL_SWIRE 0x10 +#define CS35L36_PCM_RX_SEL_DIAG 0x04 + +#define CS35L36_GLOBAL_EN_MASK 0x01 +#define CS35L36_GLOBAL_EN_SHIFT 0x00 + +#define CS35L36_AMP_PCM_INV_MASK 0x4000 +#define CS35L36_AMP_PCM_INV_SHIFT 14 + +#define CS35L36_AMP_VOL_PCM_MASK 0x3FF8 +#define CS35L36_AMP_VOL_PCM_SHIFT 3 +#define CS35L36_DIGITAL_MUTE 0x04CF + +#define CS35L36_AMP_RAMP_MASK 0x0007 +#define CS35L36_AMP_RAMP_SHIFT 0 + +#define CS35L36_AMP_MUTE_MASK 0x0010 +#define CS35L36_AMP_MUTE_SHIFT 4 + +#define CS35L36_GLOBAL_RESYNC_FS1_MASK 0x00000200 +#define CS35L36_GLOBAL_RESYNC_FS2_MASK 0x00000400 +#define CS35L36_SYNC_GLOBAL_OVR_MASK 0x00000002 +#define CS35L36_SYNC_GLOBAL_OVR_SHIFT 1 + +#define CS35L36_REFCLK_IN_MASK 0x00100000 +#define CS35L36_PLL_UNLOCK_MASK 0x00002000 + +#define CS35L36_ASP_RX_UDF_MASK 0x00000040 +#define CS35L36_ASP_RX_OVF_MASK 0x00000080 + +#define CS35L36_IMON_POL_MASK 0x02 +#define CS35L36_IMON_POL_SHIFT 1 + +#define CS35L36_VMON_POL_MASK 0x01 +#define CS35L36_VMON_POL_SHIFT 0 + +#define CS35L36_PDN_DONE 0x40 +#define CS35L36_PDN_DONE_SHIFT 6 +#define CS35L36_PUP_DONE 0x80 +#define CS35L36_PUP_DONE_SHIFT 7 +#define CS35L36_GLOBAL_EN_ASSRT 0x20 +#define CS35L36_PUP_DONE_IRQ_UNMASK 0x7F +#define CS35L36_PUP_DONE_IRQ_MASK 0xBF + +#define CS35L36_FS1_WINDOW_MASK 0x000007FF +#define CS35L36_FS2_WINDOW_MASK 0x00FFF800 +#define CS35L36_FS2_WINDOW_SHIFT 12 + +#define CS35L36_PLL_FFL_IGAIN_MASK 0x0F +#define CS35L36_PLL_IGAIN_MASK 0x3F0 +#define CS35L36_PLL_IGAIN_SHIFT 4 +#define CS35L36_PLL_IGAIN 0x04 + +#define CS35L36_BST_EN_MASK 0x30 +#define CS35L36_BST_EN 0x02 +#define CS35L36_BST_DIS_VP 0x01 +#define CS35L36_BST_DIS_EXTN 0x00 +#define CS35L36_BST_EN_SHIFT 4 +#define CS35L36_BST_MAN_IPKCOMP_MASK 0x200 +#define CS35L36_BST_MAN_IPKCOMP_SHIFT 9 + +#define CS35L36_BST_MAN_IPKCOMP_EN_MASK 0x100 +#define CS35L36_BST_MAN_IPKCOMP_EN_SHIFT 8 + +#define CS35L36_BST_IPK_MASK 0x7F +#define CS35L36_BST_OVP_THLD_MASK 0x3F +#define CS35L36_BST_OVP_THLD_11V 0x10 +#define CS35L36_BST_OVP_TRIM_MASK 0x00078000 +#define CS35L36_BST_OVP_TRIM_SHIFT 15 +#define CS35L36_BST_OVP_TRIM_11V 0x0C +#define CS35L36_BST_CTRL_LIM_MASK 0x04 +#define CS35L36_BST_CTRL_LIM_SHIFT 2 +#define CS35L36_BST_CTRL_10V_CLAMP 0x96 + +#define CS35L36_NG_AMP_EN_MASK 0x3F00 +#define CS35L36_NG_DELAY_MASK 0x70 +#define CS35L36_NG_DELAY_SHIFT 4 +#define CS35L36_AMP_ZC_SHIFT 10 +#define CS35L36_PDM_LDM_ENTER_SHIFT 3 +#define CS35L36_PDM_LDM_EXIT_SHIFT 4 + +#define CS35L36_BSTCVRT_K1_MASK 0xFF +#define CS35L36_BSTCVRT_K2_MASK 0xFF00 +#define CS35L36_BSTCVRT_K2_SHIFT 8 +#define CS35L36_BSTCVRT_SLOPE_MASK 0xFF00 +#define CS35L36_BSTCVRT_SLOPE_SHIFT 8 +#define CS35L36_BSTCVRT_CCMFREQ_MASK 0x0F +#define CS35L36_BSTCVRT_LBSTVAL_MASK 0x03 +#define CS35L35_BSTCVRT_CTL_MASK 0xFF +#define CS35L35_BSTCVRT_CTL_SEL_MASK 0x03 +#define CS35L36_DCM_AUTO_MASK 0x01 + +#define CS35L36_INT1_MASK_DEFAULT 0xF9BA7FFF +#define CS35L36_INT1_MASK_RESET 0xFFFFFFFF +#define CS35L36_INT3_MASK_DEFAULT 0xFFFFEFFF +#define CS35L36_INT3_MASK_RESET 0xFFFFFFFF + + +#define CS35L36_AMP_SHORT_ERR 0x1000 +#define CS35L36_BST_SHORT_ERR 0x40000 +#define CS35L36_TEMP_WARN 0x2000000 +#define CS35L36_TEMP_ERR 0x4000000 +#define CS35L36_BST_OVP_ERR 0x10000 +#define CS35L36_BST_DCM_UVP_ERR 0x20000 + +#define CS35L36_AMP_SHORT_ERR_RLS 0x02 +#define CS35L36_BST_SHORT_ERR_RLS 0x04 +#define CS35L36_BST_OVP_ERR_RLS 0x08 +#define CS35L36_BST_UVP_ERR_RLS 0x10 +#define CS35L36_TEMP_WARN_ERR_RLS 0x20 +#define CS35L36_TEMP_ERR_RLS 0x40 +#define CS35L36_TEMP_THLD_MASK 0x03 + +#define CS35L36_REV_B0 0xb0 +#define CS35L36_REV_A0 0xa0 +#define CS35L36_B0_PAC_PATCH 0x00DD0102 + +#define CS35L36_OTP_ECC_EN_MASK 0x400 +#define CS35L36_OTP_ECC_EN_SHIFT 10 +#define CS35L36_OTP_RUN_BOOT_MASK 0x01 +#define CS35L36_OTP_BOOT_DONE 0x2000000 +#define CS35L36_PAC_RESET_MASK 0x04 +#define CS35L36_PAC_RESET_SHIFT 2 +#define CS35L36_PAC_STALL_MASK 0x02 +#define CS35L36_PAC_STALL_SHIFT 1 +#define CS35L36_PAC_ENABLE_MASK 0x00000001 +#define CS35L36_PAC_MEM_ACCESS 0x01 +#define CS35L36_PAC_MEM_ACCESS_CLR 0 +#define CS35L36_SOFT_RESET 0x5AAA +#define CS35L36_MCU_BOOT_COMPLETE 0x02 +#define CS35L36_MCU_CONFIG_UNMASK 0x00FEFFFF +#define CS35L36_MCU_CONFIG_CLR 0x00010000 +#define CS35L36_MCU_CONFIG_MASK 0x00FFFFFF +#define CS35L36_GPIO_INT_SEL_MASK 0x0000003B +#define CS35L36_GPIO_INT_SEL_UNMASK 0x0000003A +#define CS35L36_PAC_RESET 0x00000000 +#define CS35L36_OTP_REV_MASK 0x00FF0000 +#define CS35L36_OTP_REV_L37 0x00CC0000 +#define CS35L36_12V_L37 37 +#define CS35L36_10V_L36 36 + +#define CS35L36_VPBR_EN_MASK 0x00001000 +#define CS35L36_VPBR_EN_SHIFT 12 + +#define CS35L36_VPBR_THLD_MASK 0x0000001F +#define CS35L36_VPBR_THLD_SHIFT 0 +#define CS35L36_VPBR_MAX_ATTN_MASK 0x00000F00 +#define CS35L36_VPBR_MAX_ATTN_SHIFT 8 +#define CS35L36_VPBR_ATK_VOL_MASK 0x0000F000 +#define CS35L36_VPBR_ATK_VOL_SHIFT 12 +#define CS35L36_VPBR_ATK_RATE_MASK 0x00070000 +#define CS35L36_VPBR_ATK_RATE_SHIFT 16 +#define CS35L36_VPBR_WAIT_MASK 0x00180000 +#define CS35L36_VPBR_WAIT_SHIFT 19 +#define CS35L36_VPBR_REL_RATE_MASK 0x00E00000 +#define CS35L36_VPBR_REL_RATE_SHIFT 21 +#define CS35L36_VPBR_MUTE_EN_MASK 0x01000000 +#define CS35L36_VPBR_MUTE_EN_SHIFT 24 + +#define CS35L36_OSC_FREQ_TRIM_MASK 0x070 +#define CS35L36_OSC_TRIM_DONE 0x08 + +#define CS35L36_FS1_DEFAULT_VAL 16 +#define CS35L36_FS2_DEFAULT_VAL 36 +#define CS35L36_FS_NOM_6MHZ 6000000 + +#define CS35L36_TEST_UNLOCK1 0x00005555 +#define CS35L36_TEST_UNLOCK2 0x0000AAAA +#define CS35L36_TEST_LOCK1 0x0000CCCC +#define CS35L36_TEST_LOCK2 0x00003333 + +#define CS35L36_PAC_PROG_MEM 512 + +#define CS35L36_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#define CS35L36_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +extern const int cs35l36_a0_pac_patch[CS35L36_PAC_PROG_MEM]; + +#endif diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index b1df4bb36105..121a8190f93e 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1376,11 +1376,7 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } - /* By default 64 BCLKs per WCLK is supported */ - dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64; - snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, - DA7219_DAI_BCLKS_PER_WCLK_MASK | DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK, dai_clk_mode); snd_soc_component_update_bits(component, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK, @@ -1395,69 +1391,83 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai, { struct snd_soc_component *component = dai->component; struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - u8 dai_bclks_per_wclk; - __le16 offset; + unsigned int ch_mask; + u8 dai_bclks_per_wclk, slot_offset; + u16 offset; + __le16 dai_offset; u32 frame_size; - /* No channels enabled so disable TDM, revert to 64-bit frames */ + /* No channels enabled so disable TDM */ if (!tx_mask) { snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL, DA7219_DAI_TDM_CH_EN_MASK | DA7219_DAI_TDM_MODE_EN_MASK, 0); - snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, - DA7219_DAI_BCLKS_PER_WCLK_MASK, - DA7219_DAI_BCLKS_PER_WCLK_64); + da7219->tdm_en = false; return 0; } /* Check we have valid slots */ - if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) { - dev_err(component->dev, "Invalid number of slots, max = %d\n", + slot_offset = ffs(tx_mask) - 1; + ch_mask = (tx_mask >> slot_offset); + if (fls(ch_mask) > DA7219_DAI_TDM_MAX_SLOTS) { + dev_err(component->dev, + "Invalid number of slots, max = %d\n", DA7219_DAI_TDM_MAX_SLOTS); return -EINVAL; } - /* Check we have a valid offset given */ - if (rx_mask > DA7219_DAI_OFFSET_MAX) { - dev_err(component->dev, "Invalid slot offset, max = %d\n", - DA7219_DAI_OFFSET_MAX); + /* + * Ensure we have a valid offset into the frame, based on slot width + * and slot offset of first slot we're interested in. + */ + offset = slot_offset * slot_width; + if (offset > DA7219_DAI_OFFSET_MAX) { + dev_err(component->dev, "Invalid frame offset %d\n", offset); return -EINVAL; } - /* Calculate & validate frame size based on slot info provided. */ - frame_size = slots * slot_width; - switch (frame_size) { - case 32: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; - break; - case 64: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; - break; - case 128: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128; - break; - case 256: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256; - break; - default: - dev_err(component->dev, "Invalid frame size %d\n", frame_size); - return -EINVAL; - } + /* + * If we're master, calculate & validate frame size based on slot info + * provided as we have a limited set of rates available. + */ + if (da7219->master) { + frame_size = slots * slot_width; + switch (frame_size) { + case 32: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; + break; + case 64: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; + break; + case 128: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128; + break; + case 256: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256; + break; + default: + dev_err(component->dev, "Invalid frame size %d\n", + frame_size); + return -EINVAL; + } - snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, - DA7219_DAI_BCLKS_PER_WCLK_MASK, - dai_bclks_per_wclk); + snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + } - offset = cpu_to_le16(rx_mask); + dai_offset = cpu_to_le16(offset); regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER, - &offset, sizeof(offset)); + &dai_offset, sizeof(dai_offset)); snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL, DA7219_DAI_TDM_CH_EN_MASK | DA7219_DAI_TDM_MODE_EN_MASK, - (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) | + (ch_mask << DA7219_DAI_TDM_CH_EN_SHIFT) | DA7219_DAI_TDM_MODE_EN_MASK); + da7219->tdm_en = true; + return 0; } @@ -1466,10 +1476,13 @@ static int da7219_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; - u8 dai_ctrl = 0, fs; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + u8 dai_ctrl = 0, dai_bclks_per_wclk = 0, fs; unsigned int channels; + int word_len = params_width(params); + int frame_size; - switch (params_width(params)) { + switch (word_len) { case 16: dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE; break; @@ -1533,6 +1546,23 @@ static int da7219_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* + * If we're master, then we have a limited set of BCLK rates we + * support. For slave mode this isn't the case and the codec can detect + * the BCLK rate automatically. + */ + if (da7219->master && !da7219->tdm_en) { + frame_size = word_len * 2; + if (frame_size <= 32) + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; + else + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; + + snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + } + snd_soc_component_update_bits(component, DA7219_DAI_CTRL, DA7219_DAI_WORD_LENGTH_MASK | DA7219_DAI_CH_NUM_MASK, diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h index 366cf46118a0..018819c631fb 100644 --- a/sound/soc/codecs/da7219.h +++ b/sound/soc/codecs/da7219.h @@ -830,6 +830,7 @@ struct da7219_priv { int clk_src; bool master; + bool tdm_en; bool alc_en; bool micbias_on_event; unsigned int mic_pga_delay; diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index b19d7a3e7a2c..5eeb0fe836a9 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1176,13 +1176,15 @@ static int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid) struct hdac_hdmi_cvt *cvt; char name[NAME_SIZE]; - cvt = kzalloc(sizeof(*cvt), GFP_KERNEL); + cvt = devm_kzalloc(&hdev->dev, sizeof(*cvt), GFP_KERNEL); if (!cvt) return -ENOMEM; cvt->nid = nid; sprintf(name, "cvt %d", cvt->nid); - cvt->name = kstrdup(name, GFP_KERNEL); + cvt->name = devm_kstrdup(&hdev->dev, name, GFP_KERNEL); + if (!cvt->name) + return -ENOMEM; list_add_tail(&cvt->head, &hdmi->cvt_list); hdmi->num_cvt++; @@ -1287,8 +1289,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, mutex_unlock(&hdmi->pin_mutex); } -static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi, - struct hdac_hdmi_pin *pin) +static int hdac_hdmi_add_ports(struct hdac_device *hdev, + struct hdac_hdmi_pin *pin) { struct hdac_hdmi_port *ports; int max_ports = HDA_MAX_PORTS; @@ -1300,7 +1302,7 @@ static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi, * implemented. */ - ports = kcalloc(max_ports, sizeof(*ports), GFP_KERNEL); + ports = devm_kcalloc(&hdev->dev, max_ports, sizeof(*ports), GFP_KERNEL); if (!ports) return -ENOMEM; @@ -1319,14 +1321,14 @@ static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid) struct hdac_hdmi_pin *pin; int ret; - pin = kzalloc(sizeof(*pin), GFP_KERNEL); + pin = devm_kzalloc(&hdev->dev, sizeof(*pin), GFP_KERNEL); if (!pin) return -ENOMEM; pin->nid = nid; pin->mst_capable = false; pin->hdev = hdev; - ret = hdac_hdmi_add_ports(hdmi, pin); + ret = hdac_hdmi_add_ports(hdev, pin); if (ret < 0) return ret; @@ -1468,8 +1470,6 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, { hda_nid_t nid; int i, num_nodes; - struct hdac_hdmi_cvt *temp_cvt, *cvt_next; - struct hdac_hdmi_pin *temp_pin, *pin_next; struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); int ret; @@ -1497,51 +1497,35 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, case AC_WID_AUD_OUT: ret = hdac_hdmi_add_cvt(hdev, nid); if (ret < 0) - goto free_widgets; + return ret; break; case AC_WID_PIN: ret = hdac_hdmi_add_pin(hdev, nid); if (ret < 0) - goto free_widgets; + return ret; break; } } if (!hdmi->num_pin || !hdmi->num_cvt) { ret = -EIO; - goto free_widgets; + dev_err(&hdev->dev, "Bad pin/cvt setup in %s\n", __func__); + return ret; } ret = hdac_hdmi_create_dais(hdev, dais, hdmi, hdmi->num_cvt); if (ret) { dev_err(&hdev->dev, "Failed to create dais with err: %d\n", - ret); - goto free_widgets; + ret); + return ret; } *num_dais = hdmi->num_cvt; ret = hdac_hdmi_init_dai_map(hdev); if (ret < 0) - goto free_widgets; - - return ret; - -free_widgets: - list_for_each_entry_safe(temp_cvt, cvt_next, &hdmi->cvt_list, head) { - list_del(&temp_cvt->head); - kfree(temp_cvt->name); - kfree(temp_cvt); - } - - list_for_each_entry_safe(temp_pin, pin_next, &hdmi->pin_list, head) { - for (i = 0; i < temp_pin->num_ports; i++) - temp_pin->ports[i].pin = NULL; - kfree(temp_pin->ports); - list_del(&temp_pin->head); - kfree(temp_pin); - } - + dev_err(&hdev->dev, "Failed to init DAI map with err: %d\n", + ret); return ret; } @@ -1782,7 +1766,7 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, * this is a new PCM device, create new pcm and * add to the pcm list */ - pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + pcm = devm_kzalloc(&hdev->dev, sizeof(*pcm), GFP_KERNEL); if (!pcm) return -ENOMEM; pcm->pcm_id = device; @@ -1798,7 +1782,6 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, dev_err(&hdev->dev, "chmap control add failed with err: %d for pcm: %d\n", err, device); - kfree(pcm); return err; } } @@ -2075,42 +2058,8 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) static int hdac_hdmi_dev_remove(struct hdac_device *hdev) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); - 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, *port_next; - int i; - snd_hdac_display_power(hdev->bus, hdev->addr, false); - list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) { - pcm->cvt = NULL; - if (list_empty(&pcm->port_list)) - continue; - - list_for_each_entry_safe(port, port_next, - &pcm->port_list, head) - list_del(&port->head); - - list_del(&pcm->head); - kfree(pcm); - } - - list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) { - list_del(&cvt->head); - kfree(cvt->name); - kfree(cvt); - } - - list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) { - for (i = 0; i < pin->num_ports; i++) - pin->ports[i].pin = NULL; - kfree(pin->ports); - list_del(&pin->head); - kfree(pin); - } - return 0; } diff --git a/sound/soc/codecs/jz4725b.c b/sound/soc/codecs/jz4725b.c index 103ccbc5d55c..766354c73076 100644 --- a/sound/soc/codecs/jz4725b.c +++ b/sound/soc/codecs/jz4725b.c @@ -173,12 +173,12 @@ static const char * const jz4725b_codec_adc_src_texts[] = { "Mic 1", "Mic 2", "Line In", "Mixer", }; static const unsigned int jz4725b_codec_adc_src_values[] = { 0, 1, 2, 3, }; -static const SOC_VALUE_ENUM_SINGLE_DECL(jz4725b_codec_adc_src_enum, - JZ4725B_CODEC_REG_CR3, - REG_CR3_INSEL_OFFSET, - REG_CR3_INSEL_MASK, - jz4725b_codec_adc_src_texts, - jz4725b_codec_adc_src_values); +static SOC_VALUE_ENUM_SINGLE_DECL(jz4725b_codec_adc_src_enum, + JZ4725B_CODEC_REG_CR3, + REG_CR3_INSEL_OFFSET, + REG_CR3_INSEL_MASK, + jz4725b_codec_adc_src_texts, + jz4725b_codec_adc_src_values); static const struct snd_kcontrol_new jz4725b_codec_adc_src_ctrl = SOC_DAPM_ENUM("Route", jz4725b_codec_adc_src_enum); diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index de3d44e9199b..8be636fe6552 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -615,7 +615,8 @@ static int max9860_probe(struct i2c_client *i2c) max9860->dvddio_nb.notifier_call = max9860_dvddio_event; - ret = regulator_register_notifier(max9860->dvddio, &max9860->dvddio_nb); + ret = devm_regulator_register_notifier(max9860->dvddio, + &max9860->dvddio_nb); if (ret) dev_err(dev, "Failed to register DVDDIO notifier: %d\n", ret); diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index b7cf7cce95fe..368b6c09474b 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016, The Linux Foundation. All rights reserved. + #include <linux/module.h> #include <linux/err.h> #include <linux/kernel.h> diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c index 423bfebabed4..a63961861e55 100644 --- a/sound/soc/codecs/msm8916-wcd-digital.c +++ b/sound/soc/codecs/msm8916-wcd-digital.c @@ -1,14 +1,5 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016, The Linux Foundation. All rights reserved. #include <linux/module.h> #include <linux/err.h> diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c index 809b7e9f03ca..c5fcc632f670 100644 --- a/sound/soc/codecs/pcm186x.c +++ b/sound/soc/codecs/pcm186x.c @@ -42,7 +42,7 @@ struct pcm186x_priv { bool is_master_mode; }; -static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 4000, 50); +static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 50, 0); static const struct snd_kcontrol_new pcm1863_snd_controls[] = { SOC_DOUBLE_R_S_TLV("ADC Capture Volume", PCM186X_PGA_VAL_CH1_L, @@ -158,7 +158,7 @@ static const struct snd_soc_dapm_widget pcm1863_dapm_widgets[] = { * Put the codec into SLEEP mode when not in use, allowing the * Energysense mechanism to operate. */ - SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1, 0), + SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1, 1), }; static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = { @@ -184,8 +184,8 @@ static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = { * Put the codec into SLEEP mode when not in use, allowing the * Energysense mechanism to operate. */ - SND_SOC_DAPM_ADC("ADC1", "HiFi Capture 1", PCM186X_POWER_CTRL, 1, 0), - SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1, 0), + SND_SOC_DAPM_ADC("ADC1", "HiFi Capture 1", PCM186X_POWER_CTRL, 1, 1), + SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1, 1), }; static const struct snd_soc_dapm_route pcm1863_dapm_routes[] = { diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c index 6714aa8d9026..32b26f1c2282 100644 --- a/sound/soc/codecs/pcm3060.c +++ b/sound/soc/codecs/pcm3060.c @@ -18,12 +18,39 @@ static int pcm3060_set_sysclk(struct snd_soc_dai *dai, int clk_id, { struct snd_soc_component *comp = dai->component; struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); + unsigned int reg; + unsigned int val; if (dir != SND_SOC_CLOCK_IN) { dev_err(comp->dev, "unsupported sysclock dir: %d\n", dir); return -EINVAL; } + switch (clk_id) { + case PCM3060_CLK_DEF: + val = 0; + break; + + case PCM3060_CLK1: + val = (dai->id == PCM3060_DAI_ID_DAC ? PCM3060_REG_CSEL : 0); + break; + + case PCM3060_CLK2: + val = (dai->id == PCM3060_DAI_ID_DAC ? 0 : PCM3060_REG_CSEL); + break; + + default: + dev_err(comp->dev, "unsupported sysclock id: %d\n", clk_id); + return -EINVAL; + } + + if (dai->id == PCM3060_DAI_ID_DAC) + reg = PCM3060_REG67; + else + reg = PCM3060_REG72; + + regmap_update_bits(priv->regmap, reg, PCM3060_REG_CSEL, val); + priv->dai[dai->id].sclk_freq = freq; return 0; @@ -287,6 +314,14 @@ int pcm3060_probe(struct device *dev) int rc; struct pcm3060_priv *priv = dev_get_drvdata(dev); + /* soft reset */ + rc = regmap_update_bits(priv->regmap, PCM3060_REG64, + PCM3060_REG_MRST, 0); + if (rc) { + dev_err(dev, "failed to reset component, rc=%d\n", rc); + return rc; + } + if (dev->of_node) pcm3060_parse_dt(dev->of_node, priv); diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h index 6a027b4a845d..75931c9a9d85 100644 --- a/sound/soc/codecs/pcm3060.h +++ b/sound/soc/codecs/pcm3060.h @@ -17,6 +17,11 @@ extern const struct regmap_config pcm3060_regmap; #define PCM3060_DAI_ID_ADC 1 #define PCM3060_DAI_IDS_NUM 2 +/* ADC and DAC can be clocked from separate or same sources CLK1 and CLK2 */ +#define PCM3060_CLK_DEF 0 /* default: CLK1->ADC, CLK2->DAC */ +#define PCM3060_CLK1 1 +#define PCM3060_CLK2 2 + struct pcm3060_priv_dai { bool is_master; unsigned int sclk_freq; diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index ae3bd533eadb..62d05b01711f 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -1540,8 +1540,9 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2; for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) { - ret = regulator_register_notifier(pcm512x->supplies[i].consumer, - &pcm512x->supply_nb[i]); + ret = devm_regulator_register_notifier( + pcm512x->supplies[i].consumer, + &pcm512x->supply_nb[i]); if (ret != 0) { dev_err(dev, "Failed to register regulator notifier: %d\n", diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index c6048d95c6d3..c544a1e35f5e 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1274,8 +1274,9 @@ static int aic31xx_codec_probe(struct snd_soc_component *component) aic31xx->disable_nb[i].nb.notifier_call = aic31xx_regulator_event; aic31xx->disable_nb[i].aic31xx = aic31xx; - ret = regulator_register_notifier(aic31xx->supplies[i].consumer, - &aic31xx->disable_nb[i].nb); + ret = devm_regulator_register_notifier( + aic31xx->supplies[i].consumer, + &aic31xx->disable_nb[i].nb); if (ret) { dev_err(component->dev, "Failed to request regulator notifier: %d\n", @@ -1298,19 +1299,8 @@ static int aic31xx_codec_probe(struct snd_soc_component *component) return 0; } -static void aic31xx_codec_remove(struct snd_soc_component *component) -{ - struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); - int i; - - for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) - regulator_unregister_notifier(aic31xx->supplies[i].consumer, - &aic31xx->disable_nb[i].nb); -} - static const struct snd_soc_component_driver soc_codec_driver_aic31xx = { .probe = aic31xx_codec_probe, - .remove = aic31xx_codec_remove, .set_bias_level = aic31xx_set_bias_level, .controls = common31xx_snd_controls, .num_controls = ARRAY_SIZE(common31xx_snd_controls), diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index e1bfba62fc08..96f1526cb258 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -970,6 +970,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) if (gpio_is_valid(aic32x4->rstn_gpio)) { ndelay(10); gpio_set_value(aic32x4->rstn_gpio, 1); + mdelay(1); } snd_soc_component_write(component, AIC32X4_RESET, 0x01); diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 6aa0edf8c5ef..283583d1db60 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1615,13 +1615,14 @@ static int aic3x_probe(struct snd_soc_component *component) for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) { aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event; aic3x->disable_nb[i].aic3x = aic3x; - ret = regulator_register_notifier(aic3x->supplies[i].consumer, - &aic3x->disable_nb[i].nb); + ret = devm_regulator_register_notifier( + aic3x->supplies[i].consumer, + &aic3x->disable_nb[i].nb); if (ret) { dev_err(component->dev, "Failed to request regulator notifier: %d\n", ret); - goto err_notif; + return ret; } } @@ -1679,29 +1680,11 @@ static int aic3x_probe(struct snd_soc_component *component) aic3x_add_widgets(component); return 0; - -err_notif: - while (i--) - regulator_unregister_notifier(aic3x->supplies[i].consumer, - &aic3x->disable_nb[i].nb); - return ret; -} - -static void aic3x_remove(struct snd_soc_component *component) -{ - struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); - int i; - - list_del(&aic3x->list); - for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) - regulator_unregister_notifier(aic3x->supplies[i].consumer, - &aic3x->disable_nb[i].nb); } static const struct snd_soc_component_driver soc_component_dev_aic3x = { .set_bias_level = aic3x_set_bias_level, .probe = aic3x_probe, - .remove = aic3x_remove, .controls = aic3x_snd_controls, .num_controls = ARRAY_SIZE(aic3x_snd_controls), .dapm_widgets = aic3x_dapm_widgets, diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 1fedf74da705..546ea735f534 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -196,7 +196,7 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component); - unsigned int iface; + unsigned int iface, mode; int i; /* The set of sample rates that can be supported depends on the @@ -240,11 +240,21 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* oversampling rate */ + if (params_rate(params) > 96000) + mode = 0x40; + else if (params_rate(params) > 48000) + mode = 0x20; + else + mode = 0x00; + dev_dbg(component->dev, "wm8741_hw_params: bit size param = %d, rate param = %d", params_width(params), params_rate(params)); snd_soc_component_update_bits(component, WM8741_FORMAT_CONTROL, WM8741_IWL_MASK, iface); + snd_soc_component_update_bits(component, WM8741_MODE_CONTROL_1, WM8741_OSR_MASK, + mode); return 0; } @@ -358,6 +368,15 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } +static int wm8741_mute(struct snd_soc_dai *codec_dai, int mute) +{ + struct snd_soc_component *component = codec_dai->component; + + snd_soc_component_update_bits(component, WM8741_VOLUME_CONTROL, + WM8741_SOFT_MASK, !!mute << WM8741_SOFT_SHIFT); + return 0; +} + #define WM8741_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ @@ -371,6 +390,7 @@ static const struct snd_soc_dai_ops wm8741_dai_ops = { .hw_params = wm8741_hw_params, .set_sysclk = wm8741_set_dai_sysclk, .set_fmt = wm8741_set_dai_fmt, + .digital_mute = wm8741_mute, }; static struct snd_soc_dai_driver wm8741_dai = { diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c index 806245c70f8b..37467c512597 100644 --- a/sound/soc/codecs/wm8770.c +++ b/sound/soc/codecs/wm8770.c @@ -666,8 +666,9 @@ static int wm8770_spi_probe(struct spi_device *spi) /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) { - ret = regulator_register_notifier(wm8770->supplies[i].consumer, - &wm8770->disable_nb[i]); + ret = devm_regulator_register_notifier( + wm8770->supplies[i].consumer, + &wm8770->disable_nb[i]); if (ret) { dev_err(&spi->dev, "Failed to register regulator notifier: %d\n", @@ -687,25 +688,12 @@ static int wm8770_spi_probe(struct spi_device *spi) return ret; } -static int wm8770_spi_remove(struct spi_device *spi) -{ - struct wm8770_priv *wm8770 = spi_get_drvdata(spi); - int i; - - for (i = 0; i < ARRAY_SIZE(wm8770->supplies); ++i) - regulator_unregister_notifier(wm8770->supplies[i].consumer, - &wm8770->disable_nb[i]); - - return 0; -} - static struct spi_driver wm8770_spi_driver = { .driver = { .name = "wm8770", .of_match_table = wm8770_of_match, }, .probe = wm8770_spi_probe, - .remove = wm8770_spi_remove }; module_spi_driver(wm8770_spi_driver); diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index efd8910b1ff7..467ed78dd2df 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3424,8 +3424,9 @@ static int wm8962_probe(struct snd_soc_component *component) /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) { - ret = regulator_register_notifier(wm8962->supplies[i].consumer, - &wm8962->disable_nb[i]); + ret = devm_regulator_register_notifier( + wm8962->supplies[i].consumer, + &wm8962->disable_nb[i]); if (ret != 0) { dev_err(component->dev, "Failed to register regulator notifier: %d\n", @@ -3467,15 +3468,11 @@ static int wm8962_probe(struct snd_soc_component *component) static void wm8962_remove(struct snd_soc_component *component) { struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); - int i; cancel_delayed_work_sync(&wm8962->mic_work); wm8962_free_gpio(component); wm8962_free_beep(component); - for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) - regulator_unregister_notifier(wm8962->supplies[i].consumer, - &wm8962->disable_nb[i]); } static const struct snd_soc_component_driver soc_component_dev_wm8962 = { diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 68c99fe37097..79ee91906bb9 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -1995,20 +1995,6 @@ static int wm8995_set_bias_level(struct snd_soc_component *component, return 0; } -static void wm8995_remove(struct snd_soc_component *component) -{ - struct wm8995_priv *wm8995; - int i; - - wm8995 = snd_soc_component_get_drvdata(component); - - for (i = 0; i < ARRAY_SIZE(wm8995->supplies); ++i) - regulator_unregister_notifier(wm8995->supplies[i].consumer, - &wm8995->disable_nb[i]); - - regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); -} - static int wm8995_probe(struct snd_soc_component *component) { struct wm8995_priv *wm8995; @@ -2021,8 +2007,9 @@ static int wm8995_probe(struct snd_soc_component *component) for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) wm8995->supplies[i].supply = wm8995_supply_names[i]; - ret = regulator_bulk_get(component->dev, ARRAY_SIZE(wm8995->supplies), - wm8995->supplies); + ret = devm_regulator_bulk_get(component->dev, + ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); if (ret) { dev_err(component->dev, "Failed to request supplies: %d\n", ret); return ret; @@ -2039,8 +2026,9 @@ static int wm8995_probe(struct snd_soc_component *component) /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) { - ret = regulator_register_notifier(wm8995->supplies[i].consumer, - &wm8995->disable_nb[i]); + ret = devm_regulator_register_notifier( + wm8995->supplies[i].consumer, + &wm8995->disable_nb[i]); if (ret) { dev_err(component->dev, "Failed to register regulator notifier: %d\n", @@ -2052,7 +2040,7 @@ static int wm8995_probe(struct snd_soc_component *component) wm8995->supplies); if (ret) { dev_err(component->dev, "Failed to enable supplies: %d\n", ret); - goto err_reg_get; + return ret; } ret = snd_soc_component_read32(component, WM8995_SOFTWARE_RESET); @@ -2099,8 +2087,6 @@ static int wm8995_probe(struct snd_soc_component *component) err_reg_enable: regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); -err_reg_get: - regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); return ret; } @@ -2188,7 +2174,6 @@ static struct snd_soc_dai_driver wm8995_dai[] = { static const struct snd_soc_component_driver soc_component_dev_wm8995 = { .probe = wm8995_probe, - .remove = wm8995_remove, .set_bias_level = wm8995_set_bias_level, .controls = wm8995_snd_controls, .num_controls = ARRAY_SIZE(wm8995_snd_controls), diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 91711f8958c5..ab04ea18c312 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2801,8 +2801,9 @@ static int wm8996_i2c_probe(struct i2c_client *i2c, /* This should really be moved into the regulator core */ for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) { - ret = regulator_register_notifier(wm8996->supplies[i].consumer, - &wm8996->disable_nb[i]); + ret = devm_regulator_register_notifier( + wm8996->supplies[i].consumer, + &wm8996->disable_nb[i]); if (ret != 0) { dev_err(&i2c->dev, "Failed to register regulator notifier: %d\n", @@ -3071,16 +3072,12 @@ err: static int wm8996_i2c_remove(struct i2c_client *client) { struct wm8996_priv *wm8996 = i2c_get_clientdata(client); - int i; wm8996_free_gpio(wm8996); if (wm8996->pdata.ldo_ena > 0) { gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); gpio_free(wm8996->pdata.ldo_ena); } - for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) - regulator_unregister_notifier(wm8996->supplies[i].consumer, - &wm8996->disable_nb[i]); return 0; } diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 1dd291cebe67..b93fdc8d2d6f 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -46,6 +46,13 @@ #define adsp_dbg(_dsp, fmt, ...) \ dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) +#define compr_err(_obj, fmt, ...) \ + adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ + ##__VA_ARGS__) +#define compr_dbg(_obj, fmt, ...) \ + adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ + ##__VA_ARGS__) + #define ADSP1_CONTROL_1 0x00 #define ADSP1_CONTROL_2 0x02 #define ADSP1_CONTROL_3 0x03 @@ -310,6 +317,12 @@ struct wm_adsp_alg_xm_struct { __be64 smoothed_power; }; +struct wm_adsp_host_buf_coeff_v1 { + __be32 host_buf_ptr; /* Host buffer pointer */ + __be32 versions; /* Version numbers */ + __be32 name[4]; /* The buffer name */ +}; + struct wm_adsp_buffer { __be32 buf1_base; /* Base addr of first buffer area */ __be32 buf1_size; /* Size of buf1 area in DSP words */ @@ -334,6 +347,7 @@ struct wm_adsp_buffer { struct wm_adsp_compr; struct wm_adsp_compr_buf { + struct list_head list; struct wm_adsp *dsp; struct wm_adsp_compr *compr; @@ -344,9 +358,13 @@ struct wm_adsp_compr_buf { u32 irq_count; int read_index; int avail; + int host_buf_mem_type; + + char *name; }; struct wm_adsp_compr { + struct list_head list; struct wm_adsp *dsp; struct wm_adsp_compr_buf *buf; @@ -357,6 +375,8 @@ struct wm_adsp_compr { unsigned int copied_total; unsigned int sample_rate; + + const char *name; }; #define WM_ADSP_DATA_WORD_SIZE 3 @@ -374,6 +394,11 @@ struct wm_adsp_compr { #define ALG_XM_FIELD(field) \ (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) +#define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 + +#define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 +#define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 + static int wm_adsp_buffer_init(struct wm_adsp *dsp); static int wm_adsp_buffer_free(struct wm_adsp *dsp); @@ -707,7 +732,7 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, mutex_lock(&dsp[e->shift_l].pwr_lock); - if (dsp[e->shift_l].booted || dsp[e->shift_l].compr) + if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list)) ret = -EBUSY; else dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; @@ -2429,6 +2454,8 @@ static int wm_adsp_common_init(struct wm_adsp *dsp) INIT_LIST_HEAD(&dsp->alg_regions); INIT_LIST_HEAD(&dsp->ctl_list); + INIT_LIST_HEAD(&dsp->compr_list); + INIT_LIST_HEAD(&dsp->buffer_list); mutex_init(&dsp->pwr_lock); @@ -2971,14 +2998,19 @@ static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) { - /* - * Note this will be more complex once each DSP can support multiple - * streams - */ - if (!compr->dsp->buffer) + struct wm_adsp_compr_buf *buf = NULL, *tmp; + + list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { + if (!tmp->name || !strcmp(compr->name, tmp->name)) { + buf = tmp; + break; + } + } + + if (!buf) return -EINVAL; - compr->buf = compr->dsp->buffer; + compr->buf = buf; compr->buf->compr = compr; return 0; @@ -3001,28 +3033,33 @@ static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) { - struct wm_adsp_compr *compr; + struct wm_adsp_compr *compr, *tmp; + struct snd_soc_pcm_runtime *rtd = stream->private_data; int ret = 0; mutex_lock(&dsp->pwr_lock); if (wm_adsp_fw[dsp->fw].num_caps == 0) { - adsp_err(dsp, "Firmware does not support compressed API\n"); + adsp_err(dsp, "%s: Firmware does not support compressed API\n", + rtd->codec_dai->name); ret = -ENXIO; goto out; } if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { - adsp_err(dsp, "Firmware does not support stream direction\n"); + adsp_err(dsp, "%s: Firmware does not support stream direction\n", + rtd->codec_dai->name); ret = -EINVAL; goto out; } - if (dsp->compr) { - /* It is expect this limitation will be removed in future */ - adsp_err(dsp, "Only a single stream supported per DSP\n"); - ret = -EBUSY; - goto out; + list_for_each_entry(tmp, &dsp->compr_list, list) { + if (!strcmp(tmp->name, rtd->codec_dai->name)) { + adsp_err(dsp, "%s: Only a single stream supported per dai\n", + rtd->codec_dai->name); + ret = -EBUSY; + goto out; + } } compr = kzalloc(sizeof(*compr), GFP_KERNEL); @@ -3033,8 +3070,9 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) compr->dsp = dsp; compr->stream = stream; + compr->name = rtd->codec_dai->name; - dsp->compr = compr; + list_add_tail(&compr->list, &dsp->compr_list); stream->runtime->private_data = compr; @@ -3053,7 +3091,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream) mutex_lock(&dsp->pwr_lock); wm_adsp_compr_detach(compr); - dsp->compr = NULL; + list_del(&compr->list); kfree(compr->raw_buf); kfree(compr); @@ -3078,9 +3116,9 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { - adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n", - params->buffer.fragment_size, - params->buffer.fragments); + compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", + params->buffer.fragment_size, + params->buffer.fragments); return -EINVAL; } @@ -3108,9 +3146,9 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, return 0; } - adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", - params->codec.id, params->codec.ch_in, params->codec.ch_out, - params->codec.sample_rate, params->codec.format); + compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", + params->codec.id, params->codec.ch_in, params->codec.ch_out, + params->codec.sample_rate, params->codec.format); return -EINVAL; } @@ -3132,8 +3170,8 @@ int wm_adsp_compr_set_params(struct snd_compr_stream *stream, compr->size = params->buffer; - adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n", - compr->size.fragment_size, compr->size.fragments); + compr_dbg(compr, "fragment_size=%d fragments=%d\n", + compr->size.fragment_size, compr->size.fragments); size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); @@ -3219,24 +3257,106 @@ static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, unsigned int field_offset, u32 *data) { - return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM, + return wm_adsp_read_data_word(buf->dsp, buf->host_buf_mem_type, buf->host_buf_ptr + field_offset, data); } static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, unsigned int field_offset, u32 data) { - return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM, + return wm_adsp_write_data_word(buf->dsp, buf->host_buf_mem_type, buf->host_buf_ptr + field_offset, data); } -static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf) +static void wm_adsp_remove_padding(u32 *buf, int nwords, int data_word_size) +{ + u8 *pack_in = (u8 *)buf; + u8 *pack_out = (u8 *)buf; + int i, j; + + /* Remove the padding bytes from the data read from the DSP */ + for (i = 0; i < nwords; i++) { + for (j = 0; j < data_word_size; j++) + *pack_out++ = *pack_in++; + + pack_in += sizeof(*buf) - data_word_size; + } +} + +static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) +{ + const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; + struct wm_adsp_buffer_region *region; + u32 offset = 0; + int i, ret; + + buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions), + GFP_KERNEL); + if (!buf->regions) + return -ENOMEM; + + for (i = 0; i < caps->num_regions; ++i) { + region = &buf->regions[i]; + + region->offset = offset; + region->mem_type = caps->region_defs[i].mem_type; + + ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, + ®ion->base_addr); + if (ret < 0) + return ret; + + ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, + &offset); + if (ret < 0) + return ret; + + region->cumulative_size = offset; + + compr_dbg(buf, + "region=%d type=%d base=%08x off=%08x size=%08x\n", + i, region->mem_type, region->base_addr, + region->offset, region->cumulative_size); + } + + return 0; +} + +static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) +{ + buf->irq_count = 0xFFFFFFFF; + buf->read_index = -1; + buf->avail = 0; +} + +static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) +{ + struct wm_adsp_compr_buf *buf; + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + + buf->dsp = dsp; + + wm_adsp_buffer_clear(buf); + + list_add_tail(&buf->list, &dsp->buffer_list); + + return buf; +} + +static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) { struct wm_adsp_alg_region *alg_region; - struct wm_adsp *dsp = buf->dsp; + struct wm_adsp_compr_buf *buf; u32 xmalg, addr, magic; int i, ret; + buf = wm_adsp_buffer_alloc(dsp); + if (!buf) + return -ENOMEM; + alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); @@ -3246,7 +3366,7 @@ static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf) return ret; if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) - return -EINVAL; + return -ENODEV; addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); for (i = 0; i < 5; ++i) { @@ -3264,48 +3384,30 @@ static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf) if (!buf->host_buf_ptr) return -EIO; - adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); - - return 0; -} - -static struct wm_coeff_ctl * -wm_adsp_find_host_buffer_ctrl(struct wm_adsp_compr_buf *buf) -{ - struct wm_adsp *dsp = buf->dsp; - struct wm_coeff_ctl *ctl; - - list_for_each_entry(ctl, &dsp->ctl_list, list) { - if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) - continue; + buf->host_buf_mem_type = WMFW_ADSP2_XM; - if (!ctl->enabled) - continue; + ret = wm_adsp_buffer_populate(buf); + if (ret < 0) + return ret; - return ctl; - } + compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); - return NULL; + return 0; } -static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) +static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) { - struct wm_adsp *dsp = buf->dsp; - struct wm_coeff_ctl *ctl; - unsigned int reg; - u32 val; - int i, ret; - - ctl = wm_adsp_find_host_buffer_ctrl(buf); - if (!ctl) - return wm_adsp_legacy_host_buf_addr(buf); + struct wm_adsp_host_buf_coeff_v1 coeff_v1; + struct wm_adsp_compr_buf *buf; + unsigned int val, reg; + int ret, i; ret = wm_coeff_base_reg(ctl, ®); if (ret) return ret; for (i = 0; i < 5; ++i) { - ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); + ret = regmap_raw_read(ctl->dsp->regmap, reg, &val, sizeof(val)); if (ret < 0) return ret; @@ -3315,108 +3417,130 @@ static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) usleep_range(1000, 2000); } - if (!val) + if (!val) { + adsp_err(ctl->dsp, "Failed to acquire host buffer\n"); return -EIO; + } + + buf = wm_adsp_buffer_alloc(ctl->dsp); + if (!buf) + return -ENOMEM; + buf->host_buf_mem_type = ctl->alg_region.type; buf->host_buf_ptr = be32_to_cpu(val); - adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); - return 0; -} + ret = wm_adsp_buffer_populate(buf); + if (ret < 0) + return ret; -static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) -{ - const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; - struct wm_adsp_buffer_region *region; - u32 offset = 0; - int i, ret; + /* + * v0 host_buffer coefficients didn't have versioning, so if the + * control is one word, assume version 0. + */ + if (ctl->len == 4) { + compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); + return 0; + } - for (i = 0; i < caps->num_regions; ++i) { - region = &buf->regions[i]; + ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1, + sizeof(coeff_v1)); + if (ret < 0) + return ret; - region->offset = offset; - region->mem_type = caps->region_defs[i].mem_type; + coeff_v1.versions = be32_to_cpu(coeff_v1.versions); + val = coeff_v1.versions & HOST_BUF_COEFF_COMPAT_VER_MASK; + val >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; - ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, - ®ion->base_addr); - if (ret < 0) - return ret; + if (val > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { + adsp_err(ctl->dsp, + "Host buffer coeff ver %u > supported version %u\n", + val, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); + return -EINVAL; + } - ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, - &offset); - if (ret < 0) - return ret; + for (i = 0; i < ARRAY_SIZE(coeff_v1.name); i++) + coeff_v1.name[i] = be32_to_cpu(coeff_v1.name[i]); - region->cumulative_size = offset; + wm_adsp_remove_padding((u32 *)&coeff_v1.name, + ARRAY_SIZE(coeff_v1.name), + WM_ADSP_DATA_WORD_SIZE); - adsp_dbg(buf->dsp, - "region=%d type=%d base=%08x off=%08x size=%08x\n", - i, region->mem_type, region->base_addr, - region->offset, region->cumulative_size); - } + buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part, + (char *)&coeff_v1.name); - return 0; -} + compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", + buf->host_buf_ptr, val); -static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) -{ - buf->irq_count = 0xFFFFFFFF; - buf->read_index = -1; - buf->avail = 0; + return val; } static int wm_adsp_buffer_init(struct wm_adsp *dsp) { - struct wm_adsp_compr_buf *buf; + struct wm_coeff_ctl *ctl; int ret; - buf = kzalloc(sizeof(*buf), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - buf->dsp = dsp; - - wm_adsp_buffer_clear(buf); + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) + continue; - ret = wm_adsp_buffer_locate(buf); - if (ret < 0) { - adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret); - goto err_buffer; - } + if (!ctl->enabled) + continue; - buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions, - sizeof(*buf->regions), GFP_KERNEL); - if (!buf->regions) { - ret = -ENOMEM; - goto err_buffer; + ret = wm_adsp_buffer_parse_coeff(ctl); + if (ret < 0) { + adsp_err(dsp, "Failed to parse coeff: %d\n", ret); + goto error; + } else if (ret == 0) { + /* Only one buffer supported for version 0 */ + return 0; + } } - ret = wm_adsp_buffer_populate(buf); - if (ret < 0) { - adsp_err(dsp, "Failed to populate host buffer: %d\n", ret); - goto err_regions; + if (list_empty(&dsp->buffer_list)) { + /* Fall back to legacy support */ + ret = wm_adsp_buffer_parse_legacy(dsp); + if (ret) { + adsp_err(dsp, "Failed to parse legacy: %d\n", ret); + goto error; + } } - dsp->buffer = buf; - return 0; -err_regions: - kfree(buf->regions); -err_buffer: - kfree(buf); +error: + wm_adsp_buffer_free(dsp); return ret; } static int wm_adsp_buffer_free(struct wm_adsp *dsp) { - if (dsp->buffer) { - wm_adsp_compr_detach(dsp->buffer->compr); + struct wm_adsp_compr_buf *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { + if (buf->compr) + wm_adsp_compr_detach(buf->compr); + + kfree(buf->name); + kfree(buf->regions); + list_del(&buf->list); + kfree(buf); + } - kfree(dsp->buffer->regions); - kfree(dsp->buffer); + return 0; +} + +static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) +{ + int ret; - dsp->buffer = NULL; + ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); + if (ret < 0) { + adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret); + return ret; + } + if (buf->error != 0) { + adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error); + return -EIO; } return 0; @@ -3428,7 +3552,7 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) struct wm_adsp *dsp = compr->dsp; int ret = 0; - adsp_dbg(dsp, "Trigger: %d\n", cmd); + compr_dbg(compr, "Trigger: %d\n", cmd); mutex_lock(&dsp->pwr_lock); @@ -3437,12 +3561,16 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) if (!wm_adsp_compr_attached(compr)) { ret = wm_adsp_compr_attach(compr); if (ret < 0) { - adsp_err(dsp, "Failed to link buffer and stream: %d\n", - ret); + compr_err(compr, "Failed to link buffer and stream: %d\n", + ret); break; } } + ret = wm_adsp_buffer_get_error(compr->buf); + if (ret < 0) + break; + wm_adsp_buffer_clear(compr->buf); /* Trigger the IRQ at one fragment of data */ @@ -3450,8 +3578,8 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) HOST_BUFFER_FIELD(high_water_mark), wm_adsp_compr_frag_words(compr)); if (ret < 0) { - adsp_err(dsp, "Failed to set high water mark: %d\n", - ret); + compr_err(compr, "Failed to set high water mark: %d\n", + ret); break; } break; @@ -3492,7 +3620,7 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) read_index = sign_extend32(next_read_index, 23); if (read_index < 0) { - adsp_dbg(buf->dsp, "Avail check on unstarted stream\n"); + compr_dbg(buf, "Avail check on unstarted stream\n"); return 0; } @@ -3510,31 +3638,14 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) if (avail < 0) avail += wm_adsp_buffer_size(buf); - adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", - buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); + compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", + buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); buf->avail = avail; return 0; } -static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) -{ - int ret; - - ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); - if (ret < 0) { - adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret); - return ret; - } - if (buf->error != 0) { - adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error); - return -EIO; - } - - return 0; -} - int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) { struct wm_adsp_compr_buf *buf; @@ -3543,39 +3654,40 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) mutex_lock(&dsp->pwr_lock); - buf = dsp->buffer; - compr = dsp->compr; - - if (!buf) { + if (list_empty(&dsp->buffer_list)) { ret = -ENODEV; goto out; } adsp_dbg(dsp, "Handling buffer IRQ\n"); - ret = wm_adsp_buffer_get_error(buf); - if (ret < 0) - goto out_notify; /* Wake poll to report error */ + list_for_each_entry(buf, &dsp->buffer_list, list) { + compr = buf->compr; - ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), - &buf->irq_count); - if (ret < 0) { - adsp_err(dsp, "Failed to get irq_count: %d\n", ret); - goto out; - } + ret = wm_adsp_buffer_get_error(buf); + if (ret < 0) + goto out_notify; /* Wake poll to report error */ - ret = wm_adsp_buffer_update_avail(buf); - if (ret < 0) { - adsp_err(dsp, "Error reading avail: %d\n", ret); - goto out; - } + ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), + &buf->irq_count); + if (ret < 0) { + compr_err(buf, "Failed to get irq_count: %d\n", ret); + goto out; + } - if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) - ret = WM_ADSP_COMPR_VOICE_TRIGGER; + ret = wm_adsp_buffer_update_avail(buf); + if (ret < 0) { + compr_err(buf, "Error reading avail: %d\n", ret); + goto out; + } + + if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) + ret = WM_ADSP_COMPR_VOICE_TRIGGER; out_notify: - if (compr && compr->stream) - snd_compr_fragment_elapsed(compr->stream); + if (compr && compr->stream) + snd_compr_fragment_elapsed(compr->stream); + } out: mutex_unlock(&dsp->pwr_lock); @@ -3589,8 +3701,7 @@ static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) if (buf->irq_count & 0x01) return 0; - adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n", - buf->irq_count); + compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count); buf->irq_count |= 0x01; @@ -3606,7 +3717,7 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, struct wm_adsp_compr_buf *buf; int ret = 0; - adsp_dbg(dsp, "Pointer request\n"); + compr_dbg(compr, "Pointer request\n"); mutex_lock(&dsp->pwr_lock); @@ -3621,7 +3732,7 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, if (buf->avail < wm_adsp_compr_frag_words(compr)) { ret = wm_adsp_buffer_update_avail(buf); if (ret < 0) { - adsp_err(dsp, "Error reading avail: %d\n", ret); + compr_err(compr, "Error reading avail: %d\n", ret); goto out; } @@ -3640,9 +3751,8 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, ret = wm_adsp_buffer_reenable_irq(buf); if (ret < 0) { - adsp_err(dsp, - "Failed to re-enable buffer IRQ: %d\n", - ret); + compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", + ret); goto out; } } @@ -3662,11 +3772,9 @@ EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) { struct wm_adsp_compr_buf *buf = compr->buf; - u8 *pack_in = (u8 *)compr->raw_buf; - u8 *pack_out = (u8 *)compr->raw_buf; unsigned int adsp_addr; int mem_type, nwords, max_read; - int i, j, ret; + int i, ret; /* Calculate read parameters */ for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) @@ -3698,13 +3806,7 @@ static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) if (ret < 0) return ret; - /* Remove the padding bytes from the data read from the DSP */ - for (i = 0; i < nwords; i++) { - for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++) - *pack_out++ = *pack_in++; - - pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE; - } + wm_adsp_remove_padding(compr->raw_buf, nwords, WM_ADSP_DATA_WORD_SIZE); /* update read index to account for words read */ buf->read_index += nwords; @@ -3725,11 +3827,10 @@ static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) static int wm_adsp_compr_read(struct wm_adsp_compr *compr, char __user *buf, size_t count) { - struct wm_adsp *dsp = compr->dsp; int ntotal = 0; int nwords, nbytes; - adsp_dbg(dsp, "Requested read of %zu bytes\n", count); + compr_dbg(compr, "Requested read of %zu bytes\n", count); if (!compr->buf || compr->buf->error) { snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); @@ -3741,17 +3842,18 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr, do { nwords = wm_adsp_buffer_capture_block(compr, count); if (nwords < 0) { - adsp_err(dsp, "Failed to capture block: %d\n", nwords); + compr_err(compr, "Failed to capture block: %d\n", + nwords); return nwords; } nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; - adsp_dbg(dsp, "Read %d bytes\n", nbytes); + compr_dbg(compr, "Read %d bytes\n", nbytes); if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { - adsp_err(dsp, "Failed to copy data to user: %d, %d\n", - ntotal, nbytes); + compr_err(compr, "Failed to copy data to user: %d, %d\n", + ntotal, nbytes); return -EFAULT; } diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 4b8778b0b06c..59e07ad16329 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -90,8 +90,8 @@ struct wm_adsp { struct work_struct boot_work; - struct wm_adsp_compr *compr; - struct wm_adsp_compr_buf *buffer; + struct list_head compr_list; + struct list_head buffer_list; struct mutex pwr_lock; |