diff options
Diffstat (limited to 'sound/soc/codecs')
82 files changed, 7690 insertions, 343 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 34c6dd04b85a..5e4e68112791 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -23,6 +23,8 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AD193X_I2C imply SND_SOC_AD1980 imply SND_SOC_AD73311 + imply SND_SOC_ADAU1372_I2C + imply SND_SOC_ADAU1372_SPI imply SND_SOC_ADAU1373 imply SND_SOC_ADAU1761_I2C imply SND_SOC_ADAU1761_SPI @@ -130,6 +132,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_MT6358 imply SND_SOC_MT6359 imply SND_SOC_MT6660 + imply SND_SOC_NAU8315 imply SND_SOC_NAU8540 imply SND_SOC_NAU8810 imply SND_SOC_NAU8822 @@ -177,10 +180,12 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT700_SDW imply SND_SOC_RT711_SDW imply SND_SOC_RT715_SDW + imply SND_SOC_RT715_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_SGTL5000 imply SND_SOC_SI476X imply SND_SOC_SIMPLE_AMPLIFIER + imply SND_SOC_SIMPLE_MUX imply SND_SOC_SIRF_AUDIO_CODEC imply SND_SOC_SPDIF imply SND_SOC_SSM2305 @@ -363,6 +368,22 @@ config SND_SOC_AD73311 config SND_SOC_ADAU_UTILS tristate +config SND_SOC_ADAU1372 + tristate + select SND_SOC_ADAU_UTILS + +config SND_SOC_ADAU1372_I2C + tristate "Analog Devices ADAU1372 CODEC (I2C)" + depends on I2C + select SND_SOC_ADAU1372 + select REGMAP_I2C + +config SND_SOC_ADAU1372_SPI + tristate "Analog Devices ADAU1372 CODEC (SPI)" + depends on SPI + select SND_SOC_ADAU1372 + select REGMAP_SPI + config SND_SOC_ADAU1373 tristate depends on I2C @@ -517,7 +538,7 @@ config SND_SOC_AK5558 select REGMAP_I2C config SND_SOC_ALC5623 - tristate "Realtek ALC5623 CODEC" + tristate "Realtek ALC5623 CODEC" depends on I2C config SND_SOC_ALC5632 @@ -728,7 +749,7 @@ config SND_SOC_JZ4770_CODEC will be called snd-soc-jz4770-codec. config SND_SOC_L3 - tristate + tristate config SND_SOC_DA7210 tristate @@ -768,10 +789,10 @@ config SND_SOC_HDMI_CODEC select HDMI config SND_SOC_ES7134 - tristate "Everest Semi ES7134 CODEC" + tristate "Everest Semi ES7134 CODEC" config SND_SOC_ES7241 - tristate "Everest Semi ES7241 CODEC" + tristate "Everest Semi ES7241 CODEC" config SND_SOC_ES8316 tristate "Everest Semi ES8316 CODEC" @@ -970,10 +991,10 @@ config SND_SOC_PCM186X_SPI select REGMAP_SPI config SND_SOC_PCM3008 - tristate + tristate config SND_SOC_PCM3060 - tristate + tristate config SND_SOC_PCM3060_I2C tristate "Texas Instruments PCM3060 CODEC - I2C" @@ -1003,7 +1024,7 @@ config SND_SOC_PCM3168A_SPI select REGMAP_SPI config SND_SOC_PCM5102A - tristate + tristate "Texas Instruments PCM5102A CODEC" config SND_SOC_PCM512x tristate @@ -1216,6 +1237,12 @@ config SND_SOC_RT715_SDW select SND_SOC_RT715 select REGMAP_SOUNDWIRE +config SND_SOC_RT715_SDCA_SDW + tristate "Realtek RT715 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" @@ -1240,6 +1267,10 @@ config SND_SOC_SIMPLE_AMPLIFIER tristate "Simple Audio Amplifier" select GPIOLIB +config SND_SOC_SIMPLE_MUX + tristate "Simple Audio Mux" + select GPIOLIB + config SND_SOC_SIRF_AUDIO_CODEC tristate "SiRF SoC internal audio codec" select REGMAP_MMIO @@ -1436,7 +1467,7 @@ config SND_SOC_UDA1334 rate) and mute. config SND_SOC_UDA134X - tristate + tristate config SND_SOC_UDA1380 tristate @@ -1760,9 +1791,13 @@ config SND_SOC_MT6660 Select N if you don't have MT6660 on board. Select M to build this as module. +config SND_SOC_NAU8315 + tristate "Nuvoton Technology Corporation NAU8315 CODEC" + depends on GPIOLIB + config SND_SOC_NAU8540 - tristate "Nuvoton Technology Corporation NAU85L40 CODEC" - depends on I2C + tristate "Nuvoton Technology Corporation NAU85L40 CODEC" + depends on I2C config SND_SOC_NAU8810 tristate "Nuvoton Technology Corporation NAU88C10 CODEC" @@ -1784,4 +1819,12 @@ config SND_SOC_TPA6130A2 tristate "Texas Instruments TPA6130A2 headphone amplifier" depends on I2C +config SND_SOC_LPASS_WSA_MACRO + depends on COMMON_CLK + tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" + +config SND_SOC_LPASS_VA_MACRO + depends on COMMON_CLK + tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)" + endmenu diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 11ce98c25d6c..f255ec74333c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -9,6 +9,9 @@ snd-soc-ad193x-i2c-objs := ad193x-i2c.o snd-soc-ad1980-objs := ad1980.o snd-soc-ad73311-objs := ad73311.o snd-soc-adau-utils-objs := adau-utils.o +snd-soc-adau1372-objs := adau1372.o +snd-soc-adau1372-i2c-objs := adau1372-i2c.o +snd-soc-adau1372-spi-objs := adau1372-spi.o snd-soc-adau1373-objs := adau1373.o snd-soc-adau1701-objs := adau1701.o snd-soc-adau17x1-objs := adau17x1.o @@ -103,6 +106,8 @@ snd-soc-l3-objs := l3.o snd-soc-lm4857-objs := lm4857.o snd-soc-lm49453-objs := lm49453.o snd-soc-lochnagar-sc-objs := lochnagar-sc.o +snd-soc-lpass-wsa-macro-objs := lpass-wsa-macro.o +snd-soc-lpass-va-macro-objs := lpass-va-macro.o snd-soc-madera-objs := madera.o snd-soc-max9759-objs := max9759.o snd-soc-max9768-objs := max9768.o @@ -129,6 +134,7 @@ snd-soc-mt6351-objs := mt6351.o snd-soc-mt6358-objs := mt6358.o snd-soc-mt6359-objs := mt6359.o snd-soc-mt6660-objs := mt6660.o +snd-soc-nau8315-objs := nau8315.o snd-soc-nau8540-objs := nau8540.o snd-soc-nau8810-objs := nau8810.o snd-soc-nau8822-objs := nau8822.o @@ -188,6 +194,7 @@ snd-soc-rt5682-i2c-objs := rt5682-i2c.o snd-soc-rt700-objs := rt700.o rt700-sdw.o snd-soc-rt711-objs := rt711.o rt711-sdw.o snd-soc-rt715-objs := rt715.o rt715-sdw.o +snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -305,6 +312,8 @@ snd-soc-tpa6130a2-objs := tpa6130a2.o snd-soc-tas2552-objs := tas2552.o snd-soc-tas2562-objs := tas2562.o snd-soc-tas2764-objs := tas2764.o +# Mux +snd-soc-simple-mux-objs := simple-mux.o obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o @@ -316,6 +325,9 @@ obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o obj-$(CONFIG_SND_SOC_ADAU_UTILS) += snd-soc-adau-utils.o +obj-$(CONFIG_SND_SOC_ADAU1372) += snd-soc-adau1372.o +obj-$(CONFIG_SND_SOC_ADAU1372_I2C) += snd-soc-adau1372-i2c.o +obj-$(CONFIG_SND_SOC_ADAU1372_SPI) += snd-soc-adau1372-spi.o obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o @@ -438,6 +450,7 @@ obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o obj-$(CONFIG_SND_SOC_MT6359) += snd-soc-mt6359.o obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o +obj-$(CONFIG_SND_SOC_NAU8315) += snd-soc-nau8315.o obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o @@ -498,6 +511,7 @@ obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o +obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o @@ -613,3 +627,8 @@ obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER) += snd-soc-simple-amplifier.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o +obj-$(CONFIG_SND_SOC_LPASS_WSA_MACRO) += snd-soc-lpass-wsa-macro.o +obj-$(CONFIG_SND_SOC_LPASS_VA_MACRO) += snd-soc-lpass-va-macro.o + +# Mux +obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c new file mode 100644 index 000000000000..fc87a76ff1ee --- /dev/null +++ b/sound/soc/codecs/adau1372-i2c.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ADAU1372 codec + * + * Copyright 2016 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + */ + +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/soc.h> + +#include "adau1372.h" + +static int adau1372_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + return adau1372_probe(&client->dev, + devm_regmap_init_i2c(client, &adau1372_regmap_config), NULL); +} + +static const struct i2c_device_id adau1372_i2c_ids[] = { + { "adau1372", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1372_i2c_ids); + +static struct i2c_driver adau1372_i2c_driver = { + .driver = { + .name = "adau1372", + }, + .probe = adau1372_i2c_probe, + .id_table = adau1372_i2c_ids, +}; +module_i2c_driver(adau1372_i2c_driver); + +MODULE_DESCRIPTION("ASoC ADAU1372 CODEC I2C driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/adau1372-spi.c b/sound/soc/codecs/adau1372-spi.c new file mode 100644 index 000000000000..51298e00fbd6 --- /dev/null +++ b/sound/soc/codecs/adau1372-spi.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ADAU1372 codec + * + * Copyright 2016 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <sound/soc.h> + +#include "adau1372.h" + +static void adau1372_spi_switch_mode(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + /* + * To get the device into SPI mode CLATCH has to be pulled low three + * times. Do this by issuing three dummy reads. + */ + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); +} + +static int adau1372_spi_probe(struct spi_device *spi) +{ + struct regmap_config config; + + config = adau1372_regmap_config; + config.read_flag_mask = 0x1; + + return adau1372_probe(&spi->dev, + devm_regmap_init_spi(spi, &config), adau1372_spi_switch_mode); +} + +static const struct spi_device_id adau1372_spi_id[] = { + { "adau1372", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adau1372_spi_id); + +static struct spi_driver adau1372_spi_driver = { + .driver = { + .name = "adau1372", + }, + .probe = adau1372_spi_probe, + .id_table = adau1372_spi_id, +}; +module_spi_driver(adau1372_spi_driver); + +MODULE_DESCRIPTION("ASoC ADAU1372 CODEC SPI driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c new file mode 100644 index 000000000000..5ccbf1b6bcf5 --- /dev/null +++ b/sound/soc/codecs/adau1372.c @@ -0,0 +1,1062 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices ADAU1372 Audio Codec driver + * + * Copyright 2016 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gcd.h> +#include <linux/gpio/consumer.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/slab.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include <sound/soc.h> + +#include "adau1372.h" +#include "adau-utils.h" + +struct adau1372 { + struct clk *clk; + struct regmap *regmap; + void (*switch_mode)(struct device *dev); + bool use_pll; + bool enabled; + bool master; + + struct snd_pcm_hw_constraint_list rate_constraints; + unsigned int slot_width; + + struct clk *mclk; + struct gpio_desc *pd_gpio; + struct device *dev; +}; + +#define ADAU1372_REG_CLK_CTRL 0x00 +#define ADAU1372_REG_PLL(x) (0x01 + (x)) +#define ADAU1372_REG_DAC_SOURCE 0x11 +#define ADAU1372_REG_SOUT_SOURCE_0_1 0x13 +#define ADAU1372_REG_SOUT_SOURCE_2_3 0x14 +#define ADAU1372_REG_SOUT_SOURCE_4_5 0x15 +#define ADAU1372_REG_SOUT_SOURCE_6_7 0x16 +#define ADAU1372_REG_ADC_SDATA_CH 0x17 +#define ADAU1372_REG_ASRCO_SOURCE_0_1 0x18 +#define ADAU1372_REG_ASRCO_SOURCE_2_3 0x19 +#define ADAU1372_REG_ASRC_MODE 0x1a +#define ADAU1372_REG_ADC_CTRL0 0x1b +#define ADAU1372_REG_ADC_CTRL1 0x1c +#define ADAU1372_REG_ADC_CTRL2 0x1d +#define ADAU1372_REG_ADC_CTRL3 0x1e +#define ADAU1372_REG_ADC_VOL(x) (0x1f + (x)) +#define ADAU1372_REG_PGA_CTRL(x) (0x23 + (x)) +#define ADAU1372_REG_PGA_BOOST 0x28 +#define ADAU1372_REG_MICBIAS 0x2d +#define ADAU1372_REG_DAC_CTRL 0x2e +#define ADAU1372_REG_DAC_VOL(x) (0x2f + (x)) +#define ADAU1372_REG_OP_STAGE_MUTE 0x31 +#define ADAU1372_REG_SAI0 0x32 +#define ADAU1372_REG_SAI1 0x33 +#define ADAU1372_REG_SOUT_CTRL 0x34 +#define ADAU1372_REG_MODE_MP(x) (0x38 + (x)) +#define ADAU1372_REG_OP_STAGE_CTRL 0x43 +#define ADAU1372_REG_DECIM_PWR 0x44 +#define ADAU1372_REG_INTERP_PWR 0x45 +#define ADAU1372_REG_BIAS_CTRL0 0x46 +#define ADAU1372_REG_BIAS_CTRL1 0x47 + +#define ADAU1372_CLK_CTRL_PLL_EN BIT(7) +#define ADAU1372_CLK_CTRL_XTAL_DIS BIT(4) +#define ADAU1372_CLK_CTRL_CLKSRC BIT(3) +#define ADAU1372_CLK_CTRL_CC_MDIV BIT(1) +#define ADAU1372_CLK_CTRL_MCLK_EN BIT(0) + +#define ADAU1372_SAI0_DELAY1 (0x0 << 6) +#define ADAU1372_SAI0_DELAY0 (0x1 << 6) +#define ADAU1372_SAI0_DELAY_MASK (0x3 << 6) +#define ADAU1372_SAI0_SAI_I2S (0x0 << 4) +#define ADAU1372_SAI0_SAI_TDM2 (0x1 << 4) +#define ADAU1372_SAI0_SAI_TDM4 (0x2 << 4) +#define ADAU1372_SAI0_SAI_TDM8 (0x3 << 4) +#define ADAU1372_SAI0_SAI_MASK (0x3 << 4) +#define ADAU1372_SAI0_FS_48 0x0 +#define ADAU1372_SAI0_FS_8 0x1 +#define ADAU1372_SAI0_FS_12 0x2 +#define ADAU1372_SAI0_FS_16 0x3 +#define ADAU1372_SAI0_FS_24 0x4 +#define ADAU1372_SAI0_FS_32 0x5 +#define ADAU1372_SAI0_FS_96 0x6 +#define ADAU1372_SAI0_FS_192 0x7 +#define ADAU1372_SAI0_FS_MASK 0xf + +#define ADAU1372_SAI1_TDM_TS BIT(7) +#define ADAU1372_SAI1_BCLK_TDMC BIT(6) +#define ADAU1372_SAI1_LR_MODE BIT(5) +#define ADAU1372_SAI1_LR_POL BIT(4) +#define ADAU1372_SAI1_BCLKRATE BIT(2) +#define ADAU1372_SAI1_BCLKEDGE BIT(1) +#define ADAU1372_SAI1_MS BIT(0) + +static const unsigned int adau1372_rates[] = { + [ADAU1372_SAI0_FS_8] = 8000, + [ADAU1372_SAI0_FS_12] = 12000, + [ADAU1372_SAI0_FS_16] = 16000, + [ADAU1372_SAI0_FS_24] = 24000, + [ADAU1372_SAI0_FS_32] = 32000, + [ADAU1372_SAI0_FS_48] = 48000, + [ADAU1372_SAI0_FS_96] = 96000, + [ADAU1372_SAI0_FS_192] = 192000, +}; + +/* 8k, 12k, 24k, 48k */ +#define ADAU1372_RATE_MASK_TDM8 0x17 +/* + 16k, 96k */ +#define ADAU1372_RATE_MASK_TDM4_MASTER (ADAU1372_RATE_MASK_TDM8 | 0x48 | 0x20) +/* +32k */ +#define ADAU1372_RATE_MASK_TDM4 (ADAU1372_RATE_MASK_TDM4_MASTER | 0x20) +/* + 192k */ +#define ADAU1372_RATE_MASK_TDM2 (ADAU1372_RATE_MASK_TDM4 | 0x80) + +static const DECLARE_TLV_DB_MINMAX(adau1372_digital_tlv, -9563, 0); +static const DECLARE_TLV_DB_SCALE(adau1372_pga_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(adau1372_pga_boost_tlv, 0, 1000, 0); + +static const char * const adau1372_bias_text[] = { + "Normal operation", "Extreme power saving", "Enhanced performance", + "Power saving", +}; + +static const unsigned int adau1372_bias_adc_values[] = { + 0, 2, 3, +}; + +static const char * const adau1372_bias_adc_text[] = { + "Normal operation", "Enhanced performance", "Power saving", +}; + +static const char * const adau1372_bias_dac_text[] = { + "Normal operation", "Power saving", "Superior performance", + "Enhanced performance", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_bias_hp_enum, + ADAU1372_REG_BIAS_CTRL0, 6, adau1372_bias_text); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe0_1_enum, + ADAU1372_REG_BIAS_CTRL0, 4, adau1372_bias_text); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc2_3_enum, + ADAU1372_REG_BIAS_CTRL0, 2, 0x3, adau1372_bias_adc_text, + adau1372_bias_adc_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc0_1_enum, + ADAU1372_REG_BIAS_CTRL0, 0, 0x3, adau1372_bias_adc_text, + adau1372_bias_adc_values); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe2_3_enum, + ADAU1372_REG_BIAS_CTRL1, 4, adau1372_bias_text); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_mic_enum, + ADAU1372_REG_BIAS_CTRL1, 2, adau1372_bias_text); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_dac_enum, + ADAU1372_REG_BIAS_CTRL1, 0, adau1372_bias_dac_text); + +static const char * const adau1372_hpf_text[] = { + "Off", + "1 Hz", + "4 Hz", + "8 Hz", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_hpf0_1_enum, ADAU1372_REG_ADC_CTRL2, 5, + adau1372_hpf_text); +static SOC_ENUM_SINGLE_DECL(adau1372_hpf2_3_enum, ADAU1372_REG_ADC_CTRL3, 5, + adau1372_hpf_text); +static const struct snd_kcontrol_new adau1372_controls[] = { + SOC_SINGLE_TLV("ADC 0 Capture Volume", ADAU1372_REG_ADC_VOL(0), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("ADC 1 Capture Volume", ADAU1372_REG_ADC_VOL(1), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("ADC 2 Capture Volume", ADAU1372_REG_ADC_VOL(2), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("ADC 3 Capture Volume", ADAU1372_REG_ADC_VOL(3), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE("ADC 0 Capture Switch", ADAU1372_REG_ADC_CTRL0, 3, 1, 1), + SOC_SINGLE("ADC 1 Capture Switch", ADAU1372_REG_ADC_CTRL0, 4, 1, 1), + SOC_SINGLE("ADC 2 Capture Switch", ADAU1372_REG_ADC_CTRL1, 3, 1, 1), + SOC_SINGLE("ADC 3 Capture Switch", ADAU1372_REG_ADC_CTRL1, 4, 1, 1), + + SOC_ENUM("ADC 0+1 High-Pass-Filter", adau1372_hpf0_1_enum), + SOC_ENUM("ADC 2+3 High-Pass-Filter", adau1372_hpf2_3_enum), + + SOC_SINGLE_TLV("PGA 0 Capture Volume", ADAU1372_REG_PGA_CTRL(0), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 1 Capture Volume", ADAU1372_REG_PGA_CTRL(1), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 2 Capture Volume", ADAU1372_REG_PGA_CTRL(2), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 3 Capture Volume", ADAU1372_REG_PGA_CTRL(3), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 0 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 0, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE_TLV("PGA 1 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 1, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE_TLV("PGA 2 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 2, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE_TLV("PGA 3 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 3, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE("PGA 0 Capture Switch", ADAU1372_REG_PGA_CTRL(0), 7, 1, 1), + SOC_SINGLE("PGA 1 Capture Switch", ADAU1372_REG_PGA_CTRL(1), 7, 1, 1), + SOC_SINGLE("PGA 2 Capture Switch", ADAU1372_REG_PGA_CTRL(2), 7, 1, 1), + SOC_SINGLE("PGA 3 Capture Switch", ADAU1372_REG_PGA_CTRL(3), 7, 1, 1), + + SOC_SINGLE_TLV("DAC 0 Playback Volume", ADAU1372_REG_DAC_VOL(0), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("DAC 1 Playback Volume", ADAU1372_REG_DAC_VOL(1), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE("DAC 0 Playback Switch", ADAU1372_REG_DAC_CTRL, 3, 1, 1), + SOC_SINGLE("DAC 1 Playback Switch", ADAU1372_REG_DAC_CTRL, 4, 1, 1), + + SOC_ENUM("Headphone Bias", adau1372_bias_hp_enum), + SOC_ENUM("Microphone Bias", adau1372_bias_mic_enum), + SOC_ENUM("AFE 0+1 Bias", adau1372_bias_afe0_1_enum), + SOC_ENUM("AFE 2+3 Bias", adau1372_bias_afe2_3_enum), + SOC_ENUM("ADC 0+1 Bias", adau1372_bias_adc0_1_enum), + SOC_ENUM("ADC 2+3 Bias", adau1372_bias_adc2_3_enum), + SOC_ENUM("DAC 0+1 Bias", adau1372_bias_dac_enum), +}; + +static const char * const adau1372_decimator_mux_text[] = { + "ADC", + "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_decimator0_1_mux_enum, ADAU1372_REG_ADC_CTRL2, + 2, adau1372_decimator_mux_text); + +static const struct snd_kcontrol_new adau1372_decimator0_1_mux_control = + SOC_DAPM_ENUM("Decimator 0+1 Capture Mux", adau1372_decimator0_1_mux_enum); + +static SOC_ENUM_SINGLE_DECL(adau1372_decimator2_3_mux_enum, ADAU1372_REG_ADC_CTRL3, + 2, adau1372_decimator_mux_text); + +static const struct snd_kcontrol_new adau1372_decimator2_3_mux_control = + SOC_DAPM_ENUM("Decimator 2+3 Capture Mux", adau1372_decimator2_3_mux_enum); + +static const unsigned int adau1372_asrco_mux_values[] = { + 4, 5, 6, 7, +}; + +static const char * const adau1372_asrco_mux_text[] = { + "Decimator0", + "Decimator1", + "Decimator2", + "Decimator3", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco0_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1, + 0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco1_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1, + 4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco2_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3, + 0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco3_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3, + 4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); + +static const struct snd_kcontrol_new adau1372_asrco0_mux_control = + SOC_DAPM_ENUM("Output ASRC0 Capture Mux", adau1372_asrco0_mux_enum); +static const struct snd_kcontrol_new adau1372_asrco1_mux_control = + SOC_DAPM_ENUM("Output ASRC1 Capture Mux", adau1372_asrco1_mux_enum); +static const struct snd_kcontrol_new adau1372_asrco2_mux_control = + SOC_DAPM_ENUM("Output ASRC2 Capture Mux", adau1372_asrco2_mux_enum); +static const struct snd_kcontrol_new adau1372_asrco3_mux_control = + SOC_DAPM_ENUM("Output ASRC3 Capture Mux", adau1372_asrco3_mux_enum); + +static const unsigned int adau1372_sout_mux_values[] = { + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +static const char * const adau1372_sout_mux_text[] = { + "Output ASRC0", + "Output ASRC1", + "Output ASRC2", + "Output ASRC3", + "Serial Input 0", + "Serial Input 1", + "Serial Input 2", + "Serial Input 3", + "Serial Input 4", + "Serial Input 5", + "Serial Input 6", + "Serial Input 7", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout0_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout1_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout2_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout3_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout4_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout5_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout6_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout7_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); + +static const struct snd_kcontrol_new adau1372_sout0_mux_control = + SOC_DAPM_ENUM("Serial Output 0 Capture Mux", adau1372_sout0_mux_enum); +static const struct snd_kcontrol_new adau1372_sout1_mux_control = + SOC_DAPM_ENUM("Serial Output 1 Capture Mux", adau1372_sout1_mux_enum); +static const struct snd_kcontrol_new adau1372_sout2_mux_control = + SOC_DAPM_ENUM("Serial Output 2 Capture Mux", adau1372_sout2_mux_enum); +static const struct snd_kcontrol_new adau1372_sout3_mux_control = + SOC_DAPM_ENUM("Serial Output 3 Capture Mux", adau1372_sout3_mux_enum); +static const struct snd_kcontrol_new adau1372_sout4_mux_control = + SOC_DAPM_ENUM("Serial Output 4 Capture Mux", adau1372_sout4_mux_enum); +static const struct snd_kcontrol_new adau1372_sout5_mux_control = + SOC_DAPM_ENUM("Serial Output 5 Capture Mux", adau1372_sout5_mux_enum); +static const struct snd_kcontrol_new adau1372_sout6_mux_control = + SOC_DAPM_ENUM("Serial Output 6 Capture Mux", adau1372_sout6_mux_enum); +static const struct snd_kcontrol_new adau1372_sout7_mux_control = + SOC_DAPM_ENUM("Serial Output 7 Capture Mux", adau1372_sout7_mux_enum); + +static const char * const adau1372_asrci_mux_text[] = { + "Serial Input 0+1", + "Serial Input 2+3", + "Serial Input 4+5", + "Serial Input 6+7", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_asrci_mux_enum, + ADAU1372_REG_ASRC_MODE, 2, adau1372_asrci_mux_text); + +static const struct snd_kcontrol_new adau1372_asrci_mux_control = + SOC_DAPM_ENUM("Input ASRC Playback Mux", adau1372_asrci_mux_enum); + +static const unsigned int adau1372_dac_mux_values[] = { + 12, 13 +}; + +static const char * const adau1372_dac_mux_text[] = { + "Input ASRC0", + "Input ASRC1", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac0_mux_enum, ADAU1372_REG_DAC_SOURCE, + 0, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac1_mux_enum, ADAU1372_REG_DAC_SOURCE, + 4, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values); + +static const struct snd_kcontrol_new adau1372_dac0_mux_control = + SOC_DAPM_ENUM("DAC 0 Playback Mux", adau1372_dac0_mux_enum); +static const struct snd_kcontrol_new adau1372_dac1_mux_control = + SOC_DAPM_ENUM("DAC 1 Playback Mux", adau1372_dac1_mux_enum); + +static const struct snd_soc_dapm_widget adau1372_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("AIN0"), + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("DMIC0_1"), + SND_SOC_DAPM_INPUT("DMIC2_3"), + + SND_SOC_DAPM_SUPPLY("MICBIAS0", ADAU1372_REG_MICBIAS, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1372_REG_MICBIAS, 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("PGA0", ADAU1372_REG_PGA_CTRL(0), 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA1", ADAU1372_REG_PGA_CTRL(1), 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA2", ADAU1372_REG_PGA_CTRL(2), 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA3", ADAU1372_REG_PGA_CTRL(3), 6, 0, NULL, 0), + SND_SOC_DAPM_ADC("ADC0", NULL, ADAU1372_REG_ADC_CTRL2, 0, 0), + SND_SOC_DAPM_ADC("ADC1", NULL, ADAU1372_REG_ADC_CTRL2, 1, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, ADAU1372_REG_ADC_CTRL3, 0, 0), + SND_SOC_DAPM_ADC("ADC3", NULL, ADAU1372_REG_ADC_CTRL3, 1, 0), + + SND_SOC_DAPM_SUPPLY("ADC0 Filter", ADAU1372_REG_DECIM_PWR, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 Filter", ADAU1372_REG_DECIM_PWR, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 Filter", ADAU1372_REG_DECIM_PWR, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC3 Filter", ADAU1372_REG_DECIM_PWR, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC0 Decimator", ADAU1372_REG_DECIM_PWR, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC1 Decimator", ADAU1372_REG_DECIM_PWR, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC2 Decimator", ADAU1372_REG_DECIM_PWR, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC3 Decimator", ADAU1372_REG_DECIM_PWR, 7, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Decimator0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control), + SND_SOC_DAPM_MUX("Decimator1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control), + SND_SOC_DAPM_MUX("Decimator2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control), + SND_SOC_DAPM_MUX("Decimator3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control), + + SND_SOC_DAPM_MUX("Output ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco0_mux_control), + SND_SOC_DAPM_MUX("Output ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco1_mux_control), + SND_SOC_DAPM_MUX("Output ASRC2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco2_mux_control), + SND_SOC_DAPM_MUX("Output ASRC3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco3_mux_control), + SND_SOC_DAPM_MUX("Serial Output 0 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout0_mux_control), + SND_SOC_DAPM_MUX("Serial Output 1 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout1_mux_control), + SND_SOC_DAPM_MUX("Serial Output 2 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout2_mux_control), + SND_SOC_DAPM_MUX("Serial Output 3 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout3_mux_control), + SND_SOC_DAPM_MUX("Serial Output 4 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout4_mux_control), + SND_SOC_DAPM_MUX("Serial Output 5 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout5_mux_control), + SND_SOC_DAPM_MUX("Serial Output 6 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout6_mux_control), + SND_SOC_DAPM_MUX("Serial Output 7 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout7_mux_control), + + SND_SOC_DAPM_AIF_IN("Serial Input 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 1", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 2", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 3", NULL, 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 4", NULL, 4, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 5", NULL, 5, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 6", NULL, 6, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 7", NULL, 7, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("Serial Output 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 1", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 2", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 3", NULL, 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 4", NULL, 4, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 5", NULL, 5, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 6", NULL, 6, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 7", NULL, 7, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("Output ASRC Supply", ADAU1372_REG_ASRC_MODE, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Input ASRC Supply", ADAU1372_REG_ASRC_MODE, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC1 Modulator", ADAU1372_REG_INTERP_PWR, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC0 Modulator", ADAU1372_REG_INTERP_PWR, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Input ASRC1 Interpolator", ADAU1372_REG_INTERP_PWR, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Input ASRC0 Interpolator", ADAU1372_REG_INTERP_PWR, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Input ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control), + SND_SOC_DAPM_MUX("Input ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control), + + SND_SOC_DAPM_MUX("DAC 0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac0_mux_control), + SND_SOC_DAPM_MUX("DAC 1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac1_mux_control), + + SND_SOC_DAPM_DAC("DAC0", NULL, ADAU1372_REG_DAC_CTRL, 0, 0), + SND_SOC_DAPM_DAC("DAC1", NULL, ADAU1372_REG_DAC_CTRL, 1, 0), + + SND_SOC_DAPM_OUT_DRV("OP_STAGE_LP", ADAU1372_REG_OP_STAGE_CTRL, 0, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("OP_STAGE_LN", ADAU1372_REG_OP_STAGE_CTRL, 1, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("OP_STAGE_RP", ADAU1372_REG_OP_STAGE_CTRL, 2, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("OP_STAGE_RN", ADAU1372_REG_OP_STAGE_CTRL, 3, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), +}; + +#define ADAU1372_SOUT_ROUTES(x) \ + { "Serial Output " #x " Capture Mux", "Output ASRC0", "Output ASRC0 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Output ASRC1", "Output ASRC1 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Output ASRC2", "Output ASRC2 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Output ASRC3", "Output ASRC3 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 0", "Serial Input 0" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 1", "Serial Input 1" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 2", "Serial Input 2" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 3", "Serial Input 3" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 4", "Serial Input 4" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 5", "Serial Input 5" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 6", "Serial Input 6" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 7", "Serial Input 7" }, \ + { "Serial Output " #x, NULL, "Serial Output " #x " Capture Mux" }, \ + { "Capture", NULL, "Serial Output " #x } + +#define ADAU1372_ASRCO_ROUTES(x) \ + { "Output ASRC" #x " Mux", "Decimator0", "Decimator0 Mux" }, \ + { "Output ASRC" #x " Mux", "Decimator1", "Decimator1 Mux" }, \ + { "Output ASRC" #x " Mux", "Decimator2", "Decimator2 Mux" }, \ + { "Output ASRC" #x " Mux", "Decimator3", "Decimator3 Mux" } + +static const struct snd_soc_dapm_route adau1372_dapm_routes[] = { + { "PGA0", NULL, "AIN0" }, + { "PGA1", NULL, "AIN1" }, + { "PGA2", NULL, "AIN2" }, + { "PGA3", NULL, "AIN3" }, + + { "ADC0", NULL, "PGA0" }, + { "ADC1", NULL, "PGA1" }, + { "ADC2", NULL, "PGA2" }, + { "ADC3", NULL, "PGA3" }, + + { "Decimator0 Mux", "ADC", "ADC0" }, + { "Decimator1 Mux", "ADC", "ADC1" }, + { "Decimator2 Mux", "ADC", "ADC2" }, + { "Decimator3 Mux", "ADC", "ADC3" }, + + { "Decimator0 Mux", "DMIC", "DMIC0_1" }, + { "Decimator1 Mux", "DMIC", "DMIC0_1" }, + { "Decimator2 Mux", "DMIC", "DMIC2_3" }, + { "Decimator3 Mux", "DMIC", "DMIC2_3" }, + + { "Decimator0 Mux", NULL, "ADC0 Filter" }, + { "Decimator1 Mux", NULL, "ADC1 Filter" }, + { "Decimator2 Mux", NULL, "ADC2 Filter" }, + { "Decimator3 Mux", NULL, "ADC3 Filter" }, + + { "Output ASRC0 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC1 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC2 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC3 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC0 Mux", NULL, "Output ASRC0 Decimator" }, + { "Output ASRC1 Mux", NULL, "Output ASRC1 Decimator" }, + { "Output ASRC2 Mux", NULL, "Output ASRC2 Decimator" }, + { "Output ASRC3 Mux", NULL, "Output ASRC3 Decimator" }, + + ADAU1372_ASRCO_ROUTES(0), + ADAU1372_ASRCO_ROUTES(1), + ADAU1372_ASRCO_ROUTES(2), + ADAU1372_ASRCO_ROUTES(3), + + ADAU1372_SOUT_ROUTES(0), + ADAU1372_SOUT_ROUTES(1), + ADAU1372_SOUT_ROUTES(2), + ADAU1372_SOUT_ROUTES(3), + ADAU1372_SOUT_ROUTES(4), + ADAU1372_SOUT_ROUTES(5), + ADAU1372_SOUT_ROUTES(6), + ADAU1372_SOUT_ROUTES(7), + + { "Serial Input 0", NULL, "Playback" }, + { "Serial Input 1", NULL, "Playback" }, + { "Serial Input 2", NULL, "Playback" }, + { "Serial Input 3", NULL, "Playback" }, + { "Serial Input 4", NULL, "Playback" }, + { "Serial Input 5", NULL, "Playback" }, + { "Serial Input 6", NULL, "Playback" }, + { "Serial Input 7", NULL, "Playback" }, + + { "Input ASRC0 Mux", "Serial Input 0+1", "Serial Input 0" }, + { "Input ASRC1 Mux", "Serial Input 0+1", "Serial Input 1" }, + { "Input ASRC0 Mux", "Serial Input 2+3", "Serial Input 2" }, + { "Input ASRC1 Mux", "Serial Input 2+3", "Serial Input 3" }, + { "Input ASRC0 Mux", "Serial Input 4+5", "Serial Input 4" }, + { "Input ASRC1 Mux", "Serial Input 4+5", "Serial Input 5" }, + { "Input ASRC0 Mux", "Serial Input 6+7", "Serial Input 6" }, + { "Input ASRC1 Mux", "Serial Input 6+7", "Serial Input 7" }, + { "Input ASRC0 Mux", NULL, "Input ASRC Supply" }, + { "Input ASRC1 Mux", NULL, "Input ASRC Supply" }, + { "Input ASRC0 Mux", NULL, "Input ASRC0 Interpolator" }, + { "Input ASRC1 Mux", NULL, "Input ASRC1 Interpolator" }, + + { "DAC 0 Mux", "Input ASRC0", "Input ASRC0 Mux" }, + { "DAC 0 Mux", "Input ASRC1", "Input ASRC1 Mux" }, + { "DAC 1 Mux", "Input ASRC0", "Input ASRC0 Mux" }, + { "DAC 1 Mux", "Input ASRC1", "Input ASRC1 Mux" }, + + { "DAC0", NULL, "DAC 0 Mux" }, + { "DAC1", NULL, "DAC 1 Mux" }, + { "DAC0", NULL, "DAC0 Modulator" }, + { "DAC1", NULL, "DAC1 Modulator" }, + + { "OP_STAGE_LP", NULL, "DAC0" }, + { "OP_STAGE_LN", NULL, "DAC0" }, + { "OP_STAGE_RP", NULL, "DAC1" }, + { "OP_STAGE_RN", NULL, "DAC1" }, + + { "HPOUTL", NULL, "OP_STAGE_LP" }, + { "HPOUTL", NULL, "OP_STAGE_LN" }, + { "HPOUTR", NULL, "OP_STAGE_RP" }, + { "HPOUTR", NULL, "OP_STAGE_RN" }, +}; + +static int adau1372_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int sai0 = 0, sai1 = 0; + bool invert_lrclk = false; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + adau1372->master = true; + sai1 |= ADAU1372_SAI1_MS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + adau1372->master = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_lrclk = true; + break; + case SND_SOC_DAIFMT_IB_NF: + invert_lrclk = false; + sai1 |= ADAU1372_SAI1_BCLKEDGE; + break; + case SND_SOC_DAIFMT_IB_IF: + invert_lrclk = true; + sai1 |= ADAU1372_SAI1_BCLKEDGE; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + sai0 |= ADAU1372_SAI0_DELAY1; + break; + case SND_SOC_DAIFMT_LEFT_J: + sai0 |= ADAU1372_SAI0_DELAY0; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_DSP_A: + sai0 |= ADAU1372_SAI0_DELAY1; + sai1 |= ADAU1372_SAI1_LR_MODE; + break; + case SND_SOC_DAIFMT_DSP_B: + sai0 |= ADAU1372_SAI0_DELAY0; + sai1 |= ADAU1372_SAI1_LR_MODE; + break; + } + + if (invert_lrclk) + sai1 |= ADAU1372_SAI1_LR_POL; + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_DELAY_MASK, sai0); + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, + ADAU1372_SAI1_MS | ADAU1372_SAI1_BCLKEDGE | + ADAU1372_SAI1_LR_MODE | ADAU1372_SAI1_LR_POL, sai1); + + return 0; +} + +static int adau1372_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + unsigned int slot_width; + unsigned int sai0, sai1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(adau1372_rates); i++) { + if (rate == adau1372_rates[i]) + break; + } + + if (i == ARRAY_SIZE(adau1372_rates)) + return -EINVAL; + + sai0 = i; + + slot_width = adau1372->slot_width; + if (slot_width == 0) + slot_width = params_width(params); + + switch (slot_width) { + case 16: + sai1 = ADAU1372_SAI1_BCLKRATE; + break; + case 32: + sai1 = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_FS_MASK, sai0); + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLKRATE, sai1); + + return 0; +} + +static int adau1372_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int sai0, sai1; + + /* I2S mode */ + if (slots == 0) { + /* The other settings dont matter in I2S mode */ + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, + ADAU1372_SAI0_SAI_MASK, ADAU1372_SAI0_SAI_I2S); + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2; + adau1372->slot_width = 0; + return 0; + } + + /* We have 8 channels anything outside that is not supported */ + if ((tx_mask & ~0xff) != 0 || (rx_mask & ~0xff) != 0) + return -EINVAL; + + switch (width) { + case 16: + sai1 = ADAU1372_SAI1_BCLK_TDMC; + break; + case 32: + sai1 = 0; + break; + default: + return -EINVAL; + } + + switch (slots) { + case 2: + sai0 = ADAU1372_SAI0_SAI_TDM2; + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2; + break; + case 4: + sai0 = ADAU1372_SAI0_SAI_TDM4; + if (adau1372->master) + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4_MASTER; + else + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4; + break; + case 8: + sai0 = ADAU1372_SAI0_SAI_TDM8; + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM8; + break; + default: + return -EINVAL; + } + + adau1372->slot_width = width; + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_SAI_MASK, sai0); + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLK_TDMC, sai1); + + /* Mask is inverted in hardware */ + regmap_write(adau1372->regmap, ADAU1372_REG_SOUT_CTRL, ~tx_mask); + + return 0; +} + +static int adau1372_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int sai1; + + if (tristate) + sai1 = ADAU1372_SAI1_TDM_TS; + else + sai1 = 0; + + return regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_TDM_TS, sai1); +} + +static int adau1372_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + + snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &adau1372->rate_constraints); + + return 0; +} + +static void adau1372_enable_pll(struct adau1372 *adau1372) +{ + unsigned int val, timeout = 0; + int ret; + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_PLL_EN, ADAU1372_CLK_CTRL_PLL_EN); + do { + /* Takes about 1ms to lock */ + usleep_range(1000, 2000); + ret = regmap_read(adau1372->regmap, ADAU1372_REG_PLL(5), &val); + if (ret) + break; + timeout++; + } while (!(val & 1) && timeout < 3); + + if (ret < 0 || !(val & 1)) + dev_err(adau1372->dev, "Failed to lock PLL\n"); +} + +static void adau1372_set_power(struct adau1372 *adau1372, bool enable) +{ + if (adau1372->enabled == enable) + return; + + if (enable) { + unsigned int clk_ctrl = ADAU1372_CLK_CTRL_MCLK_EN; + + clk_prepare_enable(adau1372->mclk); + if (adau1372->pd_gpio) + gpiod_set_value(adau1372->pd_gpio, 0); + + if (adau1372->switch_mode) + adau1372->switch_mode(adau1372->dev); + + regcache_cache_only(adau1372->regmap, false); + + /* + * Clocks needs to be enabled before any other register can be + * accessed. + */ + if (adau1372->use_pll) { + adau1372_enable_pll(adau1372); + clk_ctrl |= ADAU1372_CLK_CTRL_CLKSRC; + } + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_CLKSRC, clk_ctrl); + regcache_sync(adau1372->regmap); + } else { + if (adau1372->pd_gpio) { + /* + * This will turn everything off and reset the register + * map. No need to do any register writes to manually + * turn things off. + */ + gpiod_set_value(adau1372->pd_gpio, 1); + regcache_mark_dirty(adau1372->regmap); + } else { + regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_PLL_EN, 0); + } + clk_disable_unprepare(adau1372->mclk); + regcache_cache_only(adau1372->regmap, true); + } + + adau1372->enabled = enable; +} + +static int adau1372_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adau1372 *adau1372 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + adau1372_set_power(adau1372, true); + break; + case SND_SOC_BIAS_OFF: + adau1372_set_power(adau1372, false); + break; + } + + return 0; +} + +static const struct snd_soc_component_driver adau1372_driver = { + .set_bias_level = adau1372_set_bias_level, + .controls = adau1372_controls, + .num_controls = ARRAY_SIZE(adau1372_controls), + .dapm_widgets = adau1372_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1372_dapm_widgets), + .dapm_routes = adau1372_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1372_dapm_routes), +}; + +static const struct snd_soc_dai_ops adau1372_dai_ops = { + .set_fmt = adau1372_set_dai_fmt, + .set_tdm_slot = adau1372_set_tdm_slot, + .set_tristate = adau1372_set_tristate, + .hw_params = adau1372_hw_params, + .startup = adau1372_startup, +}; + +#define ADAU1372_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver adau1372_dai_driver = { + .name = "adau1372", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = ADAU1372_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = ADAU1372_FORMATS, + .sig_bits = 24, + }, + .ops = &adau1372_dai_ops, + .symmetric_rates = 1, +}; + +static int adau1372_setup_pll(struct adau1372 *adau1372, unsigned int rate) +{ + u8 regs[5]; + unsigned int i; + int ret; + + ret = adau_calc_pll_cfg(rate, 49152000, regs); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + regmap_write(adau1372->regmap, ADAU1372_REG_PLL(i), regs[i]); + + return 0; +} + +int adau1372_probe(struct device *dev, struct regmap *regmap, + void (*switch_mode)(struct device *dev)) +{ + struct adau1372 *adau1372; + unsigned int clk_ctrl; + unsigned long rate; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + adau1372 = devm_kzalloc(dev, sizeof(*adau1372), GFP_KERNEL); + if (!adau1372) + return -ENOMEM; + + adau1372->clk = devm_clk_get(dev, "mclk"); + if (IS_ERR(adau1372->clk)) + return PTR_ERR(adau1372->clk); + + adau1372->pd_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(adau1372->pd_gpio)) + return PTR_ERR(adau1372->pd_gpio); + + adau1372->regmap = regmap; + adau1372->switch_mode = switch_mode; + adau1372->dev = dev; + adau1372->rate_constraints.list = adau1372_rates; + adau1372->rate_constraints.count = ARRAY_SIZE(adau1372_rates); + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2; + + dev_set_drvdata(dev, adau1372); + + /* + * The datasheet says that the internal MCLK always needs to run at + * 12.288MHz. Automatically choose a valid configuration from the + * external clock. + */ + rate = clk_get_rate(adau1372->clk); + + switch (rate) { + case 12288000: + clk_ctrl = ADAU1372_CLK_CTRL_CC_MDIV; + break; + case 24576000: + clk_ctrl = 0; + break; + default: + clk_ctrl = 0; + ret = adau1372_setup_pll(adau1372, rate); + if (ret < 0) + return ret; + adau1372->use_pll = true; + break; + } + + /* + * Most of the registers are inaccessible unless the internal clock is + * enabled. + */ + regcache_cache_only(regmap, true); + + regmap_update_bits(regmap, ADAU1372_REG_CLK_CTRL, ADAU1372_CLK_CTRL_CC_MDIV, clk_ctrl); + + /* + * No pinctrl support yet, put the multi-purpose pins in the most + * sensible mode for general purpose CODEC operation. + */ + regmap_write(regmap, ADAU1372_REG_MODE_MP(1), 0x00); /* SDATA OUT */ + regmap_write(regmap, ADAU1372_REG_MODE_MP(6), 0x12); /* CLOCKOUT */ + + regmap_write(regmap, ADAU1372_REG_OP_STAGE_MUTE, 0x0); + + regmap_write(regmap, 0x7, 0x01); /* CLOCK OUT */ + + return devm_snd_soc_register_component(dev, &adau1372_driver, &adau1372_dai_driver, 1); +} +EXPORT_SYMBOL(adau1372_probe); + +static const struct reg_default adau1372_reg_defaults[] = { + { ADAU1372_REG_CLK_CTRL, 0x00 }, + { ADAU1372_REG_PLL(0), 0x00 }, + { ADAU1372_REG_PLL(1), 0x00 }, + { ADAU1372_REG_PLL(2), 0x00 }, + { ADAU1372_REG_PLL(3), 0x00 }, + { ADAU1372_REG_PLL(4), 0x00 }, + { ADAU1372_REG_PLL(5), 0x00 }, + { ADAU1372_REG_DAC_SOURCE, 0x10 }, + { ADAU1372_REG_SOUT_SOURCE_0_1, 0x54 }, + { ADAU1372_REG_SOUT_SOURCE_2_3, 0x76 }, + { ADAU1372_REG_SOUT_SOURCE_4_5, 0x54 }, + { ADAU1372_REG_SOUT_SOURCE_6_7, 0x76 }, + { ADAU1372_REG_ADC_SDATA_CH, 0x04 }, + { ADAU1372_REG_ASRCO_SOURCE_0_1, 0x10 }, + { ADAU1372_REG_ASRCO_SOURCE_2_3, 0x32 }, + { ADAU1372_REG_ASRC_MODE, 0x00 }, + { ADAU1372_REG_ADC_CTRL0, 0x19 }, + { ADAU1372_REG_ADC_CTRL1, 0x19 }, + { ADAU1372_REG_ADC_CTRL2, 0x00 }, + { ADAU1372_REG_ADC_CTRL3, 0x00 }, + { ADAU1372_REG_ADC_VOL(0), 0x00 }, + { ADAU1372_REG_ADC_VOL(1), 0x00 }, + { ADAU1372_REG_ADC_VOL(2), 0x00 }, + { ADAU1372_REG_ADC_VOL(3), 0x00 }, + { ADAU1372_REG_PGA_CTRL(0), 0x40 }, + { ADAU1372_REG_PGA_CTRL(1), 0x40 }, + { ADAU1372_REG_PGA_CTRL(2), 0x40 }, + { ADAU1372_REG_PGA_CTRL(3), 0x40 }, + { ADAU1372_REG_PGA_BOOST, 0x00 }, + { ADAU1372_REG_MICBIAS, 0x00 }, + { ADAU1372_REG_DAC_CTRL, 0x18 }, + { ADAU1372_REG_DAC_VOL(0), 0x00 }, + { ADAU1372_REG_DAC_VOL(1), 0x00 }, + { ADAU1372_REG_OP_STAGE_MUTE, 0x0f }, + { ADAU1372_REG_SAI0, 0x00 }, + { ADAU1372_REG_SAI1, 0x00 }, + { ADAU1372_REG_SOUT_CTRL, 0x00 }, + { ADAU1372_REG_MODE_MP(0), 0x00 }, + { ADAU1372_REG_MODE_MP(1), 0x10 }, + { ADAU1372_REG_MODE_MP(4), 0x00 }, + { ADAU1372_REG_MODE_MP(5), 0x00 }, + { ADAU1372_REG_MODE_MP(6), 0x11 }, + { ADAU1372_REG_OP_STAGE_CTRL, 0x0f }, + { ADAU1372_REG_DECIM_PWR, 0x00 }, + { ADAU1372_REG_INTERP_PWR, 0x00 }, + { ADAU1372_REG_BIAS_CTRL0, 0x00 }, + { ADAU1372_REG_BIAS_CTRL1, 0x00 }, +}; + +static bool adau1372_volatile_register(struct device *dev, unsigned int reg) +{ + if (reg == ADAU1372_REG_PLL(5)) + return true; + + return false; +} + +const struct regmap_config adau1372_regmap_config = { + .val_bits = 8, + .reg_bits = 16, + .max_register = 0x4d, + + .reg_defaults = adau1372_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1372_reg_defaults), + .volatile_reg = adau1372_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(adau1372_regmap_config); + +MODULE_DESCRIPTION("ASoC ADAU1372 CODEC driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/adau1372.h b/sound/soc/codecs/adau1372.h new file mode 100644 index 000000000000..a9d2c59b73a9 --- /dev/null +++ b/sound/soc/codecs/adau1372.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADAU1372 driver + * + * Copyright 2016 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + */ + +#ifndef SOUND_SOC_CODECS_ADAU1372_H +#define SOUND_SOC_CODECS_ADAU1372_H + +#include <linux/regmap.h> + +struct device; + +int adau1372_probe(struct device *dev, struct regmap *regmap, + void (*switch_mode)(struct device *dev)); + +extern const struct regmap_config adau1372_regmap_config; + +#endif diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index 0a36e523584c..8260f49caa24 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -12,7 +12,6 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/module.h> -#include <linux/platform_data/adau1977.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -24,6 +23,8 @@ #include <sound/soc.h> #include <sound/tlv.h> +#include <dt-bindings/sound/adi,adau1977.h> + #include "adau1977.h" #define ADAU1977_REG_POWER 0x00 @@ -881,13 +882,9 @@ static const struct snd_soc_component_driver adau1977_component_driver = { static int adau1977_setup_micbias(struct adau1977 *adau1977) { - struct adau1977_platform_data *pdata = adau1977->dev->platform_data; unsigned int micbias; - if (pdata) - micbias = pdata->micbias; - else if (device_property_read_u32(adau1977->dev, "adi,micbias", - &micbias)) + if (device_property_read_u32(adau1977->dev, "adi,micbias", &micbias)) micbias = ADAU1977_MICBIAS_8V5; if (micbias > ADAU1977_MICBIAS_9V0) { diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 4fd99280d7db..75a649108106 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -373,6 +373,7 @@ static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_CBM_CFM: capture |= ADAV80X_CAPTURE_MODE_MASTER; playback |= ADAV80X_PLAYBACK_MODE_MASTER; + break; case SND_SOC_DAIFMT_CBS_CFS: break; default: diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index f44d9a4a8507..5d46ae85566c 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -404,11 +404,13 @@ static int ak4118_i2c_probe(struct i2c_client *i2c, &soc_component_drv_ak4118, &ak4118_dai, 1); } +#ifdef CONFIG_OF static const struct of_device_id ak4118_of_match[] = { { .compatible = "asahi-kasei,ak4118", }, {} }; MODULE_DEVICE_TABLE(of, ak4118_of_match); +#endif static const struct i2c_device_id ak4118_id_table[] = { { "ak4118", 0 }, diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 2f076d5ee284..8a32b0139cb0 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -415,7 +415,7 @@ static int ak5558_i2c_remove(struct i2c_client *i2c) return 0; } -static const struct of_device_id ak5558_i2c_dt_ids[] = { +static const struct of_device_id ak5558_i2c_dt_ids[] __maybe_unused = { { .compatible = "asahi-kasei,ak5558"}, { } }; diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 3d1761a531f5..54f489837162 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -1068,11 +1068,13 @@ static const struct i2c_device_id alc5623_i2c_table[] = { }; MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table); +#ifdef CONFIG_OF static const struct of_device_id alc5623_of_match[] = { { .compatible = "realtek,alc5623", }, { } }; MODULE_DEVICE_TABLE(of, alc5623_of_match); +#endif /* i2c codec control layer */ static struct i2c_driver alc5623_i2c_driver = { diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index 9d6dcd3ffa57..bde5ded67754 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1167,11 +1167,13 @@ static const struct i2c_device_id alc5632_i2c_table[] = { }; MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table); +#ifdef CONFIG_OF static const struct of_device_id alc5632_of_match[] = { { .compatible = "realtek,alc5632", }, { } }; MODULE_DEVICE_TABLE(of, alc5632_of_match); +#endif /* i2c codec control layer */ static struct i2c_driver alc5632_i2c_driver = { diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 1228f2de0297..e32871b3f68a 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1034,6 +1034,7 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, priv->out_down_delay++; break; } + break; default: break; } diff --git a/sound/soc/codecs/bd28623.c b/sound/soc/codecs/bd28623.c index 31904ef5c88b..a6267cb86d86 100644 --- a/sound/soc/codecs/bd28623.c +++ b/sound/soc/codecs/bd28623.c @@ -222,7 +222,7 @@ static int bd28623_probe(struct platform_device *pdev) &soc_dai_bd, 1); } -static const struct of_device_id bd28623_of_match[] = { +static const struct of_device_id bd28623_of_match[] __maybe_unused = { { .compatible = "rohm,bd28623", }, {} }; diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 28f039adfa13..5c3b7e5e55d2 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -332,7 +332,7 @@ static int i2s_rx_event(struct snd_soc_dapm_widget *w, snd_soc_dapm_to_component(w->dapm); struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - struct ec_param_ec_codec_i2s_rx p; + struct ec_param_ec_codec_i2s_rx p = {}; switch (event) { case SND_SOC_DAPM_PRE_PMU: diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index f772628f233e..796b894c390f 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -944,6 +944,7 @@ static int cs42l52_beep_event(struct input_dev *dev, unsigned int type, case SND_BELL: if (hz) hz = 261; + break; case SND_TONE: break; default: diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 97024a6ac96d..bb9599cc832b 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -1008,6 +1008,7 @@ static int cs42l56_beep_event(struct input_dev *dev, unsigned int type, case SND_BELL: if (hz) hz = 261; + break; case SND_TONE: break; default: diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c index 6e34106c268f..52dc29942ec2 100644 --- a/sound/soc/codecs/cs47l92.c +++ b/sound/soc/codecs/cs47l92.c @@ -201,6 +201,7 @@ static int cs47l92_outclk_ev(struct snd_soc_dapm_widget *w, default: break; } + break; default: break; } diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 2ad00ed21bec..2f10991a8bdb 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -1579,7 +1579,7 @@ static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = { .id = CX2072X_DAI_DSP, .probe = cx2072x_dsp_dai_probe, .playback = { - .stream_name = "Playback", + .stream_name = "DSP Playback", .channels_min = 2, .channels_max = 2, .rates = CX2072X_RATES_DSP, @@ -1591,7 +1591,7 @@ static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = { .name = "cx2072x-aec", .id = 3, .capture = { - .stream_name = "Capture", + .stream_name = "AEC Capture", .channels_min = 2, .channels_max = 2, .rates = CX2072X_RATES_DSP, diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index 6d78bccb55c3..2bfafbe9e3dc 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -2278,12 +2278,14 @@ static irqreturn_t da7218_irq_thread(int irq, void *data) * DT */ +#ifdef CONFIG_OF static const struct of_device_id da7218_of_match[] = { { .compatible = "dlg,da7217", .data = (void *) DA7217_DEV_ID }, { .compatible = "dlg,da7218", .data = (void *) DA7218_DEV_ID }, { } }; MODULE_DEVICE_TABLE(of, da7218_of_match); +#endif static inline int da7218_of_get_id(struct device *dev) { diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 0b3b7909efc9..e9b45daec0ca 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1702,11 +1702,13 @@ static struct snd_soc_dai_driver da7219_dai = { * DT/ACPI */ +#ifdef CONFIG_OF static const struct of_device_id da7219_of_match[] = { { .compatible = "dlg,da7219", }, { } }; MODULE_DEVICE_TABLE(of, da7219_of_match); +#endif #ifdef CONFIG_ACPI static const struct acpi_device_id da7219_acpi_match[] = { diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index b0d9ca6de685..aed92f615b02 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1519,11 +1519,13 @@ static const struct i2c_device_id da9055_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, da9055_i2c_id); +#ifdef CONFIG_OF static const struct of_device_id da9055_of_match[] = { { .compatible = "dlg,da9055-codec", }, { } }; MODULE_DEVICE_TABLE(of, da9055_of_match); +#endif /* I2C codec control layer */ static struct i2c_driver da9055_i2c_driver = { diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c index 00518406eb2b..e2b79879354b 100644 --- a/sound/soc/codecs/es7134.c +++ b/sound/soc/codecs/es7134.c @@ -183,7 +183,7 @@ static const struct snd_soc_dapm_route es7134_extra_routes[] = { { "Playback", NULL, "VDD", } }; -static const struct es7134_chip es7134_chip = { +static const struct es7134_chip es7134_chip __maybe_unused = { .dai_drv = &es7134_dai, .modes = es7134_modes, .mode_num = ARRAY_SIZE(es7134_modes), @@ -261,7 +261,7 @@ static const struct snd_soc_dapm_route es7154_extra_routes[] = { { "Playback", NULL, "PVDD", } }; -static const struct es7134_chip es7154_chip = { +static const struct es7134_chip es7154_chip __maybe_unused = { .dai_drv = &es7154_dai, .modes = es7154_modes, .mode_num = ARRAY_SIZE(es7154_modes), diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c index 87991bd4acef..2344a0b03518 100644 --- a/sound/soc/codecs/es7241.c +++ b/sound/soc/codecs/es7241.c @@ -203,7 +203,7 @@ static const struct es7241_clock_mode es7241_modes[] = { }, }; -static const struct es7241_chip es7241_chip = { +static const struct es7241_chip es7241_chip __maybe_unused = { .modes = es7241_modes, .mode_num = ARRAY_SIZE(es7241_modes), }; diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index bd5d230c5df2..f9ec5cf82599 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -834,11 +834,13 @@ static const struct i2c_device_id es8316_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, es8316_i2c_id); +#ifdef CONFIG_OF static const struct of_device_id es8316_of_match[] = { { .compatible = "everest,es8316", }, {}, }; MODULE_DEVICE_TABLE(of, es8316_of_match); +#endif #ifdef CONFIG_ACPI static const struct acpi_device_id es8316_acpi_match[] = { diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c index ae9e1c70ca57..e1235e695b0f 100644 --- a/sound/soc/codecs/gtm601.c +++ b/sound/soc/codecs/gtm601.c @@ -87,7 +87,7 @@ static int gtm601_platform_probe(struct platform_device *pdev) (struct snd_soc_dai_driver *)dai_driver, 1); } -static const struct of_device_id gtm601_codec_of_match[] = { +static const struct of_device_id gtm601_codec_of_match[] __maybe_unused = { { .compatible = "option,gtm601", .data = (void *)>m601_dai }, { .compatible = "broadmobi,bm818", .data = (void *)&bm818_dai }, {}, diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 403d4c6a49a8..d5fcc4db8284 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -282,6 +282,7 @@ struct hdmi_codec_priv { static const struct snd_soc_dapm_widget hdmi_widgets[] = { SND_SOC_DAPM_OUTPUT("TX"), + SND_SOC_DAPM_OUTPUT("RX"), }; enum { @@ -389,6 +390,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int ret = 0; mutex_lock(&hcp->lock); @@ -404,7 +406,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, goto err; } - if (hcp->hcd.ops->get_eld) { + if (tx && hcp->hcd.ops->get_eld) { ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, hcp->eld, sizeof(hcp->eld)); if (ret) @@ -660,14 +662,20 @@ static int hdmi_dai_probe(struct snd_soc_dai *dai) { struct snd_soc_dapm_context *dapm; struct hdmi_codec_daifmt *daifmt; - struct snd_soc_dapm_route route = { - .sink = "TX", - .source = dai->driver->playback.stream_name, + struct snd_soc_dapm_route route[] = { + { + .sink = "TX", + .source = dai->driver->playback.stream_name, + }, + { + .sink = dai->driver->capture.stream_name, + .source = "RX", + }, }; int ret; dapm = snd_soc_component_get_dapm(dai->component); - ret = snd_soc_dapm_add_routes(dapm, &route, 1); + ret = snd_soc_dapm_add_routes(dapm, route, 2); if (ret) return ret; @@ -692,10 +700,16 @@ static void plugged_cb(struct device *dev, bool plugged) { struct hdmi_codec_priv *hcp = dev_get_drvdata(dev); - if (plugged) + if (plugged) { + if (hcp->hcd.ops->get_eld) { + hcp->hcd.ops->get_eld(dev->parent, hcp->hcd.data, + hcp->eld, sizeof(hcp->eld)); + } hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT); - else + } else { hdmi_codec_jack_report(hcp, 0); + memset(hcp->eld, 0, sizeof(hcp->eld)); + } } static int hdmi_codec_set_jack(struct snd_soc_component *component, @@ -751,6 +765,14 @@ static const struct snd_soc_dai_driver hdmi_i2s_dai = { .formats = I2S_FORMATS, .sig_bits = 24, }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = HDMI_RATES, + .formats = I2S_FORMATS, + .sig_bits = 24, + }, .ops = &hdmi_codec_i2s_dai_ops, .pcm_new = hdmi_codec_pcm_new, }; @@ -767,6 +789,13 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = { .rates = HDMI_RATES, .formats = SPDIF_FORMATS, }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = HDMI_RATES, + .formats = SPDIF_FORMATS, + }, .ops = &hdmi_codec_spdif_dai_ops, .pcm_new = hdmi_codec_pcm_new, }; diff --git a/sound/soc/codecs/inno_rk3036.c b/sound/soc/codecs/inno_rk3036.c index d0e8f0d2fbc1..4dbce24c5f76 100644 --- a/sound/soc/codecs/inno_rk3036.c +++ b/sound/soc/codecs/inno_rk3036.c @@ -467,7 +467,7 @@ static int rk3036_codec_platform_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id rk3036_codec_of_match[] = { +static const struct of_device_id rk3036_codec_of_match[] __maybe_unused = { { .compatible = "rockchip,rk3036-codec", }, {} }; diff --git a/sound/soc/codecs/jz4725b.c b/sound/soc/codecs/jz4725b.c index e49374c72e70..5201a8f6d7b6 100644 --- a/sound/soc/codecs/jz4725b.c +++ b/sound/soc/codecs/jz4725b.c @@ -198,15 +198,15 @@ static int jz4725b_out_stage_enable(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - return regmap_update_bits(map, JZ4725B_CODEC_REG_IFR, - BIT(REG_IFR_RAMP_UP_DONE_OFFSET), 0); + return regmap_clear_bits(map, JZ4725B_CODEC_REG_IFR, + BIT(REG_IFR_RAMP_UP_DONE_OFFSET)); case SND_SOC_DAPM_POST_PMU: return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR, val, val & BIT(REG_IFR_RAMP_UP_DONE_OFFSET), 100000, 500000); case SND_SOC_DAPM_PRE_PMD: - return regmap_update_bits(map, JZ4725B_CODEC_REG_IFR, - BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET), 0); + return regmap_clear_bits(map, JZ4725B_CODEC_REG_IFR, + BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET)); case SND_SOC_DAPM_POST_PMD: return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR, val, val & BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET), @@ -303,24 +303,22 @@ static int jz4725b_codec_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_ON: - regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, - BIT(REG_PMR2_SB_SLEEP_OFFSET), 0); + regmap_clear_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_SLEEP_OFFSET)); break; case SND_SOC_BIAS_PREPARE: /* Enable sound hardware */ - regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, - BIT(REG_PMR2_SB_OFFSET), 0); + regmap_clear_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_OFFSET)); msleep(224); break; case SND_SOC_BIAS_STANDBY: - regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, - BIT(REG_PMR2_SB_SLEEP_OFFSET), - BIT(REG_PMR2_SB_SLEEP_OFFSET)); + regmap_set_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_SLEEP_OFFSET)); break; case SND_SOC_BIAS_OFF: - regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, - BIT(REG_PMR2_SB_OFFSET), - BIT(REG_PMR2_SB_OFFSET)); + regmap_set_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_OFFSET)); break; } diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index c9900d1cd5c2..5e58bfee2b49 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -219,12 +219,11 @@ static struct snd_soc_dai_driver jz4740_codec_dai = { static void jz4740_codec_wakeup(struct regmap *regmap) { - regmap_update_bits(regmap, JZ4740_REG_CODEC_1, - JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET); + regmap_set_bits(regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_RESET); udelay(2); - regmap_update_bits(regmap, JZ4740_REG_CODEC_1, - JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0); + regmap_clear_bits(regmap, JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET); regcache_sync(regmap); } @@ -235,7 +234,6 @@ static int jz4740_codec_set_bias_level(struct snd_soc_component *component, struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(component); struct regmap *regmap = jz4740_codec->regmap; unsigned int mask; - unsigned int value; switch (level) { case SND_SOC_BIAS_ON: @@ -244,9 +242,8 @@ static int jz4740_codec_set_bias_level(struct snd_soc_component *component, mask = JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE | JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; - value = 0; - regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value); + regmap_clear_bits(regmap, JZ4740_REG_CODEC_1, mask); break; case SND_SOC_BIAS_STANDBY: /* The only way to clear the suspend flag is to reset the codec */ @@ -256,17 +253,12 @@ static int jz4740_codec_set_bias_level(struct snd_soc_component *component, mask = JZ4740_CODEC_1_VREF_DISABLE | JZ4740_CODEC_1_VREF_AMP_DISABLE | JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; - value = JZ4740_CODEC_1_VREF_DISABLE | - JZ4740_CODEC_1_VREF_AMP_DISABLE | - JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; - regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value); + regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask); break; case SND_SOC_BIAS_OFF: mask = JZ4740_CODEC_1_SUSPEND; - value = JZ4740_CODEC_1_SUSPEND; - - regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value); + regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask); regcache_mark_dirty(regmap); break; default: diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c index 298689a07168..c9fe7f72bfcb 100644 --- a/sound/soc/codecs/jz4770.c +++ b/sound/soc/codecs/jz4770.c @@ -98,7 +98,7 @@ enum { #define REG_CR_HP_MUTE BIT(7) #define REG_CR_HP_LOAD BIT(6) #define REG_CR_HP_SB_OFFSET 4 -#define REG_CR_HP_SB_HPCM BIT(3) +#define REG_CR_HP_SB_HPCM_OFFSET 3 #define REG_CR_HP_SEL_OFFSET 0 #define REG_CR_HP_SEL_MASK (0x3 << REG_CR_HP_SEL_OFFSET) @@ -190,18 +190,21 @@ static int jz4770_codec_set_bias_level(struct snd_soc_component *codec, switch (level) { case SND_SOC_BIAS_PREPARE: - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, - REG_CR_VIC_SB, 0); + /* Reset all interrupt flags. */ + regmap_write(regmap, JZ4770_CODEC_REG_IFR, REG_IFR_ALL_MASK); + + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB); msleep(250); - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, - REG_CR_VIC_SB_SLEEP, 0); + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB_SLEEP); msleep(400); break; case SND_SOC_BIAS_STANDBY: - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, - REG_CR_VIC_SB_SLEEP, REG_CR_VIC_SB_SLEEP); - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_VIC, - REG_CR_VIC_SB, REG_CR_VIC_SB); + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB_SLEEP); + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB); fallthrough; default: break; @@ -284,7 +287,7 @@ static int jz4770_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direc err = regmap_read_poll_timeout(jz_codec->regmap, JZ4770_CODEC_REG_IFR, val, val & gain_bit, - 1000, 100 * USEC_PER_MSEC); + 1000, 1 * USEC_PER_SEC); if (err) { dev_err(jz_codec->dev, "Timeout while setting digital mute: %d", err); @@ -292,8 +295,8 @@ static int jz4770_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direc } /* clear GUP/GDO flag */ - regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, - gain_bit, gain_bit); + regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, + gain_bit); } return 0; @@ -368,9 +371,9 @@ static int hpout_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - /* set cap-less, unmute HP */ - regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP, - REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE, 0); + /* unmute HP */ + regmap_clear_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_MUTE); break; case SND_SOC_DAPM_POST_PMU: @@ -378,36 +381,35 @@ static int hpout_event(struct snd_soc_dapm_widget *w, err = regmap_read_poll_timeout(jz_codec->regmap, JZ4770_CODEC_REG_IFR, val, val & REG_IFR_RUP, - 1000, 100 * USEC_PER_MSEC); + 1000, 1 * USEC_PER_SEC); if (err) { dev_err(jz_codec->dev, "RUP timeout: %d", err); return err; } /* clear RUP flag */ - regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, - REG_IFR_RUP, REG_IFR_RUP); + regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, + REG_IFR_RUP); break; case SND_SOC_DAPM_POST_PMD: - /* set cap-couple, mute HP */ - regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP, - REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE, - REG_CR_HP_SB_HPCM | REG_CR_HP_MUTE); + /* mute HP */ + regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_MUTE); err = regmap_read_poll_timeout(jz_codec->regmap, JZ4770_CODEC_REG_IFR, val, val & REG_IFR_RDO, - 1000, 100 * USEC_PER_MSEC); + 1000, 1 * USEC_PER_SEC); if (err) { dev_err(jz_codec->dev, "RDO timeout: %d", err); return err; } /* clear RDO flag */ - regmap_update_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, - REG_IFR_RDO, REG_IFR_RDO); + regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, + REG_IFR_RDO); break; } @@ -517,6 +519,9 @@ static const struct snd_soc_dapm_widget jz4770_codec_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4770_CODEC_REG_CR_MIC, REG_CR_MIC_BIAS_SB_OFFSET, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Cap-less", JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_SB_HPCM_OFFSET, 1, NULL, 0), + SND_SOC_DAPM_INPUT("MIC1P"), SND_SOC_DAPM_INPUT("MIC1N"), SND_SOC_DAPM_INPUT("MIC2P"), @@ -592,70 +597,58 @@ static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec) regcache_cache_only(regmap, true); /* default HP output to PCM */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP, - REG_CR_HP_SEL_MASK, REG_CR_HP_SEL_MASK); + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_SEL_MASK); /* default line output to PCM */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_LO, - REG_CR_LO_SEL_MASK, REG_CR_LO_SEL_MASK); + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_LO, REG_CR_LO_SEL_MASK); /* Disable stereo mic */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_MIC, - BIT(REG_CR_MIC_STEREO_OFFSET), 0); + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_MIC, + BIT(REG_CR_MIC_STEREO_OFFSET)); /* Set mic 1 as default source for ADC */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_ADC, - REG_CR_ADC_IN_SEL_MASK, 0); + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_ADC, + REG_CR_ADC_IN_SEL_MASK); /* ADC/DAC: serial + i2s */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_AICR_ADC, - REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S, - REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S); - regmap_update_bits(regmap, JZ4770_CODEC_REG_AICR_DAC, - REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S, - REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S); + regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_ADC, + REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S); + regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_DAC, + REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S); /* The generated IRQ is a high level */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_ICR, - REG_ICR_INT_FORM_MASK, 0); + regmap_clear_bits(regmap, JZ4770_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK); regmap_update_bits(regmap, JZ4770_CODEC_REG_IMR, REG_IMR_ALL_MASK, REG_IMR_JACK_MASK | REG_IMR_RUP_MASK | REG_IMR_RDO_MASK | REG_IMR_GUP_MASK | REG_IMR_GDO_MASK); /* 12M oscillator */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_CCR, - REG_CCR_CRYSTAL_MASK, 0); + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CCR, REG_CCR_CRYSTAL_MASK); /* 0: 16ohm/220uF, 1: 10kohm/1uF */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP, - REG_CR_HP_LOAD, 0); + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_LOAD); /* disable automatic gain */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN, 0); + regmap_clear_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN); /* Disable DAC lrswap */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_DAC, - REG_CR_DAC_LRSWAP, REG_CR_DAC_LRSWAP); + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_DAC, REG_CR_DAC_LRSWAP); /* Independent L/R DAC gain control */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_GCR_DACL, - REG_GCR_DACL_RLGOD, 0); + regmap_clear_bits(regmap, JZ4770_CODEC_REG_GCR_DACL, + REG_GCR_DACL_RLGOD); /* Disable ADC lrswap */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_ADC, - REG_CR_ADC_LRSWAP, REG_CR_ADC_LRSWAP); + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_ADC, REG_CR_ADC_LRSWAP); /* default to cap-less mode(0) */ - regmap_update_bits(regmap, JZ4770_CODEC_REG_CR_HP, - REG_CR_HP_SB_HPCM, 0); + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP, + BIT(REG_CR_HP_SB_HPCM_OFFSET)); /* Send collected updates. */ regcache_cache_only(regmap, false); regcache_sync(regmap); - - /* Reset all interrupt flags. */ - regmap_write(regmap, JZ4770_CODEC_REG_IFR, REG_IFR_ALL_MASK); } static int jz4770_codec_codec_probe(struct snd_soc_component *codec) @@ -814,7 +807,7 @@ static int jz4770_codec_io_wait(struct jz_codec *codec) return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg, !(reg & ICDC_RGADW_RGWR), - 1000, 10 * USEC_PER_MSEC); + 1000, 1 * USEC_PER_SEC); } static int jz4770_codec_reg_read(void *context, unsigned int reg, diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c new file mode 100644 index 000000000000..91e6890d6efc --- /dev/null +++ b/sound/soc/codecs/lpass-va-macro.c @@ -0,0 +1,1497 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_clk.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +/* VA macro registers */ +#define CDC_VA_CLK_RST_CTRL_MCLK_CONTROL (0x0000) +#define CDC_VA_MCLK_CONTROL_EN BIT(0) +#define CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004) +#define CDC_VA_FS_CONTROL_EN BIT(0) +#define CDC_VA_CLK_RST_CTRL_SWR_CONTROL (0x0008) +#define CDC_VA_TOP_CSR_TOP_CFG0 (0x0080) +#define CDC_VA_FS_BROADCAST_EN BIT(1) +#define CDC_VA_TOP_CSR_DMIC0_CTL (0x0084) +#define CDC_VA_TOP_CSR_DMIC1_CTL (0x0088) +#define CDC_VA_TOP_CSR_DMIC2_CTL (0x008C) +#define CDC_VA_TOP_CSR_DMIC3_CTL (0x0090) +#define CDC_VA_DMIC_EN_MASK BIT(0) +#define CDC_VA_DMIC_ENABLE BIT(0) +#define CDC_VA_DMIC_CLK_SEL_MASK GENMASK(3, 1) +#define CDC_VA_DMIC_CLK_SEL_SHFT 1 +#define CDC_VA_DMIC_CLK_SEL_DIV0 0x0 +#define CDC_VA_DMIC_CLK_SEL_DIV1 0x2 +#define CDC_VA_DMIC_CLK_SEL_DIV2 0x4 +#define CDC_VA_DMIC_CLK_SEL_DIV3 0x6 +#define CDC_VA_DMIC_CLK_SEL_DIV4 0x8 +#define CDC_VA_DMIC_CLK_SEL_DIV5 0xa +#define CDC_VA_TOP_CSR_DMIC_CFG (0x0094) +#define CDC_VA_RESET_ALL_DMICS_MASK BIT(7) +#define CDC_VA_RESET_ALL_DMICS_RESET BIT(7) +#define CDC_VA_RESET_ALL_DMICS_DISABLE 0 +#define CDC_VA_DMIC3_FREQ_CHANGE_MASK BIT(3) +#define CDC_VA_DMIC3_FREQ_CHANGE_EN BIT(3) +#define CDC_VA_DMIC2_FREQ_CHANGE_MASK BIT(2) +#define CDC_VA_DMIC2_FREQ_CHANGE_EN BIT(2) +#define CDC_VA_DMIC1_FREQ_CHANGE_MASK BIT(1) +#define CDC_VA_DMIC1_FREQ_CHANGE_EN BIT(1) +#define CDC_VA_DMIC0_FREQ_CHANGE_MASK BIT(0) +#define CDC_VA_DMIC0_FREQ_CHANGE_EN BIT(0) +#define CDC_VA_DMIC_FREQ_CHANGE_DISABLE 0 +#define CDC_VA_TOP_CSR_DEBUG_BUS (0x009C) +#define CDC_VA_TOP_CSR_DEBUG_EN (0x00A0) +#define CDC_VA_TOP_CSR_TX_I2S_CTL (0x00A4) +#define CDC_VA_TOP_CSR_I2S_CLK (0x00A8) +#define CDC_VA_TOP_CSR_I2S_RESET (0x00AC) +#define CDC_VA_TOP_CSR_CORE_ID_0 (0x00C0) +#define CDC_VA_TOP_CSR_CORE_ID_1 (0x00C4) +#define CDC_VA_TOP_CSR_CORE_ID_2 (0x00C8) +#define CDC_VA_TOP_CSR_CORE_ID_3 (0x00CC) +#define CDC_VA_TOP_CSR_SWR_MIC_CTL0 (0x00D0) +#define CDC_VA_TOP_CSR_SWR_MIC_CTL1 (0x00D4) +#define CDC_VA_TOP_CSR_SWR_MIC_CTL2 (0x00D8) +#define CDC_VA_TOP_CSR_SWR_CTRL (0x00DC) +#define CDC_VA_INP_MUX_ADC_MUX0_CFG0 (0x0100) +#define CDC_VA_INP_MUX_ADC_MUX0_CFG1 (0x0104) +#define CDC_VA_INP_MUX_ADC_MUX1_CFG0 (0x0108) +#define CDC_VA_INP_MUX_ADC_MUX1_CFG1 (0x010C) +#define CDC_VA_INP_MUX_ADC_MUX2_CFG0 (0x0110) +#define CDC_VA_INP_MUX_ADC_MUX2_CFG1 (0x0114) +#define CDC_VA_INP_MUX_ADC_MUX3_CFG0 (0x0118) +#define CDC_VA_INP_MUX_ADC_MUX3_CFG1 (0x011C) +#define CDC_VA_TX0_TX_PATH_CTL (0x0400) +#define CDC_VA_TX_PATH_CLK_EN_MASK BIT(5) +#define CDC_VA_TX_PATH_CLK_EN BIT(5) +#define CDC_VA_TX_PATH_CLK_DISABLE 0 +#define CDC_VA_TX_PATH_PGA_MUTE_EN_MASK BIT(4) +#define CDC_VA_TX_PATH_PGA_MUTE_EN BIT(4) +#define CDC_VA_TX_PATH_PGA_MUTE_DISABLE 0 +#define CDC_VA_TX0_TX_PATH_CFG0 (0x0404) +#define CDC_VA_ADC_MODE_MASK GENMASK(2, 1) +#define CDC_VA_ADC_MODE_SHIFT 1 +#define TX_HPF_CUT_OFF_FREQ_MASK GENMASK(6, 5) +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 +#define CDC_VA_TX0_TX_PATH_CFG1 (0x0408) +#define CDC_VA_TX0_TX_VOL_CTL (0x040C) +#define CDC_VA_TX0_TX_PATH_SEC0 (0x0410) +#define CDC_VA_TX0_TX_PATH_SEC1 (0x0414) +#define CDC_VA_TX0_TX_PATH_SEC2 (0x0418) +#define CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK BIT(1) +#define CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_REQ BIT(1) +#define CDC_VA_TX_HPF_ZERO_GATE_MASK BIT(0) +#define CDC_VA_TX_HPF_ZERO_NO_GATE BIT(0) +#define CDC_VA_TX_HPF_ZERO_GATE 0 +#define CDC_VA_TX0_TX_PATH_SEC3 (0x041C) +#define CDC_VA_TX0_TX_PATH_SEC4 (0x0420) +#define CDC_VA_TX0_TX_PATH_SEC5 (0x0424) +#define CDC_VA_TX0_TX_PATH_SEC6 (0x0428) +#define CDC_VA_TX0_TX_PATH_SEC7 (0x042C) +#define CDC_VA_TX1_TX_PATH_CTL (0x0480) +#define CDC_VA_TX1_TX_PATH_CFG0 (0x0484) +#define CDC_VA_TX1_TX_PATH_CFG1 (0x0488) +#define CDC_VA_TX1_TX_VOL_CTL (0x048C) +#define CDC_VA_TX1_TX_PATH_SEC0 (0x0490) +#define CDC_VA_TX1_TX_PATH_SEC1 (0x0494) +#define CDC_VA_TX1_TX_PATH_SEC2 (0x0498) +#define CDC_VA_TX1_TX_PATH_SEC3 (0x049C) +#define CDC_VA_TX1_TX_PATH_SEC4 (0x04A0) +#define CDC_VA_TX1_TX_PATH_SEC5 (0x04A4) +#define CDC_VA_TX1_TX_PATH_SEC6 (0x04A8) +#define CDC_VA_TX2_TX_PATH_CTL (0x0500) +#define CDC_VA_TX2_TX_PATH_CFG0 (0x0504) +#define CDC_VA_TX2_TX_PATH_CFG1 (0x0508) +#define CDC_VA_TX2_TX_VOL_CTL (0x050C) +#define CDC_VA_TX2_TX_PATH_SEC0 (0x0510) +#define CDC_VA_TX2_TX_PATH_SEC1 (0x0514) +#define CDC_VA_TX2_TX_PATH_SEC2 (0x0518) +#define CDC_VA_TX2_TX_PATH_SEC3 (0x051C) +#define CDC_VA_TX2_TX_PATH_SEC4 (0x0520) +#define CDC_VA_TX2_TX_PATH_SEC5 (0x0524) +#define CDC_VA_TX2_TX_PATH_SEC6 (0x0528) +#define CDC_VA_TX3_TX_PATH_CTL (0x0580) +#define CDC_VA_TX3_TX_PATH_CFG0 (0x0584) +#define CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK BIT(7) +#define CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC BIT(7) +#define CDC_VA_TX_PATH_ADC_DMIC_SEL_ADC 0 +#define CDC_VA_TX3_TX_PATH_CFG1 (0x0588) +#define CDC_VA_TX3_TX_VOL_CTL (0x058C) +#define CDC_VA_TX3_TX_PATH_SEC0 (0x0590) +#define CDC_VA_TX3_TX_PATH_SEC1 (0x0594) +#define CDC_VA_TX3_TX_PATH_SEC2 (0x0598) +#define CDC_VA_TX3_TX_PATH_SEC3 (0x059C) +#define CDC_VA_TX3_TX_PATH_SEC4 (0x05A0) +#define CDC_VA_TX3_TX_PATH_SEC5 (0x05A4) +#define CDC_VA_TX3_TX_PATH_SEC6 (0x05A8) + +#define VA_MAX_OFFSET (0x07A8) + +#define VA_MACRO_NUM_DECIMATORS 4 +#define VA_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define VA_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define VA_MACRO_MCLK_FREQ 9600000 +#define VA_MACRO_TX_PATH_OFFSET 0x80 +#define VA_MACRO_SWR_MIC_MUX_SEL_MASK 0xF +#define VA_MACRO_ADC_MUX_CFG_OFFSET 0x8 + +static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); + +enum { + VA_MACRO_AIF_INVALID = 0, + VA_MACRO_AIF1_CAP, + VA_MACRO_AIF2_CAP, + VA_MACRO_AIF3_CAP, + VA_MACRO_MAX_DAIS, +}; + +enum { + VA_MACRO_DEC0, + VA_MACRO_DEC1, + VA_MACRO_DEC2, + VA_MACRO_DEC3, + VA_MACRO_DEC4, + VA_MACRO_DEC5, + VA_MACRO_DEC6, + VA_MACRO_DEC7, + VA_MACRO_DEC_MAX, +}; + +enum { + VA_MACRO_CLK_DIV_2, + VA_MACRO_CLK_DIV_3, + VA_MACRO_CLK_DIV_4, + VA_MACRO_CLK_DIV_6, + VA_MACRO_CLK_DIV_8, + VA_MACRO_CLK_DIV_16, +}; + +#define VA_NUM_CLKS_MAX 3 + +struct va_macro { + struct device *dev; + unsigned long active_ch_mask[VA_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[VA_MACRO_MAX_DAIS]; + unsigned long active_decimator[VA_MACRO_MAX_DAIS]; + u16 dmic_clk_div; + + int dec_mode[VA_MACRO_NUM_DECIMATORS]; + struct regmap *regmap; + struct clk_bulk_data clks[VA_NUM_CLKS_MAX]; + struct clk_hw hw; + + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 dmic_6_7_clk_cnt; + u8 dmic_0_1_clk_div; + u8 dmic_2_3_clk_div; + u8 dmic_4_5_clk_div; + u8 dmic_6_7_clk_div; +}; + +#define to_va_macro(_hw) container_of(_hw, struct va_macro, hw) + +static bool va_is_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_VA_TOP_CSR_CORE_ID_0: + case CDC_VA_TOP_CSR_CORE_ID_1: + case CDC_VA_TOP_CSR_CORE_ID_2: + case CDC_VA_TOP_CSR_CORE_ID_3: + case CDC_VA_TOP_CSR_DMIC0_CTL: + case CDC_VA_TOP_CSR_DMIC1_CTL: + case CDC_VA_TOP_CSR_DMIC2_CTL: + case CDC_VA_TOP_CSR_DMIC3_CTL: + return true; + } + return false; +} + +static const struct reg_default va_defaults[] = { + /* VA macro */ + { CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, 0x00}, + { CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00}, + { CDC_VA_CLK_RST_CTRL_SWR_CONTROL, 0x00}, + { CDC_VA_TOP_CSR_TOP_CFG0, 0x00}, + { CDC_VA_TOP_CSR_DMIC0_CTL, 0x00}, + { CDC_VA_TOP_CSR_DMIC1_CTL, 0x00}, + { CDC_VA_TOP_CSR_DMIC2_CTL, 0x00}, + { CDC_VA_TOP_CSR_DMIC3_CTL, 0x00}, + { CDC_VA_TOP_CSR_DMIC_CFG, 0x80}, + { CDC_VA_TOP_CSR_DEBUG_BUS, 0x00}, + { CDC_VA_TOP_CSR_DEBUG_EN, 0x00}, + { CDC_VA_TOP_CSR_TX_I2S_CTL, 0x0C}, + { CDC_VA_TOP_CSR_I2S_CLK, 0x00}, + { CDC_VA_TOP_CSR_I2S_RESET, 0x00}, + { CDC_VA_TOP_CSR_CORE_ID_0, 0x00}, + { CDC_VA_TOP_CSR_CORE_ID_1, 0x00}, + { CDC_VA_TOP_CSR_CORE_ID_2, 0x00}, + { CDC_VA_TOP_CSR_CORE_ID_3, 0x00}, + { CDC_VA_TOP_CSR_SWR_MIC_CTL0, 0xEE}, + { CDC_VA_TOP_CSR_SWR_MIC_CTL1, 0xEE}, + { CDC_VA_TOP_CSR_SWR_MIC_CTL2, 0xEE}, + { CDC_VA_TOP_CSR_SWR_CTRL, 0x06}, + + /* VA core */ + { CDC_VA_INP_MUX_ADC_MUX0_CFG0, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX0_CFG1, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX1_CFG0, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX1_CFG1, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX2_CFG0, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX2_CFG1, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX3_CFG0, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX3_CFG1, 0x00}, + { CDC_VA_TX0_TX_PATH_CTL, 0x04}, + { CDC_VA_TX0_TX_PATH_CFG0, 0x10}, + { CDC_VA_TX0_TX_PATH_CFG1, 0x0B}, + { CDC_VA_TX0_TX_VOL_CTL, 0x00}, + { CDC_VA_TX0_TX_PATH_SEC0, 0x00}, + { CDC_VA_TX0_TX_PATH_SEC1, 0x00}, + { CDC_VA_TX0_TX_PATH_SEC2, 0x01}, + { CDC_VA_TX0_TX_PATH_SEC3, 0x3C}, + { CDC_VA_TX0_TX_PATH_SEC4, 0x20}, + { CDC_VA_TX0_TX_PATH_SEC5, 0x00}, + { CDC_VA_TX0_TX_PATH_SEC6, 0x00}, + { CDC_VA_TX0_TX_PATH_SEC7, 0x25}, + { CDC_VA_TX1_TX_PATH_CTL, 0x04}, + { CDC_VA_TX1_TX_PATH_CFG0, 0x10}, + { CDC_VA_TX1_TX_PATH_CFG1, 0x0B}, + { CDC_VA_TX1_TX_VOL_CTL, 0x00}, + { CDC_VA_TX1_TX_PATH_SEC0, 0x00}, + { CDC_VA_TX1_TX_PATH_SEC1, 0x00}, + { CDC_VA_TX1_TX_PATH_SEC2, 0x01}, + { CDC_VA_TX1_TX_PATH_SEC3, 0x3C}, + { CDC_VA_TX1_TX_PATH_SEC4, 0x20}, + { CDC_VA_TX1_TX_PATH_SEC5, 0x00}, + { CDC_VA_TX1_TX_PATH_SEC6, 0x00}, + { CDC_VA_TX2_TX_PATH_CTL, 0x04}, + { CDC_VA_TX2_TX_PATH_CFG0, 0x10}, + { CDC_VA_TX2_TX_PATH_CFG1, 0x0B}, + { CDC_VA_TX2_TX_VOL_CTL, 0x00}, + { CDC_VA_TX2_TX_PATH_SEC0, 0x00}, + { CDC_VA_TX2_TX_PATH_SEC1, 0x00}, + { CDC_VA_TX2_TX_PATH_SEC2, 0x01}, + { CDC_VA_TX2_TX_PATH_SEC3, 0x3C}, + { CDC_VA_TX2_TX_PATH_SEC4, 0x20}, + { CDC_VA_TX2_TX_PATH_SEC5, 0x00}, + { CDC_VA_TX2_TX_PATH_SEC6, 0x00}, + { CDC_VA_TX3_TX_PATH_CTL, 0x04}, + { CDC_VA_TX3_TX_PATH_CFG0, 0x10}, + { CDC_VA_TX3_TX_PATH_CFG1, 0x0B}, + { CDC_VA_TX3_TX_VOL_CTL, 0x00}, + { CDC_VA_TX3_TX_PATH_SEC0, 0x00}, + { CDC_VA_TX3_TX_PATH_SEC1, 0x00}, + { CDC_VA_TX3_TX_PATH_SEC2, 0x01}, + { CDC_VA_TX3_TX_PATH_SEC3, 0x3C}, + { CDC_VA_TX3_TX_PATH_SEC4, 0x20}, + { CDC_VA_TX3_TX_PATH_SEC5, 0x00}, + { CDC_VA_TX3_TX_PATH_SEC6, 0x00}, +}; + +static bool va_is_rw_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_VA_CLK_RST_CTRL_MCLK_CONTROL: + case CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL: + case CDC_VA_CLK_RST_CTRL_SWR_CONTROL: + case CDC_VA_TOP_CSR_TOP_CFG0: + case CDC_VA_TOP_CSR_DMIC0_CTL: + case CDC_VA_TOP_CSR_DMIC1_CTL: + case CDC_VA_TOP_CSR_DMIC2_CTL: + case CDC_VA_TOP_CSR_DMIC3_CTL: + case CDC_VA_TOP_CSR_DMIC_CFG: + case CDC_VA_TOP_CSR_DEBUG_BUS: + case CDC_VA_TOP_CSR_DEBUG_EN: + case CDC_VA_TOP_CSR_TX_I2S_CTL: + case CDC_VA_TOP_CSR_I2S_CLK: + case CDC_VA_TOP_CSR_I2S_RESET: + case CDC_VA_INP_MUX_ADC_MUX0_CFG0: + case CDC_VA_INP_MUX_ADC_MUX0_CFG1: + case CDC_VA_INP_MUX_ADC_MUX1_CFG0: + case CDC_VA_INP_MUX_ADC_MUX1_CFG1: + case CDC_VA_INP_MUX_ADC_MUX2_CFG0: + case CDC_VA_INP_MUX_ADC_MUX2_CFG1: + case CDC_VA_INP_MUX_ADC_MUX3_CFG0: + case CDC_VA_INP_MUX_ADC_MUX3_CFG1: + case CDC_VA_TX0_TX_PATH_CTL: + case CDC_VA_TX0_TX_PATH_CFG0: + case CDC_VA_TX0_TX_PATH_CFG1: + case CDC_VA_TX0_TX_VOL_CTL: + case CDC_VA_TX0_TX_PATH_SEC0: + case CDC_VA_TX0_TX_PATH_SEC1: + case CDC_VA_TX0_TX_PATH_SEC2: + case CDC_VA_TX0_TX_PATH_SEC3: + case CDC_VA_TX0_TX_PATH_SEC4: + case CDC_VA_TX0_TX_PATH_SEC5: + case CDC_VA_TX0_TX_PATH_SEC6: + case CDC_VA_TX0_TX_PATH_SEC7: + case CDC_VA_TX1_TX_PATH_CTL: + case CDC_VA_TX1_TX_PATH_CFG0: + case CDC_VA_TX1_TX_PATH_CFG1: + case CDC_VA_TX1_TX_VOL_CTL: + case CDC_VA_TX1_TX_PATH_SEC0: + case CDC_VA_TX1_TX_PATH_SEC1: + case CDC_VA_TX1_TX_PATH_SEC2: + case CDC_VA_TX1_TX_PATH_SEC3: + case CDC_VA_TX1_TX_PATH_SEC4: + case CDC_VA_TX1_TX_PATH_SEC5: + case CDC_VA_TX1_TX_PATH_SEC6: + case CDC_VA_TX2_TX_PATH_CTL: + case CDC_VA_TX2_TX_PATH_CFG0: + case CDC_VA_TX2_TX_PATH_CFG1: + case CDC_VA_TX2_TX_VOL_CTL: + case CDC_VA_TX2_TX_PATH_SEC0: + case CDC_VA_TX2_TX_PATH_SEC1: + case CDC_VA_TX2_TX_PATH_SEC2: + case CDC_VA_TX2_TX_PATH_SEC3: + case CDC_VA_TX2_TX_PATH_SEC4: + case CDC_VA_TX2_TX_PATH_SEC5: + case CDC_VA_TX2_TX_PATH_SEC6: + case CDC_VA_TX3_TX_PATH_CTL: + case CDC_VA_TX3_TX_PATH_CFG0: + case CDC_VA_TX3_TX_PATH_CFG1: + case CDC_VA_TX3_TX_VOL_CTL: + case CDC_VA_TX3_TX_PATH_SEC0: + case CDC_VA_TX3_TX_PATH_SEC1: + case CDC_VA_TX3_TX_PATH_SEC2: + case CDC_VA_TX3_TX_PATH_SEC3: + case CDC_VA_TX3_TX_PATH_SEC4: + case CDC_VA_TX3_TX_PATH_SEC5: + case CDC_VA_TX3_TX_PATH_SEC6: + return true; + } + + return false; +} + +static bool va_is_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_VA_TOP_CSR_CORE_ID_0: + case CDC_VA_TOP_CSR_CORE_ID_1: + case CDC_VA_TOP_CSR_CORE_ID_2: + case CDC_VA_TOP_CSR_CORE_ID_3: + return true; + } + + return va_is_rw_register(dev, reg); +} + +static const struct regmap_config va_regmap_config = { + .name = "va_macro", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .cache_type = REGCACHE_FLAT, + .reg_defaults = va_defaults, + .num_reg_defaults = ARRAY_SIZE(va_defaults), + .max_register = VA_MAX_OFFSET, + .volatile_reg = va_is_volatile_register, + .readable_reg = va_is_readable_register, + .writeable_reg = va_is_rw_register, +}; + +static int va_clk_rsc_fs_gen_request(struct va_macro *va, bool enable) +{ + struct regmap *regmap = va->regmap; + + if (enable) { + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, + CDC_VA_MCLK_CONTROL_EN, + CDC_VA_MCLK_CONTROL_EN); + + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_VA_FS_CONTROL_EN, + CDC_VA_FS_CONTROL_EN); + + regmap_update_bits(regmap, CDC_VA_TOP_CSR_TOP_CFG0, + CDC_VA_FS_BROADCAST_EN, + CDC_VA_FS_BROADCAST_EN); + } else { + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, + CDC_VA_MCLK_CONTROL_EN, 0x0); + + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_VA_FS_CONTROL_EN, 0x0); + + regmap_update_bits(regmap, CDC_VA_TOP_CSR_TOP_CFG0, + CDC_VA_FS_BROADCAST_EN, 0x0); + } + + return 0; +} + +static int va_macro_mclk_enable(struct va_macro *va, bool mclk_enable) +{ + struct regmap *regmap = va->regmap; + + if (mclk_enable) { + va_clk_rsc_fs_gen_request(va, true); + regcache_mark_dirty(regmap); + regcache_sync_region(regmap, 0x0, VA_MAX_OFFSET); + } else { + va_clk_rsc_fs_gen_request(va, false); + } + + return 0; +} + +static int va_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct va_macro *va = snd_soc_component_get_drvdata(comp); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return va_macro_mclk_enable(va, true); + case SND_SOC_DAPM_POST_PMD: + return va_macro_mclk_enable(va, false); + } + + return 0; +} + +static int va_macro_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg; + + val = ucontrol->value.enumerated.item[0]; + + switch (e->reg) { + case CDC_VA_INP_MUX_ADC_MUX0_CFG0: + mic_sel_reg = CDC_VA_TX0_TX_PATH_CFG0; + break; + case CDC_VA_INP_MUX_ADC_MUX1_CFG0: + mic_sel_reg = CDC_VA_TX1_TX_PATH_CFG0; + break; + case CDC_VA_INP_MUX_ADC_MUX2_CFG0: + mic_sel_reg = CDC_VA_TX2_TX_PATH_CFG0; + break; + case CDC_VA_INP_MUX_ADC_MUX3_CFG0: + mic_sel_reg = CDC_VA_TX3_TX_PATH_CFG0; + break; + default: + dev_err(component->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + if (val != 0) + snd_soc_component_update_bits(component, mic_sel_reg, + CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK, + CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int va_macro_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + u32 dai_id = widget->shift; + u32 dec_id = mc->shift; + struct va_macro *va = snd_soc_component_get_drvdata(component); + + if (test_bit(dec_id, &va->active_ch_mask[dai_id])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int va_macro_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct snd_soc_dapm_update *update = NULL; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + u32 dai_id = widget->shift; + u32 dec_id = mc->shift; + u32 enable = ucontrol->value.integer.value[0]; + struct va_macro *va = snd_soc_component_get_drvdata(component); + + if (enable) { + set_bit(dec_id, &va->active_ch_mask[dai_id]); + va->active_ch_cnt[dai_id]++; + va->active_decimator[dai_id] = dec_id; + } else { + clear_bit(dec_id, &va->active_ch_mask[dai_id]); + va->active_ch_cnt[dai_id]--; + va->active_decimator[dai_id] = -1; + } + + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int va_dmic_clk_enable(struct snd_soc_component *component, + u32 dmic, bool enable) +{ + struct va_macro *va = snd_soc_component_get_drvdata(component); + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 *dmic_clk_div; + u8 freq_change_mask; + u8 clk_div; + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(va->dmic_0_1_clk_cnt); + dmic_clk_div = &(va->dmic_0_1_clk_div); + dmic_clk_reg = CDC_VA_TOP_CSR_DMIC0_CTL; + freq_change_mask = CDC_VA_DMIC0_FREQ_CHANGE_MASK; + break; + case 2: + case 3: + dmic_clk_cnt = &(va->dmic_2_3_clk_cnt); + dmic_clk_div = &(va->dmic_2_3_clk_div); + dmic_clk_reg = CDC_VA_TOP_CSR_DMIC1_CTL; + freq_change_mask = CDC_VA_DMIC1_FREQ_CHANGE_MASK; + break; + case 4: + case 5: + dmic_clk_cnt = &(va->dmic_4_5_clk_cnt); + dmic_clk_div = &(va->dmic_4_5_clk_div); + dmic_clk_reg = CDC_VA_TOP_CSR_DMIC2_CTL; + freq_change_mask = CDC_VA_DMIC2_FREQ_CHANGE_MASK; + break; + case 6: + case 7: + dmic_clk_cnt = &(va->dmic_6_7_clk_cnt); + dmic_clk_div = &(va->dmic_6_7_clk_div); + dmic_clk_reg = CDC_VA_TOP_CSR_DMIC3_CTL; + freq_change_mask = CDC_VA_DMIC3_FREQ_CHANGE_MASK; + break; + default: + dev_err(component->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + } + + if (enable) { + clk_div = va->dmic_clk_div; + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_component_update_bits(component, + CDC_VA_TOP_CSR_DMIC_CFG, + CDC_VA_RESET_ALL_DMICS_MASK, + CDC_VA_RESET_ALL_DMICS_DISABLE); + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_CLK_SEL_MASK, + clk_div << CDC_VA_DMIC_CLK_SEL_SHFT); + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_EN_MASK, + CDC_VA_DMIC_ENABLE); + } else { + if (*dmic_clk_div > clk_div) { + snd_soc_component_update_bits(component, + CDC_VA_TOP_CSR_DMIC_CFG, + freq_change_mask, + freq_change_mask); + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_CLK_SEL_MASK, + clk_div << CDC_VA_DMIC_CLK_SEL_SHFT); + snd_soc_component_update_bits(component, + CDC_VA_TOP_CSR_DMIC_CFG, + freq_change_mask, + CDC_VA_DMIC_FREQ_CHANGE_DISABLE); + } else { + clk_div = *dmic_clk_div; + } + } + *dmic_clk_div = clk_div; + } else { + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_EN_MASK, 0); + clk_div = 0; + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_CLK_SEL_MASK, + clk_div << CDC_VA_DMIC_CLK_SEL_SHFT); + } else { + clk_div = va->dmic_clk_div; + if (*dmic_clk_div > clk_div) { + clk_div = va->dmic_clk_div; + snd_soc_component_update_bits(component, + CDC_VA_TOP_CSR_DMIC_CFG, + freq_change_mask, + freq_change_mask); + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_CLK_SEL_MASK, + clk_div << CDC_VA_DMIC_CLK_SEL_SHFT); + snd_soc_component_update_bits(component, + CDC_VA_TOP_CSR_DMIC_CFG, + freq_change_mask, + CDC_VA_DMIC_FREQ_CHANGE_DISABLE); + } else { + clk_div = *dmic_clk_div; + } + } + *dmic_clk_div = clk_div; + } + + return 0; +} + +static int va_macro_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + unsigned int dmic = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + va_dmic_clk_enable(comp, dmic, true); + break; + case SND_SOC_DAPM_POST_PMD: + va_dmic_clk_enable(comp, dmic, false); + break; + } + + return 0; +} + +static int va_macro_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + unsigned int decimator; + u16 tx_vol_ctl_reg, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + u8 hpf_cut_off_freq; + + struct va_macro *va = snd_soc_component_get_drvdata(comp); + + decimator = w->shift; + + tx_vol_ctl_reg = CDC_VA_TX0_TX_PATH_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + hpf_gate_reg = CDC_VA_TX0_TX_PATH_SEC2 + + VA_MACRO_TX_PATH_OFFSET * decimator; + dec_cfg_reg = CDC_VA_TX0_TX_PATH_CFG0 + + VA_MACRO_TX_PATH_OFFSET * decimator; + tx_gain_ctl_reg = CDC_VA_TX0_TX_VOL_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(comp, + dec_cfg_reg, CDC_VA_ADC_MODE_MASK, + va->dec_mode[decimator] << CDC_VA_ADC_MODE_SHIFT); + /* Enable TX PGA Mute */ + break; + case SND_SOC_DAPM_POST_PMU: + /* Enable TX CLK */ + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, + CDC_VA_TX_PATH_CLK_EN_MASK, + CDC_VA_TX_PATH_CLK_EN); + snd_soc_component_update_bits(comp, hpf_gate_reg, + CDC_VA_TX_HPF_ZERO_GATE_MASK, + CDC_VA_TX_HPF_ZERO_GATE); + + usleep_range(1000, 1010); + hpf_cut_off_freq = (snd_soc_component_read(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + + snd_soc_component_update_bits(comp, hpf_gate_reg, + CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK, + CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_REQ); + + /* + * Minimum 1 clk cycle delay is required as per HW spec + */ + usleep_range(1000, 1010); + + snd_soc_component_update_bits(comp, + hpf_gate_reg, + CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK, + 0x0); + } + + + usleep_range(1000, 1010); + snd_soc_component_update_bits(comp, hpf_gate_reg, + CDC_VA_TX_HPF_ZERO_GATE_MASK, + CDC_VA_TX_HPF_ZERO_NO_GATE); + /* + * 6ms delay is required as per HW spec + */ + usleep_range(6000, 6010); + /* apply gain after decimator is enabled */ + snd_soc_component_write(comp, tx_gain_ctl_reg, + snd_soc_component_read(comp, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable TX CLK */ + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, + CDC_VA_TX_PATH_CLK_EN_MASK, + CDC_VA_TX_PATH_CLK_DISABLE); + break; + } + return 0; +} + +static int va_macro_dec_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct va_macro *va = snd_soc_component_get_drvdata(comp); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + ucontrol->value.integer.value[0] = va->dec_mode[path]; + + return 0; +} + +static int va_macro_dec_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + int value = ucontrol->value.integer.value[0]; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + struct va_macro *va = snd_soc_component_get_drvdata(comp); + + va->dec_mode[path] = value; + + return 0; +} + +static int va_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int tx_fs_rate; + struct snd_soc_component *component = dai->component; + u32 decimator, sample_rate; + u16 tx_fs_reg; + struct device *va_dev = component->dev; + struct va_macro *va = snd_soc_component_get_drvdata(component); + + sample_rate = params_rate(params); + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(va_dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + } + + for_each_set_bit(decimator, &va->active_ch_mask[dai->id], + VA_MACRO_DEC_MAX) { + tx_fs_reg = CDC_VA_TX0_TX_PATH_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + snd_soc_component_update_bits(component, tx_fs_reg, 0x0F, + tx_fs_rate); + } + return 0; +} + +static int va_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_component *component = dai->component; + struct device *va_dev = component->dev; + struct va_macro *va = snd_soc_component_get_drvdata(component); + + switch (dai->id) { + case VA_MACRO_AIF1_CAP: + case VA_MACRO_AIF2_CAP: + case VA_MACRO_AIF3_CAP: + *tx_slot = va->active_ch_mask[dai->id]; + *tx_num = va->active_ch_cnt[dai->id]; + break; + default: + dev_err(va_dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static int va_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct va_macro *va = snd_soc_component_get_drvdata(component); + u16 tx_vol_ctl_reg, decimator; + + decimator = va->active_decimator[dai->id]; + + tx_vol_ctl_reg = CDC_VA_TX0_TX_PATH_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + if (mute) + snd_soc_component_update_bits(component, tx_vol_ctl_reg, + CDC_VA_TX_PATH_PGA_MUTE_EN_MASK, + CDC_VA_TX_PATH_PGA_MUTE_EN); + else + snd_soc_component_update_bits(component, tx_vol_ctl_reg, + CDC_VA_TX_PATH_PGA_MUTE_EN_MASK, + CDC_VA_TX_PATH_PGA_MUTE_DISABLE); + + return 0; +} + +static struct snd_soc_dai_ops va_macro_dai_ops = { + .hw_params = va_macro_hw_params, + .get_channel_map = va_macro_get_channel_map, + .mute_stream = va_macro_digital_mute, +}; + +static struct snd_soc_dai_driver va_macro_dais[] = { + { + .name = "va_macro_tx1", + .id = VA_MACRO_AIF1_CAP, + .capture = { + .stream_name = "VA_AIF1 Capture", + .rates = VA_MACRO_RATES, + .formats = VA_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &va_macro_dai_ops, + }, + { + .name = "va_macro_tx2", + .id = VA_MACRO_AIF2_CAP, + .capture = { + .stream_name = "VA_AIF2 Capture", + .rates = VA_MACRO_RATES, + .formats = VA_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &va_macro_dai_ops, + }, + { + .name = "va_macro_tx3", + .id = VA_MACRO_AIF3_CAP, + .capture = { + .stream_name = "VA_AIF3 Capture", + .rates = VA_MACRO_RATES, + .formats = VA_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &va_macro_dai_ops, + }, +}; + +static const char * const adc_mux_text[] = { + "VA_DMIC", "SWR_MIC" +}; + +static SOC_ENUM_SINGLE_DECL(va_dec0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(va_dec1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(va_dec2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(va_dec3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG1, + 0, adc_mux_text); + +static const struct snd_kcontrol_new va_dec0_mux = SOC_DAPM_ENUM("va_dec0", + va_dec0_enum); +static const struct snd_kcontrol_new va_dec1_mux = SOC_DAPM_ENUM("va_dec1", + va_dec1_enum); +static const struct snd_kcontrol_new va_dec2_mux = SOC_DAPM_ENUM("va_dec2", + va_dec2_enum); +static const struct snd_kcontrol_new va_dec3_mux = SOC_DAPM_ENUM("va_dec3", + va_dec3_enum); + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "DMIC6", "DMIC7" +}; + +static SOC_ENUM_SINGLE_DECL(va_dmic0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(va_dmic1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(va_dmic2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(va_dmic3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG0, + 4, dmic_mux_text); + +static const struct snd_kcontrol_new va_dmic0_mux = SOC_DAPM_ENUM_EXT("va_dmic0", + va_dmic0_enum, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +static const struct snd_kcontrol_new va_dmic1_mux = SOC_DAPM_ENUM_EXT("va_dmic1", + va_dmic1_enum, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +static const struct snd_kcontrol_new va_dmic2_mux = SOC_DAPM_ENUM_EXT("va_dmic2", + va_dmic2_enum, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +static const struct snd_kcontrol_new va_dmic3_mux = SOC_DAPM_ENUM_EXT("va_dmic3", + va_dmic3_enum, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +static const struct snd_kcontrol_new va_aif1_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), +}; + +static const struct snd_kcontrol_new va_aif2_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), +}; + +static const struct snd_kcontrol_new va_aif3_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), +}; + +static const struct snd_soc_dapm_widget va_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("VA_AIF1 CAP", "VA_AIF1 Capture", 0, + SND_SOC_NOPM, VA_MACRO_AIF1_CAP, 0), + + SND_SOC_DAPM_AIF_OUT("VA_AIF2 CAP", "VA_AIF2 Capture", 0, + SND_SOC_NOPM, VA_MACRO_AIF2_CAP, 0), + + SND_SOC_DAPM_AIF_OUT("VA_AIF3 CAP", "VA_AIF3 Capture", 0, + SND_SOC_NOPM, VA_MACRO_AIF3_CAP, 0), + + SND_SOC_DAPM_MIXER("VA_AIF1_CAP Mixer", SND_SOC_NOPM, + VA_MACRO_AIF1_CAP, 0, + va_aif1_cap_mixer, ARRAY_SIZE(va_aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("VA_AIF2_CAP Mixer", SND_SOC_NOPM, + VA_MACRO_AIF2_CAP, 0, + va_aif2_cap_mixer, ARRAY_SIZE(va_aif2_cap_mixer)), + + SND_SOC_DAPM_MIXER("VA_AIF3_CAP Mixer", SND_SOC_NOPM, + VA_MACRO_AIF3_CAP, 0, + va_aif3_cap_mixer, ARRAY_SIZE(va_aif3_cap_mixer)), + + SND_SOC_DAPM_MUX("VA DMIC MUX0", SND_SOC_NOPM, 0, 0, &va_dmic0_mux), + SND_SOC_DAPM_MUX("VA DMIC MUX1", SND_SOC_NOPM, 0, 0, &va_dmic1_mux), + SND_SOC_DAPM_MUX("VA DMIC MUX2", SND_SOC_NOPM, 0, 0, &va_dmic2_mux), + SND_SOC_DAPM_MUX("VA DMIC MUX3", SND_SOC_NOPM, 0, 0, &va_dmic3_mux), + + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micb", 0, 0), + SND_SOC_DAPM_INPUT("DMIC0 Pin"), + SND_SOC_DAPM_INPUT("DMIC1 Pin"), + SND_SOC_DAPM_INPUT("DMIC2 Pin"), + SND_SOC_DAPM_INPUT("DMIC3 Pin"), + SND_SOC_DAPM_INPUT("DMIC4 Pin"), + SND_SOC_DAPM_INPUT("DMIC5 Pin"), + SND_SOC_DAPM_INPUT("DMIC6 Pin"), + SND_SOC_DAPM_INPUT("DMIC7 Pin"), + + SND_SOC_DAPM_ADC_E("VA DMIC0", NULL, SND_SOC_NOPM, 0, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC1", NULL, SND_SOC_NOPM, 1, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC2", NULL, SND_SOC_NOPM, 2, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC3", NULL, SND_SOC_NOPM, 3, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC4", NULL, SND_SOC_NOPM, 4, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC5", NULL, SND_SOC_NOPM, 5, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC6", NULL, SND_SOC_NOPM, 6, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC7", NULL, SND_SOC_NOPM, 7, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("VA SWR_ADC0"), + SND_SOC_DAPM_INPUT("VA SWR_ADC1"), + SND_SOC_DAPM_INPUT("VA SWR_ADC2"), + SND_SOC_DAPM_INPUT("VA SWR_ADC3"), + SND_SOC_DAPM_INPUT("VA SWR_MIC0"), + SND_SOC_DAPM_INPUT("VA SWR_MIC1"), + SND_SOC_DAPM_INPUT("VA SWR_MIC2"), + SND_SOC_DAPM_INPUT("VA SWR_MIC3"), + SND_SOC_DAPM_INPUT("VA SWR_MIC4"), + SND_SOC_DAPM_INPUT("VA SWR_MIC5"), + SND_SOC_DAPM_INPUT("VA SWR_MIC6"), + SND_SOC_DAPM_INPUT("VA SWR_MIC7"), + + SND_SOC_DAPM_MUX_E("VA DEC0 MUX", SND_SOC_NOPM, VA_MACRO_DEC0, 0, + &va_dec0_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC1 MUX", SND_SOC_NOPM, VA_MACRO_DEC1, 0, + &va_dec1_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC2 MUX", SND_SOC_NOPM, VA_MACRO_DEC2, 0, + &va_dec2_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC3 MUX", SND_SOC_NOPM, VA_MACRO_DEC3, 0, + &va_dec3_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("VA_MCLK", -1, SND_SOC_NOPM, 0, 0, + va_macro_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route va_audio_map[] = { + {"VA_AIF1 CAP", NULL, "VA_MCLK"}, + {"VA_AIF2 CAP", NULL, "VA_MCLK"}, + {"VA_AIF3 CAP", NULL, "VA_MCLK"}, + + {"VA_AIF1 CAP", NULL, "VA_AIF1_CAP Mixer"}, + {"VA_AIF2 CAP", NULL, "VA_AIF2_CAP Mixer"}, + {"VA_AIF3 CAP", NULL, "VA_AIF3_CAP Mixer"}, + + {"VA_AIF1_CAP Mixer", "DEC0", "VA DEC0 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC1", "VA DEC1 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC2", "VA DEC2 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC3", "VA DEC3 MUX"}, + + {"VA_AIF2_CAP Mixer", "DEC0", "VA DEC0 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC1", "VA DEC1 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC2", "VA DEC2 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC3", "VA DEC3 MUX"}, + + {"VA_AIF3_CAP Mixer", "DEC0", "VA DEC0 MUX"}, + {"VA_AIF3_CAP Mixer", "DEC1", "VA DEC1 MUX"}, + {"VA_AIF3_CAP Mixer", "DEC2", "VA DEC2 MUX"}, + {"VA_AIF3_CAP Mixer", "DEC3", "VA DEC3 MUX"}, + + {"VA DEC0 MUX", "VA_DMIC", "VA DMIC MUX0"}, + {"VA DMIC MUX0", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX0", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX0", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX0", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX0", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX0", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX0", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX0", "DMIC7", "VA DMIC7"}, + + {"VA DEC1 MUX", "VA_DMIC", "VA DMIC MUX1"}, + {"VA DMIC MUX1", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX1", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX1", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX1", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX1", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX1", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX1", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX1", "DMIC7", "VA DMIC7"}, + + {"VA DEC2 MUX", "VA_DMIC", "VA DMIC MUX2"}, + {"VA DMIC MUX2", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX2", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX2", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX2", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX2", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX2", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX2", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX2", "DMIC7", "VA DMIC7"}, + + {"VA DEC3 MUX", "VA_DMIC", "VA DMIC MUX3"}, + {"VA DMIC MUX3", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX3", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX3", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX3", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX3", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX3", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX3", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX3", "DMIC7", "VA DMIC7"}, + + { "VA DMIC0", NULL, "DMIC0 Pin" }, + { "VA DMIC1", NULL, "DMIC1 Pin" }, + { "VA DMIC2", NULL, "DMIC2 Pin" }, + { "VA DMIC3", NULL, "DMIC3 Pin" }, + { "VA DMIC4", NULL, "DMIC4 Pin" }, + { "VA DMIC5", NULL, "DMIC5 Pin" }, + { "VA DMIC6", NULL, "DMIC6 Pin" }, + { "VA DMIC7", NULL, "DMIC7 Pin" }, +}; + +static const char * const dec_mode_mux_text[] = { + "ADC_DEFAULT", "ADC_LOW_PWR", "ADC_HIGH_PERF", +}; + +static const struct soc_enum dec_mode_mux_enum[] = { + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), +}; + +static const struct snd_kcontrol_new va_macro_snd_controls[] = { + SOC_SINGLE_S8_TLV("VA_DEC0 Volume", CDC_VA_TX0_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("VA_DEC1 Volume", CDC_VA_TX1_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("VA_DEC2 Volume", CDC_VA_TX2_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("VA_DEC3 Volume", CDC_VA_TX3_TX_VOL_CTL, + -84, 40, digital_gain), + + SOC_ENUM_EXT("VA_DEC0 MODE", dec_mode_mux_enum[0], + va_macro_dec_mode_get, va_macro_dec_mode_put), + SOC_ENUM_EXT("VA_DEC1 MODE", dec_mode_mux_enum[1], + va_macro_dec_mode_get, va_macro_dec_mode_put), + SOC_ENUM_EXT("VA_DEC2 MODE", dec_mode_mux_enum[2], + va_macro_dec_mode_get, va_macro_dec_mode_put), + SOC_ENUM_EXT("VA_DEC3 MODE", dec_mode_mux_enum[3], + va_macro_dec_mode_get, va_macro_dec_mode_put), +}; + +static int va_macro_component_probe(struct snd_soc_component *component) +{ + struct va_macro *va = snd_soc_component_get_drvdata(component); + + snd_soc_component_init_regmap(component, va->regmap); + + return 0; +} + +static const struct snd_soc_component_driver va_macro_component_drv = { + .name = "VA MACRO", + .probe = va_macro_component_probe, + .controls = va_macro_snd_controls, + .num_controls = ARRAY_SIZE(va_macro_snd_controls), + .dapm_widgets = va_macro_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(va_macro_dapm_widgets), + .dapm_routes = va_audio_map, + .num_dapm_routes = ARRAY_SIZE(va_audio_map), +}; + +static int fsgen_gate_enable(struct clk_hw *hw) +{ + return va_macro_mclk_enable(to_va_macro(hw), true); +} + +static void fsgen_gate_disable(struct clk_hw *hw) +{ + va_macro_mclk_enable(to_va_macro(hw), false); +} + +static int fsgen_gate_is_enabled(struct clk_hw *hw) +{ + struct va_macro *va = to_va_macro(hw); + int val; + + regmap_read(va->regmap, CDC_VA_TOP_CSR_TOP_CFG0, &val); + + return !!(val & CDC_VA_FS_BROADCAST_EN); +} + +static const struct clk_ops fsgen_gate_ops = { + .prepare = fsgen_gate_enable, + .unprepare = fsgen_gate_disable, + .is_enabled = fsgen_gate_is_enabled, +}; + +static int va_macro_register_fsgen_output(struct va_macro *va) +{ + struct clk *parent = va->clks[2].clk; + struct device *dev = va->dev; + struct device_node *np = dev->of_node; + const char *parent_clk_name; + const char *clk_name = "fsgen"; + struct clk_init_data init; + int ret; + + parent_clk_name = __clk_get_name(parent); + + of_property_read_string(np, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = &fsgen_gate_ops; + init.flags = 0; + init.parent_names = &parent_clk_name; + init.num_parents = 1; + va->hw.init = &init; + ret = devm_clk_hw_register(va->dev, &va->hw); + if (ret) + return ret; + + return of_clk_add_provider(np, of_clk_src_simple_get, va->hw.clk); +} + +static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, + struct va_macro *va) +{ + u32 div_factor; + u32 mclk_rate = VA_MACRO_MCLK_FREQ; + + if (!dmic_sample_rate || mclk_rate % dmic_sample_rate != 0) + goto undefined_rate; + + div_factor = mclk_rate / dmic_sample_rate; + + switch (div_factor) { + case 2: + va->dmic_clk_div = VA_MACRO_CLK_DIV_2; + break; + case 3: + va->dmic_clk_div = VA_MACRO_CLK_DIV_3; + break; + case 4: + va->dmic_clk_div = VA_MACRO_CLK_DIV_4; + break; + case 6: + va->dmic_clk_div = VA_MACRO_CLK_DIV_6; + break; + case 8: + va->dmic_clk_div = VA_MACRO_CLK_DIV_8; + break; + case 16: + va->dmic_clk_div = VA_MACRO_CLK_DIV_16; + break; + default: + /* Any other DIV factor is invalid */ + goto undefined_rate; + } + + return dmic_sample_rate; + +undefined_rate: + dev_err(va->dev, "%s: Invalid rate %d, for mclk %d\n", + __func__, dmic_sample_rate, mclk_rate); + dmic_sample_rate = 0; + + return dmic_sample_rate; +} + +static int va_macro_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct va_macro *va; + void __iomem *base; + u32 sample_rate = 0; + int ret; + + va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL); + if (!va) + return -ENOMEM; + + va->dev = dev; + va->clks[0].id = "macro"; + va->clks[1].id = "dcodec"; + va->clks[2].id = "mclk"; + + ret = devm_clk_bulk_get(dev, VA_NUM_CLKS_MAX, va->clks); + if (ret) { + dev_err(dev, "Error getting VA Clocks (%d)\n", ret); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "qcom,dmic-sample-rate", + &sample_rate); + if (ret) { + dev_err(dev, "qcom,dmic-sample-rate dt entry missing\n"); + va->dmic_clk_div = VA_MACRO_CLK_DIV_2; + } else { + ret = va_macro_validate_dmic_sample_rate(sample_rate, va); + if (!ret) + return -EINVAL; + } + + /* mclk rate */ + clk_set_rate(va->clks[1].clk, VA_MACRO_MCLK_FREQ); + ret = clk_bulk_prepare_enable(VA_NUM_CLKS_MAX, va->clks); + if (ret) + return ret; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto err; + } + + va->regmap = devm_regmap_init_mmio(dev, base, &va_regmap_config); + if (IS_ERR(va->regmap)) { + ret = -EINVAL; + goto err; + } + + dev_set_drvdata(dev, va); + ret = va_macro_register_fsgen_output(va); + if (ret) + goto err; + + ret = devm_snd_soc_register_component(dev, &va_macro_component_drv, + va_macro_dais, + ARRAY_SIZE(va_macro_dais)); + if (ret) + goto soc_err; + + return ret; + +soc_err: + of_clk_del_provider(pdev->dev.of_node); +err: + clk_bulk_disable_unprepare(VA_NUM_CLKS_MAX, va->clks); + + return ret; +} + +static int va_macro_remove(struct platform_device *pdev) +{ + struct va_macro *va = dev_get_drvdata(&pdev->dev); + + of_clk_del_provider(pdev->dev.of_node); + clk_bulk_disable_unprepare(VA_NUM_CLKS_MAX, va->clks); + + return 0; +} + +static const struct of_device_id va_macro_dt_match[] = { + { .compatible = "qcom,sm8250-lpass-va-macro" }, + {} +}; +MODULE_DEVICE_TABLE(of, va_macro_dt_match); + +static struct platform_driver va_macro_driver = { + .driver = { + .name = "va_macro", + .of_match_table = va_macro_dt_match, + .suppress_bind_attrs = true, + }, + .probe = va_macro_probe, + .remove = va_macro_remove, +}; + +module_platform_driver(va_macro_driver); +MODULE_DESCRIPTION("VA macro driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c new file mode 100644 index 000000000000..25f1df214ca5 --- /dev/null +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -0,0 +1,2464 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/of_clk.h> +#include <linux/clk-provider.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <linux/of_platform.h> +#include <sound/tlv.h> +#include "lpass-wsa-macro.h" + +#define CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL (0x0000) +#define CDC_WSA_MCLK_EN_MASK BIT(0) +#define CDC_WSA_MCLK_ENABLE BIT(0) +#define CDC_WSA_MCLK_DISABLE 0 +#define CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004) +#define CDC_WSA_FS_CNT_EN_MASK BIT(0) +#define CDC_WSA_FS_CNT_ENABLE BIT(0) +#define CDC_WSA_FS_CNT_DISABLE 0 +#define CDC_WSA_CLK_RST_CTRL_SWR_CONTROL (0x0008) +#define CDC_WSA_SWR_CLK_EN_MASK BIT(0) +#define CDC_WSA_SWR_CLK_ENABLE BIT(0) +#define CDC_WSA_SWR_RST_EN_MASK BIT(1) +#define CDC_WSA_SWR_RST_ENABLE BIT(1) +#define CDC_WSA_SWR_RST_DISABLE 0 +#define CDC_WSA_TOP_TOP_CFG0 (0x0080) +#define CDC_WSA_TOP_TOP_CFG1 (0x0084) +#define CDC_WSA_TOP_FREQ_MCLK (0x0088) +#define CDC_WSA_TOP_DEBUG_BUS_SEL (0x008C) +#define CDC_WSA_TOP_DEBUG_EN0 (0x0090) +#define CDC_WSA_TOP_DEBUG_EN1 (0x0094) +#define CDC_WSA_TOP_DEBUG_DSM_LB (0x0098) +#define CDC_WSA_TOP_RX_I2S_CTL (0x009C) +#define CDC_WSA_TOP_TX_I2S_CTL (0x00A0) +#define CDC_WSA_TOP_I2S_CLK (0x00A4) +#define CDC_WSA_TOP_I2S_RESET (0x00A8) +#define CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 (0x0100) +#define CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK GENMASK(5, 3) +#define CDC_WSA_RX_INTX_2_SEL_MASK GENMASK(2, 0) +#define CDC_WSA_RX_INP_MUX_RX_INT0_CFG1 (0x0104) +#define CDC_WSA_RX_INP_MUX_RX_INT1_CFG0 (0x0108) +#define CDC_WSA_RX_INP_MUX_RX_INT1_CFG1 (0x010C) +#define CDC_WSA_RX_INP_MUX_RX_MIX_CFG0 (0x0110) +#define CDC_WSA_RX_MIX_TX1_SEL_MASK GENMASK(5, 3) +#define CDC_WSA_RX_MIX_TX1_SEL_SHFT 3 +#define CDC_WSA_RX_MIX_TX0_SEL_MASK GENMASK(2, 0) +#define CDC_WSA_RX_INP_MUX_RX_EC_CFG0 (0x0114) +#define CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0 (0x0118) +#define CDC_WSA_TX0_SPKR_PROT_PATH_CTL (0x0244) +#define CDC_WSA_TX_SPKR_PROT_RESET_MASK BIT(5) +#define CDC_WSA_TX_SPKR_PROT_RESET BIT(5) +#define CDC_WSA_TX_SPKR_PROT_NO_RESET 0 +#define CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK BIT(4) +#define CDC_WSA_TX_SPKR_PROT_CLK_ENABLE BIT(4) +#define CDC_WSA_TX_SPKR_PROT_CLK_DISABLE 0 +#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK GENMASK(3, 0) +#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K 0 +#define CDC_WSA_TX0_SPKR_PROT_PATH_CFG0 (0x0248) +#define CDC_WSA_TX1_SPKR_PROT_PATH_CTL (0x0264) +#define CDC_WSA_TX1_SPKR_PROT_PATH_CFG0 (0x0268) +#define CDC_WSA_TX2_SPKR_PROT_PATH_CTL (0x0284) +#define CDC_WSA_TX2_SPKR_PROT_PATH_CFG0 (0x0288) +#define CDC_WSA_TX3_SPKR_PROT_PATH_CTL (0x02A4) +#define CDC_WSA_TX3_SPKR_PROT_PATH_CFG0 (0x02A8) +#define CDC_WSA_INTR_CTRL_CFG (0x0340) +#define CDC_WSA_INTR_CTRL_CLR_COMMIT (0x0344) +#define CDC_WSA_INTR_CTRL_PIN1_MASK0 (0x0360) +#define CDC_WSA_INTR_CTRL_PIN1_STATUS0 (0x0368) +#define CDC_WSA_INTR_CTRL_PIN1_CLEAR0 (0x0370) +#define CDC_WSA_INTR_CTRL_PIN2_MASK0 (0x0380) +#define CDC_WSA_INTR_CTRL_PIN2_STATUS0 (0x0388) +#define CDC_WSA_INTR_CTRL_PIN2_CLEAR0 (0x0390) +#define CDC_WSA_INTR_CTRL_LEVEL0 (0x03C0) +#define CDC_WSA_INTR_CTRL_BYPASS0 (0x03C8) +#define CDC_WSA_INTR_CTRL_SET0 (0x03D0) +#define CDC_WSA_RX0_RX_PATH_CTL (0x0400) +#define CDC_WSA_RX_PATH_CLK_EN_MASK BIT(5) +#define CDC_WSA_RX_PATH_CLK_ENABLE BIT(5) +#define CDC_WSA_RX_PATH_CLK_DISABLE 0 +#define CDC_WSA_RX_PATH_PGA_MUTE_EN_MASK BIT(4) +#define CDC_WSA_RX_PATH_PGA_MUTE_ENABLE BIT(4) +#define CDC_WSA_RX_PATH_PGA_MUTE_DISABLE 0 +#define CDC_WSA_RX0_RX_PATH_CFG0 (0x0404) +#define CDC_WSA_RX_PATH_COMP_EN_MASK BIT(1) +#define CDC_WSA_RX_PATH_COMP_ENABLE BIT(1) +#define CDC_WSA_RX_PATH_HD2_EN_MASK BIT(2) +#define CDC_WSA_RX_PATH_HD2_ENABLE BIT(2) +#define CDC_WSA_RX_PATH_SPKR_RATE_MASK BIT(3) +#define CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072 BIT(3) +#define CDC_WSA_RX0_RX_PATH_CFG1 (0x0408) +#define CDC_WSA_RX_PATH_SMART_BST_EN_MASK BIT(0) +#define CDC_WSA_RX_PATH_SMART_BST_ENABLE BIT(0) +#define CDC_WSA_RX_PATH_SMART_BST_DISABLE 0 +#define CDC_WSA_RX0_RX_PATH_CFG2 (0x040C) +#define CDC_WSA_RX0_RX_PATH_CFG3 (0x0410) +#define CDC_WSA_RX_DC_DCOEFF_MASK GENMASK(1, 0) +#define CDC_WSA_RX0_RX_VOL_CTL (0x0414) +#define CDC_WSA_RX0_RX_PATH_MIX_CTL (0x0418) +#define CDC_WSA_RX_PATH_MIX_CLK_EN_MASK BIT(5) +#define CDC_WSA_RX_PATH_MIX_CLK_ENABLE BIT(5) +#define CDC_WSA_RX_PATH_MIX_CLK_DISABLE 0 +#define CDC_WSA_RX0_RX_PATH_MIX_CFG (0x041C) +#define CDC_WSA_RX0_RX_VOL_MIX_CTL (0x0420) +#define CDC_WSA_RX0_RX_PATH_SEC0 (0x0424) +#define CDC_WSA_RX0_RX_PATH_SEC1 (0x0428) +#define CDC_WSA_RX_PGA_HALF_DB_MASK BIT(0) +#define CDC_WSA_RX_PGA_HALF_DB_ENABLE BIT(0) +#define CDC_WSA_RX_PGA_HALF_DB_DISABLE 0 +#define CDC_WSA_RX0_RX_PATH_SEC2 (0x042C) +#define CDC_WSA_RX0_RX_PATH_SEC3 (0x0430) +#define CDC_WSA_RX_PATH_HD2_SCALE_MASK GENMASK(1, 0) +#define CDC_WSA_RX_PATH_HD2_ALPHA_MASK GENMASK(5, 2) +#define CDC_WSA_RX0_RX_PATH_SEC5 (0x0438) +#define CDC_WSA_RX0_RX_PATH_SEC6 (0x043C) +#define CDC_WSA_RX0_RX_PATH_SEC7 (0x0440) +#define CDC_WSA_RX0_RX_PATH_MIX_SEC0 (0x0444) +#define CDC_WSA_RX0_RX_PATH_MIX_SEC1 (0x0448) +#define CDC_WSA_RX0_RX_PATH_DSMDEM_CTL (0x044C) +#define CDC_WSA_RX_DSMDEM_CLK_EN_MASK BIT(0) +#define CDC_WSA_RX_DSMDEM_CLK_ENABLE BIT(0) +#define CDC_WSA_RX1_RX_PATH_CTL (0x0480) +#define CDC_WSA_RX1_RX_PATH_CFG0 (0x0484) +#define CDC_WSA_RX1_RX_PATH_CFG1 (0x0488) +#define CDC_WSA_RX1_RX_PATH_CFG2 (0x048C) +#define CDC_WSA_RX1_RX_PATH_CFG3 (0x0490) +#define CDC_WSA_RX1_RX_VOL_CTL (0x0494) +#define CDC_WSA_RX1_RX_PATH_MIX_CTL (0x0498) +#define CDC_WSA_RX1_RX_PATH_MIX_CFG (0x049C) +#define CDC_WSA_RX1_RX_VOL_MIX_CTL (0x04A0) +#define CDC_WSA_RX1_RX_PATH_SEC0 (0x04A4) +#define CDC_WSA_RX1_RX_PATH_SEC1 (0x04A8) +#define CDC_WSA_RX1_RX_PATH_SEC2 (0x04AC) +#define CDC_WSA_RX1_RX_PATH_SEC3 (0x04B0) +#define CDC_WSA_RX1_RX_PATH_SEC5 (0x04B8) +#define CDC_WSA_RX1_RX_PATH_SEC6 (0x04BC) +#define CDC_WSA_RX1_RX_PATH_SEC7 (0x04C0) +#define CDC_WSA_RX1_RX_PATH_MIX_SEC0 (0x04C4) +#define CDC_WSA_RX1_RX_PATH_MIX_SEC1 (0x04C8) +#define CDC_WSA_RX1_RX_PATH_DSMDEM_CTL (0x04CC) +#define CDC_WSA_BOOST0_BOOST_PATH_CTL (0x0500) +#define CDC_WSA_BOOST_PATH_CLK_EN_MASK BIT(4) +#define CDC_WSA_BOOST_PATH_CLK_ENABLE BIT(4) +#define CDC_WSA_BOOST_PATH_CLK_DISABLE 0 +#define CDC_WSA_BOOST0_BOOST_CTL (0x0504) +#define CDC_WSA_BOOST0_BOOST_CFG1 (0x0508) +#define CDC_WSA_BOOST0_BOOST_CFG2 (0x050C) +#define CDC_WSA_BOOST1_BOOST_PATH_CTL (0x0540) +#define CDC_WSA_BOOST1_BOOST_CTL (0x0544) +#define CDC_WSA_BOOST1_BOOST_CFG1 (0x0548) +#define CDC_WSA_BOOST1_BOOST_CFG2 (0x054C) +#define CDC_WSA_COMPANDER0_CTL0 (0x0580) +#define CDC_WSA_COMPANDER_CLK_EN_MASK BIT(0) +#define CDC_WSA_COMPANDER_CLK_ENABLE BIT(0) +#define CDC_WSA_COMPANDER_SOFT_RST_MASK BIT(1) +#define CDC_WSA_COMPANDER_SOFT_RST_ENABLE BIT(1) +#define CDC_WSA_COMPANDER_HALT_MASK BIT(2) +#define CDC_WSA_COMPANDER_HALT BIT(2) +#define CDC_WSA_COMPANDER0_CTL1 (0x0584) +#define CDC_WSA_COMPANDER0_CTL2 (0x0588) +#define CDC_WSA_COMPANDER0_CTL3 (0x058C) +#define CDC_WSA_COMPANDER0_CTL4 (0x0590) +#define CDC_WSA_COMPANDER0_CTL5 (0x0594) +#define CDC_WSA_COMPANDER0_CTL6 (0x0598) +#define CDC_WSA_COMPANDER0_CTL7 (0x059C) +#define CDC_WSA_COMPANDER1_CTL0 (0x05C0) +#define CDC_WSA_COMPANDER1_CTL1 (0x05C4) +#define CDC_WSA_COMPANDER1_CTL2 (0x05C8) +#define CDC_WSA_COMPANDER1_CTL3 (0x05CC) +#define CDC_WSA_COMPANDER1_CTL4 (0x05D0) +#define CDC_WSA_COMPANDER1_CTL5 (0x05D4) +#define CDC_WSA_COMPANDER1_CTL6 (0x05D8) +#define CDC_WSA_COMPANDER1_CTL7 (0x05DC) +#define CDC_WSA_SOFTCLIP0_CRC (0x0600) +#define CDC_WSA_SOFTCLIP_CLK_EN_MASK BIT(0) +#define CDC_WSA_SOFTCLIP_CLK_ENABLE BIT(0) +#define CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0604) +#define CDC_WSA_SOFTCLIP_EN_MASK BIT(0) +#define CDC_WSA_SOFTCLIP_ENABLE BIT(0) +#define CDC_WSA_SOFTCLIP1_CRC (0x0640) +#define CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0644) +#define CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL (0x0680) +#define CDC_WSA_EC_HQ_EC_CLK_EN_MASK BIT(0) +#define CDC_WSA_EC_HQ_EC_CLK_ENABLE BIT(0) +#define CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 (0x0684) +#define CDC_WSA_EC_HQ_EC_REF_PCM_RATE_MASK GENMASK(4, 1) +#define CDC_WSA_EC_HQ_EC_REF_PCM_RATE_48K BIT(3) +#define CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL (0x06C0) +#define CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0 (0x06C4) +#define CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL (0x0700) +#define CDC_WSA_SPLINE_ASRC0_CTL0 (0x0704) +#define CDC_WSA_SPLINE_ASRC0_CTL1 (0x0708) +#define CDC_WSA_SPLINE_ASRC0_FIFO_CTL (0x070C) +#define CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB (0x0710) +#define CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB (0x0714) +#define CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB (0x0718) +#define CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB (0x071C) +#define CDC_WSA_SPLINE_ASRC0_STATUS_FIFO (0x0720) +#define CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL (0x0740) +#define CDC_WSA_SPLINE_ASRC1_CTL0 (0x0744) +#define CDC_WSA_SPLINE_ASRC1_CTL1 (0x0748) +#define CDC_WSA_SPLINE_ASRC1_FIFO_CTL (0x074C) +#define CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB (0x0750) +#define CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB (0x0754) +#define CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB (0x0758) +#define CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB (0x075C) +#define CDC_WSA_SPLINE_ASRC1_STATUS_FIFO (0x0760) +#define WSA_MAX_OFFSET (0x0760) + +#define WSA_MACRO_RX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define WSA_MACRO_RX_MIX_RATES (SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define WSA_MACRO_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define WSA_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_48000) +#define WSA_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define NUM_INTERPOLATORS 2 +#define WSA_NUM_CLKS_MAX 5 +#define WSA_MACRO_MCLK_FREQ 19200000 +#define WSA_MACRO_MUX_INP_SHFT 0x3 +#define WSA_MACRO_MUX_INP_MASK1 0x07 +#define WSA_MACRO_MUX_INP_MASK2 0x38 +#define WSA_MACRO_MUX_CFG_OFFSET 0x8 +#define WSA_MACRO_MUX_CFG1_OFFSET 0x4 +#define WSA_MACRO_RX_COMP_OFFSET 0x40 +#define WSA_MACRO_RX_SOFTCLIP_OFFSET 0x40 +#define WSA_MACRO_RX_PATH_OFFSET 0x80 +#define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10 +#define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C +#define WSA_MACRO_FS_RATE_MASK 0x0F +#define WSA_MACRO_EC_MIX_TX0_MASK 0x03 +#define WSA_MACRO_EC_MIX_TX1_MASK 0x18 +#define WSA_MACRO_MAX_DMA_CH_PER_PORT 0x2 + +enum { + WSA_MACRO_GAIN_OFFSET_M1P5_DB, + WSA_MACRO_GAIN_OFFSET_0_DB, +}; +enum { + WSA_MACRO_RX0 = 0, + WSA_MACRO_RX1, + WSA_MACRO_RX_MIX, + WSA_MACRO_RX_MIX0 = WSA_MACRO_RX_MIX, + WSA_MACRO_RX_MIX1, + WSA_MACRO_RX_MAX, +}; + +enum { + WSA_MACRO_TX0 = 0, + WSA_MACRO_TX1, + WSA_MACRO_TX_MAX, +}; + +enum { + WSA_MACRO_EC0_MUX = 0, + WSA_MACRO_EC1_MUX, + WSA_MACRO_EC_MUX_MAX, +}; + +enum { + WSA_MACRO_COMP1, /* SPK_L */ + WSA_MACRO_COMP2, /* SPK_R */ + WSA_MACRO_COMP_MAX +}; + +enum { + WSA_MACRO_SOFTCLIP0, /* RX0 */ + WSA_MACRO_SOFTCLIP1, /* RX1 */ + WSA_MACRO_SOFTCLIP_MAX +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate int_prim_sample_rate_val[] = { + {8000, 0x0}, /* 8K */ + {16000, 0x1}, /* 16K */ + {24000, -EINVAL},/* 24K */ + {32000, 0x3}, /* 32K */ + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ + {384000, 0x7}, /* 384K */ + {44100, 0x8}, /* 44.1K */ +}; + +static struct interp_sample_rate int_mix_sample_rate_val[] = { + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ +}; + +enum { + WSA_MACRO_AIF_INVALID = 0, + WSA_MACRO_AIF1_PB, + WSA_MACRO_AIF_MIX1_PB, + WSA_MACRO_AIF_VI, + WSA_MACRO_AIF_ECHO, + WSA_MACRO_MAX_DAIS, +}; + +struct wsa_macro { + struct device *dev; + int comp_enabled[WSA_MACRO_COMP_MAX]; + int ec_hq[WSA_MACRO_RX1 + 1]; + u16 prim_int_users[WSA_MACRO_RX1 + 1]; + u16 wsa_mclk_users; + bool reset_swr; + unsigned long active_ch_mask[WSA_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[WSA_MACRO_MAX_DAIS]; + int rx_port_value[WSA_MACRO_RX_MAX]; + int ear_spkr_gain; + int spkr_gain_offset; + int spkr_mode; + int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX]; + int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX]; + struct regmap *regmap; + struct clk_bulk_data clks[WSA_NUM_CLKS_MAX]; + struct clk_hw hw; +}; +#define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw) + +static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); + +static const char *const rx_text[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1" +}; + +static const char *const rx_mix_text[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1" +}; + +static const char *const rx_mix_ec_text[] = { + "ZERO", "RX_MIX_TX0", "RX_MIX_TX1" +}; + +static const char *const rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF_MIX1_PB" +}; + +static const char *const rx_sidetone_mix_text[] = { + "ZERO", "SRC0" +}; + +static const char * const wsa_macro_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_ear_spkr_pa_gain_enum, + wsa_macro_ear_spkr_pa_gain_text); + +/* RX INT0 */ +static const struct soc_enum rx0_prim_inp0_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 0, 7, rx_text); + +static const struct soc_enum rx0_prim_inp1_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 3, 7, rx_text); + +static const struct soc_enum rx0_prim_inp2_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 3, 7, rx_text); + +static const struct soc_enum rx0_mix_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 0, 5, rx_mix_text); + +static const struct soc_enum rx0_sidetone_mix_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_sidetone_mix_text); + +static const struct snd_kcontrol_new rx0_prim_inp0_mux = + SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum); + +static const struct snd_kcontrol_new rx0_prim_inp1_mux = + SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum); + +static const struct snd_kcontrol_new rx0_prim_inp2_mux = + SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum); + +static const struct snd_kcontrol_new rx0_mix_mux = + SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum); + +static const struct snd_kcontrol_new rx0_sidetone_mix_mux = + SOC_DAPM_ENUM("WSA_RX0 SIDETONE MIX Mux", rx0_sidetone_mix_enum); + +/* RX INT1 */ +static const struct soc_enum rx1_prim_inp0_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 0, 7, rx_text); + +static const struct soc_enum rx1_prim_inp1_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 3, 7, rx_text); + +static const struct soc_enum rx1_prim_inp2_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 3, 7, rx_text); + +static const struct soc_enum rx1_mix_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 0, 5, rx_mix_text); + +static const struct snd_kcontrol_new rx1_prim_inp0_mux = + SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum); + +static const struct snd_kcontrol_new rx1_prim_inp1_mux = + SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum); + +static const struct snd_kcontrol_new rx1_prim_inp2_mux = + SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum); + +static const struct snd_kcontrol_new rx1_mix_mux = + SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum); + +static const struct soc_enum rx_mix_ec0_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, + 0, 3, rx_mix_ec_text); + +static const struct soc_enum rx_mix_ec1_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, + 3, 3, rx_mix_ec_text); + +static const struct snd_kcontrol_new rx_mix_ec0_mux = + SOC_DAPM_ENUM("WSA RX_MIX EC0_Mux", rx_mix_ec0_enum); + +static const struct snd_kcontrol_new rx_mix_ec1_mux = + SOC_DAPM_ENUM("WSA RX_MIX EC1_Mux", rx_mix_ec1_enum); + +static const struct reg_default wsa_defaults[] = { + /* WSA Macro */ + { CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, 0x00}, + { CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00}, + { CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, 0x00}, + { CDC_WSA_TOP_TOP_CFG0, 0x00}, + { CDC_WSA_TOP_TOP_CFG1, 0x00}, + { CDC_WSA_TOP_FREQ_MCLK, 0x00}, + { CDC_WSA_TOP_DEBUG_BUS_SEL, 0x00}, + { CDC_WSA_TOP_DEBUG_EN0, 0x00}, + { CDC_WSA_TOP_DEBUG_EN1, 0x00}, + { CDC_WSA_TOP_DEBUG_DSM_LB, 0x88}, + { CDC_WSA_TOP_RX_I2S_CTL, 0x0C}, + { CDC_WSA_TOP_TX_I2S_CTL, 0x0C}, + { CDC_WSA_TOP_I2S_CLK, 0x02}, + { CDC_WSA_TOP_I2S_RESET, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_EC_CFG0, 0x00}, + { CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, 0x00}, + { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_INTR_CTRL_CFG, 0x00}, + { CDC_WSA_INTR_CTRL_CLR_COMMIT, 0x00}, + { CDC_WSA_INTR_CTRL_PIN1_MASK0, 0xFF}, + { CDC_WSA_INTR_CTRL_PIN1_STATUS0, 0x00}, + { CDC_WSA_INTR_CTRL_PIN1_CLEAR0, 0x00}, + { CDC_WSA_INTR_CTRL_PIN2_MASK0, 0xFF}, + { CDC_WSA_INTR_CTRL_PIN2_STATUS0, 0x00}, + { CDC_WSA_INTR_CTRL_PIN2_CLEAR0, 0x00}, + { CDC_WSA_INTR_CTRL_LEVEL0, 0x00}, + { CDC_WSA_INTR_CTRL_BYPASS0, 0x00}, + { CDC_WSA_INTR_CTRL_SET0, 0x00}, + { CDC_WSA_RX0_RX_PATH_CTL, 0x04}, + { CDC_WSA_RX0_RX_PATH_CFG0, 0x00}, + { CDC_WSA_RX0_RX_PATH_CFG1, 0x64}, + { CDC_WSA_RX0_RX_PATH_CFG2, 0x8F}, + { CDC_WSA_RX0_RX_PATH_CFG3, 0x00}, + { CDC_WSA_RX0_RX_VOL_CTL, 0x00}, + { CDC_WSA_RX0_RX_PATH_MIX_CTL, 0x04}, + { CDC_WSA_RX0_RX_PATH_MIX_CFG, 0x7E}, + { CDC_WSA_RX0_RX_VOL_MIX_CTL, 0x00}, + { CDC_WSA_RX0_RX_PATH_SEC0, 0x04}, + { CDC_WSA_RX0_RX_PATH_SEC1, 0x08}, + { CDC_WSA_RX0_RX_PATH_SEC2, 0x00}, + { CDC_WSA_RX0_RX_PATH_SEC3, 0x00}, + { CDC_WSA_RX0_RX_PATH_SEC5, 0x00}, + { CDC_WSA_RX0_RX_PATH_SEC6, 0x00}, + { CDC_WSA_RX0_RX_PATH_SEC7, 0x00}, + { CDC_WSA_RX0_RX_PATH_MIX_SEC0, 0x08}, + { CDC_WSA_RX0_RX_PATH_MIX_SEC1, 0x00}, + { CDC_WSA_RX0_RX_PATH_DSMDEM_CTL, 0x00}, + { CDC_WSA_RX1_RX_PATH_CFG0, 0x00}, + { CDC_WSA_RX1_RX_PATH_CFG1, 0x64}, + { CDC_WSA_RX1_RX_PATH_CFG2, 0x8F}, + { CDC_WSA_RX1_RX_PATH_CFG3, 0x00}, + { CDC_WSA_RX1_RX_VOL_CTL, 0x00}, + { CDC_WSA_RX1_RX_PATH_MIX_CTL, 0x04}, + { CDC_WSA_RX1_RX_PATH_MIX_CFG, 0x7E}, + { CDC_WSA_RX1_RX_VOL_MIX_CTL, 0x00}, + { CDC_WSA_RX1_RX_PATH_SEC0, 0x04}, + { CDC_WSA_RX1_RX_PATH_SEC1, 0x08}, + { CDC_WSA_RX1_RX_PATH_SEC2, 0x00}, + { CDC_WSA_RX1_RX_PATH_SEC3, 0x00}, + { CDC_WSA_RX1_RX_PATH_SEC5, 0x00}, + { CDC_WSA_RX1_RX_PATH_SEC6, 0x00}, + { CDC_WSA_RX1_RX_PATH_SEC7, 0x00}, + { CDC_WSA_RX1_RX_PATH_MIX_SEC0, 0x08}, + { CDC_WSA_RX1_RX_PATH_MIX_SEC1, 0x00}, + { CDC_WSA_RX1_RX_PATH_DSMDEM_CTL, 0x00}, + { CDC_WSA_BOOST0_BOOST_PATH_CTL, 0x00}, + { CDC_WSA_BOOST0_BOOST_CTL, 0xD0}, + { CDC_WSA_BOOST0_BOOST_CFG1, 0x89}, + { CDC_WSA_BOOST0_BOOST_CFG2, 0x04}, + { CDC_WSA_BOOST1_BOOST_PATH_CTL, 0x00}, + { CDC_WSA_BOOST1_BOOST_CTL, 0xD0}, + { CDC_WSA_BOOST1_BOOST_CFG1, 0x89}, + { CDC_WSA_BOOST1_BOOST_CFG2, 0x04}, + { CDC_WSA_COMPANDER0_CTL0, 0x60}, + { CDC_WSA_COMPANDER0_CTL1, 0xDB}, + { CDC_WSA_COMPANDER0_CTL2, 0xFF}, + { CDC_WSA_COMPANDER0_CTL3, 0x35}, + { CDC_WSA_COMPANDER0_CTL4, 0xFF}, + { CDC_WSA_COMPANDER0_CTL5, 0x00}, + { CDC_WSA_COMPANDER0_CTL6, 0x01}, + { CDC_WSA_COMPANDER0_CTL7, 0x28}, + { CDC_WSA_COMPANDER1_CTL0, 0x60}, + { CDC_WSA_COMPANDER1_CTL1, 0xDB}, + { CDC_WSA_COMPANDER1_CTL2, 0xFF}, + { CDC_WSA_COMPANDER1_CTL3, 0x35}, + { CDC_WSA_COMPANDER1_CTL4, 0xFF}, + { CDC_WSA_COMPANDER1_CTL5, 0x00}, + { CDC_WSA_COMPANDER1_CTL6, 0x01}, + { CDC_WSA_COMPANDER1_CTL7, 0x28}, + { CDC_WSA_SOFTCLIP0_CRC, 0x00}, + { CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38}, + { CDC_WSA_SOFTCLIP1_CRC, 0x00}, + { CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, + { CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL, 0x00}, + { CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0, 0x01}, + { CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL, 0x00}, + { CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0, 0x01}, + { CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL, 0x00}, + { CDC_WSA_SPLINE_ASRC0_CTL0, 0x00}, + { CDC_WSA_SPLINE_ASRC0_CTL1, 0x00}, + { CDC_WSA_SPLINE_ASRC0_FIFO_CTL, 0xA8}, + { CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00}, + { CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00}, + { CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00}, + { CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00}, + { CDC_WSA_SPLINE_ASRC0_STATUS_FIFO, 0x00}, + { CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL, 0x00}, + { CDC_WSA_SPLINE_ASRC1_CTL0, 0x00}, + { CDC_WSA_SPLINE_ASRC1_CTL1, 0x00}, + { CDC_WSA_SPLINE_ASRC1_FIFO_CTL, 0xA8}, + { CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00}, + { CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00}, + { CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00}, + { CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00}, + { CDC_WSA_SPLINE_ASRC1_STATUS_FIFO, 0x00}, +}; + +static bool wsa_is_wronly_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case CDC_WSA_INTR_CTRL_CLR_COMMIT: + case CDC_WSA_INTR_CTRL_PIN1_CLEAR0: + case CDC_WSA_INTR_CTRL_PIN2_CLEAR0: + return true; + } + + return false; +} + +static bool wsa_is_rw_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL: + case CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL: + case CDC_WSA_CLK_RST_CTRL_SWR_CONTROL: + case CDC_WSA_TOP_TOP_CFG0: + case CDC_WSA_TOP_TOP_CFG1: + case CDC_WSA_TOP_FREQ_MCLK: + case CDC_WSA_TOP_DEBUG_BUS_SEL: + case CDC_WSA_TOP_DEBUG_EN0: + case CDC_WSA_TOP_DEBUG_EN1: + case CDC_WSA_TOP_DEBUG_DSM_LB: + case CDC_WSA_TOP_RX_I2S_CTL: + case CDC_WSA_TOP_TX_I2S_CTL: + case CDC_WSA_TOP_I2S_CLK: + case CDC_WSA_TOP_I2S_RESET: + case CDC_WSA_RX_INP_MUX_RX_INT0_CFG0: + case CDC_WSA_RX_INP_MUX_RX_INT0_CFG1: + case CDC_WSA_RX_INP_MUX_RX_INT1_CFG0: + case CDC_WSA_RX_INP_MUX_RX_INT1_CFG1: + case CDC_WSA_RX_INP_MUX_RX_MIX_CFG0: + case CDC_WSA_RX_INP_MUX_RX_EC_CFG0: + case CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0: + case CDC_WSA_TX0_SPKR_PROT_PATH_CTL: + case CDC_WSA_TX0_SPKR_PROT_PATH_CFG0: + case CDC_WSA_TX1_SPKR_PROT_PATH_CTL: + case CDC_WSA_TX1_SPKR_PROT_PATH_CFG0: + case CDC_WSA_TX2_SPKR_PROT_PATH_CTL: + case CDC_WSA_TX2_SPKR_PROT_PATH_CFG0: + case CDC_WSA_TX3_SPKR_PROT_PATH_CTL: + case CDC_WSA_TX3_SPKR_PROT_PATH_CFG0: + case CDC_WSA_INTR_CTRL_CFG: + case CDC_WSA_INTR_CTRL_PIN1_MASK0: + case CDC_WSA_INTR_CTRL_PIN2_MASK0: + case CDC_WSA_INTR_CTRL_LEVEL0: + case CDC_WSA_INTR_CTRL_BYPASS0: + case CDC_WSA_INTR_CTRL_SET0: + case CDC_WSA_RX0_RX_PATH_CTL: + case CDC_WSA_RX0_RX_PATH_CFG0: + case CDC_WSA_RX0_RX_PATH_CFG1: + case CDC_WSA_RX0_RX_PATH_CFG2: + case CDC_WSA_RX0_RX_PATH_CFG3: + case CDC_WSA_RX0_RX_VOL_CTL: + case CDC_WSA_RX0_RX_PATH_MIX_CTL: + case CDC_WSA_RX0_RX_PATH_MIX_CFG: + case CDC_WSA_RX0_RX_VOL_MIX_CTL: + case CDC_WSA_RX0_RX_PATH_SEC0: + case CDC_WSA_RX0_RX_PATH_SEC1: + case CDC_WSA_RX0_RX_PATH_SEC2: + case CDC_WSA_RX0_RX_PATH_SEC3: + case CDC_WSA_RX0_RX_PATH_SEC5: + case CDC_WSA_RX0_RX_PATH_SEC6: + case CDC_WSA_RX0_RX_PATH_SEC7: + case CDC_WSA_RX0_RX_PATH_MIX_SEC0: + case CDC_WSA_RX0_RX_PATH_MIX_SEC1: + case CDC_WSA_RX0_RX_PATH_DSMDEM_CTL: + case CDC_WSA_RX1_RX_PATH_CTL: + case CDC_WSA_RX1_RX_PATH_CFG0: + case CDC_WSA_RX1_RX_PATH_CFG1: + case CDC_WSA_RX1_RX_PATH_CFG2: + case CDC_WSA_RX1_RX_PATH_CFG3: + case CDC_WSA_RX1_RX_VOL_CTL: + case CDC_WSA_RX1_RX_PATH_MIX_CTL: + case CDC_WSA_RX1_RX_PATH_MIX_CFG: + case CDC_WSA_RX1_RX_VOL_MIX_CTL: + case CDC_WSA_RX1_RX_PATH_SEC0: + case CDC_WSA_RX1_RX_PATH_SEC1: + case CDC_WSA_RX1_RX_PATH_SEC2: + case CDC_WSA_RX1_RX_PATH_SEC3: + case CDC_WSA_RX1_RX_PATH_SEC5: + case CDC_WSA_RX1_RX_PATH_SEC6: + case CDC_WSA_RX1_RX_PATH_SEC7: + case CDC_WSA_RX1_RX_PATH_MIX_SEC0: + case CDC_WSA_RX1_RX_PATH_MIX_SEC1: + case CDC_WSA_RX1_RX_PATH_DSMDEM_CTL: + case CDC_WSA_BOOST0_BOOST_PATH_CTL: + case CDC_WSA_BOOST0_BOOST_CTL: + case CDC_WSA_BOOST0_BOOST_CFG1: + case CDC_WSA_BOOST0_BOOST_CFG2: + case CDC_WSA_BOOST1_BOOST_PATH_CTL: + case CDC_WSA_BOOST1_BOOST_CTL: + case CDC_WSA_BOOST1_BOOST_CFG1: + case CDC_WSA_BOOST1_BOOST_CFG2: + case CDC_WSA_COMPANDER0_CTL0: + case CDC_WSA_COMPANDER0_CTL1: + case CDC_WSA_COMPANDER0_CTL2: + case CDC_WSA_COMPANDER0_CTL3: + case CDC_WSA_COMPANDER0_CTL4: + case CDC_WSA_COMPANDER0_CTL5: + case CDC_WSA_COMPANDER0_CTL7: + case CDC_WSA_COMPANDER1_CTL0: + case CDC_WSA_COMPANDER1_CTL1: + case CDC_WSA_COMPANDER1_CTL2: + case CDC_WSA_COMPANDER1_CTL3: + case CDC_WSA_COMPANDER1_CTL4: + case CDC_WSA_COMPANDER1_CTL5: + case CDC_WSA_COMPANDER1_CTL7: + case CDC_WSA_SOFTCLIP0_CRC: + case CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL: + case CDC_WSA_SOFTCLIP1_CRC: + case CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL: + case CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL: + case CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0: + case CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL: + case CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0: + case CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL: + case CDC_WSA_SPLINE_ASRC0_CTL0: + case CDC_WSA_SPLINE_ASRC0_CTL1: + case CDC_WSA_SPLINE_ASRC0_FIFO_CTL: + case CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL: + case CDC_WSA_SPLINE_ASRC1_CTL0: + case CDC_WSA_SPLINE_ASRC1_CTL1: + case CDC_WSA_SPLINE_ASRC1_FIFO_CTL: + return true; + } + + return false; +} + +static bool wsa_is_writeable_register(struct device *dev, unsigned int reg) +{ + bool ret; + + ret = wsa_is_rw_register(dev, reg); + if (!ret) + return wsa_is_wronly_register(dev, reg); + + return ret; +} + +static bool wsa_is_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_INTR_CTRL_CLR_COMMIT: + case CDC_WSA_INTR_CTRL_PIN1_CLEAR0: + case CDC_WSA_INTR_CTRL_PIN2_CLEAR0: + case CDC_WSA_INTR_CTRL_PIN1_STATUS0: + case CDC_WSA_INTR_CTRL_PIN2_STATUS0: + case CDC_WSA_COMPANDER0_CTL6: + case CDC_WSA_COMPANDER1_CTL6: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FIFO: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO: + return true; + } + + return wsa_is_rw_register(dev, reg); +} + +static bool wsa_is_volatile_register(struct device *dev, unsigned int reg) +{ + /* Update volatile list for rx/tx macros */ + switch (reg) { + case CDC_WSA_INTR_CTRL_PIN1_STATUS0: + case CDC_WSA_INTR_CTRL_PIN2_STATUS0: + case CDC_WSA_COMPANDER0_CTL6: + case CDC_WSA_COMPANDER1_CTL6: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FIFO: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO: + return true; + } + return false; +} + +static const struct regmap_config wsa_regmap_config = { + .name = "wsa_macro", + .reg_bits = 16, + .val_bits = 32, /* 8 but with 32 bit read/write */ + .reg_stride = 4, + .cache_type = REGCACHE_FLAT, + .reg_defaults = wsa_defaults, + .num_reg_defaults = ARRAY_SIZE(wsa_defaults), + .max_register = WSA_MAX_OFFSET, + .writeable_reg = wsa_is_writeable_register, + .volatile_reg = wsa_is_volatile_register, + .readable_reg = wsa_is_readable_register, +}; + +/** + * wsa_macro_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @component: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode) +{ + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + wsa->spkr_mode = mode; + + switch (mode) { + case WSA_MACRO_SPKR_MODE_1: + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x00); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x00); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x00); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x00); + snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x44); + snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x44); + break; + default: + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01); + snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x58); + snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x58); + break; + } + return 0; +} +EXPORT_SYMBOL(wsa_macro_set_spkr_mode); + +static int wsa_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 int_prim_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j, port; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_component *component = dai->component; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + for_each_set_bit(port, &wsa->active_ch_mask[dai->id], WSA_MACRO_RX_MAX) { + int_1_mix1_inp = port; + if ((int_1_mix1_inp < WSA_MACRO_RX0) || (int_1_mix1_inp > WSA_MACRO_RX_MIX1)) { + dev_err(component->dev, "%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg0 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the cdc_dma rx port + * is connected + */ + for (j = 0; j < NUM_INTERPOLATORS; j++) { + int_mux_cfg1 = int_mux_cfg0 + WSA_MACRO_MUX_CFG1_OFFSET; + int_mux_cfg0_val = snd_soc_component_read(component, + int_mux_cfg0); + int_mux_cfg1_val = snd_soc_component_read(component, + int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & WSA_MACRO_MUX_INP_MASK1; + inp1_sel = (int_mux_cfg0_val >> WSA_MACRO_MUX_INP_SHFT) & + WSA_MACRO_MUX_INP_MASK1; + inp2_sel = (int_mux_cfg1_val >> WSA_MACRO_MUX_INP_SHFT) & + WSA_MACRO_MUX_INP_MASK1; + if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || + (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || + (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) { + int_fs_reg = CDC_WSA_RX0_RX_PATH_CTL + + WSA_MACRO_RX_PATH_OFFSET * j; + /* sample_rate is in Hz */ + snd_soc_component_update_bits(component, int_fs_reg, + WSA_MACRO_FS_RATE_MASK, + int_prim_fs_rate_reg_val); + } + int_mux_cfg0 += WSA_MACRO_MUX_CFG_OFFSET; + } + } + + return 0; +} + +static int wsa_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 int_mix_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j, port; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_component *component = dai->component; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + for_each_set_bit(port, &wsa->active_ch_mask[dai->id], WSA_MACRO_RX_MAX) { + int_2_inp = port; + if ((int_2_inp < WSA_MACRO_RX0) || (int_2_inp > WSA_MACRO_RX_MIX1)) { + dev_err(component->dev, "%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg1 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < NUM_INTERPOLATORS; j++) { + int_mux_cfg1_val = snd_soc_component_read(component, + int_mux_cfg1) & + WSA_MACRO_MUX_INP_MASK1; + if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) { + int_fs_reg = CDC_WSA_RX0_RX_PATH_MIX_CTL + + WSA_MACRO_RX_PATH_OFFSET * j; + + snd_soc_component_update_bits(component, + int_fs_reg, + WSA_MACRO_FS_RATE_MASK, + int_mix_fs_rate_reg_val); + } + int_mux_cfg1 += WSA_MACRO_MUX_CFG_OFFSET; + } + } + return 0; +} + +static int wsa_macro_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + /* set mixing path rate */ + for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) { + if (sample_rate == int_mix_sample_rate_val[i].sample_rate) { + rate_val = int_mix_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) || (rate_val < 0)) + goto prim_rate; + + ret = wsa_macro_set_mix_interpolator_rate(dai, (u8) rate_val, sample_rate); +prim_rate: + /* set primary path sample rate */ + for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) { + if (sample_rate == int_prim_sample_rate_val[i].sample_rate) { + rate_val = int_prim_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) || (rate_val < 0)) + return -EINVAL; + + ret = wsa_macro_set_prim_interpolator_rate(dai, (u8) rate_val, sample_rate); + + return ret; +} + +static int wsa_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int ret; + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = wsa_macro_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(component->dev, + "%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + break; + default: + break; + } + return 0; +} + +static int wsa_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_component *component = dai->component; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u16 val, mask = 0, cnt = 0, temp; + + switch (dai->id) { + case WSA_MACRO_AIF_VI: + *tx_slot = wsa->active_ch_mask[dai->id]; + *tx_num = wsa->active_ch_cnt[dai->id]; + break; + case WSA_MACRO_AIF1_PB: + case WSA_MACRO_AIF_MIX1_PB: + for_each_set_bit(temp, &wsa->active_ch_mask[dai->id], + WSA_MACRO_RX_MAX) { + mask |= (1 << temp); + if (++cnt == WSA_MACRO_MAX_DMA_CH_PER_PORT) + break; + } + if (mask & 0x0C) + mask = mask >> 0x2; + *rx_slot = mask; + *rx_num = cnt; + break; + case WSA_MACRO_AIF_ECHO: + val = snd_soc_component_read(component, CDC_WSA_RX_INP_MUX_RX_MIX_CFG0); + if (val & WSA_MACRO_EC_MIX_TX1_MASK) { + mask |= 0x2; + cnt++; + } + if (val & WSA_MACRO_EC_MIX_TX0_MASK) { + mask |= 0x1; + cnt++; + } + *tx_slot = mask; + *tx_num = cnt; + break; + default: + dev_err(component->dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static struct snd_soc_dai_ops wsa_macro_dai_ops = { + .hw_params = wsa_macro_hw_params, + .get_channel_map = wsa_macro_get_channel_map, +}; + +static struct snd_soc_dai_driver wsa_macro_dai[] = { + { + .name = "wsa_macro_rx1", + .id = WSA_MACRO_AIF1_PB, + .playback = { + .stream_name = "WSA_AIF1 Playback", + .rates = WSA_MACRO_RX_RATES, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_rx_mix", + .id = WSA_MACRO_AIF_MIX1_PB, + .playback = { + .stream_name = "WSA_AIF_MIX1 Playback", + .rates = WSA_MACRO_RX_MIX_RATES, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 192000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_vifeedback", + .id = WSA_MACRO_AIF_VI, + .capture = { + .stream_name = "WSA_AIF_VI Capture", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_echo", + .id = WSA_MACRO_AIF_ECHO, + .capture = { + .stream_name = "WSA_AIF_ECHO Capture", + .rates = WSA_MACRO_ECHO_RATES, + .formats = WSA_MACRO_ECHO_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, +}; + +static void wsa_macro_mclk_enable(struct wsa_macro *wsa, bool mclk_enable) +{ + struct regmap *regmap = wsa->regmap; + + if (mclk_enable) { + if (wsa->wsa_mclk_users == 0) { + regcache_mark_dirty(regmap); + regcache_sync(regmap); + /* 9.6MHz MCLK, set value 0x00 if other frequency */ + regmap_update_bits(regmap, CDC_WSA_TOP_FREQ_MCLK, 0x01, 0x01); + regmap_update_bits(regmap, + CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, + CDC_WSA_MCLK_EN_MASK, + CDC_WSA_MCLK_ENABLE); + regmap_update_bits(regmap, + CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_WSA_FS_CNT_EN_MASK, + CDC_WSA_FS_CNT_ENABLE); + } + wsa->wsa_mclk_users++; + } else { + if (wsa->wsa_mclk_users <= 0) { + dev_err(wsa->dev, "clock already disabled\n"); + wsa->wsa_mclk_users = 0; + return; + } + wsa->wsa_mclk_users--; + if (wsa->wsa_mclk_users == 0) { + regmap_update_bits(regmap, + CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_WSA_FS_CNT_EN_MASK, + CDC_WSA_FS_CNT_DISABLE); + regmap_update_bits(regmap, + CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, + CDC_WSA_MCLK_EN_MASK, + CDC_WSA_MCLK_DISABLE); + } + } +} + +static int wsa_macro_mclk_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 wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + wsa_macro_mclk_enable(wsa, event == SND_SOC_DAPM_PRE_PMU); + return 0; +} + +static int wsa_macro_enable_vi_feedback(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 wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u32 tx_reg0, tx_reg1; + + if (test_bit(WSA_MACRO_TX0, &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + tx_reg0 = CDC_WSA_TX0_SPKR_PROT_PATH_CTL; + tx_reg1 = CDC_WSA_TX1_SPKR_PROT_PATH_CTL; + } else if (test_bit(WSA_MACRO_TX1, &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + tx_reg0 = CDC_WSA_TX2_SPKR_PROT_PATH_CTL; + tx_reg1 = CDC_WSA_TX3_SPKR_PROT_PATH_CTL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Enable V&I sensing */ + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_RESET); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_RESET); + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK, + CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK, + CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K); + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK, + CDC_WSA_TX_SPKR_PROT_CLK_ENABLE); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK, + CDC_WSA_TX_SPKR_PROT_CLK_ENABLE); + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_NO_RESET); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_NO_RESET); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable V&I sensing */ + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_RESET); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_RESET); + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK, + CDC_WSA_TX_SPKR_PROT_CLK_DISABLE); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK, + CDC_WSA_TX_SPKR_PROT_CLK_DISABLE); + break; + } + + return 0; +} + +static int wsa_macro_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + int val; + + switch (w->reg) { + case CDC_WSA_RX0_RX_PATH_MIX_CTL: + gain_reg = CDC_WSA_RX0_RX_VOL_MIX_CTL; + break; + case CDC_WSA_RX1_RX_PATH_MIX_CTL: + gain_reg = CDC_WSA_RX1_RX_VOL_MIX_CTL; + break; + default: + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read(component, gain_reg); + snd_soc_component_write(component, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, w->reg, + CDC_WSA_RX_PATH_MIX_CLK_EN_MASK, + CDC_WSA_RX_PATH_MIX_CLK_DISABLE); + break; + } + + return 0; +} + +static void wsa_macro_hd2_control(struct snd_soc_component *component, + u16 reg, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg; + + if (reg == CDC_WSA_RX0_RX_PATH_CTL) { + hd2_scale_reg = CDC_WSA_RX0_RX_PATH_SEC3; + hd2_enable_reg = CDC_WSA_RX0_RX_PATH_CFG0; + } + if (reg == CDC_WSA_RX1_RX_PATH_CTL) { + hd2_scale_reg = CDC_WSA_RX1_RX_PATH_SEC3; + hd2_enable_reg = CDC_WSA_RX1_RX_PATH_CFG0; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(component, hd2_scale_reg, + CDC_WSA_RX_PATH_HD2_ALPHA_MASK, + 0x10); + snd_soc_component_update_bits(component, hd2_scale_reg, + CDC_WSA_RX_PATH_HD2_SCALE_MASK, + 0x1); + snd_soc_component_update_bits(component, hd2_enable_reg, + CDC_WSA_RX_PATH_HD2_EN_MASK, + CDC_WSA_RX_PATH_HD2_ENABLE); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, hd2_enable_reg, + CDC_WSA_RX_PATH_HD2_EN_MASK, 0); + snd_soc_component_update_bits(component, hd2_scale_reg, + CDC_WSA_RX_PATH_HD2_SCALE_MASK, + 0); + snd_soc_component_update_bits(component, hd2_scale_reg, + CDC_WSA_RX_PATH_HD2_ALPHA_MASK, + 0); + } +} + +static int wsa_macro_config_compander(struct snd_soc_component *component, + int comp, int event) +{ + u16 comp_ctl0_reg, rx_path_cfg0_reg; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + if (!wsa->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = CDC_WSA_COMPANDER0_CTL0 + + (comp * WSA_MACRO_RX_COMP_OFFSET); + rx_path_cfg0_reg = CDC_WSA_RX0_RX_PATH_CFG0 + + (comp * WSA_MACRO_RX_PATH_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_CLK_EN_MASK, + CDC_WSA_COMPANDER_CLK_ENABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_SOFT_RST_MASK, + CDC_WSA_COMPANDER_SOFT_RST_ENABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_SOFT_RST_MASK, + 0); + snd_soc_component_update_bits(component, rx_path_cfg0_reg, + CDC_WSA_RX_PATH_COMP_EN_MASK, + CDC_WSA_RX_PATH_COMP_ENABLE); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_HALT_MASK, + CDC_WSA_COMPANDER_HALT); + snd_soc_component_update_bits(component, rx_path_cfg0_reg, + CDC_WSA_RX_PATH_COMP_EN_MASK, 0); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_SOFT_RST_MASK, + CDC_WSA_COMPANDER_SOFT_RST_ENABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_SOFT_RST_MASK, + 0); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_CLK_EN_MASK, 0); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_HALT_MASK, 0); + } + + return 0; +} + +static void wsa_macro_enable_softclip_clk(struct snd_soc_component *component, + struct wsa_macro *wsa, + int path, + bool enable) +{ + u16 softclip_clk_reg = CDC_WSA_SOFTCLIP0_CRC + + (path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + u8 softclip_mux_mask = (1 << path); + u8 softclip_mux_value = (1 << path); + + if (enable) { + if (wsa->softclip_clk_users[path] == 0) { + snd_soc_component_update_bits(component, + softclip_clk_reg, + CDC_WSA_SOFTCLIP_CLK_EN_MASK, + CDC_WSA_SOFTCLIP_CLK_ENABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, + softclip_mux_mask, softclip_mux_value); + } + wsa->softclip_clk_users[path]++; + } else { + wsa->softclip_clk_users[path]--; + if (wsa->softclip_clk_users[path] == 0) { + snd_soc_component_update_bits(component, + softclip_clk_reg, + CDC_WSA_SOFTCLIP_CLK_EN_MASK, + 0); + snd_soc_component_update_bits(component, + CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, + softclip_mux_mask, 0x00); + } + } +} + +static int wsa_macro_config_softclip(struct snd_soc_component *component, + int path, int event) +{ + u16 softclip_ctrl_reg; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + int softclip_path = 0; + + if (path == WSA_MACRO_COMP1) + softclip_path = WSA_MACRO_SOFTCLIP0; + else if (path == WSA_MACRO_COMP2) + softclip_path = WSA_MACRO_SOFTCLIP1; + + if (!wsa->is_softclip_on[softclip_path]) + return 0; + + softclip_ctrl_reg = CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL + + (softclip_path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Softclip clock and mux */ + wsa_macro_enable_softclip_clk(component, wsa, softclip_path, + true); + /* Enable Softclip control */ + snd_soc_component_update_bits(component, softclip_ctrl_reg, + CDC_WSA_SOFTCLIP_EN_MASK, + CDC_WSA_SOFTCLIP_ENABLE); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, softclip_ctrl_reg, + CDC_WSA_SOFTCLIP_EN_MASK, 0); + wsa_macro_enable_softclip_clk(component, wsa, softclip_path, + false); + } + + return 0; +} + +static bool wsa_macro_adie_lb(struct snd_soc_component *component, + int interp_idx) +{ + u16 int_mux_cfg0, int_mux_cfg1; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 int_n_inp0, int_n_inp1, int_n_inp2; + + int_mux_cfg0 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 + interp_idx * 8; + int_mux_cfg1 = int_mux_cfg0 + 4; + int_mux_cfg0_val = snd_soc_component_read(component, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_component_read(component, int_mux_cfg1); + + int_n_inp0 = int_mux_cfg0_val & 0x0F; + if (int_n_inp0 == INTn_1_INP_SEL_DEC0 || + int_n_inp0 == INTn_1_INP_SEL_DEC1) + return true; + + int_n_inp1 = int_mux_cfg0_val >> 4; + if (int_n_inp1 == INTn_1_INP_SEL_DEC0 || + int_n_inp1 == INTn_1_INP_SEL_DEC1) + return true; + + int_n_inp2 = int_mux_cfg1_val >> 4; + if (int_n_inp2 == INTn_1_INP_SEL_DEC0 || + int_n_inp2 == INTn_1_INP_SEL_DEC1) + return true; + + return false; +} + +static int wsa_macro_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 reg; + + reg = CDC_WSA_RX0_RX_PATH_CTL + WSA_MACRO_RX_PATH_OFFSET * w->shift; + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wsa_macro_adie_lb(component, w->shift)) { + snd_soc_component_update_bits(component, reg, + CDC_WSA_RX_PATH_CLK_EN_MASK, + CDC_WSA_RX_PATH_CLK_ENABLE); + } + break; + default: + break; + } + return 0; +} + +static int wsa_macro_interp_get_primary_reg(u16 reg, u16 *ind) +{ + u16 prim_int_reg = 0; + + switch (reg) { + case CDC_WSA_RX0_RX_PATH_CTL: + case CDC_WSA_RX0_RX_PATH_MIX_CTL: + prim_int_reg = CDC_WSA_RX0_RX_PATH_CTL; + *ind = 0; + break; + case CDC_WSA_RX1_RX_PATH_CTL: + case CDC_WSA_RX1_RX_PATH_MIX_CTL: + prim_int_reg = CDC_WSA_RX1_RX_PATH_CTL; + *ind = 1; + break; + } + + return prim_int_reg; +} + +static int wsa_macro_enable_prim_interpolator(struct snd_soc_component *component, + u16 reg, int event) +{ + u16 prim_int_reg; + u16 ind = 0; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + prim_int_reg = wsa_macro_interp_get_primary_reg(reg, &ind); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wsa->prim_int_users[ind]++; + if (wsa->prim_int_users[ind] == 1) { + snd_soc_component_update_bits(component, + prim_int_reg + WSA_MACRO_RX_PATH_CFG3_OFFSET, + CDC_WSA_RX_DC_DCOEFF_MASK, + 0x3); + snd_soc_component_update_bits(component, prim_int_reg, + CDC_WSA_RX_PATH_PGA_MUTE_EN_MASK, + CDC_WSA_RX_PATH_PGA_MUTE_ENABLE); + wsa_macro_hd2_control(component, prim_int_reg, event); + snd_soc_component_update_bits(component, + prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET, + CDC_WSA_RX_DSMDEM_CLK_EN_MASK, + CDC_WSA_RX_DSMDEM_CLK_ENABLE); + } + if ((reg != prim_int_reg) && + ((snd_soc_component_read( + component, prim_int_reg)) & 0x10)) + snd_soc_component_update_bits(component, reg, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + wsa->prim_int_users[ind]--; + if (wsa->prim_int_users[ind] == 0) { + snd_soc_component_update_bits(component, + prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET, + CDC_WSA_RX_DSMDEM_CLK_EN_MASK, 0); + wsa_macro_hd2_control(component, prim_int_reg, event); + } + break; + } + + return 0; +} + +static int wsa_macro_config_ear_spkr_gain(struct snd_soc_component *component, + struct wsa_macro *wsa, + int event, int gain_reg) +{ + int comp_gain_offset, val; + + switch (wsa->spkr_mode) { + /* Compander gain in WSA_MACRO_SPKR_MODE1 case is 12 dB */ + case WSA_MACRO_SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (wsa->comp_enabled[WSA_MACRO_COMP1] && + (gain_reg == CDC_WSA_RX0_RX_VOL_CTL) && + (wsa->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + wsa->ear_spkr_gain - 1; + snd_soc_component_write(component, gain_reg, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX0 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (wsa->comp_enabled[WSA_MACRO_COMP1] && + (gain_reg == CDC_WSA_RX0_RX_VOL_CTL) && + (wsa->ear_spkr_gain != 0)) { + snd_soc_component_write(component, gain_reg, 0x0); + } + break; + } + + return 0; +} + +static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + if (w->shift == WSA_MACRO_COMP1) { + reg = CDC_WSA_RX0_RX_PATH_CTL; + gain_reg = CDC_WSA_RX0_RX_VOL_CTL; + } else if (w->shift == WSA_MACRO_COMP2) { + reg = CDC_WSA_RX1_RX_PATH_CTL; + gain_reg = CDC_WSA_RX1_RX_VOL_CTL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Reset if needed */ + wsa_macro_enable_prim_interpolator(component, reg, event); + break; + case SND_SOC_DAPM_POST_PMU: + wsa_macro_config_compander(component, w->shift, event); + wsa_macro_config_softclip(component, w->shift, event); + /* apply gain after int clk is enabled */ + if ((wsa->spkr_gain_offset == WSA_MACRO_GAIN_OFFSET_M1P5_DB) && + (wsa->comp_enabled[WSA_MACRO_COMP1] || + wsa->comp_enabled[WSA_MACRO_COMP2])) { + snd_soc_component_update_bits(component, + CDC_WSA_RX0_RX_PATH_SEC1, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_ENABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX0_RX_PATH_MIX_SEC0, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_ENABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX1_RX_PATH_SEC1, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_ENABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX1_RX_PATH_MIX_SEC0, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_ENABLE); + offset_val = -2; + } + val = snd_soc_component_read(component, gain_reg); + val += offset_val; + snd_soc_component_write(component, gain_reg, val); + wsa_macro_config_ear_spkr_gain(component, wsa, + event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + wsa_macro_config_compander(component, w->shift, event); + wsa_macro_config_softclip(component, w->shift, event); + wsa_macro_enable_prim_interpolator(component, reg, event); + if ((wsa->spkr_gain_offset == WSA_MACRO_GAIN_OFFSET_M1P5_DB) && + (wsa->comp_enabled[WSA_MACRO_COMP1] || + wsa->comp_enabled[WSA_MACRO_COMP2])) { + snd_soc_component_update_bits(component, + CDC_WSA_RX0_RX_PATH_SEC1, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_DISABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX0_RX_PATH_MIX_SEC0, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_DISABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX1_RX_PATH_SEC1, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_DISABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX1_RX_PATH_MIX_SEC0, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_DISABLE); + offset_val = 2; + val = snd_soc_component_read(component, gain_reg); + val += offset_val; + snd_soc_component_write(component, gain_reg, val); + } + wsa_macro_config_ear_spkr_gain(component, wsa, + event, gain_reg); + break; + } + + return 0; +} + +static int wsa_macro_spk_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); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + if (!strcmp(w->name, "WSA_RX INT0 CHAIN")) { + boost_path_ctl = CDC_WSA_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = CDC_WSA_RX0_RX_PATH_CFG1; + reg = CDC_WSA_RX0_RX_PATH_CTL; + reg_mix = CDC_WSA_RX0_RX_PATH_MIX_CTL; + } else if (!strcmp(w->name, "WSA_RX INT1 CHAIN")) { + boost_path_ctl = CDC_WSA_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = CDC_WSA_RX1_RX_PATH_CFG1; + reg = CDC_WSA_RX1_RX_PATH_CTL; + reg_mix = CDC_WSA_RX1_RX_PATH_MIX_CTL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, boost_path_cfg1, + CDC_WSA_RX_PATH_SMART_BST_EN_MASK, + CDC_WSA_RX_PATH_SMART_BST_ENABLE); + snd_soc_component_update_bits(component, boost_path_ctl, + CDC_WSA_BOOST_PATH_CLK_EN_MASK, + CDC_WSA_BOOST_PATH_CLK_ENABLE); + if ((snd_soc_component_read(component, reg_mix)) & 0x10) + snd_soc_component_update_bits(component, reg_mix, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, reg, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, boost_path_ctl, + CDC_WSA_BOOST_PATH_CLK_EN_MASK, + CDC_WSA_BOOST_PATH_CLK_DISABLE); + snd_soc_component_update_bits(component, boost_path_cfg1, + CDC_WSA_RX_PATH_SMART_BST_EN_MASK, + CDC_WSA_RX_PATH_SMART_BST_DISABLE); + break; + } + + return 0; +} + +static int wsa_macro_enable_echo(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 wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u16 val, ec_tx, ec_hq_reg; + + val = snd_soc_component_read(component, CDC_WSA_RX_INP_MUX_RX_MIX_CFG0); + + switch (w->shift) { + case WSA_MACRO_EC0_MUX: + val = val & CDC_WSA_RX_MIX_TX0_SEL_MASK; + ec_tx = val - 1; + break; + case WSA_MACRO_EC1_MUX: + val = val & CDC_WSA_RX_MIX_TX1_SEL_MASK; + ec_tx = (val >> CDC_WSA_RX_MIX_TX1_SEL_SHFT) - 1; + break; + } + + if (wsa->ec_hq[ec_tx]) { + ec_hq_reg = CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL + 0x40 * ec_tx; + snd_soc_component_update_bits(component, ec_hq_reg, + CDC_WSA_EC_HQ_EC_CLK_EN_MASK, + CDC_WSA_EC_HQ_EC_CLK_ENABLE); + ec_hq_reg = CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 + 0x40 * ec_tx; + /* default set to 48k */ + snd_soc_component_update_bits(component, ec_hq_reg, + CDC_WSA_EC_HQ_EC_REF_PCM_RATE_MASK, + CDC_WSA_EC_HQ_EC_REF_PCM_RATE_48K); + } + + return 0; +} + +static int wsa_macro_get_ec_hq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int ec_tx = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wsa->ec_hq[ec_tx]; + + return 0; +} + +static int wsa_macro_set_ec_hq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int ec_tx = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + wsa->ec_hq[ec_tx] = value; + + return 0; +} + +static int wsa_macro_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wsa->comp_enabled[comp]; + return 0; +} + +static int wsa_macro_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + wsa->comp_enabled[comp] = value; + + return 0; +} + +static int wsa_macro_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wsa->ear_spkr_gain; + + return 0; +} + +static int wsa_macro_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + wsa->ear_spkr_gain = ucontrol->value.integer.value[0]; + + return 0; +} + +static int wsa_macro_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = + wsa->rx_port_value[widget->shift]; + return 0; +} + +static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 rx_port_value = ucontrol->value.integer.value[0]; + u32 bit_input; + u32 aif_rst; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + aif_rst = wsa->rx_port_value[widget->shift]; + if (!rx_port_value) { + if (aif_rst == 0) { + dev_err(component->dev, "%s: AIF reset already\n", __func__); + return 0; + } + if (aif_rst >= WSA_MACRO_RX_MAX) { + dev_err(component->dev, "%s: Invalid AIF reset\n", __func__); + return 0; + } + } + wsa->rx_port_value[widget->shift] = rx_port_value; + + bit_input = widget->shift; + + switch (rx_port_value) { + case 0: + if (wsa->active_ch_cnt[aif_rst]) { + clear_bit(bit_input, + &wsa->active_ch_mask[aif_rst]); + wsa->active_ch_cnt[aif_rst]--; + } + break; + case 1: + case 2: + set_bit(bit_input, + &wsa->active_ch_mask[rx_port_value]); + wsa->active_ch_cnt[rx_port_value]++; + break; + default: + dev_err(component->dev, + "%s: Invalid AIF_ID for WSA RX MUX %d\n", + __func__, rx_port_value); + return -EINVAL; + } + + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + return 0; +} + +static int wsa_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + int path = ((struct soc_mixer_control *)kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = wsa->is_softclip_on[path]; + + return 0; +} + +static int wsa_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + int path = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + + wsa->is_softclip_on[path] = ucontrol->value.integer.value[0]; + + return 0; +} + +static const struct snd_kcontrol_new wsa_macro_snd_controls[] = { + SOC_ENUM_EXT("EAR SPKR PA Gain", wsa_macro_ear_spkr_pa_gain_enum, + wsa_macro_ear_spkr_pa_gain_get, + wsa_macro_ear_spkr_pa_gain_put), + SOC_SINGLE_EXT("WSA_Softclip0 Enable", SND_SOC_NOPM, + WSA_MACRO_SOFTCLIP0, 1, 0, + wsa_macro_soft_clip_enable_get, + wsa_macro_soft_clip_enable_put), + SOC_SINGLE_EXT("WSA_Softclip1 Enable", SND_SOC_NOPM, + WSA_MACRO_SOFTCLIP1, 1, 0, + wsa_macro_soft_clip_enable_get, + wsa_macro_soft_clip_enable_put), + + SOC_SINGLE_S8_TLV("WSA_RX0 Digital Volume", CDC_WSA_RX0_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("WSA_RX1 Digital Volume", CDC_WSA_RX1_RX_VOL_CTL, + -84, 40, digital_gain), + + SOC_SINGLE("WSA_RX0 Digital Mute", CDC_WSA_RX0_RX_PATH_CTL, 4, 1, 0), + SOC_SINGLE("WSA_RX1 Digital Mute", CDC_WSA_RX1_RX_PATH_CTL, 4, 1, 0), + SOC_SINGLE("WSA_RX0_MIX Digital Mute", CDC_WSA_RX0_RX_PATH_MIX_CTL, 4, + 1, 0), + SOC_SINGLE("WSA_RX1_MIX Digital Mute", CDC_WSA_RX1_RX_PATH_MIX_CTL, 4, + 1, 0), + SOC_SINGLE_EXT("WSA_COMP1 Switch", SND_SOC_NOPM, WSA_MACRO_COMP1, 1, 0, + wsa_macro_get_compander, wsa_macro_set_compander), + SOC_SINGLE_EXT("WSA_COMP2 Switch", SND_SOC_NOPM, WSA_MACRO_COMP2, 1, 0, + wsa_macro_get_compander, wsa_macro_set_compander), + SOC_SINGLE_EXT("WSA_RX0 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX0, 1, 0, + wsa_macro_get_ec_hq, wsa_macro_set_ec_hq), + SOC_SINGLE_EXT("WSA_RX1 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX1, 1, 0, + wsa_macro_get_ec_hq, wsa_macro_set_ec_hq), +}; + +static const struct soc_enum rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_mux_text), rx_mux_text); + +static const struct snd_kcontrol_new rx_mux[WSA_MACRO_RX_MAX] = { + SOC_DAPM_ENUM_EXT("WSA RX0 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX1 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX_MIX0 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX_MIX1 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), +}; + +static int wsa_macro_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u32 spk_tx_id = mixer->shift; + u32 dai_id = widget->shift; + + if (test_bit(spk_tx_id, &wsa->active_ch_mask[dai_id])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int wsa_macro_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u32 enable = ucontrol->value.integer.value[0]; + u32 spk_tx_id = mixer->shift; + + if (enable) { + if (spk_tx_id == WSA_MACRO_TX0 && + !test_bit(WSA_MACRO_TX0, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + set_bit(WSA_MACRO_TX0, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa->active_ch_cnt[WSA_MACRO_AIF_VI]++; + } + if (spk_tx_id == WSA_MACRO_TX1 && + !test_bit(WSA_MACRO_TX1, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + set_bit(WSA_MACRO_TX1, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa->active_ch_cnt[WSA_MACRO_AIF_VI]++; + } + } else { + if (spk_tx_id == WSA_MACRO_TX0 && + test_bit(WSA_MACRO_TX0, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + clear_bit(WSA_MACRO_TX0, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa->active_ch_cnt[WSA_MACRO_AIF_VI]--; + } + if (spk_tx_id == WSA_MACRO_TX1 && + test_bit(WSA_MACRO_TX1, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + clear_bit(WSA_MACRO_TX1, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa->active_ch_cnt[WSA_MACRO_AIF_VI]--; + } + } + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +static const struct snd_kcontrol_new aif_vi_mixer[] = { + SOC_SINGLE_EXT("WSA_SPKR_VI_1", SND_SOC_NOPM, WSA_MACRO_TX0, 1, 0, + wsa_macro_vi_feed_mixer_get, + wsa_macro_vi_feed_mixer_put), + SOC_SINGLE_EXT("WSA_SPKR_VI_2", SND_SOC_NOPM, WSA_MACRO_TX1, 1, 0, + wsa_macro_vi_feed_mixer_get, + wsa_macro_vi_feed_mixer_put), +}; + +static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("WSA AIF1 PB", "WSA_AIF1 Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("WSA AIF_MIX1 PB", "WSA_AIF_MIX1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT_E("WSA AIF_VI", "WSA_AIF_VI Capture", 0, + SND_SOC_NOPM, WSA_MACRO_AIF_VI, 0, + wsa_macro_enable_vi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT("WSA AIF_ECHO", "WSA_AIF_ECHO Capture", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("WSA_AIF_VI Mixer", SND_SOC_NOPM, WSA_MACRO_AIF_VI, + 0, aif_vi_mixer, ARRAY_SIZE(aif_vi_mixer)), + SND_SOC_DAPM_MUX_E("WSA RX_MIX EC0_MUX", SND_SOC_NOPM, + WSA_MACRO_EC0_MUX, 0, + &rx_mix_ec0_mux, wsa_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA RX_MIX EC1_MUX", SND_SOC_NOPM, + WSA_MACRO_EC1_MUX, 0, + &rx_mix_ec1_mux, wsa_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("WSA RX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX0, 0, + &rx_mux[WSA_MACRO_RX0]), + SND_SOC_DAPM_MUX("WSA RX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX1, 0, + &rx_mux[WSA_MACRO_RX1]), + SND_SOC_DAPM_MUX("WSA RX_MIX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, 0, + &rx_mux[WSA_MACRO_RX_MIX0]), + SND_SOC_DAPM_MUX("WSA RX_MIX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, 0, + &rx_mux[WSA_MACRO_RX_MIX1]), + + SND_SOC_DAPM_MIXER("WSA RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX_MIX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX_MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux), + SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux), + SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux), + SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", CDC_WSA_RX0_RX_PATH_MIX_CTL, + 0, 0, &rx0_mix_mux, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux), + SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux), + SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux), + SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", CDC_WSA_RX1_RX_PATH_MIX_CTL, + 0, 0, &rx1_mix_mux, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 MIX", SND_SOC_NOPM, 0, 0, NULL, 0, + wsa_macro_enable_main_path, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 MIX", SND_SOC_NOPM, 1, 0, NULL, 0, + wsa_macro_enable_main_path, SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_MIXER("WSA_RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA_RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("WSA_RX0 INT0 SIDETONE MIX", CDC_WSA_RX0_RX_PATH_CFG1, + 4, 0, &rx0_sidetone_mix_mux), + + SND_SOC_DAPM_INPUT("WSA SRC0_INP"), + SND_SOC_DAPM_INPUT("WSA_TX DEC0_INP"), + SND_SOC_DAPM_INPUT("WSA_TX DEC1_INP"), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 INTERP", SND_SOC_NOPM, + WSA_MACRO_COMP1, 0, NULL, 0, + wsa_macro_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 INTERP", SND_SOC_NOPM, + WSA_MACRO_COMP2, 0, NULL, 0, + wsa_macro_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, wsa_macro_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, wsa_macro_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("VIINPUT_WSA"), + SND_SOC_DAPM_OUTPUT("WSA_SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("WSA_SPK2 OUT"), + + SND_SOC_DAPM_SUPPLY("WSA_RX0_CLK", CDC_WSA_RX0_RX_PATH_CTL, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("WSA_RX1_CLK", CDC_WSA_RX1_RX_PATH_CTL, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("WSA_RX_MIX0_CLK", CDC_WSA_RX0_RX_PATH_MIX_CTL, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("WSA_RX_MIX1_CLK", CDC_WSA_RX1_RX_PATH_MIX_CTL, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("WSA_MCLK", 0, SND_SOC_NOPM, 0, 0, + wsa_macro_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route wsa_audio_map[] = { + /* VI Feedback */ + {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_1", "VIINPUT_WSA"}, + {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_2", "VIINPUT_WSA"}, + {"WSA AIF_VI", NULL, "WSA_AIF_VI Mixer"}, + {"WSA AIF_VI", NULL, "WSA_MCLK"}, + + {"WSA RX_MIX EC0_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"}, + {"WSA RX_MIX EC1_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"}, + {"WSA RX_MIX EC0_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"}, + {"WSA RX_MIX EC1_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"}, + {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC0_MUX"}, + {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC1_MUX"}, + {"WSA AIF_ECHO", NULL, "WSA_MCLK"}, + + {"WSA AIF1 PB", NULL, "WSA_MCLK"}, + {"WSA AIF_MIX1 PB", NULL, "WSA_MCLK"}, + + {"WSA RX0 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX1 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX_MIX0 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX_MIX1 MUX", "AIF1_PB", "WSA AIF1 PB"}, + + {"WSA RX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX_MIX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX_MIX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + + {"WSA RX0", NULL, "WSA RX0 MUX"}, + {"WSA RX1", NULL, "WSA RX1 MUX"}, + {"WSA RX_MIX0", NULL, "WSA RX_MIX0 MUX"}, + {"WSA RX_MIX1", NULL, "WSA RX_MIX1 MUX"}, + + {"WSA RX0", NULL, "WSA_RX0_CLK"}, + {"WSA RX1", NULL, "WSA_RX1_CLK"}, + {"WSA RX_MIX0", NULL, "WSA_RX_MIX0_CLK"}, + {"WSA RX_MIX1", NULL, "WSA_RX_MIX1_CLK"}, + + {"WSA_RX0 INP0", "RX0", "WSA RX0"}, + {"WSA_RX0 INP0", "RX1", "WSA RX1"}, + {"WSA_RX0 INP0", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP0", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP0", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP0", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP0"}, + + {"WSA_RX0 INP1", "RX0", "WSA RX0"}, + {"WSA_RX0 INP1", "RX1", "WSA RX1"}, + {"WSA_RX0 INP1", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP1", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP1", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP1", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP1"}, + + {"WSA_RX0 INP2", "RX0", "WSA RX0"}, + {"WSA_RX0 INP2", "RX1", "WSA RX1"}, + {"WSA_RX0 INP2", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP2", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP2", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP2", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP2"}, + + {"WSA_RX0 MIX INP", "RX0", "WSA RX0"}, + {"WSA_RX0 MIX INP", "RX1", "WSA RX1"}, + {"WSA_RX0 MIX INP", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 MIX INP", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX0 MIX INP"}, + + {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX INT0 MIX"}, + {"WSA_RX INT0 INTERP", NULL, "WSA_RX INT0 SEC MIX"}, + {"WSA_RX0 INT0 SIDETONE MIX", "SRC0", "WSA SRC0_INP"}, + {"WSA_RX INT0 INTERP", NULL, "WSA_RX0 INT0 SIDETONE MIX"}, + {"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 INTERP"}, + + {"WSA_SPK1 OUT", NULL, "WSA_RX INT0 CHAIN"}, + {"WSA_SPK1 OUT", NULL, "WSA_MCLK"}, + + {"WSA_RX1 INP0", "RX0", "WSA RX0"}, + {"WSA_RX1 INP0", "RX1", "WSA RX1"}, + {"WSA_RX1 INP0", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP0", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP0", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP0", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP0"}, + + {"WSA_RX1 INP1", "RX0", "WSA RX0"}, + {"WSA_RX1 INP1", "RX1", "WSA RX1"}, + {"WSA_RX1 INP1", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP1", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP1", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP1", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP1"}, + + {"WSA_RX1 INP2", "RX0", "WSA RX0"}, + {"WSA_RX1 INP2", "RX1", "WSA RX1"}, + {"WSA_RX1 INP2", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP2", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP2", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP2", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP2"}, + + {"WSA_RX1 MIX INP", "RX0", "WSA RX0"}, + {"WSA_RX1 MIX INP", "RX1", "WSA RX1"}, + {"WSA_RX1 MIX INP", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 MIX INP", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX1 MIX INP"}, + + {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX INT1 MIX"}, + {"WSA_RX INT1 INTERP", NULL, "WSA_RX INT1 SEC MIX"}, + + {"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 INTERP"}, + {"WSA_SPK2 OUT", NULL, "WSA_RX INT1 CHAIN"}, + {"WSA_SPK2 OUT", NULL, "WSA_MCLK"}, +}; + +static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) +{ + struct regmap *regmap = wsa->regmap; + + if (enable) { + wsa_macro_mclk_enable(wsa, true); + + /* reset swr ip */ + if (wsa->reset_swr) + regmap_update_bits(regmap, + CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_RST_EN_MASK, + CDC_WSA_SWR_RST_ENABLE); + + regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_CLK_EN_MASK, + CDC_WSA_SWR_CLK_ENABLE); + + /* Bring out of reset */ + if (wsa->reset_swr) + regmap_update_bits(regmap, + CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_RST_EN_MASK, + CDC_WSA_SWR_RST_DISABLE); + wsa->reset_swr = false; + } else { + regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_CLK_EN_MASK, 0); + wsa_macro_mclk_enable(wsa, false); + } + + return 0; +} + +static int wsa_macro_component_probe(struct snd_soc_component *comp) +{ + struct wsa_macro *wsa = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa->regmap); + + wsa->spkr_gain_offset = WSA_MACRO_GAIN_OFFSET_M1P5_DB; + + /* set SPKR rate to FS_2P4_3P072 */ + snd_soc_component_update_bits(comp, CDC_WSA_RX0_RX_PATH_CFG1, + CDC_WSA_RX_PATH_SPKR_RATE_MASK, + CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072); + + snd_soc_component_update_bits(comp, CDC_WSA_RX1_RX_PATH_CFG1, + CDC_WSA_RX_PATH_SPKR_RATE_MASK, + CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072); + + wsa_macro_set_spkr_mode(comp, WSA_MACRO_SPKR_MODE_1); + + return 0; +} + +static int swclk_gate_enable(struct clk_hw *hw) +{ + return wsa_swrm_clock(to_wsa_macro(hw), true); +} + +static void swclk_gate_disable(struct clk_hw *hw) +{ + wsa_swrm_clock(to_wsa_macro(hw), false); +} + +static int swclk_gate_is_enabled(struct clk_hw *hw) +{ + struct wsa_macro *wsa = to_wsa_macro(hw); + int ret, val; + + regmap_read(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, &val); + ret = val & BIT(0); + + return ret; +} + +static unsigned long swclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate / 2; +} + +static const struct clk_ops swclk_gate_ops = { + .prepare = swclk_gate_enable, + .unprepare = swclk_gate_disable, + .is_enabled = swclk_gate_is_enabled, + .recalc_rate = swclk_recalc_rate, +}; + +static struct clk *wsa_macro_register_mclk_output(struct wsa_macro *wsa) +{ + struct device *dev = wsa->dev; + struct device_node *np = dev->of_node; + const char *parent_clk_name; + const char *clk_name = "mclk"; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + parent_clk_name = __clk_get_name(wsa->clks[2].clk); + + init.name = clk_name; + init.ops = &swclk_gate_ops; + init.flags = 0; + init.parent_names = &parent_clk_name; + init.num_parents = 1; + wsa->hw.init = &init; + hw = &wsa->hw; + ret = clk_hw_register(wsa->dev, hw); + if (ret) + return ERR_PTR(ret); + + of_clk_add_provider(np, of_clk_src_simple_get, hw->clk); + + return NULL; +} + +static const struct snd_soc_component_driver wsa_macro_component_drv = { + .name = "WSA MACRO", + .probe = wsa_macro_component_probe, + .controls = wsa_macro_snd_controls, + .num_controls = ARRAY_SIZE(wsa_macro_snd_controls), + .dapm_widgets = wsa_macro_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets), + .dapm_routes = wsa_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa_audio_map), +}; + +static int wsa_macro_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wsa_macro *wsa; + void __iomem *base; + int ret; + + wsa = devm_kzalloc(dev, sizeof(*wsa), GFP_KERNEL); + if (!wsa) + return -ENOMEM; + + wsa->clks[0].id = "macro"; + wsa->clks[1].id = "dcodec"; + wsa->clks[2].id = "mclk"; + wsa->clks[3].id = "npl"; + wsa->clks[4].id = "fsgen"; + + ret = devm_clk_bulk_get(dev, WSA_NUM_CLKS_MAX, wsa->clks); + if (ret) { + dev_err(dev, "Error getting WSA Clocks (%d)\n", ret); + return ret; + } + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + wsa->regmap = devm_regmap_init_mmio(dev, base, &wsa_regmap_config); + + dev_set_drvdata(dev, wsa); + + wsa->reset_swr = true; + wsa->dev = dev; + + /* set MCLK and NPL rates */ + clk_set_rate(wsa->clks[2].clk, WSA_MACRO_MCLK_FREQ); + clk_set_rate(wsa->clks[3].clk, WSA_MACRO_MCLK_FREQ); + + ret = clk_bulk_prepare_enable(WSA_NUM_CLKS_MAX, wsa->clks); + if (ret) + return ret; + + wsa_macro_register_mclk_output(wsa); + + ret = devm_snd_soc_register_component(dev, &wsa_macro_component_drv, + wsa_macro_dai, + ARRAY_SIZE(wsa_macro_dai)); + if (ret) + goto err; + + return ret; +err: + clk_bulk_disable_unprepare(WSA_NUM_CLKS_MAX, wsa->clks); + + return ret; + +} + +static int wsa_macro_remove(struct platform_device *pdev) +{ + struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev); + + of_clk_del_provider(pdev->dev.of_node); + + clk_bulk_disable_unprepare(WSA_NUM_CLKS_MAX, wsa->clks); + + return 0; +} + +static const struct of_device_id wsa_macro_dt_match[] = { + {.compatible = "qcom,sm8250-lpass-wsa-macro"}, + {} +}; +MODULE_DEVICE_TABLE(of, wsa_macro_dt_match); + +static struct platform_driver wsa_macro_driver = { + .driver = { + .name = "wsa_macro", + .of_match_table = wsa_macro_dt_match, + }, + .probe = wsa_macro_probe, + .remove = wsa_macro_remove, +}; + +module_platform_driver(wsa_macro_driver); +MODULE_DESCRIPTION("WSA macro driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-wsa-macro.h b/sound/soc/codecs/lpass-wsa-macro.h new file mode 100644 index 000000000000..d3d62b3f6500 --- /dev/null +++ b/sound/soc/codecs/lpass-wsa-macro.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __LPASS_WSA_MACRO_H__ +#define __LPASS_WSA_MACRO_H__ + +/* + * Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + WSA_MACRO_SPKR_MODE_DEFAULT, + WSA_MACRO_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode); + +#endif /* __LPASS_WSA_MACRO_H__ */ diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c index 680f31a6493a..f4ed7e04673f 100644 --- a/sound/soc/codecs/madera.c +++ b/sound/soc/codecs/madera.c @@ -3019,11 +3019,11 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, tar = 2 << MADERA_AIF1_RATE_SHIFT; break; case MADERA_CLK_ASYNCCLK_1: - reg = MADERA_ASYNC_SAMPLE_RATE_1, + reg = MADERA_ASYNC_SAMPLE_RATE_1; tar = 8 << MADERA_AIF1_RATE_SHIFT; break; case MADERA_CLK_ASYNCCLK_2: - reg = MADERA_ASYNC_SAMPLE_RATE_2, + reg = MADERA_ASYNC_SAMPLE_RATE_2; tar = 9 << MADERA_AIF1_RATE_SHIFT; break; default: diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 945a79e4f3eb..06276ff5f8a3 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2668,12 +2668,14 @@ static const struct i2c_device_id max98090_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); +#ifdef CONFIG_OF static const struct of_device_id max98090_of_match[] = { { .compatible = "maxim,max98090", }, { .compatible = "maxim,max98091", }, { } }; MODULE_DEVICE_TABLE(of, max98090_of_match); +#endif #ifdef CONFIG_ACPI static const struct acpi_device_id max98090_acpi_match[] = { diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 9bdc6392382a..736cd70be725 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -2148,11 +2148,13 @@ static const struct i2c_device_id max98095_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, max98095_i2c_id); +#ifdef CONFIG_OF static const struct of_device_id max98095_of_match[] = { { .compatible = "maxim,max98095", }, { } }; MODULE_DEVICE_TABLE(of, max98095_of_match); +#endif static struct i2c_driver max98095_i2c_driver = { .driver = { diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c index dfee05f985bd..e424779db02b 100644 --- a/sound/soc/codecs/max98371.c +++ b/sound/soc/codecs/max98371.c @@ -408,16 +408,17 @@ static const struct i2c_device_id max98371_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, max98371_i2c_id); +#ifdef CONFIG_OF static const struct of_device_id max98371_of_match[] = { { .compatible = "maxim,max98371", }, { } }; MODULE_DEVICE_TABLE(of, max98371_of_match); +#endif static struct i2c_driver max98371_i2c_driver = { .driver = { .name = "max98371", - .pm = NULL, .of_match_table = of_match_ptr(max98371_of_match), }, .probe = max98371_i2c_probe, diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index fa589d834f9a..ec2e79c57357 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -247,7 +247,7 @@ static __maybe_unused int max98373_suspend(struct device *dev) struct max98373_priv *max98373 = dev_get_drvdata(dev); regcache_cache_only(max98373->regmap, true); - regcache_mark_dirty(max98373->regmap); + return 0; } diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index aef2746bfb94..512e6f2513d3 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -649,11 +649,13 @@ static const struct i2c_device_id max9867_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, max9867_i2c_id); +#ifdef CONFIG_OF static const struct of_device_id max9867_of_match[] = { { .compatible = "maxim,max9867", }, { } }; MODULE_DEVICE_TABLE(of, max9867_of_match); +#endif static struct i2c_driver max9867_i2c_driver = { .driver = { diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index b3e1a54fff88..ddaccc24b0cb 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -627,17 +627,18 @@ static const struct i2c_device_id max98925_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, max98925_i2c_id); +#ifdef CONFIG_OF static const struct of_device_id max98925_of_match[] = { { .compatible = "maxim,max98925", }, { } }; MODULE_DEVICE_TABLE(of, max98925_of_match); +#endif static struct i2c_driver max98925_i2c_driver = { .driver = { .name = "max98925", .of_match_table = of_match_ptr(max98925_of_match), - .pm = NULL, }, .probe = max98925_i2c_probe, .id_table = max98925_i2c_id, diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index c4dfa8ab1d49..f286e572263e 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -571,17 +571,18 @@ static const struct i2c_device_id max98926_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, max98926_i2c_id); +#ifdef CONFIG_OF static const struct of_device_id max98926_of_match[] = { { .compatible = "maxim,max98926", }, { } }; MODULE_DEVICE_TABLE(of, max98926_of_match); +#endif static struct i2c_driver max98926_i2c_driver = { .driver = { .name = "max98926", .of_match_table = of_match_ptr(max98926_of_match), - .pm = NULL, }, .probe = max98926_i2c_probe, .id_table = max98926_i2c_id, diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c index 81aafb553bdd..6de0d744fa9e 100644 --- a/sound/soc/codecs/mt6359.c +++ b/sound/soc/codecs/mt6359.c @@ -68,6 +68,38 @@ static void mt6359_reset_capture_gpio(struct mt6359_priv *priv) 0x3 << 0, 0x0); } +/* use only when doing mtkaif calibraiton at the boot time */ +static void mt6359_set_dcxo(struct mt6359_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6359_DCXO_CW12, + 0x1 << RG_XO_AUDIO_EN_M_SFT, + (enable ? 1 : 0) << RG_XO_AUDIO_EN_M_SFT); +} + +/* use only when doing mtkaif calibraiton at the boot time */ +static void mt6359_set_clksq(struct mt6359_priv *priv, bool enable) +{ + /* Enable/disable CLKSQ 26MHz */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON23, + RG_CLKSQ_EN_MASK_SFT, + (enable ? 1 : 0) << RG_CLKSQ_EN_SFT); +} + +/* use only when doing mtkaif calibraiton at the boot time */ +static void mt6359_set_aud_global_bias(struct mt6359_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON13, + RG_AUDGLB_PWRDN_VA32_MASK_SFT, + (enable ? 0 : 1) << RG_AUDGLB_PWRDN_VA32_SFT); +} + +/* use only when doing mtkaif calibraiton at the boot time */ +static void mt6359_set_topck(struct mt6359_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6359_AUD_TOP_CKPDN_CON0, + 0x0066, enable ? 0x0 : 0x66); +} + static void mt6359_set_decoder_clk(struct mt6359_priv *priv, bool enable) { regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON13, @@ -122,6 +154,84 @@ static void mt6359_mtkaif_tx_disable(struct mt6359_priv *priv) 0xff00, 0x3000); } +void mt6359_set_mtkaif_protocol(struct snd_soc_component *cmpnt, + int mtkaif_protocol) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + priv->mtkaif_protocol = mtkaif_protocol; +} +EXPORT_SYMBOL_GPL(mt6359_set_mtkaif_protocol); + +void mt6359_mtkaif_calibration_enable(struct snd_soc_component *cmpnt) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + mt6359_set_playback_gpio(priv); + mt6359_set_capture_gpio(priv); + mt6359_mtkaif_tx_enable(priv); + + mt6359_set_dcxo(priv, true); + mt6359_set_aud_global_bias(priv, true); + mt6359_set_clksq(priv, true); + mt6359_set_topck(priv, true); + + /* set dat_miso_loopback on */ + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT, + 1 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT, + 1 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1, + RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT, + 1 << RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT); +} +EXPORT_SYMBOL_GPL(mt6359_mtkaif_calibration_enable); + +void mt6359_mtkaif_calibration_disable(struct snd_soc_component *cmpnt) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + /* set dat_miso_loopback off */ + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT, + 0 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT, + 0 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1, + RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT, + 0 << RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT); + + mt6359_set_topck(priv, false); + mt6359_set_clksq(priv, false); + mt6359_set_aud_global_bias(priv, false); + mt6359_set_dcxo(priv, false); + + mt6359_mtkaif_tx_disable(priv); + mt6359_reset_playback_gpio(priv); + mt6359_reset_capture_gpio(priv); +} +EXPORT_SYMBOL_GPL(mt6359_mtkaif_calibration_disable); + +void mt6359_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt, + int phase_1, int phase_2, int phase_3) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT, + phase_1 << RG_AUD_PAD_TOP_PHASE_MODE_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT, + phase_2 << RG_AUD_PAD_TOP_PHASE_MODE2_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1, + RG_AUD_PAD_TOP_PHASE_MODE3_MASK_SFT, + phase_3 << RG_AUD_PAD_TOP_PHASE_MODE3_SFT); +} +EXPORT_SYMBOL_GPL(mt6359_set_mtkaif_calibration_phase); + static void zcd_disable(struct mt6359_priv *priv) { regmap_write(priv->regmap, MT6359_ZCD_CON0, 0x0000); @@ -1833,9 +1943,6 @@ static const struct snd_soc_dapm_widget mt6359_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF, MT6359_DCXO_CW12, RG_XO_AUDIO_EN_M_SFT, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY_S("LDO_VAUD18", SUPPLY_SEQ_LDO_VAUD18, - MT6359_LDO_VAUD18_CON0, - RG_LDO_VAUD18_EN_SFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB, MT6359_AUDDEC_ANA_CON13, RG_AUDGLB_PWRDN_VA32_SFT, 1, NULL, 0), @@ -1855,6 +1962,8 @@ static const struct snd_soc_dapm_widget mt6359_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY_S("AUDIF_CK", SUPPLY_SEQ_TOP_CK, MT6359_AUD_TOP_CKPDN_CON0, RG_AUDIF_CK_PDN_SFT, 1, NULL, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("vaud18", 0, 0), + /* Digital Clock */ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST, MT6359_AUDIO_TOP_CON0, @@ -2204,7 +2313,7 @@ static int mt_dcc_clk_connect(struct snd_soc_dapm_widget *source, static const struct snd_soc_dapm_route mt6359_dapm_routes[] = { /* Capture */ {"AIFTX_Supply", NULL, "CLK_BUF"}, - {"AIFTX_Supply", NULL, "LDO_VAUD18"}, + {"AIFTX_Supply", NULL, "vaud18"}, {"AIFTX_Supply", NULL, "AUDGLB"}, {"AIFTX_Supply", NULL, "CLKSQ Audio"}, {"AIFTX_Supply", NULL, "AUD_CK"}, @@ -2332,7 +2441,7 @@ static const struct snd_soc_dapm_route mt6359_dapm_routes[] = { /* DL Supply */ {"DL Power Supply", NULL, "CLK_BUF"}, - {"DL Power Supply", NULL, "LDO_VAUD18"}, + {"DL Power Supply", NULL, "vaud18"}, {"DL Power Supply", NULL, "AUDGLB"}, {"DL Power Supply", NULL, "CLKSQ Audio"}, {"DL Power Supply", NULL, "AUDNCP_CK"}, @@ -2697,20 +2806,6 @@ static int mt6359_platform_driver_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, priv); priv->dev = &pdev->dev; - priv->avdd_reg = devm_regulator_get(&pdev->dev, "vaud18"); - if (IS_ERR(priv->avdd_reg)) { - dev_err(&pdev->dev, "%s(), have no vaud18 supply: %ld", - __func__, PTR_ERR(priv->avdd_reg)); - return PTR_ERR(priv->avdd_reg); - } - - ret = regulator_enable(priv->avdd_reg); - if (ret) { - dev_err(&pdev->dev, "%s(), failed to enable regulator!\n", - __func__); - return ret; - } - ret = mt6359_parse_dt(priv); if (ret) { dev_warn(&pdev->dev, "%s() failed to parse dts\n", __func__); @@ -2723,30 +2818,11 @@ static int mt6359_platform_driver_probe(struct platform_device *pdev) ARRAY_SIZE(mt6359_dai_driver)); } -static int mt6359_platform_driver_remove(struct platform_device *pdev) -{ - struct mt6359_priv *priv = dev_get_drvdata(&pdev->dev); - int ret; - - dev_dbg(&pdev->dev, "%s(), dev name %s\n", - __func__, dev_name(&pdev->dev)); - - ret = regulator_disable(priv->avdd_reg); - if (ret) { - dev_err(&pdev->dev, "%s(), failed to disable regulator!\n", - __func__); - return ret; - } - - return 0; -} - static struct platform_driver mt6359_platform_driver = { .driver = { .name = "mt6359-sound", }, .probe = mt6359_platform_driver_probe, - .remove = mt6359_platform_driver_remove, }; module_platform_driver(mt6359_platform_driver) diff --git a/sound/soc/codecs/mt6359.h b/sound/soc/codecs/mt6359.h index 3792e534a91b..35f806b7396d 100644 --- a/sound/soc/codecs/mt6359.h +++ b/sound/soc/codecs/mt6359.h @@ -135,11 +135,6 @@ /* MT6359_DCXO_CW12 */ #define RG_XO_AUDIO_EN_M_SFT 13 -/* LDO_VAUD18_CON0 */ -#define RG_LDO_VAUD18_EN_SFT 0 -#define RG_LDO_VAUD18_EN_MASK 0x1 -#define RG_LDO_VAUD18_EN_MASK_SFT (0x1 << 0) - /* AUD_TOP_CKPDN_CON0 */ #define RG_VOW13M_CK_PDN_SFT 13 #define RG_VOW13M_CK_PDN_MASK 0x1 @@ -2132,7 +2127,6 @@ #define MT6359_DCXO_CW11 0x7a6 #define MT6359_DCXO_CW12 0x7a8 -#define MT6359_LDO_VAUD18_CON0 0x1c98 #define MT6359_GPIO_MODE0 0xcc #define MT6359_GPIO_MODE0_SET 0xce @@ -2469,7 +2463,6 @@ enum { enum { /* common */ SUPPLY_SEQ_CLK_BUF, - SUPPLY_SEQ_LDO_VAUD18, SUPPLY_SEQ_AUD_GLB, SUPPLY_SEQ_HP_PULL_DOWN, SUPPLY_SEQ_CLKSQ, @@ -2629,7 +2622,6 @@ struct mt6359_priv { int hp_gain_ctl; int hp_hifi_mode; int mtkaif_protocol; - struct regulator *avdd_reg; }; #define CODEC_MT6359_NAME "mtk-codec-mt6359" @@ -2637,4 +2629,11 @@ struct mt6359_priv { (type) == MIC_TYPE_MUX_DCC_ECM_DIFF || \ (type) == MIC_TYPE_MUX_DCC_ECM_SINGLE) +void mt6359_set_mtkaif_protocol(struct snd_soc_component *cmpnt, + int mtkaif_protocol); +void mt6359_mtkaif_calibration_enable(struct snd_soc_component *cmpnt); +void mt6359_mtkaif_calibration_disable(struct snd_soc_component *cmpnt); +void mt6359_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt, + int phase_1, int phase_2, int phase_3); + #endif/* end _MT6359_H_ */ diff --git a/sound/soc/codecs/nau8315.c b/sound/soc/codecs/nau8315.c new file mode 100644 index 000000000000..2b66e3f7a8b7 --- /dev/null +++ b/sound/soc/codecs/nau8315.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// nau8315.c -- NAU8315 ALSA SoC Audio Amplifier Driver +// +// Copyright 2020 Nuvoton Technology Crop. +// +// Author: David Lin <ctlin0@nuvoton.com> +// +// Based on MAX98357A.c + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/soc-dapm.h> + +struct nau8315_priv { + struct gpio_desc *enable; + int enpin_switch; +}; + +static int nau8315_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8315_priv *nau8315 = + snd_soc_component_get_drvdata(component); + + if (!nau8315->enable) + return 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (nau8315->enpin_switch) { + gpiod_set_value(nau8315->enable, 1); + dev_dbg(component->dev, "set enable to 1"); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + gpiod_set_value(nau8315->enable, 0); + dev_dbg(component->dev, "set enable to 0"); + break; + } + + return 0; +} + +static int nau8315_enpin_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 nau8315_priv *nau8315 = + snd_soc_component_get_drvdata(component); + + if (event & SND_SOC_DAPM_PRE_PMU) + nau8315->enpin_switch = 1; + else if (event & SND_SOC_DAPM_POST_PMD) + nau8315->enpin_switch = 0; + + return 0; +} + +static const struct snd_soc_dapm_widget nau8315_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("Speaker"), + SND_SOC_DAPM_OUT_DRV_E("EN_Pin", SND_SOC_NOPM, 0, 0, NULL, 0, + nau8315_enpin_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route nau8315_dapm_routes[] = { + {"EN_Pin", NULL, "HiFi Playback"}, + {"Speaker", NULL, "EN_Pin"}, +}; + +static const struct snd_soc_component_driver nau8315_component_driver = { + .dapm_widgets = nau8315_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8315_dapm_widgets), + .dapm_routes = nau8315_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8315_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct snd_soc_dai_ops nau8315_dai_ops = { + .trigger = nau8315_daiops_trigger, +}; + +#define NAU8315_RATES SNDRV_PCM_RATE_8000_96000 +#define NAU8315_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE) + +static struct snd_soc_dai_driver nau8315_dai_driver = { + .name = "nau8315-hifi", + .playback = { + .stream_name = "HiFi Playback", + .formats = NAU8315_FORMATS, + .rates = NAU8315_RATES, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &nau8315_dai_ops, +}; + +static int nau8315_platform_probe(struct platform_device *pdev) +{ + struct nau8315_priv *nau8315; + + nau8315 = devm_kzalloc(&pdev->dev, sizeof(*nau8315), GFP_KERNEL); + if (!nau8315) + return -ENOMEM; + + nau8315->enable = devm_gpiod_get_optional(&pdev->dev, + "enable", GPIOD_OUT_LOW); + if (IS_ERR(nau8315->enable)) + return PTR_ERR(nau8315->enable); + + dev_set_drvdata(&pdev->dev, nau8315); + + return devm_snd_soc_register_component(&pdev->dev, + &nau8315_component_driver, + &nau8315_dai_driver, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id nau8315_device_id[] = { + { .compatible = "nuvoton,nau8315" }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8315_device_id); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id nau8315_acpi_match[] = { + { "NVTN2010", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, nau8315_acpi_match); +#endif + +static struct platform_driver nau8315_platform_driver = { + .driver = { + .name = "nau8315", + .of_match_table = of_match_ptr(nau8315_device_id), + .acpi_match_table = ACPI_PTR(nau8315_acpi_match), + }, + .probe = nau8315_platform_probe, +}; +module_platform_driver(nau8315_platform_driver); + +MODULE_DESCRIPTION("ASoC NAU8315 Mono Class-D Amplifier Driver"); +MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c index 327ec584f240..7a6be45f8149 100644 --- a/sound/soc/codecs/pcm1789-i2c.c +++ b/sound/soc/codecs/pcm1789-i2c.c @@ -33,11 +33,13 @@ static int pcm1789_i2c_remove(struct i2c_client *client) return pcm1789_common_exit(&client->dev); } +#ifdef CONFIG_OF static const struct of_device_id pcm1789_of_match[] = { { .compatible = "ti,pcm1789", }, { } }; MODULE_DEVICE_TABLE(of, pcm1789_of_match); +#endif static const struct i2c_device_id pcm1789_i2c_ids[] = { { "pcm1789", 0 }, diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c index 36e01678bef4..34a3d596f288 100644 --- a/sound/soc/codecs/pcm179x-i2c.c +++ b/sound/soc/codecs/pcm179x-i2c.c @@ -30,11 +30,13 @@ static int pcm179x_i2c_probe(struct i2c_client *client, return pcm179x_common_init(&client->dev, regmap); } +#ifdef CONFIG_OF static const struct of_device_id pcm179x_of_match[] = { { .compatible = "ti,pcm1792a", }, { } }; MODULE_DEVICE_TABLE(of, pcm179x_of_match); +#endif static const struct i2c_device_id pcm179x_i2c_ids[] = { { "pcm179x", 0 }, diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 8153d3d01654..4dc844f3c1fc 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -1168,8 +1168,6 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); int alen; int gpio; - int clock_output; - int master_mode; int ret; dev_dbg(component->dev, "hw_params %u Hz, %u channels\n", @@ -1195,19 +1193,15 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - ret = regmap_update_bits(pcm512x->regmap, - PCM512x_BCLK_LRCLK_CFG, - PCM512x_BCKP - | PCM512x_BCKO | PCM512x_LRKO, - 0); - if (ret != 0) { - dev_err(component->dev, - "Failed to enable slave mode: %d\n", ret); - return ret; - } + ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1, + PCM512x_ALEN, alen); + if (ret != 0) { + dev_err(component->dev, "Failed to set frame size: %d\n", ret); + return ret; + } + if ((pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) == + SND_SOC_DAIFMT_CBS_CFS) { ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT, PCM512x_DCAS, 0); if (ret != 0) { @@ -1216,24 +1210,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, ret); return ret; } - return 0; - case SND_SOC_DAIFMT_CBM_CFM: - clock_output = PCM512x_BCKO | PCM512x_LRKO; - master_mode = PCM512x_RLRK | PCM512x_RBCK; - break; - case SND_SOC_DAIFMT_CBM_CFS: - clock_output = PCM512x_BCKO; - master_mode = PCM512x_RBCK; - break; - default: - return -EINVAL; - } - - ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1, - PCM512x_ALEN, alen); - if (ret != 0) { - dev_err(component->dev, "Failed to set frame size: %d\n", ret); - return ret; + goto skip_pll; } if (pcm512x->pll_out) { @@ -1316,25 +1293,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, dev_err(component->dev, "Failed to enable pll: %d\n", ret); return ret; } - } - ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG, - PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO, - clock_output); - if (ret != 0) { - dev_err(component->dev, "Failed to enable clock output: %d\n", ret); - return ret; - } - - ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE, - PCM512x_RLRK | PCM512x_RBCK, - master_mode); - if (ret != 0) { - dev_err(component->dev, "Failed to enable master mode: %d\n", ret); - return ret; - } - - if (pcm512x->pll_out) { gpio = PCM512x_G1OE << (pcm512x->pll_out - 1); ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN, gpio, gpio); @@ -1368,6 +1327,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, return ret; } +skip_pll: return 0; } @@ -1375,6 +1335,80 @@ static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int afmt; + int offset = 0; + int clock_output; + int master_mode; + int ret; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + clock_output = 0; + master_mode = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + clock_output = PCM512x_BCKO | PCM512x_LRKO; + master_mode = PCM512x_RLRK | PCM512x_RBCK; + break; + case SND_SOC_DAIFMT_CBM_CFS: + clock_output = PCM512x_BCKO; + master_mode = PCM512x_RBCK; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG, + PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO, + clock_output); + if (ret != 0) { + dev_err(component->dev, "Failed to enable clock output: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE, + PCM512x_RLRK | PCM512x_RBCK, + master_mode); + if (ret != 0) { + dev_err(component->dev, "Failed to enable master mode: %d\n", ret); + return ret; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + afmt = PCM512x_AFMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + afmt = PCM512x_AFMT_RTJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + afmt = PCM512x_AFMT_LTJ; + break; + case SND_SOC_DAIFMT_DSP_A: + offset = 1; + fallthrough; + case SND_SOC_DAIFMT_DSP_B: + afmt = PCM512x_AFMT_DSP; + break; + default: + dev_err(component->dev, "unsupported DAI format: 0x%x\n", + pcm512x->fmt); + return -EINVAL; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1, + PCM512x_AFMT, afmt); + if (ret != 0) { + dev_err(component->dev, "Failed to set data format: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_2, + 0xFF, offset); + if (ret != 0) { + dev_err(component->dev, "Failed to set data offset: %d\n", ret); + return ret; + } pcm512x->fmt = fmt; diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c index 940a2fa933ed..bfefefcc76d8 100644 --- a/sound/soc/codecs/rk3328_codec.c +++ b/sound/soc/codecs/rk3328_codec.c @@ -499,7 +499,7 @@ static int rk3328_platform_probe(struct platform_device *pdev) ARRAY_SIZE(rk3328_dai)); } -static const struct of_device_id rk3328_codec_of_match[] = { +static const struct of_device_id rk3328_codec_of_match[] __maybe_unused = { { .compatible = "rockchip,rk3328-codec", }, {}, }; diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 3db07293c70b..32e6bcf763d1 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -497,18 +497,40 @@ static void rt1015_calibrate(struct rt1015_priv *rt1015) snd_soc_dapm_mutex_lock(&component->dapm); regcache_cache_bypass(regmap, true); - regmap_write(regmap, RT1015_PWR1, 0xd7df); - regmap_write(regmap, RT1015_PWR4, 0x00b2); - regmap_write(regmap, RT1015_CLSD_INTERNAL8, 0x2008); + regmap_write(regmap, RT1015_PWR9, 0xAA60); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0089); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x008A); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x008C); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x008D); + regmap_write(regmap, RT1015_PWR4, 0x80B2); + regmap_write(regmap, RT1015_CLASSD_SEQ, 0x5797); + regmap_write(regmap, RT1015_CLSD_INTERNAL8, 0x2100); + regmap_write(regmap, RT1015_CLSD_INTERNAL9, 0x0100); + regmap_write(regmap, RT1015_PWR5, 0x2175); + regmap_write(regmap, RT1015_MIXER1, 0x005D); + regmap_write(regmap, RT1015_CLSD_INTERNAL1, 0x00A1); + regmap_write(regmap, RT1015_CLSD_INTERNAL2, 0x12F7); + regmap_write(regmap, RT1015_DC_CALIB_CLSD1, 0x1205); + msleep(200); + regmap_write(regmap, RT1015_CLSD_INTERNAL8, 0x2000); + regmap_write(regmap, RT1015_CLSD_INTERNAL9, 0x0180); + regmap_write(regmap, RT1015_CLSD_INTERNAL1, 0x00A1); + regmap_write(regmap, RT1015_DC_CALIB_CLSD1, 0x0A05); + msleep(200); + regmap_write(regmap, RT1015_PWR4, 0x00B2); + regmap_write(regmap, RT1015_CLSD_INTERNAL8, 0x2028); regmap_write(regmap, RT1015_CLSD_INTERNAL9, 0x0140); - regmap_write(regmap, RT1015_GAT_BOOST, 0x0efe); - regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000d); - regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000e); - regmap_write(regmap, RT1015_DC_CALIB_CLSD1, 0x5a00); - regmap_write(regmap, RT1015_DC_CALIB_CLSD1, 0x5a01); - regmap_write(regmap, RT1015_DC_CALIB_CLSD1, 0x5a05); - msleep(500); - regmap_write(regmap, RT1015_PWR1, 0x0); + regmap_write(regmap, RT1015_PWR5, 0x0175); + regmap_write(regmap, RT1015_CLSD_INTERNAL1, 0x1721); + regmap_write(regmap, RT1015_CLASSD_SEQ, 0x570E); + regmap_write(regmap, RT1015_MIXER1, 0x203D); + regmap_write(regmap, RT1015_DC_CALIB_CLSD1, 0x5A01); + regmap_write(regmap, RT1015_CLSD_INTERNAL2, 0x12FF); + regmap_write(regmap, RT1015_GAT_BOOST, 0x0eFE); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x008E); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0088); + regmap_write(regmap, RT1015_SYS_RST1, 0x05F5); + regmap_write(regmap, RT1015_SYS_RST2, 0x0b9a); regcache_cache_bypass(regmap, false); regcache_mark_dirty(regmap); @@ -604,6 +626,8 @@ static int r1015_dac_event(struct snd_soc_dapm_widget *w, snd_soc_component_write(component, RT1015_SYS_RST1, 0x05f7); snd_soc_component_write(component, + RT1015_SYS_RST2, 0x0b0a); + snd_soc_component_write(component, RT1015_GAT_BOOST, 0xacfe); snd_soc_component_write(component, RT1015_PWR9, 0xaa00); @@ -611,9 +635,13 @@ static int r1015_dac_event(struct snd_soc_dapm_widget *w, RT1015_GAT_BOOST, 0xecfe); } else { snd_soc_component_write(component, + 0x032d, 0xaa60); + snd_soc_component_write(component, RT1015_SYS_RST1, 0x05f7); snd_soc_component_write(component, - RT1015_PWR_STATE_CTRL, 0x026e); + RT1015_SYS_RST2, 0x0b0a); + snd_soc_component_write(component, + RT1015_PWR_STATE_CTRL, 0x008e); } break; @@ -627,11 +655,17 @@ static int r1015_dac_event(struct snd_soc_dapm_widget *w, RT1015_PWR9, 0xa800); snd_soc_component_write(component, RT1015_SYS_RST1, 0x05f5); + snd_soc_component_write(component, + RT1015_SYS_RST2, 0x0b9a); } else { snd_soc_component_write(component, - RT1015_PWR_STATE_CTRL, 0x0268); + 0x032d, 0xaa60); + snd_soc_component_write(component, + RT1015_PWR_STATE_CTRL, 0x0088); snd_soc_component_write(component, RT1015_SYS_RST1, 0x05f5); + snd_soc_component_write(component, + RT1015_SYS_RST2, 0x0b9a); } rt1015->dac_is_used = 0; @@ -664,38 +698,12 @@ static int rt1015_amp_drv_event(struct snd_soc_dapm_widget *w, } static const struct snd_soc_dapm_widget rt1015_dapm_widgets[] = { - SND_SOC_DAPM_SUPPLY("LDO2", RT1015_PWR1, RT1015_PWR_LDO2_BIT, 0, - NULL, 0), - SND_SOC_DAPM_SUPPLY("INT RC CLK", RT1015_PWR1, RT1015_PWR_INTCLK_BIT, - 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ISENSE", RT1015_PWR1, RT1015_PWR_ISENSE_BIT, 0, - NULL, 0), - SND_SOC_DAPM_SUPPLY("VSENSE", RT1015_PWR1, RT1015_PWR_VSENSE_BIT, 0, - NULL, 0), SND_SOC_DAPM_SUPPLY("PLL", RT1015_PWR1, RT1015_PWR_PLL_BIT, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("BG1 BG2", RT1015_PWR1, RT1015_PWR_BG_1_2_BIT, 0, - NULL, 0), - SND_SOC_DAPM_SUPPLY("MBIAS BG", RT1015_PWR1, RT1015_PWR_MBIAS_BG_BIT, 0, - NULL, 0), - SND_SOC_DAPM_SUPPLY("VBAT", RT1015_PWR1, RT1015_PWR_VBAT_BIT, 0, NULL, - 0), - SND_SOC_DAPM_SUPPLY("MBIAS", RT1015_PWR1, RT1015_PWR_MBIAS_BIT, 0, - NULL, 0), - SND_SOC_DAPM_SUPPLY("ADCV", RT1015_PWR1, RT1015_PWR_ADCV_BIT, 0, NULL, - 0), - SND_SOC_DAPM_SUPPLY("MIXERV", RT1015_PWR1, RT1015_PWR_MIXERV_BIT, 0, - NULL, 0), - SND_SOC_DAPM_SUPPLY("SUMV", RT1015_PWR1, RT1015_PWR_SUMV_BIT, 0, NULL, - 0), - SND_SOC_DAPM_SUPPLY("VREFLV", RT1015_PWR1, RT1015_PWR_VREFLV_BIT, 0, - NULL, 0), - SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_DAC_E("DAC", NULL, RT1015_PWR1, RT1015_PWR_DAC_BIT, 0, + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, r1015_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt1015_amp_drv_event, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_OUTPUT("SPO"), @@ -703,19 +711,7 @@ static const struct snd_soc_dapm_widget rt1015_dapm_widgets[] = { static const struct snd_soc_dapm_route rt1015_dapm_routes[] = { { "DAC", NULL, "AIFRX" }, - { "DAC", NULL, "LDO2" }, { "DAC", NULL, "PLL", rt1015_is_sys_clk_from_pll}, - { "DAC", NULL, "INT RC CLK" }, - { "DAC", NULL, "ISENSE" }, - { "DAC", NULL, "VSENSE" }, - { "DAC", NULL, "BG1 BG2" }, - { "DAC", NULL, "MBIAS BG" }, - { "DAC", NULL, "VBAT" }, - { "DAC", NULL, "MBIAS" }, - { "DAC", NULL, "ADCV" }, - { "DAC", NULL, "MIXERV" }, - { "DAC", NULL, "SUMV" }, - { "DAC", NULL, "VREFLV" }, { "Amp Drv", NULL, "DAC" }, { "SPO", NULL, "Amp Drv" }, }; @@ -950,6 +946,106 @@ static int rt1015_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) return 0; } +static int rt1015_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int val = 0, rx_slotnum, tx_slotnum; + int ret = 0, first_bit; + + switch (slots) { + case 2: + val |= RT1015_I2S_TX_2CH; + break; + case 4: + val |= RT1015_I2S_TX_4CH; + break; + case 6: + val |= RT1015_I2S_TX_6CH; + break; + case 8: + val |= RT1015_I2S_TX_8CH; + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + switch (slot_width) { + case 16: + val |= RT1015_I2S_CH_TX_LEN_16B; + break; + case 20: + val |= RT1015_I2S_CH_TX_LEN_20B; + break; + case 24: + val |= RT1015_I2S_CH_TX_LEN_24B; + break; + case 32: + val |= RT1015_I2S_CH_TX_LEN_32B; + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + /* Rx slot configuration */ + rx_slotnum = hweight_long(rx_mask); + if (rx_slotnum != 1) { + ret = -EINVAL; + dev_err(component->dev, "too many rx slots or zero slot\n"); + goto _set_tdm_err_; + } + + /* This is an assumption that the system sends stereo audio to the amplifier typically. + * And the stereo audio is placed in slot 0/2/4/6 as the starting slot. + * The users could select the channel from L/R/L+R by "Mono LR Select" control. + */ + first_bit = __ffs(rx_mask); + switch (first_bit) { + case 0: + case 2: + case 4: + case 6: + snd_soc_component_update_bits(component, + RT1015_TDM1_4, + RT1015_TDM_I2S_TX_L_DAC1_1_MASK | + RT1015_TDM_I2S_TX_R_DAC1_1_MASK, + (first_bit << RT1015_TDM_I2S_TX_L_DAC1_1_SFT) | + ((first_bit+1) << RT1015_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + case 1: + case 3: + case 5: + case 7: + snd_soc_component_update_bits(component, + RT1015_TDM1_4, + RT1015_TDM_I2S_TX_L_DAC1_1_MASK | + RT1015_TDM_I2S_TX_R_DAC1_1_MASK, + ((first_bit-1) << RT1015_TDM_I2S_TX_L_DAC1_1_SFT) | + (first_bit << RT1015_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + /* Tx slot configuration */ + tx_slotnum = hweight_long(tx_mask); + if (tx_slotnum) { + ret = -EINVAL; + dev_err(component->dev, "doesn't need to support tx slots\n"); + goto _set_tdm_err_; + } + + snd_soc_component_update_bits(component, RT1015_TDM1_1, + RT1015_I2S_CH_TX_MASK | RT1015_I2S_CH_RX_MASK | + RT1015_I2S_CH_TX_LEN_MASK | RT1015_I2S_CH_RX_LEN_MASK, val); + +_set_tdm_err_: + return ret; +} + static int rt1015_probe(struct snd_soc_component *component) { struct rt1015_priv *rt1015 = @@ -958,7 +1054,6 @@ static int rt1015_probe(struct snd_soc_component *component) rt1015->component = component; rt1015->bclk_ratio = 0; rt1015->cali_done = 0; - snd_soc_component_write(component, RT1015_BAT_RPO_STEP1, 0x061c); INIT_DELAYED_WORK(&rt1015->flush_work, rt1015_flush_work); @@ -981,6 +1076,7 @@ static struct snd_soc_dai_ops rt1015_aif_dai_ops = { .hw_params = rt1015_hw_params, .set_fmt = rt1015_set_dai_fmt, .set_bclk_ratio = rt1015_set_bclk_ratio, + .set_tdm_slot = rt1015_set_tdm_slot, }; static struct snd_soc_dai_driver rt1015_dai[] = { @@ -1111,8 +1207,13 @@ static int rt1015_i2c_probe(struct i2c_client *i2c, rt1015->hw_config = (i2c->addr == 0x29) ? RT1015_HW_29 : RT1015_HW_28; - regmap_read(rt1015->regmap, RT1015_DEVICE_ID, &val); - if ((val != RT1015_DEVICE_ID_VAL) && (val != RT1015_DEVICE_ID_VAL2)) { + ret = regmap_read(rt1015->regmap, RT1015_DEVICE_ID, &val); + if (ret) { + dev_err(&i2c->dev, + "Failed to read device register: %d\n", ret); + return ret; + } else if ((val != RT1015_DEVICE_ID_VAL) && + (val != RT1015_DEVICE_ID_VAL2)) { dev_err(&i2c->dev, "Device with ID register %x is not rt1015\n", val); return -ENODEV; diff --git a/sound/soc/codecs/rt1015.h b/sound/soc/codecs/rt1015.h index 15cadb361ec3..b6ea753014e1 100644 --- a/sound/soc/codecs/rt1015.h +++ b/sound/soc/codecs/rt1015.h @@ -214,6 +214,12 @@ #define RT1015_ID_VERA 0x0 #define RT1015_ID_VERB 0x1 +/* 0x00f2 */ +#define RT1015_MONO_LR_SEL_MASK (0x3 << 4) +#define RT1015_MONO_L_CHANNEL (0x0 << 4) +#define RT1015_MONO_R_CHANNEL (0x1 << 4) +#define RT1015_MONO_LR_MIX_CHANNEL (0x2 << 4) + /* 0x0102 */ #define RT1015_DAC_VOL_MASK (0x7f << 9) #define RT1015_DAC_VOL_SFT 9 @@ -276,6 +282,42 @@ #define RT1015_TDM_INV_BCLK_MASK (0x1 << 15) #define RT1015_TDM_INV_BCLK_SFT 15 #define RT1015_TDM_INV_BCLK (0x1 << 15) +#define RT1015_I2S_CH_TX_MASK (0x3 << 10) +#define RT1015_I2S_CH_TX_SFT 10 +#define RT1015_I2S_TX_2CH (0x0 << 10) +#define RT1015_I2S_TX_4CH (0x1 << 10) +#define RT1015_I2S_TX_6CH (0x2 << 10) +#define RT1015_I2S_TX_8CH (0x3 << 10) +#define RT1015_I2S_CH_RX_MASK (0x3 << 8) +#define RT1015_I2S_CH_RX_SFT 8 +#define RT1015_I2S_RX_2CH (0x0 << 8) +#define RT1015_I2S_RX_4CH (0x1 << 8) +#define RT1015_I2S_RX_6CH (0x2 << 8) +#define RT1015_I2S_RX_8CH (0x3 << 8) +#define RT1015_I2S_LR_CH_SEL_MASK (0x1 << 7) +#define RT1015_I2S_LR_CH_SEL_SFT 7 +#define RT1015_I2S_LEFT_CH_SEL (0x0 << 7) +#define RT1015_I2S_RIGHT_CH_SEL (0x1 << 7) +#define RT1015_I2S_CH_TX_LEN_MASK (0x7 << 4) +#define RT1015_I2S_CH_TX_LEN_SFT 4 +#define RT1015_I2S_CH_TX_LEN_16B (0x0 << 4) +#define RT1015_I2S_CH_TX_LEN_20B (0x1 << 4) +#define RT1015_I2S_CH_TX_LEN_24B (0x2 << 4) +#define RT1015_I2S_CH_TX_LEN_32B (0x3 << 4) +#define RT1015_I2S_CH_TX_LEN_8B (0x4 << 4) +#define RT1015_I2S_CH_RX_LEN_MASK (0x7 << 0) +#define RT1015_I2S_CH_RX_LEN_SFT 0 +#define RT1015_I2S_CH_RX_LEN_16B (0x0 << 0) +#define RT1015_I2S_CH_RX_LEN_20B (0x1 << 0) +#define RT1015_I2S_CH_RX_LEN_24B (0x2 << 0) +#define RT1015_I2S_CH_RX_LEN_32B (0x3 << 0) +#define RT1015_I2S_CH_RX_LEN_8B (0x4 << 0) + +/* TDM1 Setting-4 (0x011a) */ +#define RT1015_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 12) +#define RT1015_TDM_I2S_TX_R_DAC1_1_MASK (0x7 << 8) +#define RT1015_TDM_I2S_TX_L_DAC1_1_SFT 12 +#define RT1015_TDM_I2S_TX_R_DAC1_1_SFT 8 /* 0x0330 */ #define RT1015_ABST_AUTO_EN_MASK (0x1 << 13) diff --git a/sound/soc/codecs/rt1015p.c b/sound/soc/codecs/rt1015p.c index 59bb60682270..671f2a2130fe 100644 --- a/sound/soc/codecs/rt1015p.c +++ b/sound/soc/codecs/rt1015p.c @@ -4,6 +4,7 @@ // // Copyright 2020 The Linux Foundation. All rights reserved. +#include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> #include <linux/gpio.h> @@ -19,60 +20,46 @@ struct rt1015p_priv { struct gpio_desc *sdb; - int sdb_switch; + bool calib_done; }; -static int rt1015p_daiops_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) +static int rt1015p_sdb_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_component *component = dai->component; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); struct rt1015p_priv *rt1015p = snd_soc_component_get_drvdata(component); if (!rt1015p->sdb) return 0; - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (rt1015p->sdb_switch) { - gpiod_set_value(rt1015p->sdb, 1); - dev_dbg(component->dev, "set sdb to 1"); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + gpiod_set_value_cansleep(rt1015p->sdb, 1); + dev_dbg(component->dev, "set sdb to 1"); + + if (!rt1015p->calib_done) { + msleep(300); + rt1015p->calib_done = true; } break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - gpiod_set_value(rt1015p->sdb, 0); + case SND_SOC_DAPM_POST_PMD: + gpiod_set_value_cansleep(rt1015p->sdb, 0); dev_dbg(component->dev, "set sdb to 0"); break; + default: + break; } return 0; } -static int rt1015p_sdb_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 rt1015p_priv *rt1015p = - snd_soc_component_get_drvdata(component); - - if (event & SND_SOC_DAPM_POST_PMU) - rt1015p->sdb_switch = 1; - else if (event & SND_SOC_DAPM_POST_PMD) - rt1015p->sdb_switch = 0; - - return 0; -} - static const struct snd_soc_dapm_widget rt1015p_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("Speaker"), SND_SOC_DAPM_OUT_DRV_E("SDB", SND_SOC_NOPM, 0, 0, NULL, 0, rt1015p_sdb_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route rt1015p_dapm_routes[] = { @@ -80,7 +67,20 @@ static const struct snd_soc_dapm_route rt1015p_dapm_routes[] = { {"Speaker", NULL, "SDB"}, }; +#ifdef CONFIG_PM +static int rt1015p_suspend(struct snd_soc_component *component) +{ + struct rt1015p_priv *rt1015p = snd_soc_component_get_drvdata(component); + + rt1015p->calib_done = false; + return 0; +} +#else +#define rt1015p_suspend NULL +#endif + static const struct snd_soc_component_driver rt1015p_component_driver = { + .suspend = rt1015p_suspend, .dapm_widgets = rt1015p_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(rt1015p_dapm_widgets), .dapm_routes = rt1015p_dapm_routes, @@ -91,10 +91,6 @@ static const struct snd_soc_component_driver rt1015p_component_driver = { .non_legacy_dai_naming = 1, }; -static const struct snd_soc_dai_ops rt1015p_dai_ops = { - .trigger = rt1015p_daiops_trigger, -}; - static struct snd_soc_dai_driver rt1015p_dai_driver = { .name = "HiFi", .playback = { @@ -104,7 +100,6 @@ static struct snd_soc_dai_driver rt1015p_dai_driver = { .channels_min = 1, .channels_max = 2, }, - .ops = &rt1015p_dai_ops, }; static int rt1015p_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index c2621b0afe6c..ec5564f780e8 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -475,7 +475,7 @@ static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, if (!stream) return -ENOMEM; - stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ if (direction == SNDRV_PCM_STREAM_PLAYBACK) diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 9e3813f7583d..0edf09d3a499 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1235,11 +1235,13 @@ static const struct i2c_device_id rt5660_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5660_i2c_id); +#ifdef CONFIG_OF static const struct of_device_id rt5660_of_match[] = { { .compatible = "realtek,rt5660", }, {}, }; MODULE_DEVICE_TABLE(of, rt5660_of_match); +#endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt5660_acpi_match[] = { diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index 6b4e0eb30c89..37d13120f5ba 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -221,6 +221,11 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, case RT5682_DMIC1_CLK_GPIO3: /* share with BCLK2 */ regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, RT5682_GP3_PIN_MASK, RT5682_GP3_PIN_DMIC_CLK); + if (rt5682->pdata.dmic_clk_driving_high) + regmap_update_bits(rt5682->regmap, + RT5682_PAD_DRIVING_CTRL, + RT5682_PAD_DRV_GP3_MASK, + 2 << RT5682_PAD_DRV_GP3_SFT); break; default: diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 58fb13132602..4d707e854875 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -103,7 +103,7 @@ static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, if (!stream) return -ENOMEM; - stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ if (direction == SNDRV_PCM_STREAM_PLAYBACK) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index d9878173ff89..4d865edadd7e 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2990,6 +2990,9 @@ int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) rt5682->pdata.dai_clk_names[RT5682_DAI_WCLK_IDX], rt5682->pdata.dai_clk_names[RT5682_DAI_BCLK_IDX]); + rt5682->pdata.dmic_clk_driving_high = device_property_read_bool(dev, + "realtek,dmic-clk-driving-high"); + return 0; } EXPORT_SYMBOL_GPL(rt5682_parse_dt); diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 354acd735ef4..99b85cfe6248 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -1271,6 +1271,20 @@ #define RT5682_CP_CLK_HP_300KHZ (0x2 << 4) #define RT5682_CP_CLK_HP_600KHZ (0x3 << 4) +/* Pad Driving Control (0x0136) */ +#define RT5682_PAD_DRV_GP1_MASK (0x3 << 14) +#define RT5682_PAD_DRV_GP1_SFT 14 +#define RT5682_PAD_DRV_GP2_MASK (0x3 << 12) +#define RT5682_PAD_DRV_GP2_SFT 12 +#define RT5682_PAD_DRV_GP3_MASK (0x3 << 10) +#define RT5682_PAD_DRV_GP3_SFT 10 +#define RT5682_PAD_DRV_GP4_MASK (0x3 << 8) +#define RT5682_PAD_DRV_GP4_SFT 8 +#define RT5682_PAD_DRV_GP5_MASK (0x3 << 6) +#define RT5682_PAD_DRV_GP5_SFT 6 +#define RT5682_PAD_DRV_GP6_MASK (0x3 << 4) +#define RT5682_PAD_DRV_GP6_SFT 4 + /* Chopper and Clock control for DAC (0x013a)*/ #define RT5682_CKXEN_DAC1_MASK (0x1 << 13) #define RT5682_CKXEN_DAC1_SFT 13 diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 687ac2153666..66ec395dbbcd 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -867,7 +867,7 @@ static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, if (!stream) return -ENOMEM; - stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ if (direction == SNDRV_PCM_STREAM_PLAYBACK) diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index f0a0691bd31c..fc7df79c3b91 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -338,7 +338,8 @@ static int rt711_update_status(struct sdw_slave *slave, static int rt711_read_prop(struct sdw_slave *slave) { struct sdw_slave_prop *prop = &slave->prop; - int nval, i; + int nval; + int i, j; u32 bit; unsigned long addr; struct sdw_dpn_prop *dpn; @@ -379,15 +380,15 @@ static int rt711_read_prop(struct sdw_slave *slave) if (!prop->sink_dpn_prop) return -ENOMEM; - i = 0; + j = 0; dpn = prop->sink_dpn_prop; addr = prop->sink_ports; for_each_set_bit(bit, &addr, 32) { - dpn[i].num = bit; - dpn[i].type = SDW_DPN_FULL; - dpn[i].simple_ch_prep_sm = true; - dpn[i].ch_prep_timeout = 10; - i++; + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; } /* set the timeout values */ diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 65b59dbfb43c..5771c02c3459 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -913,7 +913,7 @@ static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, if (!stream) return -ENOMEM; - stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ if (direction == SNDRV_PCM_STREAM_PLAYBACK) diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c new file mode 100644 index 000000000000..889b6b3b0009 --- /dev/null +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt715-sdca-sdw.c -- rt715 ALSA SoC audio driver +// +// Copyright(c) 2020 Realtek Semiconductor Corp. +// +// + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "rt715-sdca.h" +#include "rt715-sdca-sdw.h" + +static bool rt715_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2230 ... 0x2239: + case 0x2f5b: + case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): + return true; + default: + return false; + } +} + +static bool rt715_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x202d ... 0x202f: /* BRA */ + case 0x2200 ... 0x2212: /* i2c debug */ + case 0x2f07: + case 0x2f1b ... 0x2f1e: + case 0x2f30 ... 0x2f34: + case 0x2f50 ... 0x2f51: + case 0x2f53 ... 0x2f59: + case 0x2f5c ... 0x2f5f: + case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): /* VAD Searching status */ + return true; + default: + return false; + } +} + +static bool rt715_sdca_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + case 0x200002b: + case 0x2000036: + case 0x2000037: + case 0x2000039: + case 0x6100000: + return true; + default: + return false; + } +} + +static bool rt715_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + return true; + default: + return false; + } +} + +static const struct regmap_config rt715_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt715_sdca_readable_register, + .volatile_reg = rt715_sdca_volatile_register, + .max_register = 0x43ffffff, + .reg_defaults = rt715_reg_defaults_sdca, + .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt715_sdca_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt715_sdca_mbq_readable_register, + .volatile_reg = rt715_sdca_mbq_volatile_register, + .max_register = 0x43ffffff, + .reg_defaults = rt715_mbq_reg_defaults_sdca, + .num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt715_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt715->status = status; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt715_io_init(&slave->dev, slave); +} + +static int rt715_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x50;/* BITMAP: 01010000 */ + prop->sink_ports = 0x0; /* BITMAP: 00000000 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + dpn = prop->src_dpn_prop; + i = 0; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + return 0; +} + +static struct sdw_slave_ops rt715_sdca_slave_ops = { + .read_prop = rt715_read_prop, + .update_status = rt715_update_status, +}; + +static int rt715_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *mbq_regmap, *regmap; + + slave->ops = &rt715_sdca_slave_ops; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap); + if (!mbq_regmap) + return -EINVAL; + + regmap = devm_regmap_init_sdw(slave, &rt715_sdca_regmap); + if (!regmap) + return -EINVAL; + + return rt715_init(&slave->dev, mbq_regmap, regmap, slave); +} + +static const struct sdw_device_id rt715_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt715_sdca_id); + +static int __maybe_unused rt715_dev_suspend(struct device *dev) +{ + struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); + + if (!rt715->hw_init) + return 0; + + regcache_cache_only(rt715->regmap, true); + regcache_mark_dirty(rt715->regmap); + regcache_cache_only(rt715->mbq_regmap, true); + regcache_mark_dirty(rt715->mbq_regmap); + + return 0; +} + +#define RT715_PROBE_TIMEOUT 2000 + +static int __maybe_unused rt715_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt715->hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->enumeration_complete, + msecs_to_jiffies(RT715_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt715->regmap, false); + regcache_sync_region(rt715->regmap, + SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, + CH_00), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00)); + regcache_cache_only(rt715->mbq_regmap, false); + regcache_sync_region(rt715->mbq_regmap, 0x2000000, 0x61020ff); + regcache_sync_region(rt715->mbq_regmap, + SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, + CH_00), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00)); + + return 0; +} + +static const struct dev_pm_ops rt715_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) + SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) +}; + +static struct sdw_driver rt715_sdw_driver = { + .driver = { + .name = "rt715-sdca", + .owner = THIS_MODULE, + .pm = &rt715_pm, + }, + .probe = rt715_sdca_sdw_probe, + .ops = &rt715_sdca_slave_ops, + .id_table = rt715_sdca_id, +}; +module_sdw_driver(rt715_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT715 driver SDW SDCA"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h new file mode 100644 index 000000000000..cd365bb60747 --- /dev/null +++ b/sound/soc/codecs/rt715-sdca-sdw.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt715-sdca-sdw.h -- RT715 ALSA SoC audio driver header + * + * Copyright(c) 2020 Realtek Semiconductor Corp. + */ + +#ifndef __RT715_SDW_SDCA_H__ +#define __RT715_SDW_SDCA_H__ + +#include <linux/soundwire/sdw_registers.h> + +static const struct reg_default rt715_reg_defaults_sdca[] = { + { 0x201a, 0x00 }, + { 0x201e, 0x00 }, + { 0x2020, 0x00 }, + { 0x2021, 0x00 }, + { 0x2022, 0x00 }, + { 0x2023, 0x00 }, + { 0x2024, 0x00 }, + { 0x2025, 0x01 }, + { 0x2026, 0x00 }, + { 0x2027, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x2f }, + { 0x2232, 0x80 }, + { 0x2233, 0x00 }, + { 0x2234, 0x00 }, + { 0x2235, 0x00 }, + { 0x2236, 0x00 }, + { 0x2237, 0x00 }, + { 0x2238, 0x00 }, + { 0x2239, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x0b }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0e }, + { 0x2f06, 0x01 }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f0b, 0x00 }, + { 0x2f0c, 0x00 }, + { 0x2f0d, 0x00 }, + { 0x2f0e, 0x12 }, + { 0x2f0f, 0x00 }, + { 0x2f10, 0x00 }, + { 0x2f11, 0x00 }, + { 0x2f12, 0x00 }, + { 0x2f13, 0x00 }, + { 0x2f14, 0x00 }, + { 0x2f15, 0x00 }, + { 0x2f16, 0x00 }, + { 0x2f17, 0x00 }, + { 0x2f18, 0x00 }, + { 0x2f19, 0x03 }, + { 0x2f1a, 0x00 }, + { 0x2f1f, 0x10 }, + { 0x2f20, 0x00 }, + { 0x2f21, 0x00 }, + { 0x2f22, 0x00 }, + { 0x2f23, 0x00 }, + { 0x2f24, 0x00 }, + { 0x2f25, 0x00 }, + { 0x2f52, 0x01 }, + { 0x2f5a, 0x02 }, + { 0x2f5b, 0x05 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, + RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, +}; + +static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { + { 0x200002b, 0x0420 }, + { 0x2000036, 0x0000 }, + { 0x2000037, 0x0000 }, + { 0x2000039, 0xaa81 }, + { 0x6100000, 0x0100 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, +}; +#endif /* __RT715_SDW_SDCA_H__ */ diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c new file mode 100644 index 000000000000..b843e47eb25b --- /dev/null +++ b/sound/soc/codecs/rt715-sdca.c @@ -0,0 +1,936 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt715-sdca.c -- rt715 ALSA SoC audio driver +// +// Copyright(c) 2020 Realtek Semiconductor Corp. +// +// +// + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pm_runtime.h> +#include <linux/pm.h> +#include <linux/soundwire/sdw.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <linux/soundwire/sdw_registers.h> + +#include "rt715-sdca.h" + +static int rt715_index_write(struct rt715_sdca_priv *rt715, unsigned int nid, + unsigned int reg, unsigned int value) +{ + struct regmap *regmap = rt715->mbq_regmap; + unsigned int addr; + int ret; + + addr = (nid << 20) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt715->slave->dev, + "Failed to set private value: %08x <= %04x %d\n", ret, addr, + value); + + return ret; +} + +static int rt715_index_read(struct rt715_sdca_priv *rt715, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + struct regmap *regmap = rt715->mbq_regmap; + unsigned int addr; + int ret; + + addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt715->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt715_index_update_bits(struct rt715_sdca_priv *rt715, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt715_index_read(rt715, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + + return rt715_index_write(rt715, nid, reg, tmp); +} + +/* SDCA Volume/Boost control */ +static int rt715_set_amp_gain_put_sdca(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + unsigned int val_l, val_r, gain_l_val, gain_r_val; + int ret; + + /* control value to 2s complement */ + /* L channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + val_l = gain_l_val; + + if (mc->shift == 8) { + gain_l_val = (gain_l_val * 10) << mc->shift; + } else { + gain_l_val = + ((abs(gain_l_val - mc->shift) * RT715_SDCA_DB_STEP) << 8) / 1000; + if (val_l <= mc->shift) { + gain_l_val = ~gain_l_val; + gain_l_val += 1; + } + gain_l_val &= 0xffff; + } + + /* R channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + val_r = gain_r_val; + + if (mc->shift == 8) { + gain_r_val = (gain_r_val * 10) << mc->shift; + } else { + gain_r_val = + ((abs(gain_r_val - mc->shift) * RT715_SDCA_DB_STEP) << 8) / 1000; + if (val_r <= mc->shift) { + gain_r_val = ~gain_r_val; + gain_r_val += 1; + } + gain_r_val &= 0xffff; + } + + /* Lch*/ + ret = regmap_write(rt715->mbq_regmap, mc->reg, gain_l_val); + if (ret != 0) { + dev_err(component->dev, "Failed to write 0x%x=0x%x\n", mc->reg, + gain_l_val); + return ret; + } + /* Rch */ + ret = regmap_write(rt715->mbq_regmap, mc->rreg, gain_r_val); + if (ret != 0) { + dev_err(component->dev, "Failed to write 0x%x=0x%x\n", mc->rreg, + gain_r_val); + return ret; + } + + return 0; +} + +static int rt715_set_amp_gain_get_sdca(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + unsigned int val_l, val_r, ctl_l, ctl_r, neg_flag = 0; + int ret; + + ret = regmap_read(rt715->mbq_regmap, mc->reg, &val_l); + if (ret < 0) + dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", mc->reg, ret); + ret = regmap_read(rt715->mbq_regmap, mc->rreg, &val_r); + if (ret < 0) + dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", mc->rreg, + ret); + + /* L channel */ + if (mc->shift == 8) { + ctl_l = (val_l >> mc->shift) / 10; + } else { + ctl_l = val_l; + if (ctl_l & BIT(15)) { + ctl_l = ~(val_l - 1) & 0xffff; + neg_flag = 1; + } + ctl_l *= 1000; + ctl_l >>= 8; + if (neg_flag) + ctl_l = mc->shift - ctl_l / RT715_SDCA_DB_STEP; + else + ctl_l = mc->shift + ctl_l / RT715_SDCA_DB_STEP; + } + + neg_flag = 0; + /* R channel */ + if (mc->shift == 8) { + ctl_r = (val_r >> mc->shift) / 10; + } else { + ctl_r = val_r; + if (ctl_r & BIT(15)) { + ctl_r = ~(val_r - 1) & 0xffff; + neg_flag = 1; + } + ctl_r *= 1000; + ctl_r >>= 8; + if (neg_flag) + ctl_r = mc->shift - ctl_r / RT715_SDCA_DB_STEP; + else + ctl_r = mc->shift + ctl_r / RT715_SDCA_DB_STEP; + } + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ + xmax, xinvert) } + +static const struct snd_kcontrol_new rt715_snd_controls_sdca[] = { + /* Capture switch */ + SOC_DOUBLE_R("FU0A Capture Switch", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), + 0, 1, 1), + SOC_DOUBLE_R("FU02 1_2 Capture Switch", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), + 0, 1, 1), + SOC_DOUBLE_R("FU02 3_4 Capture Switch", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_03), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_04), + 0, 1, 1), + SOC_DOUBLE_R("FU06 1_2 Capture Switch", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), + 0, 1, 1), + SOC_DOUBLE_R("FU06 3_4 Capture Switch", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_03), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_04), + 0, 1, 1), + /* Volume Control */ + SOC_DOUBLE_R_EXT_TLV("FU0A Capture Volume", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_02), + 0x2f, 0x7f, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU02 1_2 Capture Volume", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_02), + 0x2f, 0x7f, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU02 3_4 Capture Volume", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, + CH_03), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, + CH_04), 0x2f, 0x7f, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU06 1_2 Capture Volume", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, + CH_01), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, + CH_02), 0x2f, 0x7f, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU06 3_4 Capture Volume", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, + CH_03), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, + CH_04), 0x2f, 0x7f, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + in_vol_tlv), + /* MIC Boost Control */ + SOC_DOUBLE_R_EXT_TLV("FU0E 1_2 Boost", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_01), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_02), 8, 3, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU0E 3_4 Boost", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_03), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_04), 8, 3, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU0E 5_6 Boost", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_05), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_06), 8, 3, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU0E 7_8 Boost", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_07), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_08), 8, 3, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU0C 1_2 Boost", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_01), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_02), 8, 3, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU0C 3_4 Boost", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_03), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_04), 8, 3, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU0C 5_6 Boost", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_05), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_06), 8, 3, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU0C 7_8 Boost", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_07), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, + CH_08), 8, 3, 0, + rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, + mic_vol_tlv), +}; + +static int rt715_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + unsigned int val, mask_sft; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + mask_sft = 8; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + rt715_index_read(rt715, RT715_VENDOR_HDA_CTL, + RT715_HDA_LEGACY_MUX_CTL1, &val); + val = (val >> mask_sft) & 0xf; + + /* + * The first two indices of ADC Mux 24/25 are routed to the same + * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2. + * To have a unique set of inputs, we skip the index1 of the muxes. + */ + if ((strstr(ucontrol->id.name, "ADC 24 Mux") || + strstr(ucontrol->id.name, "ADC 25 Mux")) && val > 0) + val -= 1; + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt715_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + mask_sft = 8; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + /* Verb ID = 0x701h, nid = e->reg */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + rt715_index_read(rt715, RT715_VENDOR_HDA_CTL, + RT715_HDA_LEGACY_MUX_CTL1, &val2); + val2 = (val2 >> mask_sft) & 0xf; + + change = val != val2; + + if (change) + rt715_index_update_bits(rt715, RT715_VENDOR_HDA_CTL, + RT715_HDA_LEGACY_MUX_CTL1, 0xf << mask_sft, val << mask_sft); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL); + + return change; +} + +static const char * const adc_22_23_mux_text[] = { + "MIC1", + "MIC2", + "LINE1", + "LINE2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +/* + * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and + * 1 will be connected to the same dmic source, therefore we skip index 1 to + * avoid misunderstanding on usage of dapm routing. + */ +static int rt715_adc_24_25_values[] = { + 0, + 2, + 3, + 4, + 5, +}; + +static const char * const adc_24_mux_text[] = { + "MIC2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +static const char * const adc_25_mux_text[] = { + "MIC1", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +static SOC_ENUM_SINGLE_DECL(rt715_adc22_enum, SND_SOC_NOPM, 0, + adc_22_23_mux_text); + +static SOC_ENUM_SINGLE_DECL(rt715_adc23_enum, SND_SOC_NOPM, 0, + adc_22_23_mux_text); + +static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum, + SND_SOC_NOPM, 0, 0xf, + adc_24_mux_text, rt715_adc_24_25_values); +static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum, + SND_SOC_NOPM, 0, 0xf, + adc_25_mux_text, rt715_adc_24_25_values); + +static const struct snd_kcontrol_new rt715_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc24_mux = + SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc25_mux = + SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum, + rt715_mux_get, rt715_mux_put); + +static int rt715_pde23_24_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 rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt715->regmap, + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN, + RT715_SDCA_REQ_POW_CTRL, + CH_00), 0x00); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt715->regmap, + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN, + RT715_SDCA_REQ_POW_CTRL, + CH_00), 0x03); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt715_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + + SND_SOC_DAPM_SUPPLY("PDE23_24", SND_SOC_NOPM, 0, 0, + rt715_pde23_24_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_ADC("ADC 07", NULL, SND_SOC_NOPM, 4, 0), + SND_SOC_DAPM_ADC("ADC 08", NULL, SND_SOC_NOPM, 4, 0), + SND_SOC_DAPM_ADC("ADC 09", NULL, SND_SOC_NOPM, 4, 0), + SND_SOC_DAPM_ADC("ADC 27", NULL, SND_SOC_NOPM, 4, 0), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc23_mux), + SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc24_mux), + SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc25_mux), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt715_audio_map[] = { + {"DP6TX", NULL, "ADC 09"}, + {"DP6TX", NULL, "ADC 08"}, + {"DP4TX", NULL, "ADC 07"}, + {"DP4TX", NULL, "ADC 27"}, + {"DP4TX", NULL, "ADC 09"}, + {"DP4TX", NULL, "ADC 08"}, + + {"LINE1", NULL, "PDE23_24"}, + {"LINE2", NULL, "PDE23_24"}, + {"MIC1", NULL, "PDE23_24"}, + {"MIC2", NULL, "PDE23_24"}, + {"DMIC1", NULL, "PDE23_24"}, + {"DMIC2", NULL, "PDE23_24"}, + {"DMIC3", NULL, "PDE23_24"}, + {"DMIC4", NULL, "PDE23_24"}, + + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 07", NULL, "ADC 24 Mux"}, + {"ADC 27", NULL, "ADC 25 Mux"}, + {"ADC 22 Mux", "MIC1", "MIC1"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "DMIC1", "DMIC1"}, + {"ADC 22 Mux", "DMIC2", "DMIC2"}, + {"ADC 22 Mux", "DMIC3", "DMIC3"}, + {"ADC 22 Mux", "DMIC4", "DMIC4"}, + {"ADC 23 Mux", "MIC1", "MIC1"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "DMIC1", "DMIC1"}, + {"ADC 23 Mux", "DMIC2", "DMIC2"}, + {"ADC 23 Mux", "DMIC3", "DMIC3"}, + {"ADC 23 Mux", "DMIC4", "DMIC4"}, + {"ADC 24 Mux", "MIC2", "MIC2"}, + {"ADC 24 Mux", "DMIC1", "DMIC1"}, + {"ADC 24 Mux", "DMIC2", "DMIC2"}, + {"ADC 24 Mux", "DMIC3", "DMIC3"}, + {"ADC 24 Mux", "DMIC4", "DMIC4"}, + {"ADC 25 Mux", "MIC1", "MIC1"}, + {"ADC 25 Mux", "DMIC1", "DMIC1"}, + {"ADC 25 Mux", "DMIC2", "DMIC2"}, + {"ADC 25 Mux", "DMIC3", "DMIC3"}, + {"ADC 25 Mux", "DMIC4", "DMIC4"}, +}; + +static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = { + .controls = rt715_snd_controls_sdca, + .num_controls = ARRAY_SIZE(rt715_snd_controls_sdca), + .dapm_widgets = rt715_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets), + .dapm_routes = rt715_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt715_audio_map), +}; + +static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + struct rt715_sdw_stream_data *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = stream; + else + dai->capture_dma_data = stream; + + return 0; +} + +static void rt715_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + +{ + struct rt715_sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + if (!stream) + return; + + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct rt715_sdw_stream_data *stream; + int retval, port, num_channels; + unsigned int val; + + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!rt715->slave) + return -EINVAL; + + switch (dai->id) { + case RT715_AIF1: + direction = SDW_DATA_DIR_TX; + port = 6; + rt715_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL, + 0xa500); + break; + case RT715_AIF2: + direction = SDW_DATA_DIR_TX; + port = 4; + rt715_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL, + 0xaf00); + break; + default: + dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = GENMASK(num_channels - 1, 0); + port_config.num = port; + + retval = sdw_stream_add_slave(rt715->slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(component->dev, "Unable to configure port, retval:%d\n", + retval); + return retval; + } + + switch (params_rate(params)) { + case 8000: + val = 0x1; + break; + case 11025: + val = 0x2; + break; + case 12000: + val = 0x3; + break; + case 16000: + val = 0x4; + break; + case 22050: + val = 0x5; + break; + case 24000: + val = 0x6; + break; + case 32000: + val = 0x7; + break; + case 44100: + val = 0x8; + break; + case 48000: + val = 0x9; + break; + case 88200: + val = 0xa; + break; + case 96000: + val = 0xb; + break; + case 176400: + val = 0xc; + break; + case 192000: + val = 0xd; + break; + case 384000: + val = 0xe; + break; + case 768000: + val = 0xf; + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + + regmap_write(rt715->regmap, + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CS_FREQ_IND_EN, + RT715_SDCA_FREQ_IND_CTRL, CH_00), val); + + return 0; +} + +static int rt715_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct rt715_sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt715->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt715->slave, stream->sdw_stream); + return 0; +} + +#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt715_ops = { + .hw_params = rt715_pcm_hw_params, + .hw_free = rt715_pcm_hw_free, + .set_sdw_stream = rt715_set_sdw_stream, + .shutdown = rt715_shutdown, +}; + +static struct snd_soc_dai_driver rt715_dai[] = { + { + .name = "rt715-aif1", + .id = RT715_AIF1, + .capture = { + .stream_name = "DP6 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT715_STEREO_RATES, + .formats = RT715_FORMATS, + }, + .ops = &rt715_ops, + }, + { + .name = "rt715-aif2", + .id = RT715_AIF2, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT715_STEREO_RATES, + .formats = RT715_FORMATS, + }, + .ops = &rt715_ops, + }, +}; + +/* Bus clock frequency */ +#define RT715_CLK_FREQ_9600000HZ 9600000 +#define RT715_CLK_FREQ_12000000HZ 12000000 +#define RT715_CLK_FREQ_6000000HZ 6000000 +#define RT715_CLK_FREQ_4800000HZ 4800000 +#define RT715_CLK_FREQ_2400000HZ 2400000 +#define RT715_CLK_FREQ_12288000HZ 12288000 + +int rt715_init(struct device *dev, struct regmap *mbq_regmap, + struct regmap *regmap, struct sdw_slave *slave) +{ + struct rt715_sdca_priv *rt715; + int ret; + + rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL); + if (!rt715) + return -ENOMEM; + + dev_set_drvdata(dev, rt715); + rt715->slave = slave; + rt715->regmap = regmap; + rt715->mbq_regmap = mbq_regmap; + rt715->hw_sdw_ver = slave->id.sdw_version; + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt715->hw_init = false; + rt715->first_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt715_sdca, + rt715_dai, + ARRAY_SIZE(rt715_dai)); + + return ret; +} + +int rt715_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); + unsigned int hw_ver; + + if (rt715->hw_init) + return 0; + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt715->first_init) { + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + + rt715->first_init = true; + } + + pm_runtime_get_noresume(&slave->dev); + + rt715_index_read(rt715, RT715_VENDOR_REG, + RT715_PRODUCT_NUM, &hw_ver); + hw_ver = hw_ver & 0x000f; + + /* set clock selector = external */ + regmap_write(rt715->regmap, + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, + RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1); + /* set GPIO_4/5/6 to be 3rd/4th DMIC usage */ + if (hw_ver == 0x0) + rt715_index_update_bits(rt715, RT715_VENDOR_REG, + RT715_AD_FUNC_EN, 0x54, 0x54); + else if (hw_ver == 0x1) { + rt715_index_update_bits(rt715, RT715_VENDOR_REG, + RT715_AD_FUNC_EN, 0x55, 0x55); + rt715_index_update_bits(rt715, RT715_VENDOR_REG, + RT715_REV_1, 0x40, 0x40); + } + /* trigger mode = VAD enable */ + regmap_write(rt715->regmap, + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x2); + /* SMPU-1 interrupt enable mask */ + regmap_update_bits(rt715->regmap, RT715_INT_MASK, 0x1, 0x1); + + /* Mark Slave initialization complete */ + rt715->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + return 0; +} + +MODULE_DESCRIPTION("ASoC rt715 driver SDW SDCA"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h new file mode 100644 index 000000000000..6326cd8c374e --- /dev/null +++ b/sound/soc/codecs/rt715-sdca.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt715-sdca.h -- RT715 ALSA SoC audio driver header + * + * Copyright(c) 2020 Realtek Semiconductor Corp. + */ + +#ifndef __RT715_SDCA_H__ +#define __RT715_SDCA_H__ + +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <linux/workqueue.h> +#include <linux/device.h> + +struct rt715_sdca_priv { + struct regmap *regmap; + struct regmap *mbq_regmap; + struct snd_soc_codec *codec; + struct sdw_slave *slave; + struct delayed_work adc_mute_work; + int dbg_nid; + int dbg_vid; + int dbg_payload; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_init; + int l_is_unmute; + int r_is_unmute; + int hw_sdw_ver; +}; + +struct rt715_sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +/* MIPI Register */ +#define RT715_INT_CTRL 0x005a +#define RT715_INT_MASK 0x005e + +/* NID */ +#define RT715_AUDIO_FUNCTION_GROUP 0x01 +#define RT715_MIC_ADC 0x07 +#define RT715_LINE_ADC 0x08 +#define RT715_MIX_ADC 0x09 +#define RT715_DMIC1 0x12 +#define RT715_DMIC2 0x13 +#define RT715_MIC1 0x18 +#define RT715_MIC2 0x19 +#define RT715_LINE1 0x1a +#define RT715_LINE2 0x1b +#define RT715_DMIC3 0x1d +#define RT715_DMIC4 0x29 +#define RT715_VENDOR_REG 0x20 +#define RT715_MUX_IN1 0x22 +#define RT715_MUX_IN2 0x23 +#define RT715_MUX_IN3 0x24 +#define RT715_MUX_IN4 0x25 +#define RT715_MIX_ADC2 0x27 +#define RT715_INLINE_CMD 0x55 +#define RT715_VENDOR_HDA_CTL 0x61 + +/* Index (NID:20h) */ +#define RT715_PRODUCT_NUM 0x0 +#define RT715_IRQ_CTRL 0x2b +#define RT715_AD_FUNC_EN 0x36 +#define RT715_REV_1 0x37 +#define RT715_SDW_INPUT_SEL 0x39 +#define RT715_EXT_DMIC_CLK_CTRL2 0x54 + +/* Index (NID:61h) */ +#define RT715_HDA_LEGACY_MUX_CTL1 0x00 + +/* SDCA (Function) */ +#define FUN_JACK_CODEC 0x01 +#define FUN_MIC_ARRAY 0x02 +#define FUN_HID 0x03 +/* SDCA (Entity) */ +#define RT715_SDCA_ST_EN 0x00 +#define RT715_SDCA_CS_FREQ_IND_EN 0x01 +#define RT715_SDCA_FU_ADC8_9_VOL 0x02 +#define RT715_SDCA_SMPU_TRIG_ST_EN 0x05 +#define RT715_SDCA_FU_ADC10_11_VOL 0x06 +#define RT715_SDCA_FU_ADC7_27_VOL 0x0a +#define RT715_SDCA_FU_AMIC_GAIN_EN 0x0c +#define RT715_SDCA_FU_DMIC_GAIN_EN 0x0e +#define RT715_SDCA_CX_CLK_SEL_EN 0x10 +#define RT715_SDCA_CREQ_POW_EN 0x18 +/* SDCA (Control) */ +#define RT715_SDCA_ST_CTRL 0x00 +#define RT715_SDCA_CX_CLK_SEL_CTRL 0x01 +#define RT715_SDCA_REQ_POW_CTRL 0x01 +#define RT715_SDCA_FU_MUTE_CTRL 0x01 +#define RT715_SDCA_FU_VOL_CTRL 0x02 +#define RT715_SDCA_FU_DMIC_GAIN_CTRL 0x0b +#define RT715_SDCA_FREQ_IND_CTRL 0x10 +#define RT715_SDCA_SMPU_TRIG_EN_CTRL 0x10 +#define RT715_SDCA_SMPU_TRIG_ST_CTRL 0x11 +/* SDCA (Channel) */ +#define CH_00 0x00 +#define CH_01 0x01 +#define CH_02 0x02 +#define CH_03 0x03 +#define CH_04 0x04 +#define CH_05 0x05 +#define CH_06 0x06 +#define CH_07 0x07 +#define CH_08 0x08 + +#define RT715_SDCA_DB_STEP 375 + +enum { + RT715_AIF1, + RT715_AIF2, +}; + +int rt715_io_init(struct device *dev, struct sdw_slave *slave); +int rt715_init(struct device *dev, struct regmap *mbq_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +#endif /* __RT715_SDCA_H__ */ diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 532c5303e7ab..9a7d393d424a 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -537,7 +537,7 @@ static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, if (!stream) return -ENOMEM; - stream->sdw_stream = (struct sdw_stream_runtime *)sdw_stream; + stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ if (direction == SNDRV_PCM_STREAM_PLAYBACK) diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h index d0d0fd2a6fdb..009a8266f606 100644 --- a/sound/soc/codecs/rt715.h +++ b/sound/soc/codecs/rt715.h @@ -207,7 +207,6 @@ struct sdw_stream_data { enum { RT715_AIF1, RT715_AIF2, - RT715_AIFS, }; #define RT715_POWER_UP_DELAY_MS 400 diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c new file mode 100644 index 000000000000..e0a09dadfa7c --- /dev/null +++ b/sound/soc/codecs/simple-mux.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020 Bootlin SA + * Author: Alexandre Belloni <alexandre.belloni@bootlin.com> + */ + +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <sound/soc.h> + +struct simple_mux { + struct gpio_desc *gpiod_mux; + unsigned int mux; +}; + +static const char * const simple_mux_texts[] = { + "Input 1", "Input 2" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(simple_mux_enum, simple_mux_texts); + +static int simple_mux_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); + struct simple_mux *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.enumerated.item[0] = priv->mux; + + return 0; +} + +static int simple_mux_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); + struct simple_mux *priv = snd_soc_component_get_drvdata(c); + + if (ucontrol->value.enumerated.item[0] > e->items) + return -EINVAL; + + if (priv->mux == ucontrol->value.enumerated.item[0]) + return 0; + + priv->mux = ucontrol->value.enumerated.item[0]; + + gpiod_set_value_cansleep(priv->gpiod_mux, priv->mux); + + return snd_soc_dapm_mux_update_power(dapm, kcontrol, + ucontrol->value.enumerated.item[0], + e, NULL); +} + +static const struct snd_kcontrol_new simple_mux_mux = + SOC_DAPM_ENUM_EXT("Muxer", simple_mux_enum, simple_mux_control_get, simple_mux_control_put); + +static const struct snd_soc_dapm_widget simple_mux_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_MUX("MUX", SND_SOC_NOPM, 0, 0, &simple_mux_mux), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route simple_mux_dapm_routes[] = { + { "OUT", NULL, "MUX" }, + { "MUX", "Input 1", "IN1" }, + { "MUX", "Input 2", "IN2" }, +}; + +static const struct snd_soc_component_driver simple_mux_component_driver = { + .dapm_widgets = simple_mux_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(simple_mux_dapm_widgets), + .dapm_routes = simple_mux_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(simple_mux_dapm_routes), +}; + +static int simple_mux_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct simple_mux *priv; + int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + + priv->gpiod_mux = devm_gpiod_get(dev, "mux", GPIOD_OUT_LOW); + if (IS_ERR(priv->gpiod_mux)) { + err = PTR_ERR(priv->gpiod_mux); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'mux' gpio: %d", err); + return err; + } + + return devm_snd_soc_register_component(dev, &simple_mux_component_driver, NULL, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id simple_mux_ids[] = { + { .compatible = "simple-audio-mux", }, + { } +}; +MODULE_DEVICE_TABLE(of, simple_mux_ids); +#endif + +static struct platform_driver simple_mux_driver = { + .driver = { + .name = "simple-mux", + .of_match_table = of_match_ptr(simple_mux_ids), + }, + .probe = simple_mux_probe, +}; + +module_platform_driver(simple_mux_driver); + +MODULE_DESCRIPTION("ASoC Simple Audio Mux driver"); +MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index f1ff204e3ad0..19965fabe949 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -802,6 +802,7 @@ static const struct i2c_device_id tas2562_id[] = { }; MODULE_DEVICE_TABLE(i2c, tas2562_id); +#ifdef CONFIG_OF static const struct of_device_id tas2562_of_match[] = { { .compatible = "ti,tas2562", }, { .compatible = "ti,tas2563", }, @@ -810,6 +811,7 @@ static const struct of_device_id tas2562_of_match[] = { { }, }; MODULE_DEVICE_TABLE(of, tas2562_of_match); +#endif static struct i2c_driver tas2562_i2c_driver = { .driver = { diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index 835a723ce5bc..a3e682376946 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -773,7 +773,7 @@ static struct snd_soc_dai_driver tas571x_dai = { .ops = &tas571x_dai_ops, }; -static const struct of_device_id tas571x_of_match[]; +static const struct of_device_id tas571x_of_match[] __maybe_unused; static int tas571x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -889,7 +889,7 @@ static int tas571x_i2c_remove(struct i2c_client *client) return 0; } -static const struct of_device_id tas571x_of_match[] = { +static const struct of_device_id tas571x_of_match[] __maybe_unused = { { .compatible = "ti,tas5707", .data = &tas5707_chip, }, { .compatible = "ti,tas5711", .data = &tas5711_chip, }, { .compatible = "ti,tas5717", .data = &tas5717_chip, }, diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 53a80246aee1..3f027c8234a6 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -1073,6 +1073,7 @@ static struct snd_soc_dai_driver adcx140_dai_driver[] = { } }; +#ifdef CONFIG_OF static const struct of_device_id tlv320adcx140_of_match[] = { { .compatible = "ti,tlv320adc3140" }, { .compatible = "ti,tlv320adc5140" }, @@ -1080,6 +1081,7 @@ static const struct of_device_id tlv320adcx140_of_match[] = { {}, }; MODULE_DEVICE_TABLE(of, tlv320adcx140_of_match); +#endif static int adcx140_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c index 5025e5c43783..dbb8f969274c 100644 --- a/sound/soc/codecs/tlv320aic23-i2c.c +++ b/sound/soc/codecs/tlv320aic23-i2c.c @@ -35,11 +35,13 @@ static const struct i2c_device_id tlv320aic23_id[] = { MODULE_DEVICE_TABLE(i2c, tlv320aic23_id); +#ifdef CONFIG_OF static const struct of_device_id tlv320aic23_of_match[] = { { .compatible = "ti,tlv320aic23", }, { } }; MODULE_DEVICE_TABLE(of, tlv320aic23_of_match); +#endif static struct i2c_driver tlv320aic23_i2c_driver = { .driver = { diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 3ed3b45fa7ba..962f5d48378a 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -366,11 +366,13 @@ static const struct i2c_device_id ts3a227e_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, ts3a227e_i2c_ids); +#ifdef CONFIG_OF static const struct of_device_id ts3a227e_of_match[] = { { .compatible = "ti,ts3a227e", }, { } }; MODULE_DEVICE_TABLE(of, ts3a227e_of_match); +#endif #ifdef CONFIG_ACPI static struct acpi_device_id ts3a227e_acpi_match[] = { diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index 3265d3e8cb28..6200fab7896f 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -66,7 +66,7 @@ static bool tscs42xx_volatile(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tscs42xx_precious(struct device *dev, unsigned int reg) @@ -81,7 +81,7 @@ static bool tscs42xx_precious(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static const struct regmap_config tscs42xx_regmap = { @@ -1294,7 +1294,7 @@ static int part_is_valid(struct tscs42xx *tscs42xx) return true; default: return false; - }; + } } static int set_sysclk(struct snd_soc_component *component) diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c index d0af16b4db2f..cd1f1a592386 100644 --- a/sound/soc/codecs/tscs454.c +++ b/sound/soc/codecs/tscs454.c @@ -177,7 +177,7 @@ static bool tscs454_volatile(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tscs454_writable(struct device *dev, unsigned int reg) @@ -197,7 +197,7 @@ static bool tscs454_writable(struct device *dev, unsigned int reg) return false; default: return true; - }; + } } static bool tscs454_readable(struct device *dev, unsigned int reg) @@ -217,7 +217,7 @@ static bool tscs454_readable(struct device *dev, unsigned int reg) return false; default: return true; - }; + } } static bool tscs454_precious(struct device *dev, unsigned int reg) @@ -246,7 +246,7 @@ static bool tscs454_precious(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static const struct regmap_range_cfg tscs454_regmap_range_cfg = { diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 4d2b1ec7c03b..9ddfed797b7e 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -3979,7 +3979,7 @@ static irqreturn_t wcd9335_slimbus_irq(int irq, void *data) } for_each_set_bit(j, &status, 32) { - tx = (j >= 16 ? true : false); + tx = (j >= 16); port_id = (tx ? j - 16 : j); regmap_read(wcd->if_regmap, WCD9335_SLIM_PGD_PORT_INT_RX_SOURCE0 + j, &val); diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 2ed3fa67027d..70d353b63fe0 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -682,9 +682,7 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, struct arizona *arizona = dev_get_drvdata(component->dev->parent); mutex_lock(&arizona->dac_comp_lock); - memcpy(&arizona->dac_comp_coeff, ucontrol->value.bytes.data, - sizeof(arizona->dac_comp_coeff)); - arizona->dac_comp_coeff = be16_to_cpu(arizona->dac_comp_coeff); + arizona->dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data); mutex_unlock(&arizona->dac_comp_lock); return 0; diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index a6aa212fa0c8..15d42ce3b21d 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -218,7 +218,8 @@ static void wm8350_pga_work(struct work_struct *work) /* PGA volumes have 6 bits of resolution to ramp */ for (i = 0; i <= 63; i++) { - out1_complete = 1, out2_complete = 1; + out1_complete = 1; + out2_complete = 1; if (out1->ramp != WM8350_RAMP_NONE) out1_complete = wm8350_out1_ramp_step(wm8350_data); if (out2->ramp != WM8350_RAMP_NONE) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 0bd3bbc2aacf..3af456010b9c 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3203,6 +3203,7 @@ static int wm8962_beep_event(struct input_dev *dev, unsigned int type, case SND_BELL: if (hz) hz = 1000; + fallthrough; case SND_TONE: break; default: diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index fc9ea198ac79..f57884113406 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -4645,8 +4645,12 @@ static int wm8994_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); - return devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_wm8994, + ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_wm8994, wm8994_dai, ARRAY_SIZE(wm8994_dai)); + if (ret < 0) + pm_runtime_disable(&pdev->dev); + + return ret; } static int wm8994_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 37e4bb3dbd8a..229f2986cd96 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -1177,6 +1177,8 @@ static int wm8997_probe(struct platform_device *pdev) goto err_spk_irqs; } + return ret; + err_spk_irqs: arizona_free_spk_irqs(arizona); diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index f6c5cc80c970..5413254295b7 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -1375,7 +1375,7 @@ static int wm8998_probe(struct platform_device *pdev) ret = arizona_init_spk_irqs(arizona); if (ret < 0) - return ret; + goto err_pm_disable; ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_wm8998, @@ -1390,6 +1390,8 @@ static int wm8998_probe(struct platform_device *pdev) err_spk_irqs: arizona_free_spk_irqs(arizona); +err_pm_disable: + pm_runtime_disable(&pdev->dev); return ret; } |