diff options
Diffstat (limited to 'sound/soc/codecs')
79 files changed, 9983 insertions, 1172 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 40bb7a1d44bc..126f897312d4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -103,6 +103,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS47L85 imply SND_SOC_CS47L90 imply SND_SOC_CS47L92 + imply SND_SOC_CS48L32 imply SND_SOC_CS53L30 imply SND_SOC_CS530X_I2C imply SND_SOC_CX20442 @@ -119,6 +120,8 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ES8326 imply SND_SOC_ES8328_SPI imply SND_SOC_ES8328_I2C + imply SND_SOC_ES8375 + imply SND_SOC_ES8389 imply SND_SOC_ES7134 imply SND_SOC_ES7241 imply SND_SOC_FRAMER @@ -234,6 +237,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT1318_SDW imply SND_SOC_RT1320_SDW imply SND_SOC_RT9120 + imply SND_SOC_RT9123 imply SND_SOC_RTQ9128 imply SND_SOC_SDW_MOCKUP imply SND_SOC_SGTL5000 @@ -260,6 +264,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TAS2770 imply SND_SOC_TAS2780 imply SND_SOC_TAS2781_COMLIB + imply SND_SOC_TAS2781_COMLIB_I2C imply SND_SOC_TAS2781_FMWLIB imply SND_SOC_TAS2781_I2C imply SND_SOC_TAS5086 @@ -403,6 +408,7 @@ config SND_SOC_WM_ADSP default y if SND_SOC_CS35L45_SPI=y default y if SND_SOC_CS35L45_I2C=y default y if SND_SOC_CS35L56=y + default y if SND_SOC_CS48L32=y default m if SND_SOC_MADERA=m default m if SND_SOC_CS47L24=m default m if SND_SOC_WM5102=m @@ -413,6 +419,7 @@ config SND_SOC_WM_ADSP default m if SND_SOC_CS35L45_SPI=m default m if SND_SOC_CS35L45_I2C=m default m if SND_SOC_CS35L56=m + default m if SND_SOC_CS48L32=m config SND_SOC_AB8500_CODEC tristate @@ -776,10 +783,9 @@ config SND_SOC_CS_AMP_LIB tristate config SND_SOC_CS_AMP_LIB_TEST - tristate "KUnit test for Cirrus Logic cs-amp-lib" - depends on KUNIT + tristate "KUnit test for Cirrus Logic cs-amp-lib" if !KUNIT_ALL_TESTS + depends on SND_SOC_CS_AMP_LIB && KUNIT default KUNIT_ALL_TESTS - select SND_SOC_CS_AMP_LIB help This builds KUnit tests for the Cirrus Logic common amplifier library. @@ -1049,6 +1055,13 @@ config SND_SOC_CS47L92 tristate depends on MFD_CS47L92 +config SND_SOC_CS48L32 + tristate "Cirrus Logic CS48L32 audio DSP" + depends on SPI_MASTER + select REGMAP_SPI + help + Build the codec driver for the Cirrus Logic CS48L32 audio DSP. + # Cirrus Logic Quad-Channel ADC config SND_SOC_CS53L30 tristate "Cirrus Logic CS53L30 CODEC" @@ -1200,6 +1213,14 @@ config SND_SOC_ES8328_SPI depends on SPI_MASTER select SND_SOC_ES8328 +config SND_SOC_ES8375 + tristate "Everest Semi ES8375 CODEC" + depends on I2C + +config SND_SOC_ES8389 + tristate "Everest Semi ES8389 CODEC" + depends on I2C + config SND_SOC_FRAMER tristate "Framer codec" depends on GENERIC_FRAMER @@ -1823,6 +1844,20 @@ config SND_SOC_RT9120 Enable support for Richtek RT9120 20W, stereo, inductor-less, high-efficiency Class-D audio amplifier. +config SND_SOC_RT9123 + tristate "Richtek RT9123 Mono Class-D Amplifier" + depends on I2C + select REGMAP_I2C + help + Enable support for the I2C control mode of Richtek RT9123 3.2W mono + Class-D audio amplifier. + +config SND_SOC_RT9123P + tristate "Richtek RT9123P Mono Class-D Amplifier" + help + Enable support for the HW control mode of Richtek RT9123P 3.2W mono + Class-D audio amplifier. + config SND_SOC_RTQ9128 tristate "Richtek RTQ9128 45W Digital Input Amplifier" depends on I2C @@ -1991,20 +2026,24 @@ config SND_SOC_TAS2780 digital input mono Class-D audio power amplifiers. config SND_SOC_TAS2781_COMLIB + tristate + +config SND_SOC_TAS2781_COMLIB_I2C depends on I2C select CRC8 select REGMAP_I2C tristate config SND_SOC_TAS2781_FMWLIB - depends on SND_SOC_TAS2781_COMLIB + select SND_SOC_TAS2781_COMLIB + select CRC8 tristate default n config SND_SOC_TAS2781_I2C tristate "Texas Instruments TAS2781 speaker amplifier based on I2C" depends on I2C - select SND_SOC_TAS2781_COMLIB + select SND_SOC_TAS2781_COMLIB_I2C select SND_SOC_TAS2781_FMWLIB help Enable support for Texas Instruments TAS2781 Smart Amplifier @@ -2239,6 +2278,7 @@ config SND_SOC_WCD938X tristate depends on SOUNDWIRE || !SOUNDWIRE select SND_SOC_WCD_CLASSH + select MULTIPLEXER config SND_SOC_WCD938X_SDW tristate "WCD9380/WCD9385 Codec - SDW" @@ -2479,7 +2519,7 @@ config SND_SOC_WM8997 depends on MFD_WM8997 && MFD_ARIZONA config SND_SOC_WM8998 - tristate + tristate "Wolfson Microelectronics WM8998 codec driver" depends on MFD_WM8998 && MFD_ARIZONA config SND_SOC_WM9081 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 10f726066b6c..6d7aa109ede7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -111,6 +111,7 @@ snd-soc-cs47l35-y := cs47l35.o snd-soc-cs47l85-y := cs47l85.o snd-soc-cs47l90-y := cs47l90.o snd-soc-cs47l92-y := cs47l92.o +snd-soc-cs48l32-y := cs48l32.o cs48l32-tables.o snd-soc-cs53l30-y := cs53l30.o snd-soc-cs530x-y := cs530x.o snd-soc-cs530x-i2c-y := cs530x-i2c.o @@ -133,6 +134,8 @@ snd-soc-es8326-y := es8326.o snd-soc-es8328-y := es8328.o snd-soc-es8328-i2c-y := es8328-i2c.o snd-soc-es8328-spi-y := es8328-spi.o +snd-soc-es8375-y := es8375.o +snd-soc-es8389-y := es8389.o snd-soc-framer-y := framer-codec.o snd-soc-gtm601-y := gtm601.o snd-soc-hdac-hdmi-y := hdac_hdmi.o @@ -270,6 +273,8 @@ snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o snd-soc-rt9120-y := rt9120.o +snd-soc-rt9123-y := rt9123.o +snd-soc-rt9123p-y := rt9123p.o snd-soc-rtq9128-y := rtq9128.o snd-soc-sdw-mockup-y := sdw-mockup.o snd-soc-sgtl5000-y := sgtl5000.o @@ -305,6 +310,7 @@ snd-soc-tas6424-y := tas6424.o snd-soc-tda7419-y := tda7419.o snd-soc-tas2770-y := tas2770.o snd-soc-tas2781-comlib-y := tas2781-comlib.o +snd-soc-tas2781-comlib-i2c-y := tas2781-comlib-i2c.o snd-soc-tas2781-fmwlib-y := tas2781-fmwlib.o snd-soc-tas2781-i2c-y := tas2781-i2c.o snd-soc-tfa9879-y := tfa9879.o @@ -529,6 +535,7 @@ obj-$(CONFIG_SND_SOC_CS47L35) += snd-soc-cs47l35.o obj-$(CONFIG_SND_SOC_CS47L85) += snd-soc-cs47l85.o obj-$(CONFIG_SND_SOC_CS47L90) += snd-soc-cs47l90.o obj-$(CONFIG_SND_SOC_CS47L92) += snd-soc-cs47l92.o +obj-$(CONFIG_SND_SOC_CS48L32) += snd-soc-cs48l32.o obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o obj-$(CONFIG_SND_SOC_CS530X) += snd-soc-cs530x.o obj-$(CONFIG_SND_SOC_CS530X_I2C) += snd-soc-cs530x-i2c.o @@ -551,6 +558,8 @@ obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o +obj-$(CONFIG_SND_SOC_ES8375) += snd-soc-es8375.o +obj-$(CONFIG_SND_SOC_ES8389) += snd-soc-es8389.o obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o @@ -684,6 +693,8 @@ obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o +obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o +obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o obj-$(CONFIG_SND_SOC_RTQ9128) += snd-soc-rtq9128.o obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o @@ -713,6 +724,7 @@ obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o obj-$(CONFIG_SND_SOC_TAS2781_COMLIB) += snd-soc-tas2781-comlib.o +obj-$(CONFIG_SND_SOC_TAS2781_COMLIB_I2C) += snd-soc-tas2781-comlib-i2c.o obj-$(CONFIG_SND_SOC_TAS2781_FMWLIB) += snd-soc-tas2781-fmwlib.o obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o @@ -841,4 +853,4 @@ obj-$(CONFIG_SND_SOC_LPASS_RX_MACRO) += snd-soc-lpass-rx-macro.o obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO) += snd-soc-lpass-tx-macro.o # Mux -obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o +obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o
\ No newline at end of file diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 0e013edfe63d..d8444a083af2 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/device.h> #include <linux/module.h> +#include <linux/of.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> @@ -127,9 +128,18 @@ static int ac97_probe(struct platform_device *pdev) &soc_component_dev_ac97, &ac97_dai, 1); } +#ifdef CONFIG_OF +static const struct of_device_id ac97_codec_of_match[] = { + { .compatible = "realtek,alc203", }, + { } +}; +MODULE_DEVICE_TABLE(of, ac97_codec_of_match); +#endif + static struct platform_driver ac97_codec_driver = { .driver = { .name = "ac97-codec", + .of_match_table = of_match_ptr(ac97_codec_of_match), }, .probe = ac97_probe, diff --git a/sound/soc/codecs/adau7118.c b/sound/soc/codecs/adau7118.c index abc4764697a5..14259807c872 100644 --- a/sound/soc/codecs/adau7118.c +++ b/sound/soc/codecs/adau7118.c @@ -169,6 +169,12 @@ static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_RIGHT_J: st->right_j = true; break; + case SND_SOC_DAIFMT_DSP_A: + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_DATA_FMT_MASK, + ADAU7118_DATA_FMT(1)); + break; default: dev_err(st->dev, "Invalid format %d", fmt & SND_SOC_DAIFMT_FORMAT_MASK); diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 5f3a68dfe7bd..57cf601d3df3 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -586,13 +586,9 @@ static const struct snd_pcm_hw_constraint_list ak4458_rate_constraints = { static int ak4458_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret; - - ret = snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &ak4458_rate_constraints); - - return ret; + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &ak4458_rate_constraints); } static const struct snd_soc_dai_ops ak4458_dai_ops = { diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c index 21a44476f48d..6525d50b7ab2 100644 --- a/sound/soc/codecs/ak5386.c +++ b/sound/soc/codecs/ak5386.c @@ -6,11 +6,13 @@ * (c) 2013 Daniel Mack <zonque@gmail.com> */ +#include <linux/device.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> -#include <linux/slab.h> -#include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/regulator/consumer.h> +#include <linux/slab.h> #include <sound/soc.h> #include <sound/pcm.h> #include <sound/initval.h> @@ -20,7 +22,7 @@ static const char * const supply_names[] = { }; struct ak5386_priv { - int reset_gpio; + struct gpio_desc *reset_gpio; struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; }; @@ -110,8 +112,7 @@ static int ak5386_hw_params(struct snd_pcm_substream *substream, * the AK5386 in power-down mode (PDN pin = āLā). */ - if (gpio_is_valid(priv->reset_gpio)) - gpio_set_value(priv->reset_gpio, 1); + gpiod_set_value(priv->reset_gpio, 1); return 0; } @@ -122,8 +123,7 @@ static int ak5386_hw_free(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(priv->reset_gpio)) - gpio_set_value(priv->reset_gpio, 0); + gpiod_set_value(priv->reset_gpio, 0); return 0; } @@ -177,14 +177,12 @@ static int ak5386_probe(struct platform_device *pdev) if (ret < 0) return ret; - priv->reset_gpio = of_get_named_gpio(dev->of_node, - "reset-gpio", 0); + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), + "Failed to get AK5386 reset GPIO\n"); - if (gpio_is_valid(priv->reset_gpio)) - if (devm_gpio_request_one(dev, priv->reset_gpio, - GPIOF_OUT_INIT_LOW, - "AK5386 Reset")) - priv->reset_gpio = -EINVAL; + gpiod_set_consumer_name(priv->reset_gpio, "AK5386 Reset"); return devm_snd_soc_register_component(dev, &soc_component_ak5386, &ak5386_dai, 1); diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c index ad16ab6812cd..3dd8428f08cc 100644 --- a/sound/soc/codecs/aw88081.c +++ b/sound/soc/codecs/aw88081.c @@ -1295,9 +1295,19 @@ static int aw88081_i2c_probe(struct i2c_client *i2c) aw88081_dai, ARRAY_SIZE(aw88081_dai)); } +#if defined(CONFIG_OF) +static const struct of_device_id aw88081_of_match[] = { + { .compatible = "awinic,aw88081" }, + { .compatible = "awinic,aw88083" }, + { } +}; +MODULE_DEVICE_TABLE(of, aw88081_of_match); +#endif + static struct i2c_driver aw88081_i2c_driver = { .driver = { .name = AW88081_I2C_NAME, + .of_match_table = of_match_ptr(aw88081_of_match), }, .probe = aw88081_i2c_probe, .id_table = aw88081_i2c_id, diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c index 6c50c4a18b6a..4f76ebe11cc7 100644 --- a/sound/soc/codecs/aw88166.c +++ b/sound/soc/codecs/aw88166.c @@ -11,6 +11,7 @@ #include <linux/firmware.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> +#include <linux/minmax.h> #include <linux/regmap.h> #include <sound/soc.h> #include "aw88166.h" @@ -909,11 +910,7 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev, goto error_operation; for (i = 0; i < len; i += AW88166_MAX_RAM_WRITE_BYTE_SIZE) { - if ((len - i) < AW88166_MAX_RAM_WRITE_BYTE_SIZE) - tmp_len = len - i; - else - tmp_len = AW88166_MAX_RAM_WRITE_BYTE_SIZE; - + tmp_len = min(len - i, AW88166_MAX_RAM_WRITE_BYTE_SIZE); ret = regmap_raw_write(aw_dev->regmap, AW88166_DSPMDAT_REG, &data[i], tmp_len); if (ret) diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c index b7ea8be0d0cb..e1430940015d 100644 --- a/sound/soc/codecs/aw88395/aw88395_device.c +++ b/sound/soc/codecs/aw88395/aw88395_device.c @@ -10,6 +10,7 @@ #include <linux/crc32.h> #include <linux/i2c.h> +#include <linux/minmax.h> #include <linux/regmap.h> #include "aw88395_device.h" #include "aw88395_reg.h" @@ -1114,11 +1115,7 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev, goto error_operation; for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) { - if ((len - i) < AW88395_MAX_RAM_WRITE_BYTE_SIZE) - tmp_len = len - i; - else - tmp_len = AW88395_MAX_RAM_WRITE_BYTE_SIZE; - + tmp_len = min(len - i, AW88395_MAX_RAM_WRITE_BYTE_SIZE); ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG, &data[i], tmp_len); if (ret) diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index ee3cc2a95f85..4b90133e5ab4 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -11,6 +11,7 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/firmware.h> +#include <linux/minmax.h> #include <linux/regmap.h> #include <sound/soc.h> #include "aw88399.h" @@ -872,11 +873,7 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev, goto error_operation; for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) { - if ((len - i) < AW88399_MAX_RAM_WRITE_BYTE_SIZE) - tmp_len = len - i; - else - tmp_len = AW88399_MAX_RAM_WRITE_BYTE_SIZE; - + tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE); ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG, &data[i], tmp_len); if (ret) diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c index 45626f99a417..f53650128fc3 100644 --- a/sound/soc/codecs/cs-amp-lib-test.c +++ b/sound/soc/codecs/cs-amp-lib-test.c @@ -5,19 +5,25 @@ // Copyright (C) 2024 Cirrus Logic, Inc. and // Cirrus Logic International Semiconductor Ltd. +#include <kunit/resource.h> #include <kunit/test.h> #include <kunit/static_stub.h> +#include <linux/device/faux.h> #include <linux/firmware/cirrus/cs_dsp.h> #include <linux/firmware/cirrus/wmfw.h> #include <linux/gpio/driver.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/overflow.h> #include <linux/platform_device.h> #include <linux/random.h> #include <sound/cs-amp-lib.h> +KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy, + struct faux_device *) + struct cs_amp_lib_test_priv { - struct platform_device amp_pdev; + struct faux_device *amp_dev; struct cirrus_amp_efi_data *cal_blob; struct list_head ctl_write_list; @@ -40,8 +46,7 @@ static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps unsigned int blob_size; int i; - blob_size = offsetof(struct cirrus_amp_efi_data, data) + - sizeof(struct cirrus_amp_cal_data) * num_amps; + blob_size = struct_size(priv->cal_blob, data, num_amps); priv->cal_blob = kunit_kzalloc(test, blob_size, GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob); @@ -49,7 +54,7 @@ static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps priv->cal_blob->size = blob_size; priv->cal_blob->count = num_amps; - get_random_bytes(priv->cal_blob->data, sizeof(struct cirrus_amp_cal_data) * num_amps); + get_random_bytes(priv->cal_blob->data, flex_array_size(priv->cal_blob, data, num_amps)); /* Ensure all timestamps are non-zero to mark the entry valid. */ for (i = 0; i < num_amps; i++) @@ -99,7 +104,7 @@ static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test) cs_amp_test_hooks->get_efi_variable, cs_amp_lib_test_get_efi_variable_nohead); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data); + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data); KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW); kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); @@ -142,7 +147,7 @@ static void cs_amp_lib_test_cal_count_too_big_test(struct kunit *test) cs_amp_test_hooks->get_efi_variable, cs_amp_lib_test_get_efi_variable_bad_count); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data); + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data); KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW); kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); @@ -169,7 +174,7 @@ static void cs_amp_lib_test_no_cal_data_test(struct kunit *test) cs_amp_test_hooks->get_efi_variable, cs_amp_lib_test_get_efi_variable_none); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data); + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data); KUNIT_EXPECT_EQ(test, ret, -ENOENT); kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); @@ -223,7 +228,7 @@ static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test) cs_amp_lib_test_get_efi_variable); target_uid = cs_amp_lib_test_get_target_uid(test); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, -1, &result_data); + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid, -1, &result_data); KUNIT_EXPECT_EQ(test, ret, 0); kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); @@ -257,7 +262,7 @@ static void cs_amp_lib_test_get_efi_cal_by_index_unchecked_test(struct kunit *te cs_amp_test_hooks->get_efi_variable, cs_amp_lib_test_get_efi_variable); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, param->amp_index, &result_data); KUNIT_EXPECT_EQ(test, ret, 0); @@ -292,7 +297,7 @@ static void cs_amp_lib_test_get_efi_cal_by_index_checked_test(struct kunit *test cs_amp_lib_test_get_efi_variable); target_uid = cs_amp_lib_test_get_target_uid(test); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid, param->amp_index, &result_data); KUNIT_EXPECT_EQ(test, ret, 0); @@ -331,7 +336,7 @@ static void cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test(struct kunit /* Get a target UID that won't match the entry */ target_uid = ~cs_amp_lib_test_get_target_uid(test); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid, param->amp_index, &result_data); KUNIT_EXPECT_EQ(test, ret, -ENOENT); @@ -363,7 +368,7 @@ static void cs_amp_lib_test_get_efi_cal_by_index_fallback_test(struct kunit *tes cs_amp_test_hooks->get_efi_variable, cs_amp_lib_test_get_efi_variable); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, param->amp_index, &result_data); KUNIT_EXPECT_EQ(test, ret, 0); @@ -405,7 +410,7 @@ static void cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test(struct kunit cs_amp_test_hooks->get_efi_variable, cs_amp_lib_test_get_efi_variable); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, -1, + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, -1, &result_data); KUNIT_EXPECT_EQ(test, ret, -ENOENT); @@ -436,7 +441,7 @@ static void cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test(struc cs_amp_test_hooks->get_efi_variable, cs_amp_lib_test_get_efi_variable); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, 99, + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, 99, &result_data); KUNIT_EXPECT_EQ(test, ret, -ENOENT); @@ -460,7 +465,7 @@ static void cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test(struct kunit cs_amp_test_hooks->get_efi_variable, cs_amp_lib_test_get_efi_variable); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 99, &result_data); + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 99, &result_data); KUNIT_EXPECT_EQ(test, ret, -ENOENT); kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); @@ -480,7 +485,7 @@ static void cs_amp_lib_test_get_efi_cal_no_uid_no_index_test(struct kunit *test) cs_amp_test_hooks->get_efi_variable, cs_amp_lib_test_get_efi_variable); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data); + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, -1, &result_data); KUNIT_EXPECT_EQ(test, ret, -ENOENT); kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); @@ -509,7 +514,7 @@ static void cs_amp_lib_test_get_efi_cal_zero_not_matched_test(struct kunit *test cs_amp_test_hooks->get_efi_variable, cs_amp_lib_test_get_efi_variable); - ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data); + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, -1, &result_data); KUNIT_EXPECT_EQ(test, ret, -ENOENT); kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); @@ -543,14 +548,14 @@ static void cs_amp_lib_test_get_efi_cal_empty_entry_test(struct kunit *test) /* Lookup by UID should not find it */ KUNIT_EXPECT_EQ(test, - cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, + cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, uid, -1, &result_data), -ENOENT); /* Get by index should ignore it */ KUNIT_EXPECT_EQ(test, - cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, + cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 2, &result_data), -ENOENT); @@ -600,7 +605,7 @@ static void cs_amp_lib_test_write_cal_data_test(struct kunit *test) dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp); - dsp->dev = &priv->amp_pdev.dev; + dsp->dev = &priv->amp_dev->dev; get_random_bytes(&data, sizeof(data)); @@ -637,14 +642,9 @@ static void cs_amp_lib_test_write_cal_data_test(struct kunit *test) KUNIT_EXPECT_EQ(test, entry->value, data.calStatus); } -static void cs_amp_lib_test_dev_release(struct device *dev) -{ -} - static int cs_amp_lib_test_case_init(struct kunit *test) { struct cs_amp_lib_test_priv *priv; - int ret; KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks); @@ -656,23 +656,16 @@ static int cs_amp_lib_test_case_init(struct kunit *test) INIT_LIST_HEAD(&priv->ctl_write_list); /* Create dummy amp driver dev */ - priv->amp_pdev.name = "cs_amp_lib_test_drv"; - priv->amp_pdev.id = -1; - priv->amp_pdev.dev.release = cs_amp_lib_test_dev_release; - ret = platform_device_register(&priv->amp_pdev); - KUNIT_ASSERT_GE_MSG(test, ret, 0, "Failed to register amp platform device\n"); + priv->amp_dev = faux_device_create("cs_amp_lib_test_drv", NULL, NULL); + KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, + faux_device_destroy_wrapper, + priv->amp_dev)); return 0; } -static void cs_amp_lib_test_case_exit(struct kunit *test) -{ - struct cs_amp_lib_test_priv *priv = test->priv; - - if (priv->amp_pdev.name) - platform_device_unregister(&priv->amp_pdev); -} - static const struct cs_amp_lib_test_param cs_amp_lib_test_get_cal_param_cases[] = { { .num_amps = 2, .amp_index = 0 }, { .num_amps = 2, .amp_index = 1 }, @@ -750,7 +743,6 @@ static struct kunit_case cs_amp_lib_test_cases[] = { static struct kunit_suite cs_amp_lib_test_suite = { .name = "snd-soc-cs-amp-lib-test", .init = cs_amp_lib_test_case_init, - .exit = cs_amp_lib_test_case_exit, .test_cases = cs_amp_lib_test_cases, }; diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index c677868c5d5f..808e67c90f7c 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -11,6 +11,7 @@ #include <linux/efi.h> #include <linux/firmware/cirrus/cs_dsp.h> #include <linux/module.h> +#include <linux/overflow.h> #include <linux/slab.h> #include <linux/types.h> #include <sound/cs-amp-lib.h> @@ -147,7 +148,7 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev) dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count); if ((efi_data->count > 128) || - offsetof(struct cirrus_amp_efi_data, data[efi_data->count]) > data_size) { + struct_size(efi_data, data, efi_data->count) > data_size) { dev_err(dev, "EFI cal variable truncated\n"); ret = -EOVERFLOW; goto err; diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 8a518df1e16e..073f1796ae29 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -17,9 +17,10 @@ static int cs35l56_i2c_probe(struct i2c_client *client) { + unsigned int id = (u32)(uintptr_t)i2c_get_match_data(client); struct cs35l56_private *cs35l56; struct device *dev = &client->dev; - const struct regmap_config *regmap_config = &cs35l56_regmap_i2c; + const struct regmap_config *regmap_config; int ret; cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL); @@ -30,6 +31,20 @@ static int cs35l56_i2c_probe(struct i2c_client *client) cs35l56->base.can_hibernate = true; i2c_set_clientdata(client, cs35l56); + + switch (id) { + case 0x3556: + regmap_config = &cs35l56_regmap_i2c; + cs35l56->base.fw_reg = &cs35l56_fw_reg; + break; + case 0x3563: + regmap_config = &cs35l63_regmap_i2c; + cs35l56->base.fw_reg = &cs35l63_fw_reg; + break; + default: + return -ENODEV; + } + cs35l56->base.regmap = devm_regmap_init_i2c(client, regmap_config); if (IS_ERR(cs35l56->base.regmap)) { ret = PTR_ERR(cs35l56->base.regmap); @@ -57,14 +72,16 @@ static void cs35l56_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id cs35l56_id_i2c[] = { - { "cs35l56" }, + { "cs35l56", 0x3556 }, + { "cs35l63", 0x3563 }, {} }; MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c); #ifdef CONFIG_ACPI static const struct acpi_device_id cs35l56_asoc_acpi_match[] = { - { "CSC355C", 0 }, + { "CSC355C", 0x3556 }, + { "CSC356C", 0x3563 }, {}, }; MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 3f91cb3f9ae7..13f602f51bf3 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -393,6 +393,74 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral, return 0; } +static int cs35l63_sdw_kick_divider(struct cs35l56_private *cs35l56, + struct sdw_slave *peripheral) +{ + unsigned int curr_scale_reg, next_scale_reg; + int curr_scale, next_scale, ret; + + if (!cs35l56->base.init_done) + return 0; + + if (peripheral->bus->params.curr_bank) { + curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; + next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; + } else { + curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; + next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; + } + + /* + * Current clock scale value must be different to new value. + * Modify current to guarantee this. If next still has the dummy + * value we wrote when it was current, the core code has not set + * a new scale so restore its original good value + */ + curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg); + if (curr_scale < 0) { + dev_err(cs35l56->base.dev, "Failed to read current clock scale: %d\n", curr_scale); + return curr_scale; + } + + next_scale = sdw_read_no_pm(peripheral, next_scale_reg); + if (next_scale < 0) { + dev_err(cs35l56->base.dev, "Failed to read next clock scale: %d\n", next_scale); + return next_scale; + } + + if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) { + next_scale = cs35l56->old_sdw_clock_scale; + ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale); + if (ret < 0) { + dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", + ret); + return ret; + } + } + + cs35l56->old_sdw_clock_scale = curr_scale; + ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE); + if (ret < 0) { + dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", ret); + return ret; + } + + dev_dbg(cs35l56->base.dev, "Next bus scale: %#x\n", next_scale); + + return 0; +} + +static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral, + struct sdw_bus_params *params) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + + if ((cs35l56->base.type == 0x63) && (cs35l56->base.rev < 0xa1)) + return cs35l63_sdw_kick_divider(cs35l56, peripheral); + + return 0; +} + static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral, enum sdw_clk_stop_mode mode, enum sdw_clk_stop_type type) @@ -408,6 +476,7 @@ static const struct sdw_slave_ops cs35l56_sdw_ops = { .read_prop = cs35l56_sdw_read_prop, .interrupt_callback = cs35l56_sdw_interrupt, .update_status = cs35l56_sdw_update_status, + .bus_config = cs35l56_sdw_bus_config, #ifdef DEBUG .clk_stop = cs35l56_sdw_clk_stop, #endif @@ -509,6 +578,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi { struct device *dev = &peripheral->dev; struct cs35l56_private *cs35l56; + const struct regmap_config *regmap_config; int ret; cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL); @@ -521,8 +591,22 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi dev_set_drvdata(dev, cs35l56); + switch ((unsigned int)id->driver_data) { + case 0x3556: + case 0x3557: + regmap_config = &cs35l56_regmap_sdw; + cs35l56->base.fw_reg = &cs35l56_fw_reg; + break; + case 0x3563: + regmap_config = &cs35l63_regmap_sdw; + cs35l56->base.fw_reg = &cs35l63_fw_reg; + break; + default: + return -ENODEV; + } + cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw, - peripheral, &cs35l56_regmap_sdw); + peripheral, regmap_config); if (IS_ERR(cs35l56->base.regmap)) { ret = PTR_ERR(cs35l56->base.regmap); return dev_err_probe(dev, ret, "Failed to allocate register map\n"); @@ -562,8 +646,9 @@ static const struct dev_pm_ops cs35l56_sdw_pm = { }; static const struct sdw_device_id cs35l56_sdw_id[] = { - SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0), - SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0), + SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556), + SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557), + SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563), {}, }; MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index e28bfefa72f3..d0831d609584 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -38,17 +38,48 @@ static const struct reg_sequence cs35l56_patch[] = { { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 }, { CS35L56_IRQ1_MASK_18, 0x1f7df0ff }, +}; +static const struct reg_sequence cs35l56_patch_fw[] = { /* These are not reset by a soft-reset, so patch to defaults. */ { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 }, { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 }, { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 }, }; +static const struct reg_sequence cs35l63_patch_fw[] = { + /* These are not reset by a soft-reset, so patch to defaults. */ + { CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 }, + { CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 }, + { CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 }, +}; + int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) { - return regmap_register_patch(cs35l56_base->regmap, cs35l56_patch, + int ret; + + ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch, ARRAY_SIZE(cs35l56_patch)); + if (ret) + return ret; + + + switch (cs35l56_base->type) { + case 0x54: + case 0x56: + case 0x57: + ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch_fw, + ARRAY_SIZE(cs35l56_patch_fw)); + break; + case 0x63: + ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw, + ARRAY_SIZE(cs35l63_patch_fw)); + break; + default: + break; + } + + return ret; } EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, "SND_SOC_CS35L56_SHARED"); @@ -82,6 +113,36 @@ static const struct reg_default cs35l56_reg_defaults[] = { { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 }, }; +static const struct reg_default cs35l63_reg_defaults[] = { + /* no defaults for OTP_MEM - first read populates cache */ + + { CS35L56_ASP1_ENABLES1, 0x00000000 }, + { CS35L56_ASP1_CONTROL1, 0x00000028 }, + { CS35L56_ASP1_CONTROL2, 0x18180200 }, + { CS35L56_ASP1_CONTROL3, 0x00000002 }, + { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, + { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, + { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, + { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, + { CS35L56_ASP1TX1_INPUT, 0x00000000 }, + { CS35L56_ASP1TX2_INPUT, 0x00000000 }, + { CS35L56_ASP1TX3_INPUT, 0x00000000 }, + { CS35L56_ASP1TX4_INPUT, 0x00000000 }, + { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, + { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, + { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, + { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 }, + { CS35L56_IRQ1_MASK_1, 0x8003ffff }, + { CS35L56_IRQ1_MASK_2, 0xffff7fff }, + { CS35L56_IRQ1_MASK_4, 0xe0ffffff }, + { CS35L56_IRQ1_MASK_8, 0x8c000fff }, + { CS35L56_IRQ1_MASK_18, 0x0760f000 }, + { CS35L56_IRQ1_MASK_20, 0x15c00000 }, + { CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 }, + { CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 }, + { CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 }, +}; + static bool cs35l56_is_dsp_memory(unsigned int reg) { switch (reg) { @@ -153,6 +214,8 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg) case CS35L56_DSP_VIRTUAL1_MBOX_6: case CS35L56_DSP_VIRTUAL1_MBOX_7: case CS35L56_DSP_VIRTUAL1_MBOX_8: + case CS35L56_DIE_STS1: + case CS35L56_DIE_STS2: case CS35L56_DSP_RESTRICT_STS1: case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END: case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0: @@ -179,7 +242,7 @@ static bool cs35l56_precious_reg(struct device *dev, unsigned int reg) } } -static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) +static bool cs35l56_common_volatile_reg(unsigned int reg) { switch (reg) { case CS35L56_DEVID: @@ -217,12 +280,32 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) case CS35L56_DSP1_SCRATCH3: case CS35L56_DSP1_SCRATCH4: return true; + default: + return cs35l56_is_dsp_memory(reg); + } +} + +static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { case CS35L56_MAIN_RENDER_USER_MUTE: case CS35L56_MAIN_RENDER_USER_VOLUME: case CS35L56_MAIN_POSTURE_NUMBER: return false; default: - return cs35l56_is_dsp_memory(reg); + return cs35l56_common_volatile_reg(reg); + } +} + +static bool cs35l63_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L63_MAIN_RENDER_USER_MUTE: + case CS35L63_MAIN_RENDER_USER_VOLUME: + case CS35L63_MAIN_POSTURE_NUMBER: + return false; + default: + return cs35l56_common_volatile_reg(reg); } } @@ -253,7 +336,8 @@ int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base) if (ret) return ret; - ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP1_PM_CUR_STATE, + ret = regmap_read_poll_timeout(cs35l56_base->regmap, + cs35l56_base->fw_reg->pm_cur_stat, val, (val == CS35L56_HALO_STATE_SHUTDOWN), CS35L56_HALO_STATE_POLL_US, CS35L56_HALO_STATE_TIMEOUT_US); @@ -278,7 +362,9 @@ int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base) CS35L56_HALO_STATE_POLL_US, CS35L56_HALO_STATE_TIMEOUT_US, false, - cs35l56_base->regmap, CS35L56_DSP1_HALO_STATE, &val); + cs35l56_base->regmap, + cs35l56_base->fw_reg->halo_state, + &val); if (poll_ret) { dev_err(cs35l56_base->dev, "Firmware boot timed out(%d): HALO_STATE=%#x\n", @@ -382,6 +468,11 @@ static const struct reg_sequence cs35l56_system_reset_seq[] = { REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET), }; +static const struct reg_sequence cs35l63_system_reset_seq[] = { + REG_SEQ0(CS35L63_DSP1_HALO_STATE, 0), + REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET), +}; + void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) { /* @@ -395,9 +486,22 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) return; } - regmap_multi_reg_write_bypassed(cs35l56_base->regmap, - cs35l56_system_reset_seq, - ARRAY_SIZE(cs35l56_system_reset_seq)); + switch (cs35l56_base->type) { + case 0x54: + case 0x56: + case 0x57: + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l56_system_reset_seq, + ARRAY_SIZE(cs35l56_system_reset_seq)); + break; + case 0x63: + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l63_system_reset_seq, + ARRAY_SIZE(cs35l63_system_reset_seq)); + break; + default: + break; + } /* On SoundWire the registers won't be accessible until it re-enumerates. */ if (is_soundwire) @@ -514,7 +618,9 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base) return ret; } - ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &val); + ret = regmap_read(cs35l56_base->regmap, + cs35l56_base->fw_reg->prot_sts, + &val); if (ret) dev_err(cs35l56_base->dev, "Failed to read PROTECTION_STATUS: %d\n", ret); else @@ -562,7 +668,7 @@ int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base) /* Firmware must have entered a power-save state */ ret = regmap_read_poll_timeout(cs35l56_base->regmap, - CS35L56_TRANSDUCER_ACTUAL_PS, + cs35l56_base->fw_reg->transducer_actual_ps, val, (val >= CS35L56_PS3), CS35L56_PS3_POLL_US, CS35L56_PS3_TIMEOUT_US); @@ -698,13 +804,29 @@ static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid) unique_id |= (u32)pte.x | ((u32)pte.y << 8) | ((u32)pte.wafer_id << 16) | ((u32)pte.dvs << 24); - dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", unique_id); - *uid = unique_id; return 0; } +static int cs35l63_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid) +{ + u32 tmp[2]; + int ret; + + ret = regmap_bulk_read(cs35l56_base->regmap, CS35L56_DIE_STS1, tmp, ARRAY_SIZE(tmp)); + if (ret) { + dev_err(cs35l56_base->dev, "Cannot obtain CS35L56_DIE_STS: %d\n", ret); + return ret; + } + + *uid = tmp[1]; + *uid <<= 32; + *uid |= tmp[0]; + + return 0; +} + /* Firmware calibration controls */ const struct cirrus_amp_cal_controls cs35l56_calibration_controls = { .alg_id = 0x9f210, @@ -725,10 +847,25 @@ int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base) if (cs35l56_base->secured) return 0; - ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid); + switch (cs35l56_base->type) { + case 0x54: + case 0x56: + case 0x57: + ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid); + break; + case 0x63: + ret = cs35l63_read_silicon_uid(cs35l56_base, &silicon_uid); + break; + default: + ret = -ENODEV; + break; + } + if (ret < 0) return ret; + dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", silicon_uid); + ret = cs_amp_get_efi_calibration_data(cs35l56_base->dev, silicon_uid, cs35l56_base->cal_index, &cs35l56_base->cal_data); @@ -752,7 +889,8 @@ int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, unsigned int prot_status; int ret; - ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &prot_status); + ret = regmap_read(cs35l56_base->regmap, + cs35l56_base->fw_reg->prot_sts, &prot_status); if (ret) { dev_err(cs35l56_base->dev, "Get PROTECTION_STATUS failed: %d\n", ret); return ret; @@ -760,7 +898,8 @@ int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, *fw_missing = !!(prot_status & CS35L56_FIRMWARE_MISSING); - ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP1_FW_VER, fw_version); + ret = regmap_read(cs35l56_base->regmap, + cs35l56_base->fw_reg->fw_ver, fw_version); if (ret) { dev_err(cs35l56_base->dev, "Get FW VER failed: %d\n", ret); return ret; @@ -770,6 +909,33 @@ int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, } EXPORT_SYMBOL_NS_GPL(cs35l56_read_prot_status, "SND_SOC_CS35L56_SHARED"); +void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp) +{ + __be32 pid, sid, tid; + int ret; + + scoped_guard(mutex, &cs_dsp->pwr_lock) { + ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_PRJCT_ID", + WMFW_ADSP2_XM, 0x9f212), + 0, &pid, sizeof(pid)); + if (!ret) + ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_CHNNL_ID", + WMFW_ADSP2_XM, 0x9f212), + 0, &sid, sizeof(sid)); + if (!ret) + ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_SNPSHT_ID", + WMFW_ADSP2_XM, 0x9f212), + 0, &tid, sizeof(tid)); + } + + if (ret) + dev_warn(cs35l56_base->dev, "Can't read tuning IDs"); + else + dev_info(cs35l56_base->dev, "Tuning PID: %#x, SID: %#x, TID: %#x\n", + be32_to_cpu(pid), be32_to_cpu(sid), be32_to_cpu(tid)); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_log_tuning, "SND_SOC_CS35L56_SHARED"); + int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) { int ret; @@ -809,6 +975,9 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) case 0x35A56: case 0x35A57: break; + case 0x35A630: + devid = devid >> 4; + break; default: dev_err(cs35l56_base->dev, "Unknown device %x\n", devid); return ret; @@ -1045,8 +1214,66 @@ const struct regmap_config cs35l56_regmap_sdw = { }; EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, "SND_SOC_CS35L56_SHARED"); +const struct regmap_config cs35l63_regmap_i2c = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_base = 0x8000, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L56_DSP1_PMEM_5114, + .reg_defaults = cs35l63_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults), + .volatile_reg = cs35l63_volatile_reg, + .readable_reg = cs35l56_readable_reg, + .precious_reg = cs35l56_precious_reg, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_i2c, "SND_SOC_CS35L56_SHARED"); + +const struct regmap_config cs35l63_regmap_sdw = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L56_DSP1_PMEM_5114, + .reg_defaults = cs35l63_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults), + .volatile_reg = cs35l63_volatile_reg, + .readable_reg = cs35l56_readable_reg, + .precious_reg = cs35l56_precious_reg, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_sdw, "SND_SOC_CS35L56_SHARED"); + +const struct cs35l56_fw_reg cs35l56_fw_reg = { + .fw_ver = CS35L56_DSP1_FW_VER, + .halo_state = CS35L56_DSP1_HALO_STATE, + .pm_cur_stat = CS35L56_DSP1_PM_CUR_STATE, + .prot_sts = CS35L56_PROTECTION_STATUS, + .transducer_actual_ps = CS35L56_TRANSDUCER_ACTUAL_PS, + .user_mute = CS35L56_MAIN_RENDER_USER_MUTE, + .user_volume = CS35L56_MAIN_RENDER_USER_VOLUME, + .posture_number = CS35L56_MAIN_POSTURE_NUMBER, +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_fw_reg, "SND_SOC_CS35L56_SHARED"); + +const struct cs35l56_fw_reg cs35l63_fw_reg = { + .fw_ver = CS35L63_DSP1_FW_VER, + .halo_state = CS35L63_DSP1_HALO_STATE, + .pm_cur_stat = CS35L63_DSP1_PM_CUR_STATE, + .prot_sts = CS35L63_PROTECTION_STATUS, + .transducer_actual_ps = CS35L63_TRANSDUCER_ACTUAL_PS, + .user_mute = CS35L63_MAIN_RENDER_USER_MUTE, + .user_volume = CS35L63_MAIN_RENDER_USER_VOLUME, + .posture_number = CS35L63_MAIN_POSTURE_NUMBER, +}; +EXPORT_SYMBOL_NS_GPL(cs35l63_fw_reg, "SND_SOC_CS35L56_SHARED"); + MODULE_DESCRIPTION("ASoC CS35L56 Shared"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); +MODULE_IMPORT_NS("FW_CS_DSP"); diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c index ca6c03a8766d..c2ddee22cd23 100644 --- a/sound/soc/codecs/cs35l56-spi.c +++ b/sound/soc/codecs/cs35l56-spi.c @@ -25,6 +25,9 @@ static int cs35l56_spi_probe(struct spi_device *spi) return -ENOMEM; spi_set_drvdata(spi, cs35l56); + + cs35l56->base.fw_reg = &cs35l56_fw_reg; + cs35l56->base.regmap = devm_regmap_init_spi(spi, regmap_config); if (IS_ERR(cs35l56->base.regmap)) { ret = PTR_ERR(cs35l56->base.regmap); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index b3158a84b87a..c78e4746e428 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -84,6 +84,25 @@ static const struct snd_kcontrol_new cs35l56_controls[] = { cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), }; +static const struct snd_kcontrol_new cs35l63_controls[] = { + SOC_SINGLE_EXT("Speaker Switch", + CS35L63_MAIN_RENDER_USER_MUTE, 0, 1, 1, + cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), + SOC_SINGLE_S_EXT_TLV("Speaker Volume", + CS35L63_MAIN_RENDER_USER_VOLUME, + CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT, + CS35L56_MAIN_RENDER_USER_VOLUME_MIN, + CS35L56_MAIN_RENDER_USER_VOLUME_MAX, + CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT, + 0, + cs35l56_dspwait_get_volsw, + cs35l56_dspwait_put_volsw, + vol_tlv), + SOC_SINGLE_EXT("Posture Number", CS35L63_MAIN_POSTURE_NUMBER, + 0, 255, 0, + cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), +}; + static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum, CS35L56_ASP1TX1_INPUT, 0, CS35L56_ASP_TXn_SRC_MASK, @@ -174,7 +193,7 @@ static int cs35l56_play_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: /* Wait for firmware to enter PS0 power state */ ret = regmap_read_poll_timeout(cs35l56->base.regmap, - CS35L56_TRANSDUCER_ACTUAL_PS, + cs35l56->base.fw_reg->transducer_actual_ps, val, (val == CS35L56_PS0), CS35L56_PS0_POLL_US, CS35L56_PS0_TIMEOUT_US); @@ -760,7 +779,8 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing goto err_unlock; } - regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, + regmap_clear_bits(cs35l56->base.regmap, + cs35l56->base.fw_reg->prot_sts, CS35L56_FIRMWARE_MISSING); cs35l56->base.fw_patched = true; @@ -827,6 +847,7 @@ static void cs35l56_dsp_work(struct work_struct *work) else cs35l56_patch(cs35l56, firmware_missing); + cs35l56_log_tuning(&cs35l56->base, &cs35l56->dsp.cs_dsp); err: pm_runtime_mark_last_busy(cs35l56->base.dev); pm_runtime_put_autosuspend(cs35l56->base.dev); @@ -837,6 +858,7 @@ static int cs35l56_component_probe(struct snd_soc_component *component) struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); struct dentry *debugfs_root = component->debugfs_root; unsigned short vendor, device; + int ret; BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values)); @@ -876,6 +898,26 @@ static int cs35l56_component_probe(struct snd_soc_component *component) debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate); debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched); + + switch (cs35l56->base.type) { + case 0x54: + case 0x56: + case 0x57: + ret = snd_soc_add_component_controls(component, cs35l56_controls, + ARRAY_SIZE(cs35l56_controls)); + break; + case 0x63: + ret = snd_soc_add_component_controls(component, cs35l63_controls, + ARRAY_SIZE(cs35l63_controls)); + break; + default: + ret = -ENODEV; + break; + } + + if (ret) + return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n"); + queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); return 0; @@ -931,8 +973,6 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l56 = { .num_dapm_widgets = ARRAY_SIZE(cs35l56_dapm_widgets), .dapm_routes = cs35l56_audio_map, .num_dapm_routes = ARRAY_SIZE(cs35l56_audio_map), - .controls = cs35l56_controls, - .num_controls = ARRAY_SIZE(cs35l56_controls), .set_bias_level = cs35l56_set_bias_level, diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 8a987ec01507..200f695efca3 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -51,6 +51,7 @@ struct cs35l56_private { u8 asp_slot_count; bool tdm_mode; bool sysclk_set; + u8 old_sdw_clock_scale; }; extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index ac19a572fe70..6165ac16c3a9 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -654,6 +654,10 @@ static int cs42l43_run_type_detect(struct cs42l43_codec *priv) reinit_completion(&priv->type_detect); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL, + CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK, + CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK); + cs42l43_start_hs_bias(priv, true); regmap_update_bits(cs42l43->regmap, CS42L43_HS2, CS42L43_HSDET_MODE_MASK, 0x3 << CS42L43_HSDET_MODE_SHIFT); @@ -665,6 +669,9 @@ static int cs42l43_run_type_detect(struct cs42l43_codec *priv) CS42L43_HSDET_MODE_MASK, 0x2 << CS42L43_HSDET_MODE_SHIFT); cs42l43_stop_hs_bias(priv); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL, + CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK, 0); + if (!time_left) return -ETIMEDOUT; @@ -702,6 +709,9 @@ static void cs42l43_clear_jack(struct cs42l43_codec *priv) CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0); regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL, CS42L43_JACK_STEREO_CONFIG_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL, + CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK); regmap_update_bits(cs42l43->regmap, CS42L43_HS2, CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK, 0x2 << CS42L43_HSDET_MODE_SHIFT); diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index cd3f92c19045..a9ffba62aaf8 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -8,27 +8,26 @@ * Author: Brian Austin <brian.austin@cirrus.com> */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> -#include <linux/init.h> #include <linux/delay.h> -#include <linux/of_gpio.h> -#include <linux/pm.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> +#include <linux/init.h> #include <linux/input.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pm.h> +#include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/workqueue.h> -#include <linux/platform_device.h> #include <sound/core.h> +#include <sound/initval.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 <sound/cs42l52.h> #include "cs42l52.h" struct sp_config { @@ -36,6 +35,24 @@ struct sp_config { u32 srate; }; +struct cs42l52_platform_data { + + /* MICBIAS Level. Check datasheet Pg48 */ + unsigned int micbias_lvl; + + /* MICA mode selection Differential or Single-ended */ + bool mica_diff_cfg; + + /* MICB mode selection Differential or Single-ended */ + bool micb_diff_cfg; + + /* Charge Pump Freq. Check datasheet Pg73 */ + unsigned int chgfreq; + + /* Reset GPIO */ + struct gpio_desc *reset_gpio; +}; + struct cs42l52_private { struct regmap *regmap; struct snd_soc_component *component; @@ -1090,7 +1107,7 @@ static const struct regmap_config cs42l52_regmap = { static int cs42l52_i2c_probe(struct i2c_client *i2c_client) { struct cs42l52_private *cs42l52; - struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev); + struct cs42l52_platform_data *pdata; int ret; unsigned int devid; unsigned int reg; @@ -1107,50 +1124,43 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client) dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); return ret; } - if (pdata) { - cs42l52->pdata = *pdata; - } else { - pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), - GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - if (i2c_client->dev.of_node) { - if (of_property_read_bool(i2c_client->dev.of_node, - "cirrus,mica-differential-cfg")) - pdata->mica_diff_cfg = true; - - if (of_property_read_bool(i2c_client->dev.of_node, - "cirrus,micb-differential-cfg")) - pdata->micb_diff_cfg = true; - - if (of_property_read_u32(i2c_client->dev.of_node, - "cirrus,micbias-lvl", &val32) >= 0) - pdata->micbias_lvl = val32; - - if (of_property_read_u32(i2c_client->dev.of_node, - "cirrus,chgfreq-divisor", &val32) >= 0) - pdata->chgfreq = val32; - - pdata->reset_gpio = - of_get_named_gpio(i2c_client->dev.of_node, - "cirrus,reset-gpio", 0); - } - cs42l52->pdata = *pdata; + + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (i2c_client->dev.of_node) { + if (of_property_read_bool(i2c_client->dev.of_node, + "cirrus,mica-differential-cfg")) + pdata->mica_diff_cfg = true; + + if (of_property_read_bool(i2c_client->dev.of_node, + "cirrus,micb-differential-cfg")) + pdata->micb_diff_cfg = true; + + if (of_property_read_u32(i2c_client->dev.of_node, + "cirrus,micbias-lvl", &val32) >= 0) + pdata->micbias_lvl = val32; + + if (of_property_read_u32(i2c_client->dev.of_node, + "cirrus,chgfreq-divisor", &val32) >= 0) + pdata->chgfreq = val32; + + pdata->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "cirrus,reset", + GPIOD_OUT_LOW); + + if (IS_ERR(pdata->reset_gpio)) + return PTR_ERR(pdata->reset_gpio); + + gpiod_set_consumer_name(pdata->reset_gpio, "CS42L52 /RST"); } + cs42l52->pdata = *pdata; + if (cs42l52->pdata.reset_gpio) { - ret = devm_gpio_request_one(&i2c_client->dev, - cs42l52->pdata.reset_gpio, - GPIOF_OUT_INIT_HIGH, - "CS42L52 /RST"); - if (ret < 0) { - dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n", - cs42l52->pdata.reset_gpio, ret); - return ret; - } - gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 0); - gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 1); + gpiod_set_value_cansleep(cs42l52->pdata.reset_gpio, 1); + gpiod_set_value_cansleep(cs42l52->pdata.reset_gpio, 0); } i2c_set_clientdata(i2c_client, cs42l52); diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index aaf90c8b7339..98fa812bc07b 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -7,32 +7,64 @@ * Author: Brian Austin <brian.austin@cirrus.com> */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> -#include <linux/init.h> #include <linux/delay.h> -#include <linux/pm.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> +#include <linux/init.h> #include <linux/input.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/workqueue.h> -#include <linux/platform_device.h> -#include <linux/regulator/consumer.h> -#include <linux/of.h> -#include <linux/of_gpio.h> #include <sound/core.h> +#include <sound/initval.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 <sound/cs42l56.h> #include "cs42l56.h" #define CS42L56_NUM_SUPPLIES 3 + +struct cs42l56_platform_data { + /* GPIO for Reset */ + struct gpio_desc *gpio_nreset; + + /* MICBIAS Level. Check datasheet Pg48 */ + unsigned int micbias_lvl; + + /* Analog Input 1A Reference 0=Single 1=Pseudo-Differential */ + unsigned int ain1a_ref_cfg; + + /* Analog Input 2A Reference 0=Single 1=Pseudo-Differential */ + unsigned int ain2a_ref_cfg; + + /* Analog Input 1B Reference 0=Single 1=Pseudo-Differential */ + unsigned int ain1b_ref_cfg; + + /* Analog Input 2B Reference 0=Single 1=Pseudo-Differential */ + unsigned int ain2b_ref_cfg; + + /* Charge Pump Freq. Check datasheet Pg62 */ + unsigned int chgfreq; + + /* HighPass Filter Right Channel Corner Frequency */ + unsigned int hpfb_freq; + + /* HighPass Filter Left Channel Corner Frequency */ + unsigned int hpfa_freq; + + /* Adaptive Power Control for LO/HP */ + unsigned int adaptive_pwr; +}; + static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = { "VA", "VCP", @@ -1161,7 +1193,13 @@ static int cs42l56_handle_of_data(struct i2c_client *i2c_client, if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0) pdata->hpfb_freq = val32; - pdata->gpio_nreset = of_get_named_gpio(np, "cirrus,gpio-nreset", 0); + pdata->gpio_nreset = devm_gpiod_get_optional(&i2c_client->dev, "cirrus,gpio-nreset", + GPIOD_OUT_LOW); + + if (IS_ERR(pdata->gpio_nreset)) + return PTR_ERR(pdata->gpio_nreset); + + gpiod_set_consumer_name(pdata->gpio_nreset, "CS42L56 /RST"); return 0; } @@ -1169,8 +1207,6 @@ static int cs42l56_handle_of_data(struct i2c_client *i2c_client, static int cs42l56_i2c_probe(struct i2c_client *i2c_client) { struct cs42l56_private *cs42l56; - struct cs42l56_platform_data *pdata = - dev_get_platdata(&i2c_client->dev); int ret, i; unsigned int devid; unsigned int alpha_rev, metal_rev; @@ -1188,31 +1224,17 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client) return ret; } - if (pdata) { - cs42l56->pdata = *pdata; - } else { - if (i2c_client->dev.of_node) { - ret = cs42l56_handle_of_data(i2c_client, - &cs42l56->pdata); - if (ret != 0) - return ret; - } + if (i2c_client->dev.of_node) { + ret = cs42l56_handle_of_data(i2c_client, &cs42l56->pdata); + if (ret != 0) + return ret; } if (cs42l56->pdata.gpio_nreset) { - ret = gpio_request_one(cs42l56->pdata.gpio_nreset, - GPIOF_OUT_INIT_HIGH, "CS42L56 /RST"); - if (ret < 0) { - dev_err(&i2c_client->dev, - "Failed to request /RST %d: %d\n", - cs42l56->pdata.gpio_nreset, ret); - return ret; - } - gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0); - gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1); + gpiod_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1); + gpiod_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0); } - i2c_set_clientdata(i2c_client, cs42l56); for (i = 0; i < ARRAY_SIZE(cs42l56->supplies); i++) diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index ddf36001100e..535a867f9f2a 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -8,26 +8,33 @@ * Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com> */ +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/of_gpio.h> #include <linux/pm.h> -#include <linux/i2c.h> #include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> +#include <sound/initval.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 <sound/cs42l73.h> -#include "cs42l73.h" #include "cirrus_legacy.h" +#include "cs42l73.h" + +struct cs42l73_platform_data { + /* RST GPIO */ + struct gpio_desc *reset_gpio; + unsigned int chgfreq; + int jack_detection; + unsigned int mclk_freq; +}; struct sp_config { u8 spc, mmcc, spfs; @@ -1276,7 +1283,7 @@ static const struct regmap_config cs42l73_regmap = { static int cs42l73_i2c_probe(struct i2c_client *i2c_client) { struct cs42l73_private *cs42l73; - struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev); + struct cs42l73_platform_data *pdata; int ret, devid; unsigned int reg; u32 val32; @@ -1292,38 +1299,27 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client) return ret; } - if (pdata) { - cs42l73->pdata = *pdata; - } else { - pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), - GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - if (i2c_client->dev.of_node) { - if (of_property_read_u32(i2c_client->dev.of_node, - "chgfreq", &val32) >= 0) - pdata->chgfreq = val32; - } - pdata->reset_gpio = of_get_named_gpio(i2c_client->dev.of_node, - "reset-gpio", 0); - cs42l73->pdata = *pdata; + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (i2c_client->dev.of_node) { + if (of_property_read_u32(i2c_client->dev.of_node, "chgfreq", &val32) >= 0) + pdata->chgfreq = val32; } + pdata->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, "reset", GPIOD_OUT_LOW); + + if (IS_ERR(pdata->reset_gpio)) + return PTR_ERR(pdata->reset_gpio); + + gpiod_set_consumer_name(pdata->reset_gpio, "CS42L73 /RST"); + cs42l73->pdata = *pdata; i2c_set_clientdata(i2c_client, cs42l73); if (cs42l73->pdata.reset_gpio) { - ret = devm_gpio_request_one(&i2c_client->dev, - cs42l73->pdata.reset_gpio, - GPIOF_OUT_INIT_HIGH, - "CS42L73 /RST"); - if (ret < 0) { - dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n", - cs42l73->pdata.reset_gpio, ret); - return ret; - } - gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0); - gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 1); + gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 1); + gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 0); } /* initialize codec */ @@ -1360,7 +1356,7 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client) return 0; err_reset: - gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0); + gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 1); return ret; } diff --git a/sound/soc/codecs/cs48l32-tables.c b/sound/soc/codecs/cs48l32-tables.c new file mode 100644 index 000000000000..59eaa9a5029f --- /dev/null +++ b/sound/soc/codecs/cs48l32-tables.c @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Regmap tables and other data for Cirrus Logic CS48L32 audio DSP. +// +// Copyright (C) 2018, 2020, 2022, 2025 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/array_size.h> +#include <linux/build_bug.h> +#include <linux/device.h> +#include <linux/linear_range.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <sound/cs48l32.h> +#include <sound/cs48l32_registers.h> + +#include "cs48l32.h" + +static const struct reg_sequence cs48l32_reva_patch[] = { + { 0x00001044, 0x0005000f }, + { 0x00001c34, 0x000037e8 }, + { 0x000046d8, 0x00000fe0 }, +}; + +int cs48l32_apply_patch(struct cs48l32 *cs48l32) +{ + int ret; + + ret = regmap_register_patch(cs48l32->regmap, cs48l32_reva_patch, + ARRAY_SIZE(cs48l32_reva_patch)); + if (ret < 0) + return dev_err_probe(cs48l32->dev, ret, "Failed to apply patch\n"); + + return 0; +} + +static const struct reg_default cs48l32_reg_default[] = { + { 0x00000c08, 0xe1000001 }, /* GPIO1_CTRL1 */ + { 0x00000c0c, 0xe1000001 }, /* GPIO2_CTRL1 */ + { 0x00000c10, 0xe1000001 }, /* GPIO3_CTRL1 */ + { 0x00000c14, 0xe1000001 }, /* GPIO4_CTRL1 */ + { 0x00000c18, 0xe1000001 }, /* GPIO5_CTRL1 */ + { 0x00000c1c, 0xe1000001 }, /* GPIO6_CTRL1 */ + { 0x00000c20, 0xe1000001 }, /* GPIO7_CTRL1 */ + { 0x00000c24, 0xe1000001 }, /* GPIO8_CTRL1 */ + { 0x00000c28, 0xe1000001 }, /* GPIO9_CTRL1 */ + { 0x00000c2c, 0xe1000001 }, /* GPIO10_CTRL1 */ + { 0x00000c30, 0xe1000001 }, /* GPIO11_CTRL1 */ + { 0x00000c34, 0xe1000001 }, /* GPIO12_CTRL1 */ + { 0x00000c38, 0xe1000001 }, /* GPIO13_CTRL1 */ + { 0x00000c3c, 0xe1000001 }, /* GPIO14_CTRL1 */ + { 0x00000c40, 0xe1000001 }, /* GPIO15_CTRL1 */ + { 0x00000c44, 0xe1000001 }, /* GPIO16_CTRL1 */ + { 0x00001020, 0x00000000 }, /* OUTPUT_SYS_CLK */ + { 0x00001044, 0x0005000f }, /* AUXPDM_CTRL */ + { 0x0000105c, 0x00000000 }, /* AUXPDM_CTRL2 */ + { 0x00001400, 0x00000002 }, /* CLOCK32K */ + { 0x00001404, 0x00000404 }, /* SYSTEM_CLOCK1 */ + { 0x00001420, 0x00000003 }, /* SAMPLE_RATE1 */ + { 0x00001424, 0x00000003 }, /* SAMPLE_RATE2 */ + { 0x00001428, 0x00000003 }, /* SAMPLE_RATE3 */ + { 0x0000142c, 0x00000003 }, /* SAMPLE_RATE4 */ + { 0x00001c00, 0x00000002 }, /* FLL1_CONTROL1 */ + { 0x00001c04, 0x88203004 }, /* FLL1_CONTROL2 */ + { 0x00001c08, 0x00000000 }, /* FLL1_CONTROL3 */ + { 0x00001c0c, 0x21f05001 }, /* FLL1_CONTROL4 */ + { 0x00001ca0, 0x00000c04 }, /* FLL1_GPIO_CLOCK */ + { 0x00002000, 0x00000006 }, /* CHARGE_PUMP1 */ + { 0x00002408, 0x000003e4 }, /* LDO2_CTRL1 */ + { 0x00002410, 0x000000e6 }, /* MICBIAS_CTRL1 */ + { 0x00002418, 0x00000222 }, /* MICBIAS_CTRL5 */ + { 0x00002710, 0x00004600 }, /* IRQ1_CTRL_AOD */ + { 0x00004000, 0x00000000 }, /* INPUT_CONTROL */ + { 0x00004008, 0x00000400 }, /* INPUT_RATE_CONTROL */ + { 0x0000400c, 0x00000000 }, /* INPUT_CONTROL2 */ + { 0x00004020, 0x00050020 }, /* INPUT1_CONTROL1 */ + { 0x00004024, 0x00000000 }, /* IN1L_CONTROL1 */ + { 0x00004028, 0x10800080 }, /* IN1L_CONTROL2 */ + { 0x00004044, 0x00000000 }, /* IN1R_CONTROL1 */ + { 0x00004048, 0x10800080 }, /* IN1R_CONTROL2 */ + { 0x00004060, 0x00050020 }, /* INPUT2_CONTROL1 */ + { 0x00004064, 0x00000000 }, /* IN2L_CONTROL1 */ + { 0x00004068, 0x10800000 }, /* IN2L_CONTROL2 */ + { 0x00004084, 0x00000000 }, /* IN2R_CONTROL1 */ + { 0x00004088, 0x10800000 }, /* IN2R_CONTROL2 */ + { 0x00004244, 0x00000002 }, /* INPUT_HPF_CONTROL */ + { 0x00004248, 0x00000022 }, /* INPUT_VOL_CONTROL */ + { 0x00004300, 0x00000000 }, /* AUXPDM_CONTROL1 */ + { 0x00004304, 0x00000000 }, /* AUXPDM_CONTROL2 */ + { 0x00004308, 0x00010008 }, /* AUXPDM1_CONTROL1 */ + { 0x00004310, 0x00010008 }, /* AUXPDM2_CONTROL1 */ + { 0x00004688, 0x00000000 }, /* ADC1L_ANA_CONTROL1 */ + { 0x0000468c, 0x00000000 }, /* ADC1R_ANA_CONTROL1 */ + { 0x00006000, 0x00000000 }, /* ASP1_ENABLES1 */ + { 0x00006004, 0x00000028 }, /* ASP1_CONTROL1 */ + { 0x00006008, 0x18180200 }, /* ASP1_CONTROL2 */ + { 0x0000600c, 0x00000002 }, /* ASP1_CONTROL3 */ + { 0x00006010, 0x03020100 }, /* ASP1_FRAME_CONTROL1 */ + { 0x00006014, 0x07060504 }, /* ASP1_FRAME_CONTROL2 */ + { 0x00006020, 0x03020100 }, /* ASP1_FRAME_CONTROL5 */ + { 0x00006024, 0x07060504 }, /* ASP1_FRAME_CONTROL6 */ + { 0x00006030, 0x00000020 }, /* ASP1_DATA_CONTROL1 */ + { 0x00006040, 0x00000020 }, /* ASP1_DATA_CONTROL5 */ + { 0x00006080, 0x00000000 }, /* ASP2_ENABLES1 */ + { 0x00006084, 0x00000028 }, /* ASP2_CONTROL1 */ + { 0x00006088, 0x18180200 }, /* ASP2_CONTROL2 */ + { 0x0000608c, 0x00000002 }, /* ASP2_CONTROL3 */ + { 0x00006090, 0x03020100 }, /* ASP2_FRAME_CONTROL1 */ + { 0x000060a0, 0x03020100 }, /* ASP2_FRAME_CONTROL5 */ + { 0x000060b0, 0x00000020 }, /* ASP2_DATA_CONTROL1 */ + { 0x000060c0, 0x00000020 }, /* ASP2_DATA_CONTROL5 */ + { 0x00008200, 0x00800000 }, /* ASP1TX1_INPUT1 */ + { 0x00008204, 0x00800000 }, /* ASP1TX1_INPUT2 */ + { 0x00008208, 0x00800000 }, /* ASP1TX1_INPUT3 */ + { 0x0000820c, 0x00800000 }, /* ASP1TX1_INPUT4 */ + { 0x00008210, 0x00800000 }, /* ASP1TX2_INPUT1 */ + { 0x00008214, 0x00800000 }, /* ASP1TX2_INPUT2 */ + { 0x00008218, 0x00800000 }, /* ASP1TX2_INPUT3 */ + { 0x0000821c, 0x00800000 }, /* ASP1TX2_INPUT4 */ + { 0x00008220, 0x00800000 }, /* ASP1TX3_INPUT1 */ + { 0x00008224, 0x00800000 }, /* ASP1TX3_INPUT2 */ + { 0x00008228, 0x00800000 }, /* ASP1TX3_INPUT3 */ + { 0x0000822c, 0x00800000 }, /* ASP1TX3_INPUT4 */ + { 0x00008230, 0x00800000 }, /* ASP1TX4_INPUT1 */ + { 0x00008234, 0x00800000 }, /* ASP1TX4_INPUT2 */ + { 0x00008238, 0x00800000 }, /* ASP1TX4_INPUT3 */ + { 0x0000823c, 0x00800000 }, /* ASP1TX4_INPUT4 */ + { 0x00008240, 0x00800000 }, /* ASP1TX5_INPUT1 */ + { 0x00008244, 0x00800000 }, /* ASP1TX5_INPUT2 */ + { 0x00008248, 0x00800000 }, /* ASP1TX5_INPUT3 */ + { 0x0000824c, 0x00800000 }, /* ASP1TX5_INPUT4 */ + { 0x00008250, 0x00800000 }, /* ASP1TX6_INPUT1 */ + { 0x00008254, 0x00800000 }, /* ASP1TX6_INPUT2 */ + { 0x00008258, 0x00800000 }, /* ASP1TX6_INPUT3 */ + { 0x0000825c, 0x00800000 }, /* ASP1TX6_INPUT4 */ + { 0x00008260, 0x00800000 }, /* ASP1TX7_INPUT1 */ + { 0x00008264, 0x00800000 }, /* ASP1TX7_INPUT2 */ + { 0x00008268, 0x00800000 }, /* ASP1TX7_INPUT3 */ + { 0x0000826c, 0x00800000 }, /* ASP1TX7_INPUT4 */ + { 0x00008270, 0x00800000 }, /* ASP1TX8_INPUT1 */ + { 0x00008274, 0x00800000 }, /* ASP1TX8_INPUT2 */ + { 0x00008278, 0x00800000 }, /* ASP1TX8_INPUT3 */ + { 0x0000827c, 0x00800000 }, /* ASP1TX8_INPUT4 */ + { 0x00008300, 0x00800000 }, /* ASP2TX1_INPUT1 */ + { 0x00008304, 0x00800000 }, /* ASP2TX1_INPUT2 */ + { 0x00008308, 0x00800000 }, /* ASP2TX1_INPUT3 */ + { 0x0000830c, 0x00800000 }, /* ASP2TX1_INPUT4 */ + { 0x00008310, 0x00800000 }, /* ASP2TX2_INPUT1 */ + { 0x00008314, 0x00800000 }, /* ASP2TX2_INPUT2 */ + { 0x00008318, 0x00800000 }, /* ASP2TX2_INPUT3 */ + { 0x0000831c, 0x00800000 }, /* ASP2TX2_INPUT4 */ + { 0x00008320, 0x00800000 }, /* ASP2TX3_INPUT1 */ + { 0x00008324, 0x00800000 }, /* ASP2TX3_INPUT2 */ + { 0x00008328, 0x00800000 }, /* ASP2TX3_INPUT3 */ + { 0x0000832c, 0x00800000 }, /* ASP2TX3_INPUT4 */ + { 0x00008330, 0x00800000 }, /* ASP2TX4_INPUT1 */ + { 0x00008334, 0x00800000 }, /* ASP2TX4_INPUT2 */ + { 0x00008338, 0x00800000 }, /* ASP2TX4_INPUT3 */ + { 0x0000833c, 0x00800000 }, /* ASP2TX4_INPUT4 */ + { 0x00008980, 0x00000000 }, /* ISRC1INT1_INPUT1 */ + { 0x00008990, 0x00000000 }, /* ISRC1INT2_INPUT1 */ + { 0x000089a0, 0x00000000 }, /* ISRC1INT3_INPUT1 */ + { 0x000089b0, 0x00000000 }, /* ISRC1INT4_INPUT1 */ + { 0x000089c0, 0x00000000 }, /* ISRC1DEC1_INPUT1 */ + { 0x000089d0, 0x00000000 }, /* ISRC1DEC2_INPUT1 */ + { 0x000089e0, 0x00000000 }, /* ISRC1DEC3_INPUT1 */ + { 0x000089f0, 0x00000000 }, /* ISRC1DEC4_INPUT1 */ + { 0x00008a00, 0x00000000 }, /* ISRC2INT1_INPUT1 */ + { 0x00008a10, 0x00000000 }, /* ISRC2INT2_INPUT1 */ + { 0x00008a40, 0x00000000 }, /* ISRC2DEC1_INPUT1 */ + { 0x00008a50, 0x00000000 }, /* ISRC2DEC2_INPUT1 */ + { 0x00008a80, 0x00000000 }, /* ISRC3INT1_INPUT1 */ + { 0x00008a90, 0x00000000 }, /* ISRC3INT2_INPUT1 */ + { 0x00008ac0, 0x00000000 }, /* ISRC3DEC1_INPUT1 */ + { 0x00008ad0, 0x00000000 }, /* ISRC3DEC2_INPUT1 */ + { 0x00008b80, 0x00800000 }, /* EQ1_INPUT1 */ + { 0x00008b84, 0x00800000 }, /* EQ1_INPUT2 */ + { 0x00008b88, 0x00800000 }, /* EQ1_INPUT3 */ + { 0x00008b8c, 0x00800000 }, /* EQ1_INPUT4 */ + { 0x00008b90, 0x00800000 }, /* EQ2_INPUT1 */ + { 0x00008b94, 0x00800000 }, /* EQ2_INPUT2 */ + { 0x00008b98, 0x00800000 }, /* EQ2_INPUT3 */ + { 0x00008b9c, 0x00800000 }, /* EQ2_INPUT4 */ + { 0x00008ba0, 0x00800000 }, /* EQ3_INPUT1 */ + { 0x00008ba4, 0x00800000 }, /* EQ3_INPUT2 */ + { 0x00008ba8, 0x00800000 }, /* EQ3_INPUT3 */ + { 0x00008bac, 0x00800000 }, /* EQ3_INPUT4 */ + { 0x00008bb0, 0x00800000 }, /* EQ4_INPUT1 */ + { 0x00008bb4, 0x00800000 }, /* EQ4_INPUT2 */ + { 0x00008bb8, 0x00800000 }, /* EQ4_INPUT3 */ + { 0x00008bbc, 0x00800000 }, /* EQ4_INPUT4 */ + { 0x00008c00, 0x00800000 }, /* DRC1L_INPUT1 */ + { 0x00008c04, 0x00800000 }, /* DRC1L_INPUT2 */ + { 0x00008c08, 0x00800000 }, /* DRC1L_INPUT3 */ + { 0x00008c0c, 0x00800000 }, /* DRC1L_INPUT4 */ + { 0x00008c10, 0x00800000 }, /* DRC1R_INPUT1 */ + { 0x00008c14, 0x00800000 }, /* DRC1R_INPUT2 */ + { 0x00008c18, 0x00800000 }, /* DRC1R_INPUT3 */ + { 0x00008c1c, 0x00800000 }, /* DRC1R_INPUT4 */ + { 0x00008c20, 0x00800000 }, /* DRC2L_INPUT1 */ + { 0x00008c24, 0x00800000 }, /* DRC2L_INPUT2 */ + { 0x00008c28, 0x00800000 }, /* DRC2L_INPUT3 */ + { 0x00008c2c, 0x00800000 }, /* DRC2L_INPUT4 */ + { 0x00008c30, 0x00800000 }, /* DRC2R_INPUT1 */ + { 0x00008c34, 0x00800000 }, /* DRC2R_INPUT2 */ + { 0x00008c38, 0x00800000 }, /* DRC2R_INPUT3 */ + { 0x00008c3c, 0x00800000 }, /* DRC2R_INPUT4 */ + { 0x00008c80, 0x00800000 }, /* LHPF1_INPUT1 */ + { 0x00008c84, 0x00800000 }, /* LHPF1_INPUT2 */ + { 0x00008c88, 0x00800000 }, /* LHPF1_INPUT3 */ + { 0x00008c8c, 0x00800000 }, /* LHPF1_INPUT4 */ + { 0x00008c90, 0x00800000 }, /* LHPF2_INPUT1 */ + { 0x00008c94, 0x00800000 }, /* LHPF2_INPUT2 */ + { 0x00008c98, 0x00800000 }, /* LHPF2_INPUT3 */ + { 0x00008c9c, 0x00800000 }, /* LHPF2_INPUT4 */ + { 0x00008ca0, 0x00800000 }, /* LHPF3_INPUT1 */ + { 0x00008ca4, 0x00800000 }, /* LHPF3_INPUT2 */ + { 0x00008ca8, 0x00800000 }, /* LHPF3_INPUT3 */ + { 0x00008cac, 0x00800000 }, /* LHPF3_INPUT4 */ + { 0x00008cb0, 0x00800000 }, /* LHPF4_INPUT1 */ + { 0x00008cb4, 0x00800000 }, /* LHPF4_INPUT2 */ + { 0x00008cb8, 0x00800000 }, /* LHPF4_INPUT3 */ + { 0x00008cbc, 0x00800000 }, /* LHPF4_INPUT4 */ + { 0x00009000, 0x00800000 }, /* DSP1RX1_INPUT1 */ + { 0x00009004, 0x00800000 }, /* DSP1RX1_INPUT2 */ + { 0x00009008, 0x00800000 }, /* DSP1RX1_INPUT3 */ + { 0x0000900c, 0x00800000 }, /* DSP1RX1_INPUT4 */ + { 0x00009010, 0x00800000 }, /* DSP1RX2_INPUT1 */ + { 0x00009014, 0x00800000 }, /* DSP1RX2_INPUT2 */ + { 0x00009018, 0x00800000 }, /* DSP1RX2_INPUT3 */ + { 0x0000901c, 0x00800000 }, /* DSP1RX2_INPUT4 */ + { 0x00009020, 0x00800000 }, /* DSP1RX3_INPUT1 */ + { 0x00009024, 0x00800000 }, /* DSP1RX3_INPUT2 */ + { 0x00009028, 0x00800000 }, /* DSP1RX3_INPUT3 */ + { 0x0000902c, 0x00800000 }, /* DSP1RX3_INPUT4 */ + { 0x00009030, 0x00800000 }, /* DSP1RX4_INPUT1 */ + { 0x00009034, 0x00800000 }, /* DSP1RX4_INPUT2 */ + { 0x00009038, 0x00800000 }, /* DSP1RX4_INPUT3 */ + { 0x0000903c, 0x00800000 }, /* DSP1RX4_INPUT4 */ + { 0x00009040, 0x00800000 }, /* DSP1RX5_INPUT1 */ + { 0x00009044, 0x00800000 }, /* DSP1RX5_INPUT2 */ + { 0x00009048, 0x00800000 }, /* DSP1RX5_INPUT3 */ + { 0x0000904c, 0x00800000 }, /* DSP1RX5_INPUT4 */ + { 0x00009050, 0x00800000 }, /* DSP1RX6_INPUT1 */ + { 0x00009054, 0x00800000 }, /* DSP1RX6_INPUT2 */ + { 0x00009058, 0x00800000 }, /* DSP1RX6_INPUT3 */ + { 0x0000905c, 0x00800000 }, /* DSP1RX6_INPUT4 */ + { 0x00009060, 0x00800000 }, /* DSP1RX7_INPUT1 */ + { 0x00009064, 0x00800000 }, /* DSP1RX7_INPUT2 */ + { 0x00009068, 0x00800000 }, /* DSP1RX7_INPUT3 */ + { 0x0000906c, 0x00800000 }, /* DSP1RX7_INPUT4 */ + { 0x00009070, 0x00800000 }, /* DSP1RX8_INPUT1 */ + { 0x00009074, 0x00800000 }, /* DSP1RX8_INPUT2 */ + { 0x00009078, 0x00800000 }, /* DSP1RX8_INPUT3 */ + { 0x0000907c, 0x00800000 }, /* DSP1RX8_INPUT4 */ + { 0x0000a400, 0x00000000 }, /* ISRC1_CONTROL1 */ + { 0x0000a404, 0x00000000 }, /* ISRC1_CONTROL2 */ + { 0x0000a510, 0x00000000 }, /* ISRC2_CONTROL1 */ + { 0x0000a514, 0x00000000 }, /* ISRC2_CONTROL2 */ + { 0x0000a620, 0x00000000 }, /* ISRC3_CONTROL1 */ + { 0x0000a624, 0x00000000 }, /* ISRC3_CONTROL2 */ + { 0x0000a800, 0x00000000 }, /* FX_SAMPLE_RATE */ + { 0x0000a808, 0x00000000 }, /* EQ_CONTROL1 */ + { 0x0000a80c, 0x00000000 }, /* EQ_CONTROL2 */ + { 0x0000a810, 0x0c0c0c0c }, /* EQ1_GAIN1 */ + { 0x0000a814, 0x0000000c }, /* EQ1_GAIN2 */ + { 0x0000a818, 0x03fe0fc8 }, /* EQ1_BAND1_COEFF1 */ + { 0x0000a81c, 0x00000b75 }, /* EQ1_BAND1_COEFF2 */ + { 0x0000a820, 0x000000e0 }, /* EQ1_BAND1_PG */ + { 0x0000a824, 0xf1361ec4 }, /* EQ1_BAND2_COEFF1 */ + { 0x0000a828, 0x00000409 }, /* EQ1_BAND2_COEFF2 */ + { 0x0000a82c, 0x000004cc }, /* EQ1_BAND2_PG */ + { 0x0000a830, 0xf3371c9b }, /* EQ1_BAND3_COEFF1 */ + { 0x0000a834, 0x0000040b }, /* EQ1_BAND3_COEFF2 */ + { 0x0000a838, 0x00000cbb }, /* EQ1_BAND3_PG */ + { 0x0000a83c, 0xf7d916f8 }, /* EQ1_BAND4_COEFF1 */ + { 0x0000a840, 0x0000040a }, /* EQ1_BAND4_COEFF2 */ + { 0x0000a844, 0x00001f14 }, /* EQ1_BAND4_PG */ + { 0x0000a848, 0x0563058c }, /* EQ1_BAND5_COEFF1 */ + { 0x0000a84c, 0x00000000 }, /* EQ1_BAND5_COEFF1 + 4 */ + { 0x0000a850, 0x00004000 }, /* EQ1_BAND5_PG */ + { 0x0000a854, 0x0c0c0c0c }, /* EQ2_GAIN1 */ + { 0x0000a858, 0x0000000c }, /* EQ2_GAIN2 */ + { 0x0000a85c, 0x03fe0fc8 }, /* EQ2_BAND1_COEFF1 */ + { 0x0000a860, 0x00000b75 }, /* EQ2_BAND1_COEFF2 */ + { 0x0000a864, 0x000000e0 }, /* EQ2_BAND1_PG */ + { 0x0000a868, 0xf1361ec4 }, /* EQ2_BAND2_COEFF1 */ + { 0x0000a86c, 0x00000409 }, /* EQ2_BAND2_COEFF2 */ + { 0x0000a870, 0x000004cc }, /* EQ2_BAND2_PG */ + { 0x0000a874, 0xf3371c9b }, /* EQ2_BAND3_COEFF1 */ + { 0x0000a878, 0x0000040b }, /* EQ2_BAND3_COEFF2 */ + { 0x0000a87c, 0x00000cbb }, /* EQ2_BAND3_PG */ + { 0x0000a880, 0xf7d916f8 }, /* EQ2_BAND4_COEFF1 */ + { 0x0000a884, 0x0000040a }, /* EQ2_BAND4_COEFF2 */ + { 0x0000a888, 0x00001f14 }, /* EQ2_BAND4_PG */ + { 0x0000a88c, 0x0563058c }, /* EQ2_BAND5_COEFF1 */ + { 0x0000a890, 0x00000000 }, /* EQ2_BAND5_COEFF1 + 4 */ + { 0x0000a894, 0x00004000 }, /* EQ2_BAND5_PG */ + { 0x0000a898, 0x0c0c0c0c }, /* EQ3_GAIN1 */ + { 0x0000a89c, 0x0000000c }, /* EQ3_GAIN2 */ + { 0x0000a8a0, 0x03fe0fc8 }, /* EQ3_BAND1_COEFF1 */ + { 0x0000a8a4, 0x00000b75 }, /* EQ3_BAND1_COEFF2 */ + { 0x0000a8a8, 0x000000e0 }, /* EQ3_BAND1_PG */ + { 0x0000a8ac, 0xf1361ec4 }, /* EQ3_BAND2_COEFF1 */ + { 0x0000a8b0, 0x00000409 }, /* EQ3_BAND2_COEFF2 */ + { 0x0000a8b4, 0x000004cc }, /* EQ3_BAND2_PG */ + { 0x0000a8b8, 0xf3371c9b }, /* EQ3_BAND3_COEFF1 */ + { 0x0000a8bc, 0x0000040b }, /* EQ3_BAND3_COEFF2 */ + { 0x0000a8c0, 0x00000cbb }, /* EQ3_BAND3_PG */ + { 0x0000a8c4, 0xf7d916f8 }, /* EQ3_BAND4_COEFF1 */ + { 0x0000a8c8, 0x0000040a }, /* EQ3_BAND4_COEFF2 */ + { 0x0000a8cc, 0x00001f14 }, /* EQ3_BAND4_PG */ + { 0x0000a8d0, 0x0563058c }, /* EQ3_BAND5_COEFF1 */ + { 0x0000a8d4, 0x00000000 }, /* EQ3_BAND5_COEFF1 + 4 */ + { 0x0000a8d8, 0x00004000 }, /* EQ3_BAND5_PG */ + { 0x0000a8dc, 0x0c0c0c0c }, /* EQ4_GAIN1 */ + { 0x0000a8e0, 0x0000000c }, /* EQ4_GAIN2 */ + { 0x0000a8e4, 0x03fe0fc8 }, /* EQ4_BAND1_COEFF1 */ + { 0x0000a8e8, 0x00000b75 }, /* EQ4_BAND1_COEFF2 */ + { 0x0000a8ec, 0x000000e0 }, /* EQ4_BAND1_PG */ + { 0x0000a8f0, 0xf1361ec4 }, /* EQ4_BAND2_COEFF1 */ + { 0x0000a8f4, 0x00000409 }, /* EQ4_BAND2_COEFF2 */ + { 0x0000a8f8, 0x000004cc }, /* EQ4_BAND2_PG */ + { 0x0000a8fc, 0xf3371c9b }, /* EQ4_BAND3_COEFF1 */ + { 0x0000a900, 0x0000040b }, /* EQ4_BAND3_COEFF2 */ + { 0x0000a904, 0x00000cbb }, /* EQ4_BAND3_PG */ + { 0x0000a908, 0xf7d916f8 }, /* EQ4_BAND4_COEFF1 */ + { 0x0000a90c, 0x0000040a }, /* EQ4_BAND4_COEFF2 */ + { 0x0000a910, 0x00001f14 }, /* EQ4_BAND4_PG */ + { 0x0000a914, 0x0563058c }, /* EQ4_BAND5_COEFF1 */ + { 0x0000a918, 0x00000000 }, /* EQ4_BAND5_COEFF1 + 4 */ + { 0x0000a91c, 0x00004000 }, /* EQ4_BAND5_PG */ + { 0x0000aa30, 0x00000000 }, /* LHPF_CONTROL1 */ + { 0x0000aa34, 0x00000000 }, /* LHPF_CONTROL2 */ + { 0x0000aa38, 0x00000000 }, /* LHPF1_COEFF */ + { 0x0000aa3c, 0x00000000 }, /* LHPF2_COEFF */ + { 0x0000aa40, 0x00000000 }, /* LHPF3_COEFF */ + { 0x0000aa44, 0x00000000 }, /* LHPF4_COEFF */ + { 0x0000ab00, 0x00000000 }, /* DRC1_CONTROL1 */ + { 0x0000ab04, 0x49130018 }, /* DRC1_CONTROL2 */ + { 0x0000ab08, 0x00000018 }, /* DRC1_CONTROL3 */ + { 0x0000ab0c, 0x00000000 }, /* DRC1_CONTROL4 */ + { 0x0000ab14, 0x00000000 }, /* DRC2_CONTROL1 */ + { 0x0000ab18, 0x49130018 }, /* DRC2_CONTROL2 */ + { 0x0000ab1c, 0x00000018 }, /* DRC2_CONTROL3 */ + { 0x0000ab20, 0x00000000 }, /* DRC2_CONTROL4 */ + { 0x0000b000, 0x00000000 }, /* TONE_GENERATOR1 */ + { 0x0000b004, 0x00100000 }, /* TONE_GENERATOR2 */ + { 0x0000b400, 0x00000000 }, /* COMFORT_NOISE_GENERATOR */ + { 0x0000b800, 0x00000000 }, /* US_CONTROL */ + { 0x0000b804, 0x00002020 }, /* US1_CONTROL */ + { 0x0000b808, 0x00000000 }, /* US1_DET_CONTROL */ + { 0x0000b814, 0x00002020 }, /* US2_CONTROL */ + { 0x0000b818, 0x00000000 }, /* US2_DET_CONTROL */ + { 0x00018110, 0x00000700 }, /* IRQ1_MASK_1 */ + { 0x00018114, 0x00000004 }, /* IRQ1_MASK_2 */ + { 0x00018120, 0x03ff0000 }, /* IRQ1_MASK_5 */ + { 0x00018124, 0x00000103 }, /* IRQ1_MASK_6 */ + { 0x00018128, 0x003f0000 }, /* IRQ1_MASK_7 */ + { 0x00018130, 0xff00000f }, /* IRQ1_MASK_9 */ + { 0x00018138, 0xffff0000 }, /* IRQ1_MASK_11 */ +}; + +static bool cs48l32_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS48L32_DEVID: + case CS48L32_REVID: + case CS48L32_OTPID: + case CS48L32_SFT_RESET: + case CS48L32_CTRL_IF_DEBUG3: + case CS48L32_MCU_CTRL1: + case CS48L32_GPIO1_CTRL1 ... CS48L32_GPIO16_CTRL1: + case CS48L32_OUTPUT_SYS_CLK: + case CS48L32_AUXPDM_CTRL: + case CS48L32_AUXPDM_CTRL2: + case CS48L32_CLOCK32K: + case CS48L32_SYSTEM_CLOCK1 ... CS48L32_SYSTEM_CLOCK2: + case CS48L32_SAMPLE_RATE1 ... CS48L32_SAMPLE_RATE4: + case CS48L32_FLL1_CONTROL1 ... CS48L32_FLL1_GPIO_CLOCK: + case CS48L32_CHARGE_PUMP1: + case CS48L32_LDO2_CTRL1: + case CS48L32_MICBIAS_CTRL1: + case CS48L32_MICBIAS_CTRL5: + case CS48L32_IRQ1_CTRL_AOD: + case CS48L32_INPUT_CONTROL: + case CS48L32_INPUT_STATUS: + case CS48L32_INPUT_RATE_CONTROL: + case CS48L32_INPUT_CONTROL2: + case CS48L32_INPUT_CONTROL3: + case CS48L32_INPUT1_CONTROL1: + case CS48L32_IN1L_CONTROL1 ... CS48L32_IN1L_CONTROL2: + case CS48L32_IN1R_CONTROL1 ... CS48L32_IN1R_CONTROL2: + case CS48L32_INPUT2_CONTROL1: + case CS48L32_IN2L_CONTROL1 ... CS48L32_IN2L_CONTROL2: + case CS48L32_IN2R_CONTROL1 ... CS48L32_IN2R_CONTROL2: + case CS48L32_INPUT_HPF_CONTROL: + case CS48L32_INPUT_VOL_CONTROL: + case CS48L32_AUXPDM_CONTROL1: + case CS48L32_AUXPDM_CONTROL2: + case CS48L32_AUXPDM1_CONTROL1: + case CS48L32_AUXPDM2_CONTROL1: + case CS48L32_ADC1L_ANA_CONTROL1: + case CS48L32_ADC1R_ANA_CONTROL1: + case CS48L32_ASP1_ENABLES1 ... CS48L32_ASP1_DATA_CONTROL5: + case CS48L32_ASP2_ENABLES1 ... CS48L32_ASP2_DATA_CONTROL5: + case CS48L32_ASP1TX1_INPUT1 ... CS48L32_ASP1TX8_INPUT4: + case CS48L32_ASP2TX1_INPUT1 ... CS48L32_ASP2TX4_INPUT4: + case CS48L32_ISRC1INT1_INPUT1 ... CS48L32_ISRC1DEC4_INPUT1: + case CS48L32_ISRC2INT1_INPUT1 ... CS48L32_ISRC2DEC2_INPUT1: + case CS48L32_ISRC3INT1_INPUT1 ... CS48L32_ISRC3DEC2_INPUT1: + case CS48L32_EQ1_INPUT1 ... CS48L32_EQ4_INPUT4: + case CS48L32_DRC1L_INPUT1 ... CS48L32_DRC1R_INPUT4: + case CS48L32_DRC2L_INPUT1 ... CS48L32_DRC2R_INPUT4: + case CS48L32_LHPF1_INPUT1 ... CS48L32_LHPF1_INPUT4: + case CS48L32_LHPF2_INPUT1 ... CS48L32_LHPF2_INPUT4: + case CS48L32_LHPF3_INPUT1 ... CS48L32_LHPF3_INPUT4: + case CS48L32_LHPF4_INPUT1 ... CS48L32_LHPF4_INPUT4: + case CS48L32_DSP1RX1_INPUT1 ... CS48L32_DSP1RX8_INPUT4: + case CS48L32_ISRC1_CONTROL1 ... CS48L32_ISRC1_CONTROL2: + case CS48L32_ISRC2_CONTROL1 ... CS48L32_ISRC2_CONTROL2: + case CS48L32_ISRC3_CONTROL1 ... CS48L32_ISRC3_CONTROL2: + case CS48L32_FX_SAMPLE_RATE: + case CS48L32_EQ_CONTROL1 ... CS48L32_EQ_CONTROL2: + case CS48L32_EQ1_GAIN1 ... CS48L32_EQ1_BAND5_PG: + case CS48L32_EQ2_GAIN1 ... CS48L32_EQ2_BAND5_PG: + case CS48L32_EQ3_GAIN1 ... CS48L32_EQ3_BAND5_PG: + case CS48L32_EQ4_GAIN1 ... CS48L32_EQ4_BAND5_PG: + case CS48L32_LHPF_CONTROL1 ... CS48L32_LHPF_CONTROL2: + case CS48L32_LHPF1_COEFF ... CS48L32_LHPF4_COEFF: + case CS48L32_DRC1_CONTROL1 ... CS48L32_DRC1_CONTROL4: + case CS48L32_DRC2_CONTROL1 ... CS48L32_DRC2_CONTROL4: + case CS48L32_TONE_GENERATOR1 ... CS48L32_TONE_GENERATOR2: + case CS48L32_COMFORT_NOISE_GENERATOR: + case CS48L32_US_CONTROL: + case CS48L32_US1_CONTROL: + case CS48L32_US1_DET_CONTROL: + case CS48L32_US2_CONTROL: + case CS48L32_US2_DET_CONTROL: + case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24: + case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8: + case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7: + case CS48L32_IRQ1_STATUS: + case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11: + case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11: + case CS48L32_IRQ1_MASK_1 ... CS48L32_IRQ1_MASK_11: + case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST: + case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1: + case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_LAST: + case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8: + case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4: + case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1: + case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST: + case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_LAST: + case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST: + return true; + default: + return false; + } +} + +static bool cs48l32_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS48L32_DEVID: + case CS48L32_REVID: + case CS48L32_OTPID: + case CS48L32_SFT_RESET: + case CS48L32_CTRL_IF_DEBUG3: + case CS48L32_MCU_CTRL1: + case CS48L32_SYSTEM_CLOCK2: + case CS48L32_FLL1_CONTROL5: + case CS48L32_FLL1_CONTROL6: + case CS48L32_INPUT_STATUS: + case CS48L32_INPUT_CONTROL3: + case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24: + case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8: + case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7: + case CS48L32_IRQ1_STATUS: + case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11: + case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11: + case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST: + case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1: + case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_LAST: + case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8: + case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4: + case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1: + case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST: + case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_LAST: + case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST: + return true; + default: + return false; + } +} + +/* + * The bus bridge requires DSP packed memory registers to be accessed in + * aligned block multiples. + * Mark precious to prevent regmap debugfs causing an illegal bus transaction. + */ +static bool cs48l32_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST: + case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST: + case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST: + return true; + default: + return false; + } +} + +static const struct regmap_config cs48l32_regmap = { + .name = "cs48l32", + .reg_bits = 32, + .reg_stride = 4, + .pad_bits = 32, + .val_bits = 32, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + + .max_register = CS48L32_DSP1_PMEM_LAST, + .readable_reg = &cs48l32_readable_register, + .volatile_reg = &cs48l32_volatile_register, + .precious_reg = &cs48l32_precious_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = cs48l32_reg_default, + .num_reg_defaults = ARRAY_SIZE(cs48l32_reg_default), +}; + +int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32) +{ + cs48l32->regmap = devm_regmap_init_spi(spi, &cs48l32_regmap); + if (IS_ERR(cs48l32->regmap)) + return PTR_ERR(cs48l32->regmap); + + return 0; +} diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c new file mode 100644 index 000000000000..90a795230d27 --- /dev/null +++ b/sound/soc/codecs/cs48l32.c @@ -0,0 +1,4073 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Cirrus Logic CS48L32 audio DSP. +// +// Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <dt-bindings/sound/cs48l32.h> +#include <linux/array_size.h> +#include <linux/build_bug.h> +#include <linux/clk.h> +#include <linux/container_of.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gcd.h> +#include <linux/gpio/consumer.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/string_choices.h> +#include <sound/cs48l32.h> +#include <sound/cs48l32_registers.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-component.h> +#include <sound/soc-dai.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "cs48l32.h" + +static const char * const cs48l32_core_supplies[] = { "vdd-a", "vdd-io" }; + +static const struct cs_dsp_region cs48l32_dsp1_regions[] = { + { .type = WMFW_HALO_PM_PACKED, .base = 0x3800000 }, + { .type = WMFW_HALO_XM_PACKED, .base = 0x2000000 }, + { .type = WMFW_ADSP2_XM, .base = 0x2800000 }, + { .type = WMFW_HALO_YM_PACKED, .base = 0x2C00000 }, + { .type = WMFW_ADSP2_YM, .base = 0x3400000 }, +}; + +static const struct cs48l32_dsp_power_reg_block cs48l32_dsp1_sram_ext_regs[] = { + { CS48L32_DSP1_XM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24 }, + { CS48L32_DSP1_YM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8 }, + { CS48L32_DSP1_PM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7 }, +}; + +static const unsigned int cs48l32_dsp1_sram_pwd_regs[] = { + CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0, + CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0, + CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0, +}; + +static const struct cs48l32_dsp_power_regs cs48l32_dsp_sram_regs = { + .ext = cs48l32_dsp1_sram_ext_regs, + .n_ext = ARRAY_SIZE(cs48l32_dsp1_sram_ext_regs), + .pwd = cs48l32_dsp1_sram_pwd_regs, + .n_pwd = ARRAY_SIZE(cs48l32_dsp1_sram_pwd_regs), +}; + +static const char * const cs48l32_mixer_texts[] = { + "None", + "Tone Generator 1", + "Tone Generator 2", + "Noise Generator", + "IN1L", + "IN1R", + "IN2L", + "IN2R", + "ASP1RX1", + "ASP1RX2", + "ASP1RX3", + "ASP1RX4", + "ASP1RX5", + "ASP1RX6", + "ASP1RX7", + "ASP1RX8", + "ASP2RX1", + "ASP2RX2", + "ASP2RX3", + "ASP2RX4", + "ISRC1INT1", + "ISRC1INT2", + "ISRC1INT3", + "ISRC1INT4", + "ISRC1DEC1", + "ISRC1DEC2", + "ISRC1DEC3", + "ISRC1DEC4", + "ISRC2INT1", + "ISRC2INT2", + "ISRC2DEC1", + "ISRC2DEC2", + "ISRC3INT1", + "ISRC3INT2", + "ISRC3DEC1", + "ISRC3DEC2", + "EQ1", + "EQ2", + "EQ3", + "EQ4", + "DRC1L", + "DRC1R", + "DRC2L", + "DRC2R", + "LHPF1", + "LHPF2", + "LHPF3", + "LHPF4", + "Ultrasonic 1", + "Ultrasonic 2", + "DSP1.1", + "DSP1.2", + "DSP1.3", + "DSP1.4", + "DSP1.5", + "DSP1.6", + "DSP1.7", + "DSP1.8", +}; + +static unsigned int cs48l32_mixer_values[] = { + 0x000, /* Silence (mute) */ + 0x004, /* Tone generator 1 */ + 0x005, /* Tone generator 2 */ + 0x00C, /* Noise Generator */ + 0x010, /* IN1L signal path */ + 0x011, /* IN1R signal path */ + 0x012, /* IN2L signal path */ + 0x013, /* IN2R signal path */ + 0x020, /* ASP1 RX1 */ + 0x021, /* ASP1 RX2 */ + 0x022, /* ASP1 RX3 */ + 0x023, /* ASP1 RX4 */ + 0x024, /* ASP1 RX5 */ + 0x025, /* ASP1 RX6 */ + 0x026, /* ASP1 RX7 */ + 0x027, /* ASP1 RX8 */ + 0x030, /* ASP2 RX1 */ + 0x031, /* ASP2 RX2 */ + 0x032, /* ASP2 RX3 */ + 0x033, /* ASP2 RX4 */ + 0x098, /* ISRC1 INT1 */ + 0x099, /* ISRC1 INT2 */ + 0x09a, /* ISRC1 INT3 */ + 0x09b, /* ISRC1 INT4 */ + 0x09C, /* ISRC1 DEC1 */ + 0x09D, /* ISRC1 DEC2 */ + 0x09e, /* ISRC1 DEC3 */ + 0x09f, /* ISRC1 DEC4 */ + 0x0A0, /* ISRC2 INT1 */ + 0x0A1, /* ISRC2 INT2 */ + 0x0A4, /* ISRC2 DEC1 */ + 0x0A5, /* ISRC2 DEC2 */ + 0x0A8, /* ISRC3 INT1 */ + 0x0A9, /* ISRC3 INT2 */ + 0x0AC, /* ISRC3 DEC1 */ + 0x0AD, /* ISRC3 DEC2 */ + 0x0B8, /* EQ1 */ + 0x0B9, /* EQ2 */ + 0x0BA, /* EQ3 */ + 0x0BB, /* EQ4 */ + 0x0C0, /* DRC1 Left */ + 0x0C1, /* DRC1 Right */ + 0x0C2, /* DRC2 Left */ + 0x0C3, /* DRC2 Right */ + 0x0C8, /* LHPF1 */ + 0x0C9, /* LHPF2 */ + 0x0CA, /* LHPF3 */ + 0x0CB, /* LHPF4 */ + 0x0D8, /* Ultrasonic 1 */ + 0x0D9, /* Ultrasonic 2 */ + 0x100, /* DSP1 channel 1 */ + 0x101, /* DSP1 channel 2 */ + 0x102, /* DSP1 channel 3 */ + 0x103, /* DSP1 channel 4 */ + 0x104, /* DSP1 channel 5 */ + 0x105, /* DSP1 channel 6 */ + 0x106, /* DSP1 channel 7 */ + 0x107, /* DSP1 channel 8 */ +}; +static_assert(ARRAY_SIZE(cs48l32_mixer_texts) == ARRAY_SIZE(cs48l32_mixer_values)); +#define CS48L32_NUM_MIXER_INPUTS ARRAY_SIZE(cs48l32_mixer_values) + +static const DECLARE_TLV_DB_SCALE(cs48l32_ana_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(cs48l32_eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(cs48l32_digital_tlv, -6400, 50, 0); +static const DECLARE_TLV_DB_SCALE(cs48l32_noise_tlv, -10800, 600, 0); +static const DECLARE_TLV_DB_SCALE(cs48l32_mixer_tlv, -3200, 100, 0); +static const DECLARE_TLV_DB_SCALE(cs48l32_us_tlv, 0, 600, 0); + +static void cs48l32_spin_sysclk(struct cs48l32_codec *cs48l32_codec) +{ + struct cs48l32 *cs48l32 = &cs48l32_codec->core; + unsigned int val; + int ret, i; + + /* Skip this if the chip is down */ + if (pm_runtime_suspended(cs48l32->dev)) + return; + + /* + * Just read a register a few times to ensure the internal + * oscillator sends out some clocks. + */ + for (i = 0; i < 4; i++) { + ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &val); + if (ret) + dev_err(cs48l32_codec->core.dev, "%s Failed to read register: %d (%d)\n", + __func__, ret, i); + } + + udelay(300); +} + +static const char * const cs48l32_rate_text[] = { + "Sample Rate 1", "Sample Rate 2", "Sample Rate 3", "Sample Rate 4", +}; + +static const unsigned int cs48l32_rate_val[] = { + 0x0, 0x1, 0x2, 0x3, +}; +static_assert(ARRAY_SIZE(cs48l32_rate_val) == ARRAY_SIZE(cs48l32_rate_text)); + +static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + int ret; + + /* Prevent any mixer mux changes while we do this */ + mutex_lock(&cs48l32_codec->rate_lock); + + /* The write must be guarded by a number of SYSCLK cycles */ + cs48l32_spin_sysclk(cs48l32_codec); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + cs48l32_spin_sysclk(cs48l32_codec); + + mutex_unlock(&cs48l32_codec->rate_lock); + + return ret; +} + +static const char * const cs48l32_sample_rate_text[] = { + "12kHz", + "24kHz", + "48kHz", + "96kHz", + "192kHz", + "384kHz", + "768kHz", + "11.025kHz", + "22.05kHz", + "44.1kHz", + "88.2kHz", + "176.4kHz", + "352.8kHz", + "705.6kHz", + "8kHz", + "16kHz", + "32kHz", +}; + +static const unsigned int cs48l32_sample_rate_val[] = { + 0x01, /* 12kHz */ + 0x02, /* 24kHz */ + 0x03, /* 48kHz */ + 0x04, /* 96kHz */ + 0x05, /* 192kHz */ + 0x06, /* 384kHz */ + 0x07, /* 768kHz */ + 0x09, /* 11.025kHz */ + 0x0a, /* 22.05kHz */ + 0x0b, /* 44.1kHz */ + 0x0c, /* 88.2kHz */ + 0x0d, /* 176.4kHz */ + 0x0e, /* 352.8kHz */ + 0x0f, /* 705.6kHz */ + 0x11, /* 8kHz */ + 0x12, /* 16kHz */ + 0x13, /* 32kHz */ +}; +static_assert(ARRAY_SIZE(cs48l32_sample_rate_val) == ARRAY_SIZE(cs48l32_sample_rate_text)); +#define CS48L32_SAMPLE_RATE_ENUM_SIZE ARRAY_SIZE(cs48l32_sample_rate_val) + +static const struct soc_enum cs48l32_sample_rate[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE1, + CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_ENUM_SIZE, + cs48l32_sample_rate_text, + cs48l32_sample_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE2, + CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_ENUM_SIZE, + cs48l32_sample_rate_text, + cs48l32_sample_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE3, + CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_ENUM_SIZE, + cs48l32_sample_rate_text, + cs48l32_sample_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE4, + CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_ENUM_SIZE, + cs48l32_sample_rate_text, + cs48l32_sample_rate_val), +}; + +static int cs48l32_inmux_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + unsigned int mux, src_val, in_type; + int ret; + + mux = ucontrol->value.enumerated.item[0]; + if (mux > 1) + return -EINVAL; + + switch (e->reg) { + case CS48L32_IN1L_CONTROL1: + in_type = cs48l32_codec->in_type[0][mux]; + break; + case CS48L32_IN1R_CONTROL1: + in_type = cs48l32_codec->in_type[1][mux]; + break; + default: + return -EINVAL; + } + + src_val = mux << e->shift_l; + + if (in_type == CS48L32_IN_TYPE_SE) + src_val |= 1 << CS48L32_INx_SRC_SHIFT; + + ret = snd_soc_component_update_bits(dapm->component, + e->reg, + CS48L32_INx_SRC_MASK, + src_val); + if (ret > 0) + snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); + + return ret; +} + +static const char * const cs48l32_inmux_texts[] = { + "Analog 1", "Analog 2", +}; + +static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxl_enum, + CS48L32_IN1L_CONTROL1, + CS48L32_INx_SRC_SHIFT + 1, + cs48l32_inmux_texts); + +static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxr_enum, + CS48L32_IN1R_CONTROL1, + CS48L32_INx_SRC_SHIFT + 1, + cs48l32_inmux_texts); + +static const struct snd_kcontrol_new cs48l32_inmux[] = { + SOC_DAPM_ENUM_EXT("IN1L Mux", cs48l32_in1muxl_enum, + snd_soc_dapm_get_enum_double, cs48l32_inmux_put), + SOC_DAPM_ENUM_EXT("IN1R Mux", cs48l32_in1muxr_enum, + snd_soc_dapm_get_enum_double, cs48l32_inmux_put), +}; + +static const char * const cs48l32_dmode_texts[] = { + "Analog", "Digital", +}; + +static int cs48l32_dmode_put(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 *component = snd_soc_dapm_to_component(dapm); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + unsigned int mode; + int ret, result; + + mode = ucontrol->value.enumerated.item[0]; + switch (mode) { + case 0: + ret = snd_soc_component_update_bits(component, + CS48L32_ADC1L_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + CS48L32_ADC1x_INT_ENA_FRC_MASK); + if (ret < 0) { + dev_err(component->dev, + "Failed to set ADC1L_INT_ENA_FRC: %d\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, + CS48L32_ADC1R_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + CS48L32_ADC1x_INT_ENA_FRC_MASK); + if (ret < 0) { + dev_err(component->dev, + "Failed to set ADC1R_INT_ENA_FRC: %d\n", ret); + return ret; + } + + result = snd_soc_component_update_bits(component, + e->reg, + BIT(CS48L32_IN1_MODE_SHIFT), + 0); + if (result < 0) { + dev_err(component->dev, "Failed to set input mode: %d\n", result); + return result; + } + + usleep_range(200, 300); + + ret = snd_soc_component_update_bits(component, + CS48L32_ADC1L_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + 0); + if (ret < 0) { + dev_err(component->dev, + "Failed to clear ADC1L_INT_ENA_FRC: %d\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, + CS48L32_ADC1R_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + 0); + if (ret < 0) { + dev_err(component->dev, + "Failed to clear ADC1R_INT_ENA_FRC: %d\n", ret); + return ret; + } + + if (result > 0) + snd_soc_dapm_mux_update_power(dapm, kcontrol, mode, e, NULL); + + return result; + case 1: + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + default: + return -EINVAL; + } +} + +static SOC_ENUM_SINGLE_DECL(cs48l32_in1dmode_enum, + CS48L32_INPUT1_CONTROL1, + CS48L32_IN1_MODE_SHIFT, + cs48l32_dmode_texts); + +static const struct snd_kcontrol_new cs48l32_dmode_mux[] = { + SOC_DAPM_ENUM_EXT("IN1 Mode", cs48l32_in1dmode_enum, + snd_soc_dapm_get_enum_double, cs48l32_dmode_put), +}; + +static const char * const cs48l32_in_texts[] = { + "IN1L", "IN1R", "IN2L", "IN2R", +}; +static_assert(ARRAY_SIZE(cs48l32_in_texts) == CS48L32_MAX_INPUT); + +static const char * const cs48l32_us_freq_texts[] = { + "16-24kHz", "20-28kHz", +}; + +static const unsigned int cs48l32_us_freq_val[] = { + 0x2, 0x3, +}; + +static const struct soc_enum cs48l32_us_freq[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL, + CS48L32_US1_FREQ_SHIFT, + CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT, + ARRAY_SIZE(cs48l32_us_freq_val), + cs48l32_us_freq_texts, + cs48l32_us_freq_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL, + CS48L32_US1_FREQ_SHIFT, + CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT, + ARRAY_SIZE(cs48l32_us_freq_val), + cs48l32_us_freq_texts, + cs48l32_us_freq_val), +}; + +static const unsigned int cs48l32_us_in_val[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static const struct soc_enum cs48l32_us_inmux_enum[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL, + CS48L32_US1_SRC_SHIFT, + CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT, + ARRAY_SIZE(cs48l32_us_in_val), + cs48l32_in_texts, + cs48l32_us_in_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL, + CS48L32_US1_SRC_SHIFT, + CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT, + ARRAY_SIZE(cs48l32_us_in_val), + cs48l32_in_texts, + cs48l32_us_in_val), +}; + +static const struct snd_kcontrol_new cs48l32_us_inmux[] = { + SOC_DAPM_ENUM("Ultrasonic 1 Input", cs48l32_us_inmux_enum[0]), + SOC_DAPM_ENUM("Ultrasonic 2 Input", cs48l32_us_inmux_enum[1]), +}; + +static const char * const cs48l32_us_det_thr_texts[] = { + "-6dB", "-9dB", "-12dB", "-15dB", "-18dB", "-21dB", "-24dB", "-27dB", +}; + +static const struct soc_enum cs48l32_us_det_thr[] = { + SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL, + CS48L32_US1_DET_THR_SHIFT, + ARRAY_SIZE(cs48l32_us_det_thr_texts), + cs48l32_us_det_thr_texts), + SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL, + CS48L32_US1_DET_THR_SHIFT, + ARRAY_SIZE(cs48l32_us_det_thr_texts), + cs48l32_us_det_thr_texts), +}; + +static const char * const cs48l32_us_det_num_texts[] = { + "1 Sample", + "2 Samples", + "4 Samples", + "8 Samples", + "16 Samples", + "32 Samples", + "64 Samples", + "128 Samples", + "256 Samples", + "512 Samples", + "1024 Samples", + "2048 Samples", + "4096 Samples", + "8192 Samples", + "16384 Samples", + "32768 Samples", +}; + +static const struct soc_enum cs48l32_us_det_num[] = { + SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL, + CS48L32_US1_DET_NUM_SHIFT, + ARRAY_SIZE(cs48l32_us_det_num_texts), + cs48l32_us_det_num_texts), + SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL, + CS48L32_US1_DET_NUM_SHIFT, + ARRAY_SIZE(cs48l32_us_det_num_texts), + cs48l32_us_det_num_texts), +}; + +static const char * const cs48l32_us_det_hold_texts[] = { + "0 Samples", + "31 Samples", + "63 Samples", + "127 Samples", + "255 Samples", + "511 Samples", + "1023 Samples", + "2047 Samples", + "4095 Samples", + "8191 Samples", + "16383 Samples", + "32767 Samples", + "65535 Samples", + "131071 Samples", + "262143 Samples", + "524287 Samples", +}; + +static const struct soc_enum cs48l32_us_det_hold[] = { + SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL, + CS48L32_US1_DET_HOLD_SHIFT, + ARRAY_SIZE(cs48l32_us_det_hold_texts), + cs48l32_us_det_hold_texts), + SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL, + CS48L32_US1_DET_HOLD_SHIFT, + ARRAY_SIZE(cs48l32_us_det_hold_texts), + cs48l32_us_det_hold_texts), +}; + +static const struct soc_enum cs48l32_us_output_rate[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL, + CS48L32_US1_RATE_SHIFT, + CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL, + CS48L32_US1_RATE_SHIFT, + CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), +}; + +static const char * const cs48l32_us_det_lpf_cut_texts[] = { + "1722Hz", "833Hz", "408Hz", "203Hz", +}; + +static const struct soc_enum cs48l32_us_det_lpf_cut[] = { + SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL, + CS48L32_US1_DET_LPF_CUT_SHIFT, + ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts), + cs48l32_us_det_lpf_cut_texts), + SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL, + CS48L32_US1_DET_LPF_CUT_SHIFT, + ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts), + cs48l32_us_det_lpf_cut_texts), +}; + +static const char * const cs48l32_us_det_dcy_texts[] = { + "0 ms", "0.79 ms", "1.58 ms", "3.16 ms", "6.33 ms", "12.67 ms", "25.34 ms", "50.69 ms", +}; + +static const struct soc_enum cs48l32_us_det_dcy[] = { + SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL, + CS48L32_US1_DET_DCY_SHIFT, + ARRAY_SIZE(cs48l32_us_det_dcy_texts), + cs48l32_us_det_dcy_texts), + SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL, + CS48L32_US1_DET_DCY_SHIFT, + ARRAY_SIZE(cs48l32_us_det_dcy_texts), + cs48l32_us_det_dcy_texts), +}; + +static const struct snd_kcontrol_new cs48l32_us_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const char * const cs48l32_vol_ramp_text[] = { + "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", "16ms/6dB", "32ms/6dB", +}; + +static SOC_ENUM_SINGLE_DECL(cs48l32_in_vd_ramp, + CS48L32_INPUT_VOL_CONTROL, + CS48L32_IN_VD_RAMP_SHIFT, + cs48l32_vol_ramp_text); + +static SOC_ENUM_SINGLE_DECL(cs48l32_in_vi_ramp, + CS48L32_INPUT_VOL_CONTROL, + CS48L32_IN_VI_RAMP_SHIFT, + cs48l32_vol_ramp_text); + +static const char * const cs48l32_in_hpf_cut_text[] = { + "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz" +}; + +static SOC_ENUM_SINGLE_DECL(cs48l32_in_hpf_cut_enum, + CS48L32_INPUT_HPF_CONTROL, + CS48L32_IN_HPF_CUT_SHIFT, + cs48l32_in_hpf_cut_text); + +static const char * const cs48l32_in_dmic_osr_text[] = { + "384kHz", "768kHz", "1.536MHz", "2.048MHz", "2.4576MHz", "3.072MHz", "6.144MHz", +}; + +static const struct soc_enum cs48l32_in_dmic_osr[] = { + SOC_ENUM_SINGLE(CS48L32_INPUT1_CONTROL1, + CS48L32_IN1_OSR_SHIFT, + ARRAY_SIZE(cs48l32_in_dmic_osr_text), + cs48l32_in_dmic_osr_text), + SOC_ENUM_SINGLE(CS48L32_INPUT2_CONTROL1, + CS48L32_IN1_OSR_SHIFT, + ARRAY_SIZE(cs48l32_in_dmic_osr_text), + cs48l32_in_dmic_osr_text), +}; + +static bool cs48l32_is_input_enabled(struct snd_soc_component *component, + unsigned int reg) +{ + unsigned int input_active; + + input_active = snd_soc_component_read(component, CS48L32_INPUT_CONTROL); + switch (reg) { + case CS48L32_IN1L_CONTROL1: + return input_active & BIT(CS48L32_IN1L_EN_SHIFT); + case CS48L32_IN1R_CONTROL1: + return input_active & BIT(CS48L32_IN1R_EN_SHIFT); + case CS48L32_IN2L_CONTROL1: + return input_active & BIT(CS48L32_IN2L_EN_SHIFT); + case CS48L32_IN2R_CONTROL1: + return input_active & BIT(CS48L32_IN2R_EN_SHIFT); + default: + return false; + } +} + +static int cs48l32_in_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int ret; + + snd_soc_dapm_mutex_lock(dapm); + + /* Cannot change rate on an active input */ + if (cs48l32_is_input_enabled(component, e->reg)) { + ret = -EBUSY; + goto exit; + } + + ret = snd_soc_put_enum_double(kcontrol, ucontrol); +exit: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static const struct soc_enum cs48l32_input_rate[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_IN1L_CONTROL1, + CS48L32_INx_RATE_SHIFT, + CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_IN1R_CONTROL1, + CS48L32_INx_RATE_SHIFT, + CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_IN2L_CONTROL1, + CS48L32_INx_RATE_SHIFT, + CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_IN2R_CONTROL1, + CS48L32_INx_RATE_SHIFT, + CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), +}; + +static int cs48l32_low_power_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + + /* Cannot change rate on an active input */ + if (cs48l32_is_input_enabled(component, mc->reg)) { + ret = -EBUSY; + goto exit; + } + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + +exit: + snd_soc_dapm_mutex_unlock(dapm); + return ret; +} + +static const struct soc_enum noise_gen_rate = + SOC_VALUE_ENUM_SINGLE(CS48L32_COMFORT_NOISE_GENERATOR, + CS48L32_NOISE_GEN_RATE_SHIFT, + CS48L32_NOISE_GEN_RATE_MASK >> CS48L32_NOISE_GEN_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val); + +static const char * const cs48l32_auxpdm_freq_texts[] = { + "3.072MHz", "2.048MHz", "1.536MHz", "768kHz", +}; + +static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_freq, + CS48L32_AUXPDM1_CONTROL1, + CS48L32_AUXPDM1_FREQ_SHIFT, + cs48l32_auxpdm_freq_texts); + +static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_freq, + CS48L32_AUXPDM2_CONTROL1, + CS48L32_AUXPDM1_FREQ_SHIFT, + cs48l32_auxpdm_freq_texts); + +static const char * const cs48l32_auxpdm_src_texts[] = { + "Analog", "IN1 Digital", "IN2 Digital", +}; + +static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_in, + CS48L32_AUXPDM_CTRL2, + CS48L32_AUXPDMDAT1_SRC_SHIFT, + cs48l32_auxpdm_src_texts); + +static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_in, + CS48L32_AUXPDM_CTRL2, + CS48L32_AUXPDMDAT2_SRC_SHIFT, + cs48l32_auxpdm_src_texts); + +static const struct snd_kcontrol_new cs48l32_auxpdm_inmux[] = { + SOC_DAPM_ENUM("AUXPDM1 Input", cs48l32_auxpdm1_in), + SOC_DAPM_ENUM("AUXPDM2 Input", cs48l32_auxpdm2_in), +}; + +static const unsigned int cs48l32_auxpdm_analog_in_val[] = { + 0x0, 0x1, +}; + +static const struct soc_enum cs48l32_auxpdm_analog_inmux_enum[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM1_CONTROL1, + CS48L32_AUXPDM1_SRC_SHIFT, + CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT, + ARRAY_SIZE(cs48l32_auxpdm_analog_in_val), + cs48l32_in_texts, + cs48l32_auxpdm_analog_in_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM2_CONTROL1, + CS48L32_AUXPDM1_SRC_SHIFT, + CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT, + ARRAY_SIZE(cs48l32_auxpdm_analog_in_val), + cs48l32_in_texts, + cs48l32_auxpdm_analog_in_val), +}; + +static const struct snd_kcontrol_new cs48l32_auxpdm_analog_inmux[] = { + SOC_DAPM_ENUM("AUXPDM1 Analog Input", cs48l32_auxpdm_analog_inmux_enum[0]), + SOC_DAPM_ENUM("AUXPDM2 Analog Input", cs48l32_auxpdm_analog_inmux_enum[1]), +}; + +static const struct snd_kcontrol_new cs48l32_auxpdm_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct soc_enum cs48l32_isrc_fsh[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1, + CS48L32_ISRC1_FSH_SHIFT, + CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1, + CS48L32_ISRC1_FSH_SHIFT, + CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1, + CS48L32_ISRC1_FSH_SHIFT, + CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), +}; + +static const struct soc_enum cs48l32_isrc_fsl[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1, + CS48L32_ISRC1_FSL_SHIFT, + CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1, + CS48L32_ISRC1_FSL_SHIFT, + CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1, + CS48L32_ISRC1_FSL_SHIFT, + CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), +}; + +static const struct soc_enum cs48l32_fx_rate = + SOC_VALUE_ENUM_SINGLE(CS48L32_FX_SAMPLE_RATE, + CS48L32_FX_RATE_SHIFT, + CS48L32_FX_RATE_MASK >> CS48L32_FX_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val); + +static const char * const cs48l32_lhpf_mode_text[] = { + "Low-pass", "High-pass" +}; + +static const struct soc_enum cs48l32_lhpf_mode[] = { + SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 0, + ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text), + SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 1, + ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text), + SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 2, + ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text), + SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 3, + ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text), +}; + +static int cs48l32_lhpf_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + __be32 *data = (__be32 *)ucontrol->value.bytes.data; + s16 val = (s16)be32_to_cpu(*data); + + if (abs(val) > CS48L32_LHPF_MAX_COEFF) { + dev_err(cs48l32_codec->core.dev, "Rejecting unstable LHPF coefficients\n"); + return -EINVAL; + } + + return snd_soc_bytes_put(kcontrol, ucontrol); +} + +static const char * const cs48l32_eq_mode_text[] = { + "Low-pass", "High-pass", +}; + +static const struct soc_enum cs48l32_eq_mode[] = { + SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 0, + ARRAY_SIZE(cs48l32_eq_mode_text), + cs48l32_eq_mode_text), + SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 1, + ARRAY_SIZE(cs48l32_eq_mode_text), + cs48l32_eq_mode_text), + SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 2, + ARRAY_SIZE(cs48l32_eq_mode_text), + cs48l32_eq_mode_text), + SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 3, + ARRAY_SIZE(cs48l32_eq_mode_text), + cs48l32_eq_mode_text), +}; + +static int cs48l32_eq_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + unsigned int item; + + item = snd_soc_enum_val_to_item(e, cs48l32_codec->eq_mode[e->shift_l]); + ucontrol->value.enumerated.item[0] = item; + + return 0; +} + +static int cs48l32_eq_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs48l32_codec *cs48l32_codec = 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; + bool changed = false; + + if (item[0] >= e->items) + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]); + + snd_soc_dapm_mutex_lock(dapm); + if (cs48l32_codec->eq_mode[e->shift_l] != val) { + cs48l32_codec->eq_mode[e->shift_l] = val; + changed = true; + } + snd_soc_dapm_mutex_unlock(dapm); + + return changed; +} + +static int cs48l32_eq_coeff_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct cs48l32_eq_control *ctl = (void *) kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = ctl->max; + + return 0; +} + +static int cs48l32_eq_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct cs48l32_eq_control *params = (void *)kcontrol->private_value; + __be16 *coeffs; + unsigned int coeff_idx; + int block_idx; + + block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1); + block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1); + + coeffs = &cs48l32_codec->eq_coefficients[block_idx][0]; + coeff_idx = (params->reg - params->block_base) / 2; + + /* High __be16 is in [coeff_idx] and low __be16 in [coeff_idx + 1] */ + if (params->shift == 0) + coeff_idx++; + + ucontrol->value.integer.value[0] = be16_to_cpu(coeffs[coeff_idx]); + + return 0; +} + +static int cs48l32_eq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct cs48l32_eq_control *params = (void *)kcontrol->private_value; + __be16 *coeffs; + unsigned int coeff_idx; + int block_idx; + + block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1); + block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1); + + coeffs = &cs48l32_codec->eq_coefficients[block_idx][0]; + coeff_idx = (params->reg - params->block_base) / 2; + + /* Put high __be16 in [coeff_idx] and low __be16 in [coeff_idx + 1] */ + if (params->shift == 0) + coeff_idx++; + + snd_soc_dapm_mutex_lock(dapm); + coeffs[coeff_idx] = cpu_to_be16(ucontrol->value.integer.value[0]); + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + +static const struct snd_kcontrol_new cs48l32_drc_activity_output_mux[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new cs48l32_dsp_trigger_output_mux[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static int cs48l32_dsp_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + unsigned int cached_rate; + const unsigned int rate_num = e->mask; + int item; + + if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates)) + return -EINVAL; + + cached_rate = cs48l32_codec->dsp_dma_rates[rate_num]; + item = snd_soc_enum_val_to_item(e, cached_rate); + ucontrol->value.enumerated.item[0] = item; + + return 0; +} + +static int cs48l32_dsp_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + const unsigned int rate_num = e->mask; + const unsigned int item = ucontrol->value.enumerated.item[0]; + unsigned int val; + bool changed = false; + + if (item >= e->items) + return -EINVAL; + + if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates)) + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item); + + snd_soc_dapm_mutex_lock(dapm); + if (cs48l32_codec->dsp_dma_rates[rate_num] != val) { + cs48l32_codec->dsp_dma_rates[rate_num] = val; + changed = true; + } + snd_soc_dapm_mutex_unlock(dapm); + + return changed; +} + +static const struct soc_enum cs48l32_dsp_rate_enum[] = { + /* RX rates */ + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 0, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 1, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 2, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 3, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 4, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 5, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 6, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 7, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + /* TX rates */ + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 8, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 9, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 10, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 11, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 12, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 13, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 14, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 15, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), +}; + +static int cs48l32_dsp_pre_run(struct wm_adsp *dsp) +{ + struct cs48l32_codec *cs48l32_codec = container_of(dsp, struct cs48l32_codec, dsp); + unsigned int reg; + const u8 *rate = cs48l32_codec->dsp_dma_rates; + int i; + + reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_RX1; + for (i = 0; i < CS48L32_DSP_N_RX_CHANNELS; ++i) { + regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate); + reg += 8; + rate++; + } + + reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_TX1; + for (i = 0; i < CS48L32_DSP_N_TX_CHANNELS; ++i) { + regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate); + reg += 8; + rate++; + } + + usleep_range(300, 600); + + return 0; +} + +static void cs48l32_dsp_memory_disable(struct cs48l32_codec *cs48l32_codec, + const struct cs48l32_dsp_power_regs *regs) +{ + struct regmap *regmap = cs48l32_codec->core.regmap; + int i, j, ret; + + for (i = 0; i < regs->n_pwd; ++i) { + ret = regmap_write(regmap, regs->pwd[i], 0); + if (ret) + goto err; + } + + for (i = 0; i < regs->n_ext; ++i) { + for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) { + ret = regmap_write(regmap, j, 0); + if (ret) + goto err; + } + } + + return; + +err: + dev_warn(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret); +} + +static int cs48l32_dsp_memory_enable(struct cs48l32_codec *cs48l32_codec, + const struct cs48l32_dsp_power_regs *regs) +{ + struct regmap *regmap = cs48l32_codec->core.regmap; + int i, j, ret; + + /* disable power-off */ + for (i = 0; i < regs->n_ext; ++i) { + for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) { + ret = regmap_write(regmap, j, 0x3); + if (ret) + goto err; + } + } + + /* power-up the banks in sequence */ + for (i = 0; i < regs->n_pwd; ++i) { + ret = regmap_write(regmap, regs->pwd[i], 0x1); + if (ret) + goto err; + + udelay(1); /* allow bank to power-up */ + + ret = regmap_write(regmap, regs->pwd[i], 0x3); + if (ret) + goto err; + + udelay(1); /* allow bank to power-up */ + } + + return 0; + +err: + dev_err(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret); + cs48l32_dsp_memory_disable(cs48l32_codec, regs); + + return ret; +} + +static int cs48l32_dsp_freq_update(struct snd_soc_dapm_widget *w, unsigned int freq_reg, + unsigned int freqsel_reg) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs48l32_codec->core.regmap; + struct wm_adsp *dsp = &cs48l32_codec->dsp; + int ret; + unsigned int freq, freq_sel, freq_sts; + + if (!freq_reg) + return -EINVAL; + + ret = regmap_read(regmap, freq_reg, &freq); + if (ret) { + dev_err(component->dev, "Failed to read #%x: %d\n", freq_reg, ret); + return ret; + } + + if (freqsel_reg) { + freq_sts = (freq & CS48L32_SYSCLK_FREQ_STS_MASK) >> CS48L32_SYSCLK_FREQ_STS_SHIFT; + + ret = regmap_read(regmap, freqsel_reg, &freq_sel); + if (ret) { + dev_err(component->dev, "Failed to read #%x: %d\n", freqsel_reg, ret); + return ret; + } + freq_sel = (freq_sel & CS48L32_SYSCLK_FREQ_MASK) >> CS48L32_SYSCLK_FREQ_SHIFT; + + if (freq_sts != freq_sel) { + dev_err(component->dev, "SYSCLK FREQ (#%x) != FREQ STS (#%x)\n", + freq_sel, freq_sts); + return -ETIMEDOUT; + } + } + + freq &= CS48L32_DSP_CLK_FREQ_MASK; + freq >>= CS48L32_DSP_CLK_FREQ_SHIFT; + + ret = regmap_write(dsp->cs_dsp.regmap, + dsp->cs_dsp.base + CS48L32_DSP_CLOCK_FREQ_OFFS, freq); + if (ret) { + dev_err(component->dev, "Failed to set HALO clock freq: %d\n", ret); + return ret; + } + + return 0; +} + +static int cs48l32_dsp_freq_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + return cs48l32_dsp_freq_update(w, CS48L32_SYSTEM_CLOCK2, CS48L32_SYSTEM_CLOCK1); + default: + return 0; + } +} + +static irqreturn_t cs48l32_irq(int irq, void *data) +{ + static const unsigned int eint1_regs[] = { + CS48L32_IRQ1_EINT_9, CS48L32_IRQ1_MASK_9, + CS48L32_IRQ1_EINT_7, CS48L32_IRQ1_MASK_7 + }; + u32 reg_vals[4]; + struct cs48l32_codec *cs48l32_codec = data; + struct regmap *regmap = cs48l32_codec->core.regmap; + irqreturn_t result = IRQ_NONE; + unsigned int eint_pending; + int i, ret; + + static_assert(ARRAY_SIZE(eint1_regs) == ARRAY_SIZE(reg_vals)); + + ret = pm_runtime_resume_and_get(cs48l32_codec->core.dev); + if (ret) { + dev_warn(cs48l32_codec->core.dev, "irq could not get pm runtime: %d\n", ret); + return IRQ_NONE; + } + + ret = regmap_read(regmap, CS48L32_IRQ1_STATUS, &eint_pending); + if (ret) { + dev_warn(cs48l32_codec->core.dev, "Read IRQ1_STATUS failed: %d\n", ret); + return IRQ_NONE; + } + if ((eint_pending & CS48L32_IRQ1_STS_MASK) == 0) + goto out; + + ret = regmap_multi_reg_read(regmap, eint1_regs, reg_vals, ARRAY_SIZE(reg_vals)); + if (ret) { + dev_warn(cs48l32_codec->core.dev, "Read IRQ regs failed: %d\n", ret); + return IRQ_NONE; + } + + for (i = 0; i < ARRAY_SIZE(reg_vals); i += 2) { + reg_vals[i] &= ~reg_vals[i + 1]; + regmap_write(regmap, eint1_regs[i], reg_vals[i]); + } + + if (reg_vals[0] & CS48L32_DSP1_IRQ0_EINT1_MASK) + wm_adsp_compr_handle_irq(&cs48l32_codec->dsp); + + if (reg_vals[2] & CS48L32_DSP1_MPU_ERR_EINT1_MASK) { + dev_warn(cs48l32_codec->core.dev, "MPU err IRQ\n"); + wm_halo_bus_error(irq, &cs48l32_codec->dsp); + } + + if (reg_vals[2] & CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK) { + dev_warn(cs48l32_codec->core.dev, "WDT expire IRQ\n"); + wm_halo_wdt_expire(irq, &cs48l32_codec->dsp); + } + + result = IRQ_HANDLED; + +out: + pm_runtime_mark_last_busy(cs48l32_codec->core.dev); + pm_runtime_put_autosuspend(cs48l32_codec->core.dev); + + return result; +} + +static int cs48l32_get_dspclk_setting(struct cs48l32_codec *cs48l32_codec, unsigned int freq, + int src, unsigned int *val) +{ + freq /= 15625; /* convert to 1/64ths of 1MHz */ + *val |= freq << CS48L32_DSP_CLK_FREQ_SHIFT; + + return 0; +} + +static int cs48l32_get_sysclk_setting(unsigned int freq) +{ + switch (freq) { + case 0: + case 5644800: + case 6144000: + return CS48L32_SYSCLK_RATE_6MHZ; + case 11289600: + case 12288000: + return CS48L32_SYSCLK_RATE_12MHZ << CS48L32_SYSCLK_FREQ_SHIFT; + case 22579200: + case 24576000: + return CS48L32_SYSCLK_RATE_24MHZ << CS48L32_SYSCLK_FREQ_SHIFT; + case 45158400: + case 49152000: + return CS48L32_SYSCLK_RATE_49MHZ << CS48L32_SYSCLK_FREQ_SHIFT; + case 90316800: + case 98304000: + return CS48L32_SYSCLK_RATE_98MHZ << CS48L32_SYSCLK_FREQ_SHIFT; + default: + return -EINVAL; + } +} + +static int cs48l32_set_pdm_fllclk(struct snd_soc_component *component, int source) +{ + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs48l32_codec->core.regmap; + unsigned int val; + + switch (source) { + case CS48L32_PDMCLK_SRC_IN1_PDMCLK: + case CS48L32_PDMCLK_SRC_IN2_PDMCLK: + case CS48L32_PDMCLK_SRC_IN3_PDMCLK: + case CS48L32_PDMCLK_SRC_IN4_PDMCLK: + case CS48L32_PDMCLK_SRC_AUXPDM1_CLK: + case CS48L32_PDMCLK_SRC_AUXPDM2_CLK: + val = source << CS48L32_PDM_FLLCLK_SRC_SHIFT; + break; + default: + dev_err(cs48l32_codec->core.dev, "Invalid PDM FLLCLK src %d\n", source); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS48L32_INPUT_CONTROL2, + CS48L32_PDM_FLLCLK_SRC_MASK, val); +} + +static int cs48l32_set_sysclk(struct snd_soc_component *component, int clk_id, int source, + unsigned int freq, int dir) +{ + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs48l32_codec->core.regmap; + char *name; + unsigned int reg; + unsigned int mask = CS48L32_SYSCLK_SRC_MASK; + unsigned int val = source << CS48L32_SYSCLK_SRC_SHIFT; + int clk_freq_sel, *clk; + + switch (clk_id) { + case CS48L32_CLK_SYSCLK_1: + name = "SYSCLK"; + reg = CS48L32_SYSTEM_CLOCK1; + clk = &cs48l32_codec->sysclk; + clk_freq_sel = cs48l32_get_sysclk_setting(freq); + mask |= CS48L32_SYSCLK_FREQ_MASK | CS48L32_SYSCLK_FRAC_MASK; + break; + case CS48L32_CLK_DSPCLK: + name = "DSPCLK"; + reg = CS48L32_DSP_CLOCK1; + clk = &cs48l32_codec->dspclk; + clk_freq_sel = cs48l32_get_dspclk_setting(cs48l32_codec, freq, source, &val); + mask |= CS48L32_DSP_CLK_FREQ_MASK; + break; + case CS48L32_CLK_PDM_FLLCLK: + return cs48l32_set_pdm_fllclk(component, source); + default: + return -EINVAL; + } + + if (clk_freq_sel < 0) { + dev_err(cs48l32_codec->core.dev, "Failed to get %s setting for %dHZ\n", name, freq); + return clk_freq_sel; + } + + *clk = freq; + + if (freq == 0) { + dev_dbg(cs48l32_codec->core.dev, "%s cleared\n", name); + return 0; + } + + val |= clk_freq_sel; + + if (freq % 6144000) + val |= CS48L32_SYSCLK_FRAC_MASK; + + dev_dbg(cs48l32_codec->core.dev, "%s set to %uHz", name, freq); + + return regmap_update_bits(regmap, reg, mask, val); +} + +static int cs48l32_is_enabled_fll(struct cs48l32_fll *fll, int base) +{ + struct regmap *regmap = fll->codec->core.regmap; + unsigned int reg; + int ret; + + ret = regmap_read(regmap, base + CS48L32_FLL_CONTROL1_OFFS, ®); + if (ret != 0) { + cs48l32_fll_err(fll, "Failed to read current state: %d\n", ret); + return ret; + } + + return reg & CS48L32_FLL_EN_MASK; +} + +static int cs48l32_wait_for_fll(struct cs48l32_fll *fll, bool requested) +{ + struct regmap *regmap = fll->codec->core.regmap; + unsigned int val = 0; + int i; + + cs48l32_fll_dbg(fll, "Waiting for FLL...\n"); + + for (i = 0; i < 30; i++) { + regmap_read(regmap, fll->sts_addr, &val); + if (!!(val & fll->sts_mask) == requested) + return 0; + + switch (i) { + case 0 ... 5: + usleep_range(75, 125); + break; + case 6 ... 20: + usleep_range(750, 1250); + break; + default: + fsleep(20000); + break; + } + } + + cs48l32_fll_warn(fll, "Timed out waiting for %s\n", requested ? "lock" : "unlock"); + + return -ETIMEDOUT; +} + +static int cs48l32_fllhj_disable(struct cs48l32_fll *fll) +{ + struct cs48l32 *cs48l32 = &fll->codec->core; + bool change; + + cs48l32_fll_dbg(fll, "Disabling FLL\n"); + + /* + * Disable lockdet, but don't set ctrl_upd update bit. This allows the + * lock status bit to clear as normal, but should the FLL be enabled + * again due to a control clock being required, the lock won't re-assert + * as the FLL config registers are automatically applied when the FLL + * enables. + */ + regmap_set_bits(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_HOLD_MASK); + regmap_clear_bits(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL2_OFFS, + CS48L32_FLL_LOCKDET_MASK); + regmap_set_bits(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL5_OFFS, + CS48L32_FLL_FRC_INTEG_UPD_MASK); + regmap_update_bits_check(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_EN_MASK, + 0, + &change); + + cs48l32_wait_for_fll(fll, false); + + /* + * ctrl_up gates the writes to all the fll's registers, setting it to 0 + * here ensures that after a runtime suspend/resume cycle when one + * enables the fll then ctrl_up is the last bit that is configured + * by the fll enable code rather than the cache sync operation which + * would have updated it much earlier before writing out all fll + * registers + */ + regmap_clear_bits(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_CTRL_UPD_MASK); + + if (change) + pm_runtime_put_autosuspend(cs48l32->dev); + + return 0; +} + +static int cs48l32_fllhj_apply(struct cs48l32_fll *fll, int fin) +{ + struct regmap *regmap = fll->codec->core.regmap; + int refdiv, fref, fout, lockdet_thr, fbdiv, fllgcd; + bool frac = false; + unsigned int fll_n, min_n, max_n, ratio, theta, lambda, hp; + unsigned int gains, num; + + cs48l32_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout); + + for (refdiv = 0; refdiv < 4; refdiv++) { + if ((fin / (1 << refdiv)) <= CS48L32_FLLHJ_MAX_THRESH) + break; + } + + fref = fin / (1 << refdiv); + fout = fll->fout; + frac = fout % fref; + + /* + * Use simple heuristic approach to find a configuration that + * should work for most input clocks. + */ + if (fref < CS48L32_FLLHJ_LOW_THRESH) { + lockdet_thr = 2; + gains = CS48L32_FLLHJ_LOW_GAINS; + + if (frac) + fbdiv = 256; + else + fbdiv = 4; + } else if (fref < CS48L32_FLLHJ_MID_THRESH) { + lockdet_thr = 8; + gains = CS48L32_FLLHJ_MID_GAINS; + fbdiv = (frac) ? 16 : 2; + } else { + lockdet_thr = 8; + gains = CS48L32_FLLHJ_HIGH_GAINS; + fbdiv = 1; + } + /* Use high performance mode for fractional configurations. */ + if (frac) { + hp = 3; + min_n = CS48L32_FLLHJ_FRAC_MIN_N; + max_n = CS48L32_FLLHJ_FRAC_MAX_N; + } else { + if (fref < CS48L32_FLLHJ_LP_INT_MODE_THRESH) + hp = 0; + else + hp = 1; + + min_n = CS48L32_FLLHJ_INT_MIN_N; + max_n = CS48L32_FLLHJ_INT_MAX_N; + } + + ratio = fout / fref; + + cs48l32_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n", refdiv, fref, frac); + + while (ratio / fbdiv < min_n) { + fbdiv /= 2; + if (fbdiv < min_n) { + cs48l32_fll_err(fll, "FBDIV (%u) < minimum N (%u)\n", fbdiv, min_n); + return -EINVAL; + } + } + while (frac && (ratio / fbdiv > max_n)) { + fbdiv *= 2; + if (fbdiv >= 1024) { + cs48l32_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv); + return -EINVAL; + } + } + + cs48l32_fll_dbg(fll, "lockdet=%d, hp=#%x, fbdiv:%d\n", lockdet_thr, hp, fbdiv); + + /* Calculate N.K values */ + fllgcd = gcd(fout, fbdiv * fref); + num = fout / fllgcd; + lambda = (fref * fbdiv) / fllgcd; + fll_n = num / lambda; + theta = num % lambda; + + cs48l32_fll_dbg(fll, "fll_n=%d, gcd=%d, theta=%d, lambda=%d\n", + fll_n, fllgcd, theta, lambda); + + /* Some sanity checks before any registers are written. */ + if (fll_n < min_n || fll_n > max_n) { + cs48l32_fll_err(fll, "N not in valid %s mode range %d-%d: %d\n", + frac ? "fractional" : "integer", min_n, max_n, fll_n); + return -EINVAL; + } + if (fbdiv < 1 || (frac && fbdiv >= 1024) || (!frac && fbdiv >= 256)) { + cs48l32_fll_err(fll, "Invalid fbdiv for %s mode (%u)\n", + frac ? "fractional" : "integer", fbdiv); + return -EINVAL; + } + + /* clear the ctrl_upd bit to guarantee we write to it later. */ + regmap_update_bits(regmap, + fll->base + CS48L32_FLL_CONTROL2_OFFS, + CS48L32_FLL_LOCKDET_THR_MASK | + CS48L32_FLL_PHASEDET_MASK | + CS48L32_FLL_REFCLK_DIV_MASK | + CS48L32_FLL_N_MASK | + CS48L32_FLL_CTRL_UPD_MASK, + (lockdet_thr << CS48L32_FLL_LOCKDET_THR_SHIFT) | + (1 << CS48L32_FLL_PHASEDET_SHIFT) | + (refdiv << CS48L32_FLL_REFCLK_DIV_SHIFT) | + (fll_n << CS48L32_FLL_N_SHIFT)); + + regmap_update_bits(regmap, + fll->base + CS48L32_FLL_CONTROL3_OFFS, + CS48L32_FLL_LAMBDA_MASK | + CS48L32_FLL_THETA_MASK, + (lambda << CS48L32_FLL_LAMBDA_SHIFT) | + (theta << CS48L32_FLL_THETA_SHIFT)); + + regmap_update_bits(regmap, + fll->base + CS48L32_FLL_CONTROL4_OFFS, + (0xffff << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) | + CS48L32_FLL_HP_MASK | + CS48L32_FLL_FB_DIV_MASK, + (gains << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) | + (hp << CS48L32_FLL_HP_SHIFT) | + (fbdiv << CS48L32_FLL_FB_DIV_SHIFT)); + + return 0; +} + +static int cs48l32_fllhj_enable(struct cs48l32_fll *fll) +{ + struct cs48l32 *cs48l32 = &fll->codec->core; + int already_enabled = cs48l32_is_enabled_fll(fll, fll->base); + int ret; + + if (already_enabled < 0) + return already_enabled; + + if (!already_enabled) + pm_runtime_get_sync(cs48l32->dev); + + cs48l32_fll_dbg(fll, "Enabling FLL, initially %s\n", + str_enabled_disabled(already_enabled)); + + /* FLLn_HOLD must be set before configuring any registers */ + regmap_set_bits(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_HOLD_MASK); + + /* Apply refclk */ + ret = cs48l32_fllhj_apply(fll, fll->ref_freq); + if (ret) { + cs48l32_fll_err(fll, "Failed to set FLL: %d\n", ret); + goto out; + } + regmap_update_bits(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL2_OFFS, + CS48L32_FLL_REFCLK_SRC_MASK, + fll->ref_src << CS48L32_FLL_REFCLK_SRC_SHIFT); + + regmap_set_bits(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_EN_MASK); + +out: + regmap_set_bits(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL2_OFFS, + CS48L32_FLL_LOCKDET_MASK); + + regmap_set_bits(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_CTRL_UPD_MASK); + + /* Release the hold so that flln locks to external frequency */ + regmap_clear_bits(cs48l32->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_HOLD_MASK); + + if (!already_enabled) + cs48l32_wait_for_fll(fll, true); + + return 0; +} + +static int cs48l32_fllhj_validate(struct cs48l32_fll *fll, + unsigned int ref_in, + unsigned int fout) +{ + if (fout && !ref_in) { + cs48l32_fll_err(fll, "fllout set without valid input clk\n"); + return -EINVAL; + } + + if (fll->fout && fout != fll->fout) { + cs48l32_fll_err(fll, "Can't change output on active FLL\n"); + return -EINVAL; + } + + if (ref_in / CS48L32_FLL_MAX_REFDIV > CS48L32_FLLHJ_MAX_THRESH) { + cs48l32_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in); + return -EINVAL; + } + + if (fout > CS48L32_FLL_MAX_FOUT) { + cs48l32_fll_err(fll, "Fout=%dMHz exceeds maximum %dMHz\n", + fout, CS48L32_FLL_MAX_FOUT); + return -EINVAL; + } + + return 0; +} + +static int cs48l32_fllhj_set_refclk(struct cs48l32_fll *fll, int source, + unsigned int fin, unsigned int fout) +{ + int ret = 0; + + if (fll->ref_src == source && fll->ref_freq == fin && fll->fout == fout) + return 0; + + if (fin && fout && cs48l32_fllhj_validate(fll, fin, fout)) + return -EINVAL; + + fll->ref_src = source; + fll->ref_freq = fin; + fll->fout = fout; + + if (fout) + ret = cs48l32_fllhj_enable(fll); + else + cs48l32_fllhj_disable(fll); + + return ret; +} + +static int cs48l32_init_fll(struct cs48l32_fll *fll) +{ + fll->ref_src = CS48L32_FLL_SRC_NONE; + + return 0; +} + +static int cs48l32_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case CS48L32_FLL1_REFCLK: + break; + default: + return -EINVAL; + } + + return cs48l32_fllhj_set_refclk(&cs48l32_codec->fll, source, fref, fout); +} + +static int cs48l32_asp_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs48l32_codec->core.regmap; + unsigned int pin_reg, last_pin_reg, hiz_reg; + + switch (dai->id) { + case 1: + pin_reg = CS48L32_GPIO3_CTRL1; + hiz_reg = CS48L32_ASP1_CONTROL3; + break; + case 2: + pin_reg = CS48L32_GPIO7_CTRL1; + hiz_reg = CS48L32_ASP2_CONTROL3; + break; + default: + return -EINVAL; + } + + for (last_pin_reg = pin_reg + 12; pin_reg <= last_pin_reg; ++pin_reg) + regmap_clear_bits(regmap, pin_reg, CS48L32_GPIOX_CTRL1_FN_MASK); + + /* DOUT high-impendance when not transmitting */ + regmap_set_bits(regmap, hiz_reg, CS48L32_ASP_DOUT_HIZ_MASK); + + return 0; +} + +static int cs48l32_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs48l32_codec->core.regmap; + unsigned int val = 0U; + unsigned int base = dai->driver->base; + unsigned int mask = CS48L32_ASP_FMT_MASK | CS48L32_ASP_BCLK_INV_MASK | + CS48L32_ASP_BCLK_MSTR_MASK | + CS48L32_ASP_FSYNC_INV_MASK | + CS48L32_ASP_FSYNC_MSTR_MASK; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + val |= (CS48L32_ASP_FMT_DSP_MODE_A << CS48L32_ASP_FMT_SHIFT); + break; + case SND_SOC_DAIFMT_DSP_B: + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) { + cs48l32_asp_err(dai, "DSP_B cannot be clock consumer\n"); + return -EINVAL; + } + val |= (CS48L32_ASP_FMT_DSP_MODE_B << CS48L32_ASP_FMT_SHIFT); + break; + case SND_SOC_DAIFMT_I2S: + val |= (CS48L32_ASP_FMT_I2S_MODE << CS48L32_ASP_FMT_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) { + cs48l32_asp_err(dai, "LEFT_J cannot be clock consumer\n"); + return -EINVAL; + } + val |= (CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE << CS48L32_ASP_FMT_SHIFT); + break; + default: + cs48l32_asp_err(dai, "Unsupported DAI format %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: + break; + case SND_SOC_DAIFMT_BC_FP: + val |= CS48L32_ASP_FSYNC_MSTR_MASK; + break; + case SND_SOC_DAIFMT_BP_FC: + val |= CS48L32_ASP_BCLK_MSTR_MASK; + break; + case SND_SOC_DAIFMT_BP_FP: + val |= CS48L32_ASP_BCLK_MSTR_MASK; + val |= CS48L32_ASP_FSYNC_MSTR_MASK; + break; + default: + cs48l32_asp_err(dai, "Unsupported clock direction %d\n", + fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + val |= CS48L32_ASP_BCLK_INV_MASK; + val |= CS48L32_ASP_FSYNC_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + val |= CS48L32_ASP_BCLK_INV_MASK; + break; + case SND_SOC_DAIFMT_NB_IF: + val |= CS48L32_ASP_FSYNC_INV_MASK; + break; + default: + return -EINVAL; + } + + regmap_update_bits(regmap, base + CS48L32_ASP_CONTROL2, mask, val); + + return 0; +} + +static const struct { + u32 freq; + u32 id; +} cs48l32_sclk_rates[] = { + { 128000, 12 }, + { 176400, 13 }, + { 192000, 14 }, + { 256000, 15 }, + { 352800, 16 }, + { 384000, 17 }, + { 512000, 18 }, + { 705600, 19 }, + { 768000, 21 }, + { 1024000, 23 }, + { 1411200, 25 }, + { 1536000, 27 }, + { 2048000, 29 }, + { 2822400, 31 }, + { 3072000, 33 }, + { 4096000, 36 }, + { 5644800, 38 }, + { 6144000, 40 }, + { 8192000, 47 }, + { 11289600, 49 }, + { 12288000, 51 }, + { 22579200, 57 }, + { 24576000, 59 }, +}; + +#define CS48L32_48K_RATE_MASK 0x0e00fe +#define CS48L32_44K1_RATE_MASK 0x00fe00 +#define CS48L32_RATE_MASK (CS48L32_48K_RATE_MASK | CS48L32_44K1_RATE_MASK) + +static const unsigned int cs48l32_sr_vals[] = { + 0, + 12000, /* CS48L32_48K_RATE_MASK */ + 24000, /* CS48L32_48K_RATE_MASK */ + 48000, /* CS48L32_48K_RATE_MASK */ + 96000, /* CS48L32_48K_RATE_MASK */ + 192000, /* CS48L32_48K_RATE_MASK */ + 384000, /* CS48L32_48K_RATE_MASK */ + 768000, /* CS48L32_48K_RATE_MASK */ + 0, + 11025, /* CS48L32_44K1_RATE_MASK */ + 22050, /* CS48L32_44K1_RATE_MASK */ + 44100, /* CS48L32_44K1_RATE_MASK */ + 88200, /* CS48L32_44K1_RATE_MASK */ + 176400, /* CS48L32_44K1_RATE_MASK */ + 352800, /* CS48L32_44K1_RATE_MASK */ + 705600, /* CS48L32_44K1_RATE_MASK */ + 0, + 8000, /* CS48L32_48K_RATE_MASK */ + 16000, /* CS48L32_48K_RATE_MASK */ + 32000, /* CS48L32_48K_RATE_MASK */ +}; + +static const struct snd_pcm_hw_constraint_list cs48l32_constraint = { + .count = ARRAY_SIZE(cs48l32_sr_vals), + .list = cs48l32_sr_vals, +}; + +static int cs48l32_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1]; + unsigned int base_rate; + + if (!substream->runtime) + return 0; + + switch (dai_priv->clk) { + case CS48L32_CLK_SYSCLK_1: + case CS48L32_CLK_SYSCLK_2: + case CS48L32_CLK_SYSCLK_3: + case CS48L32_CLK_SYSCLK_4: + base_rate = cs48l32_codec->sysclk; + break; + default: + return 0; + } + + if (base_rate == 0) + dai_priv->constraint.mask = CS48L32_RATE_MASK; + else if (base_rate % 4000) + dai_priv->constraint.mask = CS48L32_44K1_RATE_MASK; + else + dai_priv->constraint.mask = CS48L32_48K_RATE_MASK; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &dai_priv->constraint); +} + +static int cs48l32_hw_params_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1]; + unsigned int sr_val, sr_reg, rate; + + rate = params_rate(params); + for (sr_val = 0; sr_val < ARRAY_SIZE(cs48l32_sr_vals); sr_val++) + if (cs48l32_sr_vals[sr_val] == rate) + break; + + if (sr_val == ARRAY_SIZE(cs48l32_sr_vals)) { + cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate); + return -EINVAL; + } + + switch (dai_priv->clk) { + case CS48L32_CLK_SYSCLK_1: + sr_reg = CS48L32_SAMPLE_RATE1; + break; + case CS48L32_CLK_SYSCLK_2: + sr_reg = CS48L32_SAMPLE_RATE2; + break; + case CS48L32_CLK_SYSCLK_3: + sr_reg = CS48L32_SAMPLE_RATE3; + break; + case CS48L32_CLK_SYSCLK_4: + sr_reg = CS48L32_SAMPLE_RATE4; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, sr_reg, CS48L32_SAMPLE_RATE_1_MASK, sr_val); + + return 0; +} + +static bool cs48l32_asp_cfg_changed(struct snd_soc_component *component, + unsigned int base, unsigned int sclk, + unsigned int slotws, unsigned int dataw) +{ + unsigned int val; + + val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL1); + if (sclk != (val & CS48L32_ASP_BCLK_FREQ_MASK)) + return true; + + val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2); + if (slotws != (val & (CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK))) + return true; + + val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL1); + if (dataw != (val & (CS48L32_ASP_TX_WL_MASK))) + return true; + + val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL5); + if (dataw != (val & (CS48L32_ASP_RX_WL_MASK))) + return true; + + return false; +} + +static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs48l32_codec->core.regmap; + int base = dai->driver->base; + int dai_id = dai->id - 1; + unsigned int rate = params_rate(params); + unsigned int dataw = snd_pcm_format_width(params_format(params)); + unsigned int asp_state = 0; + int sclk, sclk_target; + unsigned int slotw, n_slots, n_slots_multiple, val; + int i, ret; + + cs48l32_asp_dbg(dai, "hwparams in: ch:%u dataw:%u rate:%u\n", + params_channels(params), dataw, rate); + /* + * The following calculations hold only under the assumption that + * symmetric_[rates|channels|samplebits] are set to 1 + */ + if (cs48l32_codec->tdm_slots[dai_id]) { + n_slots = cs48l32_codec->tdm_slots[dai_id]; + slotw = cs48l32_codec->tdm_width[dai_id]; + } else { + n_slots = params_channels(params); + slotw = dataw; + } + + val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2); + val = (val & CS48L32_ASP_FMT_MASK) >> CS48L32_ASP_FMT_SHIFT; + if (val == CS48L32_ASP_FMT_I2S_MODE) + n_slots_multiple = 2; + else + n_slots_multiple = 1; + + sclk_target = snd_soc_tdm_params_to_bclk(params, slotw, n_slots, n_slots_multiple); + + for (i = 0; i < ARRAY_SIZE(cs48l32_sclk_rates); i++) { + if ((cs48l32_sclk_rates[i].freq >= sclk_target) && + (cs48l32_sclk_rates[i].freq % rate == 0)) { + sclk = cs48l32_sclk_rates[i].id; + break; + } + } + if (i == ARRAY_SIZE(cs48l32_sclk_rates)) { + cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate); + return -EINVAL; + } + + cs48l32_asp_dbg(dai, "hwparams out: n_slots:%u dataw:%u slotw:%u bclk:%u bclkid:%u\n", + n_slots, dataw, slotw, sclk_target, sclk); + + slotw = (slotw << CS48L32_ASP_TX_WIDTH_SHIFT) | + (slotw << CS48L32_ASP_RX_WIDTH_SHIFT); + + if (!cs48l32_asp_cfg_changed(component, base, sclk, slotw, dataw)) + return cs48l32_hw_params_rate(substream, params, dai); + + /* ASP must be disabled while changing configuration */ + asp_state = snd_soc_component_read(component, base + CS48L32_ASP_ENABLES1); + regmap_clear_bits(regmap, base + CS48L32_ASP_ENABLES1, 0xff00ff); + + ret = cs48l32_hw_params_rate(substream, params, dai); + if (ret != 0) + goto restore_asp; + + regmap_update_bits_async(regmap, + base + CS48L32_ASP_CONTROL1, + CS48L32_ASP_BCLK_FREQ_MASK, + sclk); + regmap_update_bits_async(regmap, + base + CS48L32_ASP_CONTROL2, + CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK, + slotw); + regmap_update_bits_async(regmap, + base + CS48L32_ASP_DATA_CONTROL1, + CS48L32_ASP_TX_WL_MASK, + dataw); + regmap_update_bits(regmap, + base + CS48L32_ASP_DATA_CONTROL5, + CS48L32_ASP_RX_WL_MASK, + dataw); + +restore_asp: + /* Restore ASP TX/RX enable state */ + regmap_update_bits(regmap, + base + CS48L32_ASP_ENABLES1, + 0xff00ff, + asp_state); + return ret; +} + +static const char *cs48l32_dai_clk_str(int clk_id) +{ + switch (clk_id) { + case CS48L32_CLK_SYSCLK_1: + case CS48L32_CLK_SYSCLK_2: + case CS48L32_CLK_SYSCLK_3: + case CS48L32_CLK_SYSCLK_4: + return "SYSCLK"; + default: + return "Unknown clock"; + } +} + +static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1]; + unsigned int base = dai->driver->base; + unsigned int current_asp_rate, target_asp_rate; + bool change_rate_domain = false; + int ret; + + if (clk_id == dai_priv->clk) + return 0; + + if (snd_soc_dai_active(dai)) { + cs48l32_asp_err(dai, "Can't change clock on active DAI\n"); + return -EBUSY; + } + + switch (clk_id) { + case CS48L32_CLK_SYSCLK_1: + target_asp_rate = 0U << CS48L32_ASP_RATE_SHIFT; + break; + case CS48L32_CLK_SYSCLK_2: + target_asp_rate = 1U << CS48L32_ASP_RATE_SHIFT; + break; + case CS48L32_CLK_SYSCLK_3: + target_asp_rate = 2U << CS48L32_ASP_RATE_SHIFT; + break; + case CS48L32_CLK_SYSCLK_4: + target_asp_rate = 3U << CS48L32_ASP_RATE_SHIFT; + break; + default: + return -EINVAL; + } + + dai_priv->clk = clk_id; + cs48l32_asp_dbg(dai, "Setting to %s\n", cs48l32_dai_clk_str(clk_id)); + + if (base) { + ret = regmap_read(cs48l32_codec->core.regmap, + base + CS48L32_ASP_CONTROL1, + ¤t_asp_rate); + if (ret != 0) { + cs48l32_asp_err(dai, "Failed to check rate: %d\n", ret); + return ret; + } + + if ((current_asp_rate & CS48L32_ASP_RATE_MASK) != + (target_asp_rate & CS48L32_ASP_RATE_MASK)) { + change_rate_domain = true; + + mutex_lock(&cs48l32_codec->rate_lock); + /* Guard the rate change with SYSCLK cycles */ + cs48l32_spin_sysclk(cs48l32_codec); + } + + snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1, + CS48L32_ASP_RATE_MASK, target_asp_rate); + + if (change_rate_domain) { + cs48l32_spin_sysclk(cs48l32_codec); + mutex_unlock(&cs48l32_codec->rate_lock); + } + } + + return 0; +} + +static void cs48l32_set_channels_to_mask(struct snd_soc_dai *dai, + unsigned int base, + int channels, unsigned int mask) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs48l32_codec->core.regmap; + int slot, i, j = 0, shift; + unsigned int frame_ctls[2] = {0, 0}; + + for (i = 0; i < channels; ++i) { + slot = ffs(mask) - 1; + if (slot < 0) + return; + + if (i - (j * 4) >= 4) { + ++j; + if (j >= 2) + break; + } + + shift = (8 * (i - j * 4)); + + frame_ctls[j] |= slot << shift; + + mask &= ~(1 << slot); /* ? mask ^= 1 << slot ? */ + } + + regmap_write(regmap, base, frame_ctls[0]); + regmap_write(regmap, base + 0x4, frame_ctls[1]); + + if (mask) + cs48l32_asp_warn(dai, "Too many channels in TDM mask\n"); +} + +static int cs48l32_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; + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + int base = dai->driver->base; + int rx_max_chan = dai->driver->playback.channels_max; + int tx_max_chan = dai->driver->capture.channels_max; + + /* Only support TDM for the physical ASPs */ + if (dai->id > CS48L32_MAX_ASP) + return -EINVAL; + + if (slots == 0) { + tx_mask = (1 << tx_max_chan) - 1; + rx_mask = (1 << rx_max_chan) - 1; + } + + cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL1, + tx_max_chan, tx_mask); + cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL5, + rx_max_chan, rx_mask); + + cs48l32_codec->tdm_width[dai->id - 1] = slot_width; + cs48l32_codec->tdm_slots[dai->id - 1] = slots; + + return 0; +} + +static const struct snd_soc_dai_ops cs48l32_dai_ops = { + .probe = &cs48l32_asp_dai_probe, + .startup = &cs48l32_startup, + .set_fmt = &cs48l32_set_fmt, + .set_tdm_slot = &cs48l32_set_tdm_slot, + .hw_params = &cs48l32_hw_params, + .set_sysclk = &cs48l32_dai_set_sysclk, +}; + +static int cs48l32_sysclk_ev(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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + + cs48l32_spin_sysclk(cs48l32_codec); + + return 0; +} + +static int cs48l32_in_ev(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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + unsigned int reg; + + if (w->shift % 2) + reg = CS48L32_IN1L_CONTROL2; + else + reg = CS48L32_IN1R_CONTROL2; + + reg += (w->shift / 2) * (CS48L32_IN2L_CONTROL2 - CS48L32_IN1L_CONTROL2); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case CS48L32_IN1L_EN_SHIFT: + snd_soc_component_update_bits(component, + CS48L32_ADC1L_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + CS48L32_ADC1x_INT_ENA_FRC_MASK); + break; + case CS48L32_IN1R_EN_SHIFT: + snd_soc_component_update_bits(component, + CS48L32_ADC1R_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + CS48L32_ADC1x_INT_ENA_FRC_MASK); + break; + default: + break; + } + cs48l32_codec->in_up_pending++; + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(200, 300); + + switch (w->shift) { + case CS48L32_IN1L_EN_SHIFT: + snd_soc_component_update_bits(component, + CS48L32_ADC1L_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + 0); + break; + case CS48L32_IN1R_EN_SHIFT: + snd_soc_component_update_bits(component, + CS48L32_ADC1R_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + 0); + break; + + default: + break; + } + cs48l32_codec->in_up_pending--; + snd_soc_component_update_bits(component, reg, CS48L32_INx_MUTE_MASK, 0); + + /* Uncached write-only register, no need for update_bits */ + if (!cs48l32_codec->in_up_pending) { + snd_soc_component_write(component, cs48l32_codec->in_vu_reg, + CS48L32_IN_VU_MASK); + } + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, reg, + CS48L32_INx_MUTE_MASK, CS48L32_INx_MUTE_MASK); + snd_soc_component_write(component, cs48l32_codec->in_vu_reg, + CS48L32_IN_VU_MASK); + break; + default: + break; + } + + return 0; +} + +static int cs48l32_in_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + /* + * Uncached write-only register, no need for update_bits. + * Will fail if codec is off but that will be handled by cs48l32_in_ev + */ + snd_soc_component_write(component, cs48l32_codec->in_vu_reg, CS48L32_IN_VU); + + return ret; +} + +static bool cs48l32_eq_filter_unstable(bool mode, __be16 in_a, __be16 in_b) +{ + s16 a = be16_to_cpu(in_a); + s16 b = be16_to_cpu(in_b); + + if (!mode) + return abs(a) > CS48L32_EQ_MAX_COEFF; + + if (abs(b) > CS48L32_EQ_MAX_COEFF) + return true; + + if (abs((a << 16) / (CS48L32_EQ_MAX_COEFF + 1 - b)) >= ((CS48L32_EQ_MAX_COEFF + 1) << 4)) + return true; + + return false; +} + +static int cs48l32_eq_ev(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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs48l32_codec->core.regmap; + unsigned int mode = cs48l32_codec->eq_mode[w->shift]; + unsigned int reg; + __be16 *data = &cs48l32_codec->eq_coefficients[w->shift][0]; + int ret = 0; + + reg = CS48L32_EQ1_BAND1_COEFF1; + reg += w->shift * (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (cs48l32_eq_filter_unstable(!!mode, data[1], data[0]) || + cs48l32_eq_filter_unstable(true, data[7], data[6]) || + cs48l32_eq_filter_unstable(true, data[13], data[12]) || + cs48l32_eq_filter_unstable(true, data[19], data[18]) || + cs48l32_eq_filter_unstable(false, data[25], data[24])) { + dev_err(cs48l32_codec->core.dev, "Rejecting unstable EQ coefficients.\n"); + ret = -EINVAL; + } else { + ret = regmap_raw_write(regmap, reg, data, CS48L32_EQ_BLOCK_SZ); + if (ret < 0) { + dev_err(cs48l32_codec->core.dev, + "Error writing EQ coefficients: %d\n", ret); + goto out; + } + + ret = snd_soc_component_update_bits(component, + CS48L32_EQ_CONTROL2, + w->mask, + mode << w->shift); + if (ret < 0) { + dev_err(cs48l32_codec->core.dev, + "Error writing EQ mode: %d\n", ret); + } + } + break; + default: + break; + } + +out: + return ret; +} + +static const struct snd_kcontrol_new cs48l32_snd_controls[] = { +SOC_ENUM("IN1 OSR", cs48l32_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", cs48l32_in_dmic_osr[1]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", CS48L32_IN1L_CONTROL2, + CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", CS48L32_IN1R_CONTROL2, + CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", cs48l32_in_hpf_cut_enum), + +SOC_SINGLE_EXT("IN1L LP Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_LP_MODE_SHIFT, + 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put), +SOC_SINGLE_EXT("IN1R LP Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_LP_MODE_SHIFT, + 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put), + +SOC_SINGLE("IN1L HPF Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", CS48L32_IN2L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", CS48L32_IN2R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0), + +SOC_SINGLE_EXT_TLV("IN1L Digital Volume", CS48L32_IN1L_CONTROL2, + CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw, + cs48l32_in_put_volsw, cs48l32_digital_tlv), +SOC_SINGLE_EXT_TLV("IN1R Digital Volume", CS48L32_IN1R_CONTROL2, + CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw, + cs48l32_in_put_volsw, cs48l32_digital_tlv), +SOC_SINGLE_EXT_TLV("IN2L Digital Volume", CS48L32_IN2L_CONTROL2, + CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw, + cs48l32_in_put_volsw, cs48l32_digital_tlv), +SOC_SINGLE_EXT_TLV("IN2R Digital Volume", CS48L32_IN2R_CONTROL2, + CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw, + cs48l32_in_put_volsw, cs48l32_digital_tlv), + +SOC_ENUM("Input Ramp Up", cs48l32_in_vi_ramp), +SOC_ENUM("Input Ramp Down", cs48l32_in_vd_ramp), + +CS48L32_RATE_ENUM("Ultrasonic 1 Rate", cs48l32_us_output_rate[0]), +CS48L32_RATE_ENUM("Ultrasonic 2 Rate", cs48l32_us_output_rate[1]), + +SOC_ENUM("Ultrasonic 1 Freq", cs48l32_us_freq[0]), +SOC_ENUM("Ultrasonic 2 Freq", cs48l32_us_freq[1]), + +SOC_SINGLE_TLV("Ultrasonic 1 Volume", CS48L32_US1_CONTROL, CS48L32_US1_GAIN_SHIFT, + 3, 0, cs48l32_us_tlv), +SOC_SINGLE_TLV("Ultrasonic 2 Volume", CS48L32_US2_CONTROL, CS48L32_US1_GAIN_SHIFT, + 3, 0, cs48l32_us_tlv), + +SOC_ENUM("Ultrasonic 1 Detect Threshold", cs48l32_us_det_thr[0]), +SOC_ENUM("Ultrasonic 2 Detect Threshold", cs48l32_us_det_thr[1]), + +SOC_ENUM("Ultrasonic 1 Detect Pulse Length", cs48l32_us_det_num[0]), +SOC_ENUM("Ultrasonic 2 Detect Pulse Length", cs48l32_us_det_num[1]), + +SOC_ENUM("Ultrasonic 1 Detect Hold", cs48l32_us_det_hold[0]), +SOC_ENUM("Ultrasonic 2 Detect Hold", cs48l32_us_det_hold[1]), + +SOC_ENUM("Ultrasonic 1 Detect Decay", cs48l32_us_det_dcy[0]), +SOC_ENUM("Ultrasonic 2 Detect Decay", cs48l32_us_det_dcy[1]), + +SOC_SINGLE("Ultrasonic 1 Detect LPF Switch", + CS48L32_US1_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0), +SOC_SINGLE("Ultrasonic 2 Detect LPF Switch", + CS48L32_US2_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0), + +SOC_ENUM("Ultrasonic 1 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[0]), +SOC_ENUM("Ultrasonic 2 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[1]), + +CS48L32_MIXER_CONTROLS("EQ1", CS48L32_EQ1_INPUT1), +CS48L32_MIXER_CONTROLS("EQ2", CS48L32_EQ2_INPUT1), +CS48L32_MIXER_CONTROLS("EQ3", CS48L32_EQ3_INPUT1), +CS48L32_MIXER_CONTROLS("EQ4", CS48L32_EQ4_INPUT1), + +SOC_ENUM_EXT("EQ1 Mode", cs48l32_eq_mode[0], cs48l32_eq_mode_get, cs48l32_eq_mode_put), + +CS48L32_EQ_COEFF_CONTROLS(EQ1), + +SOC_SINGLE_TLV("EQ1 B1 Volume", CS48L32_EQ1_GAIN1, 0, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", CS48L32_EQ1_GAIN1, 8, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", CS48L32_EQ1_GAIN1, 16, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", CS48L32_EQ1_GAIN1, 24, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", CS48L32_EQ1_GAIN2, 0, 24, 0, cs48l32_eq_tlv), + +SOC_ENUM_EXT("EQ2 Mode", cs48l32_eq_mode[1], cs48l32_eq_mode_get, cs48l32_eq_mode_put), +CS48L32_EQ_COEFF_CONTROLS(EQ2), +SOC_SINGLE_TLV("EQ2 B1 Volume", CS48L32_EQ2_GAIN1, 0, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", CS48L32_EQ2_GAIN1, 8, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", CS48L32_EQ2_GAIN1, 16, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", CS48L32_EQ2_GAIN1, 24, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", CS48L32_EQ2_GAIN2, 0, 24, 0, cs48l32_eq_tlv), + +SOC_ENUM_EXT("EQ3 Mode", cs48l32_eq_mode[2], cs48l32_eq_mode_get, cs48l32_eq_mode_put), +CS48L32_EQ_COEFF_CONTROLS(EQ3), +SOC_SINGLE_TLV("EQ3 B1 Volume", CS48L32_EQ3_GAIN1, 0, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", CS48L32_EQ3_GAIN1, 8, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", CS48L32_EQ3_GAIN1, 16, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", CS48L32_EQ3_GAIN1, 24, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", CS48L32_EQ3_GAIN2, 0, 24, 0, cs48l32_eq_tlv), + +SOC_ENUM_EXT("EQ4 Mode", cs48l32_eq_mode[3], cs48l32_eq_mode_get, cs48l32_eq_mode_put), +CS48L32_EQ_COEFF_CONTROLS(EQ4), +SOC_SINGLE_TLV("EQ4 B1 Volume", CS48L32_EQ4_GAIN1, 0, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", CS48L32_EQ4_GAIN1, 8, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", CS48L32_EQ4_GAIN1, 16, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", CS48L32_EQ4_GAIN1, 24, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", CS48L32_EQ4_GAIN2, 0, 24, 0, cs48l32_eq_tlv), + +CS48L32_MIXER_CONTROLS("DRC1L", CS48L32_DRC1L_INPUT1), +CS48L32_MIXER_CONTROLS("DRC1R", CS48L32_DRC1R_INPUT1), +CS48L32_MIXER_CONTROLS("DRC2L", CS48L32_DRC2L_INPUT1), +CS48L32_MIXER_CONTROLS("DRC2R", CS48L32_DRC2R_INPUT1), + +SND_SOC_BYTES_MASK("DRC1 Coefficients", CS48L32_DRC1_CONTROL1, 4, + BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)), +SND_SOC_BYTES_MASK("DRC2 Coefficients", CS48L32_DRC2_CONTROL1, 4, + BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)), + +CS48L32_MIXER_CONTROLS("LHPF1", CS48L32_LHPF1_INPUT1), +CS48L32_MIXER_CONTROLS("LHPF2", CS48L32_LHPF2_INPUT1), +CS48L32_MIXER_CONTROLS("LHPF3", CS48L32_LHPF3_INPUT1), +CS48L32_MIXER_CONTROLS("LHPF4", CS48L32_LHPF4_INPUT1), + +CS48L32_LHPF_CONTROL("LHPF1 Coefficients", CS48L32_LHPF1_COEFF), +CS48L32_LHPF_CONTROL("LHPF2 Coefficients", CS48L32_LHPF2_COEFF), +CS48L32_LHPF_CONTROL("LHPF3 Coefficients", CS48L32_LHPF3_COEFF), +CS48L32_LHPF_CONTROL("LHPF4 Coefficients", CS48L32_LHPF4_COEFF), + +SOC_ENUM("LHPF1 Mode", cs48l32_lhpf_mode[0]), +SOC_ENUM("LHPF2 Mode", cs48l32_lhpf_mode[1]), +SOC_ENUM("LHPF3 Mode", cs48l32_lhpf_mode[2]), +SOC_ENUM("LHPF4 Mode", cs48l32_lhpf_mode[3]), + +CS48L32_RATE_CONTROL("Sample Rate 1", 1), +CS48L32_RATE_CONTROL("Sample Rate 2", 2), +CS48L32_RATE_CONTROL("Sample Rate 3", 3), +CS48L32_RATE_CONTROL("Sample Rate 4", 4), + +CS48L32_RATE_ENUM("FX Rate", cs48l32_fx_rate), + +CS48L32_RATE_ENUM("ISRC1 FSL", cs48l32_isrc_fsl[0]), +CS48L32_RATE_ENUM("ISRC2 FSL", cs48l32_isrc_fsl[1]), +CS48L32_RATE_ENUM("ISRC3 FSL", cs48l32_isrc_fsl[2]), +CS48L32_RATE_ENUM("ISRC1 FSH", cs48l32_isrc_fsh[0]), +CS48L32_RATE_ENUM("ISRC2 FSH", cs48l32_isrc_fsh[1]), +CS48L32_RATE_ENUM("ISRC3 FSH", cs48l32_isrc_fsh[2]), + +SOC_ENUM("AUXPDM1 Rate", cs48l32_auxpdm1_freq), +SOC_ENUM("AUXPDM2 Rate", cs48l32_auxpdm2_freq), + +SOC_ENUM_EXT("IN1L Rate", cs48l32_input_rate[0], snd_soc_get_enum_double, cs48l32_in_rate_put), +SOC_ENUM_EXT("IN1R Rate", cs48l32_input_rate[1], snd_soc_get_enum_double, cs48l32_in_rate_put), +SOC_ENUM_EXT("IN2L Rate", cs48l32_input_rate[2], snd_soc_get_enum_double, cs48l32_in_rate_put), +SOC_ENUM_EXT("IN2R Rate", cs48l32_input_rate[3], snd_soc_get_enum_double, cs48l32_in_rate_put), + +CS48L32_RATE_ENUM("Noise Generator Rate", noise_gen_rate), + +SOC_SINGLE_TLV("Noise Generator Volume", CS48L32_COMFORT_NOISE_GENERATOR, + CS48L32_NOISE_GEN_GAIN_SHIFT, 0x12, 0, cs48l32_noise_tlv), + +CS48L32_MIXER_CONTROLS("ASP1TX1", CS48L32_ASP1TX1_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX2", CS48L32_ASP1TX2_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX3", CS48L32_ASP1TX3_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX4", CS48L32_ASP1TX4_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX5", CS48L32_ASP1TX5_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX6", CS48L32_ASP1TX6_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX7", CS48L32_ASP1TX7_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX8", CS48L32_ASP1TX8_INPUT1), + +CS48L32_MIXER_CONTROLS("ASP2TX1", CS48L32_ASP2TX1_INPUT1), +CS48L32_MIXER_CONTROLS("ASP2TX2", CS48L32_ASP2TX2_INPUT1), +CS48L32_MIXER_CONTROLS("ASP2TX3", CS48L32_ASP2TX3_INPUT1), +CS48L32_MIXER_CONTROLS("ASP2TX4", CS48L32_ASP2TX4_INPUT1), + +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + +CS48L32_MIXER_CONTROLS("DSP1RX1", CS48L32_DSP1RX1_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX2", CS48L32_DSP1RX2_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX3", CS48L32_DSP1RX3_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX4", CS48L32_DSP1RX4_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX5", CS48L32_DSP1RX5_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX6", CS48L32_DSP1RX6_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX7", CS48L32_DSP1RX7_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX8", CS48L32_DSP1RX8_INPUT1), + +WM_ADSP_FW_CONTROL("DSP1", 0), + +CS48L32_DSP_RATE_CONTROL("DSP1RX1", 0), +CS48L32_DSP_RATE_CONTROL("DSP1RX2", 1), +CS48L32_DSP_RATE_CONTROL("DSP1RX3", 2), +CS48L32_DSP_RATE_CONTROL("DSP1RX4", 3), +CS48L32_DSP_RATE_CONTROL("DSP1RX5", 4), +CS48L32_DSP_RATE_CONTROL("DSP1RX6", 5), +CS48L32_DSP_RATE_CONTROL("DSP1RX7", 6), +CS48L32_DSP_RATE_CONTROL("DSP1RX8", 7), +CS48L32_DSP_RATE_CONTROL("DSP1TX1", 8), +CS48L32_DSP_RATE_CONTROL("DSP1TX2", 9), +CS48L32_DSP_RATE_CONTROL("DSP1TX3", 10), +CS48L32_DSP_RATE_CONTROL("DSP1TX4", 11), +CS48L32_DSP_RATE_CONTROL("DSP1TX5", 12), +CS48L32_DSP_RATE_CONTROL("DSP1TX6", 13), +CS48L32_DSP_RATE_CONTROL("DSP1TX7", 14), +CS48L32_DSP_RATE_CONTROL("DSP1TX8", 15), +}; + +CS48L32_MIXER_ENUMS(EQ1, CS48L32_EQ1_INPUT1); +CS48L32_MIXER_ENUMS(EQ2, CS48L32_EQ2_INPUT1); +CS48L32_MIXER_ENUMS(EQ3, CS48L32_EQ3_INPUT1); +CS48L32_MIXER_ENUMS(EQ4, CS48L32_EQ4_INPUT1); + +CS48L32_MIXER_ENUMS(DRC1L, CS48L32_DRC1L_INPUT1); +CS48L32_MIXER_ENUMS(DRC1R, CS48L32_DRC1R_INPUT1); +CS48L32_MIXER_ENUMS(DRC2L, CS48L32_DRC2L_INPUT1); +CS48L32_MIXER_ENUMS(DRC2R, CS48L32_DRC2R_INPUT1); + +CS48L32_MIXER_ENUMS(LHPF1, CS48L32_LHPF1_INPUT1); +CS48L32_MIXER_ENUMS(LHPF2, CS48L32_LHPF2_INPUT1); +CS48L32_MIXER_ENUMS(LHPF3, CS48L32_LHPF3_INPUT1); +CS48L32_MIXER_ENUMS(LHPF4, CS48L32_LHPF4_INPUT1); + +CS48L32_MIXER_ENUMS(ASP1TX1, CS48L32_ASP1TX1_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX2, CS48L32_ASP1TX2_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX3, CS48L32_ASP1TX3_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX4, CS48L32_ASP1TX4_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX5, CS48L32_ASP1TX5_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX6, CS48L32_ASP1TX6_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX7, CS48L32_ASP1TX7_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX8, CS48L32_ASP1TX8_INPUT1); + +CS48L32_MIXER_ENUMS(ASP2TX1, CS48L32_ASP2TX1_INPUT1); +CS48L32_MIXER_ENUMS(ASP2TX2, CS48L32_ASP2TX2_INPUT1); +CS48L32_MIXER_ENUMS(ASP2TX3, CS48L32_ASP2TX3_INPUT1); +CS48L32_MIXER_ENUMS(ASP2TX4, CS48L32_ASP2TX4_INPUT1); + +CS48L32_MUX_ENUMS(ISRC1INT1, CS48L32_ISRC1INT1_INPUT1); +CS48L32_MUX_ENUMS(ISRC1INT2, CS48L32_ISRC1INT2_INPUT1); +CS48L32_MUX_ENUMS(ISRC1INT3, CS48L32_ISRC1INT3_INPUT1); +CS48L32_MUX_ENUMS(ISRC1INT4, CS48L32_ISRC1INT4_INPUT1); + +CS48L32_MUX_ENUMS(ISRC1DEC1, CS48L32_ISRC1DEC1_INPUT1); +CS48L32_MUX_ENUMS(ISRC1DEC2, CS48L32_ISRC1DEC2_INPUT1); +CS48L32_MUX_ENUMS(ISRC1DEC3, CS48L32_ISRC1DEC3_INPUT1); +CS48L32_MUX_ENUMS(ISRC1DEC4, CS48L32_ISRC1DEC4_INPUT1); + +CS48L32_MUX_ENUMS(ISRC2INT1, CS48L32_ISRC2INT1_INPUT1); +CS48L32_MUX_ENUMS(ISRC2INT2, CS48L32_ISRC2INT2_INPUT1); + +CS48L32_MUX_ENUMS(ISRC2DEC1, CS48L32_ISRC2DEC1_INPUT1); +CS48L32_MUX_ENUMS(ISRC2DEC2, CS48L32_ISRC2DEC2_INPUT1); + +CS48L32_MUX_ENUMS(ISRC3INT1, CS48L32_ISRC3INT1_INPUT1); +CS48L32_MUX_ENUMS(ISRC3INT2, CS48L32_ISRC3INT2_INPUT1); + +CS48L32_MUX_ENUMS(ISRC3DEC1, CS48L32_ISRC3DEC1_INPUT1); +CS48L32_MUX_ENUMS(ISRC3DEC2, CS48L32_ISRC3DEC2_INPUT1); + +CS48L32_MIXER_ENUMS(DSP1RX1, CS48L32_DSP1RX1_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX2, CS48L32_DSP1RX2_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX3, CS48L32_DSP1RX3_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX4, CS48L32_DSP1RX4_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX5, CS48L32_DSP1RX5_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX6, CS48L32_DSP1RX6_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX7, CS48L32_DSP1RX7_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX8, CS48L32_DSP1RX8_INPUT1); + +static int cs48l32_dsp_mem_ev(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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + return cs48l32_dsp_memory_enable(cs48l32_codec, &cs48l32_dsp_sram_regs); + case SND_SOC_DAPM_PRE_PMD: + cs48l32_dsp_memory_disable(cs48l32_codec, &cs48l32_dsp_sram_regs); + return 0; + default: + return 0; + } +} + +static const struct snd_soc_dapm_widget cs48l32_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", CS48L32_SYSTEM_CLOCK1, CS48L32_SYSCLK_EN_SHIFT, 0, + cs48l32_sysclk_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-cp", 20, 0), + +SND_SOC_DAPM_SUPPLY("VOUT_MIC", CS48L32_CHARGE_PUMP1, CS48L32_CP2_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("VOUT_MIC_REGULATED", CS48L32_CHARGE_PUMP1, CS48L32_CP2_BYPASS_SHIFT, + 1, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1", CS48L32_MICBIAS_CTRL1, CS48L32_MICB1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1A", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1A_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1B", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1B_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1C", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1C_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("DSP1MEM", SND_SOC_NOPM, 0, 0, cs48l32_dsp_mem_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +CS48L32_DSP_FREQ_WIDGET_EV("DSP1", 0, cs48l32_dsp_freq_ev), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), + +SND_SOC_DAPM_INPUT("IN1LN_1"), +SND_SOC_DAPM_INPUT("IN1LN_2"), +SND_SOC_DAPM_INPUT("IN1LP_1"), +SND_SOC_DAPM_INPUT("IN1LP_2"), +SND_SOC_DAPM_INPUT("IN1RN_1"), +SND_SOC_DAPM_INPUT("IN1RN_2"), +SND_SOC_DAPM_INPUT("IN1RP_1"), +SND_SOC_DAPM_INPUT("IN1RP_2"), +SND_SOC_DAPM_INPUT("IN1_PDMCLK"), +SND_SOC_DAPM_INPUT("IN1_PDMDATA"), + +SND_SOC_DAPM_INPUT("IN2_PDMCLK"), +SND_SOC_DAPM_INPUT("IN2_PDMDATA"), + +SND_SOC_DAPM_MUX("Ultrasonic 1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[0]), +SND_SOC_DAPM_MUX("Ultrasonic 2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[1]), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_OUTPUT("DSP Trigger Out"), + +SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[0]), +SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[1]), + +SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]), +SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]), + +SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS48L32_ASP1_ENABLES1, 0, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS48L32_ASP1_ENABLES1, 1, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS48L32_ASP1_ENABLES1, 2, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS48L32_ASP1_ENABLES1, 3, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX5", NULL, 4, CS48L32_ASP1_ENABLES1, 4, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX6", NULL, 5, CS48L32_ASP1_ENABLES1, 5, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX7", NULL, 6, CS48L32_ASP1_ENABLES1, 6, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX8", NULL, 7, CS48L32_ASP1_ENABLES1, 7, 0), + +SND_SOC_DAPM_AIF_OUT("ASP2TX1", NULL, 0, CS48L32_ASP2_ENABLES1, 0, 0), +SND_SOC_DAPM_AIF_OUT("ASP2TX2", NULL, 1, CS48L32_ASP2_ENABLES1, 1, 0), +SND_SOC_DAPM_AIF_OUT("ASP2TX3", NULL, 2, CS48L32_ASP2_ENABLES1, 2, 0), +SND_SOC_DAPM_AIF_OUT("ASP2TX4", NULL, 3, CS48L32_ASP2_ENABLES1, 3, 0), + +SND_SOC_DAPM_SWITCH("AUXPDM1 Output", CS48L32_AUXPDM_CONTROL1, 0, 0, &cs48l32_auxpdm_switch[0]), +SND_SOC_DAPM_SWITCH("AUXPDM2 Output", CS48L32_AUXPDM_CONTROL1, 1, 0, &cs48l32_auxpdm_switch[1]), + +SND_SOC_DAPM_MUX("AUXPDM1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[0]), +SND_SOC_DAPM_MUX("AUXPDM2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[1]), + +SND_SOC_DAPM_MUX("AUXPDM1 Analog Input", SND_SOC_NOPM, 0, 0, + &cs48l32_auxpdm_analog_inmux[0]), +SND_SOC_DAPM_MUX("AUXPDM2 Analog Input", SND_SOC_NOPM, 0, 0, + &cs48l32_auxpdm_analog_inmux[1]), + +SND_SOC_DAPM_SWITCH("Ultrasonic 1 Detect", CS48L32_US_CONTROL, + CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[0]), +SND_SOC_DAPM_SWITCH("Ultrasonic 2 Detect", CS48L32_US_CONTROL, + CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[1]), + +/* + * mux_in widgets : arranged in the order of sources + * specified in CS48L32_MIXER_INPUT_ROUTES + */ +SND_SOC_DAPM_PGA("Tone Generator 1", CS48L32_TONE_GENERATOR1, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", CS48L32_TONE_GENERATOR1, 1, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", CS48L32_COMFORT_NOISE_GENERATOR, + CS48L32_NOISE_GEN_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("IN1L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1L_EN_SHIFT, + 0, NULL, 0, cs48l32_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1R_EN_SHIFT, + 0, NULL, 0, cs48l32_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2L_EN_SHIFT, + 0, NULL, 0, cs48l32_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2R_EN_SHIFT, + 0, NULL, 0, cs48l32_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS48L32_ASP1_ENABLES1, 16, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS48L32_ASP1_ENABLES1, 17, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX3", NULL, 2, CS48L32_ASP1_ENABLES1, 18, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX4", NULL, 3, CS48L32_ASP1_ENABLES1, 19, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX5", NULL, 4, CS48L32_ASP1_ENABLES1, 20, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX6", NULL, 5, CS48L32_ASP1_ENABLES1, 21, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX7", NULL, 6, CS48L32_ASP1_ENABLES1, 22, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX8", NULL, 7, CS48L32_ASP1_ENABLES1, 23, 0), + +SND_SOC_DAPM_AIF_IN("ASP2RX1", NULL, 0, CS48L32_ASP2_ENABLES1, 16, 0), +SND_SOC_DAPM_AIF_IN("ASP2RX2", NULL, 1, CS48L32_ASP2_ENABLES1, 17, 0), +SND_SOC_DAPM_AIF_IN("ASP2RX3", NULL, 2, CS48L32_ASP2_ENABLES1, 18, 0), +SND_SOC_DAPM_AIF_IN("ASP2RX4", NULL, 3, CS48L32_ASP2_ENABLES1, 19, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC3_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC4_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT3_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT4_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3DEC1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3INT1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("EQ1", CS48L32_EQ_CONTROL1, 0, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("EQ2", CS48L32_EQ_CONTROL1, 1, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("EQ3", CS48L32_EQ_CONTROL1, 2, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("EQ4", CS48L32_EQ_CONTROL1, 3, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_PGA("DRC1L", CS48L32_DRC1_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", CS48L32_DRC1_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", CS48L32_DRC2_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", CS48L32_DRC2_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", CS48L32_LHPF_CONTROL1, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", CS48L32_LHPF_CONTROL1, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", CS48L32_LHPF_CONTROL1, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", CS48L32_LHPF_CONTROL1, 3, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Ultrasonic 1", CS48L32_US_CONTROL, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("Ultrasonic 2", CS48L32_US_CONTROL, 1, 0, NULL, 0), + +WM_ADSP2("DSP1", 0, wm_adsp_early_event), + +/* end of ordered widget list */ + +CS48L32_MIXER_WIDGETS(EQ1, "EQ1"), +CS48L32_MIXER_WIDGETS(EQ2, "EQ2"), +CS48L32_MIXER_WIDGETS(EQ3, "EQ3"), +CS48L32_MIXER_WIDGETS(EQ4, "EQ4"), + +CS48L32_MIXER_WIDGETS(DRC1L, "DRC1L"), +CS48L32_MIXER_WIDGETS(DRC1R, "DRC1R"), +CS48L32_MIXER_WIDGETS(DRC2L, "DRC2L"), +CS48L32_MIXER_WIDGETS(DRC2R, "DRC2R"), + +SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0, + &cs48l32_drc_activity_output_mux[0]), +SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0, + &cs48l32_drc_activity_output_mux[1]), + +CS48L32_MIXER_WIDGETS(LHPF1, "LHPF1"), +CS48L32_MIXER_WIDGETS(LHPF2, "LHPF2"), +CS48L32_MIXER_WIDGETS(LHPF3, "LHPF3"), +CS48L32_MIXER_WIDGETS(LHPF4, "LHPF4"), + +CS48L32_MIXER_WIDGETS(ASP1TX1, "ASP1TX1"), +CS48L32_MIXER_WIDGETS(ASP1TX2, "ASP1TX2"), +CS48L32_MIXER_WIDGETS(ASP1TX3, "ASP1TX3"), +CS48L32_MIXER_WIDGETS(ASP1TX4, "ASP1TX4"), +CS48L32_MIXER_WIDGETS(ASP1TX5, "ASP1TX5"), +CS48L32_MIXER_WIDGETS(ASP1TX6, "ASP1TX6"), +CS48L32_MIXER_WIDGETS(ASP1TX7, "ASP1TX7"), +CS48L32_MIXER_WIDGETS(ASP1TX8, "ASP1TX8"), + +CS48L32_MIXER_WIDGETS(ASP2TX1, "ASP2TX1"), +CS48L32_MIXER_WIDGETS(ASP2TX2, "ASP2TX2"), +CS48L32_MIXER_WIDGETS(ASP2TX3, "ASP2TX3"), +CS48L32_MIXER_WIDGETS(ASP2TX4, "ASP2TX4"), + +CS48L32_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +CS48L32_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +CS48L32_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +CS48L32_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +CS48L32_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +CS48L32_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +CS48L32_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +CS48L32_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +CS48L32_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +CS48L32_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +CS48L32_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +CS48L32_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +CS48L32_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"), +CS48L32_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"), + +CS48L32_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"), +CS48L32_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"), + +CS48L32_MIXER_WIDGETS(DSP1RX1, "DSP1RX1"), +CS48L32_MIXER_WIDGETS(DSP1RX2, "DSP1RX2"), +CS48L32_MIXER_WIDGETS(DSP1RX3, "DSP1RX3"), +CS48L32_MIXER_WIDGETS(DSP1RX4, "DSP1RX4"), +CS48L32_MIXER_WIDGETS(DSP1RX5, "DSP1RX5"), +CS48L32_MIXER_WIDGETS(DSP1RX6, "DSP1RX6"), +CS48L32_MIXER_WIDGETS(DSP1RX7, "DSP1RX7"), +CS48L32_MIXER_WIDGETS(DSP1RX8, "DSP1RX8"), + +SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0, + &cs48l32_dsp_trigger_output_mux[0]), + +SND_SOC_DAPM_OUTPUT("AUXPDM1_CLK"), +SND_SOC_DAPM_OUTPUT("AUXPDM1_DOUT"), +SND_SOC_DAPM_OUTPUT("AUXPDM2_CLK"), +SND_SOC_DAPM_OUTPUT("AUXPDM2_DOUT"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), + +SND_SOC_DAPM_OUTPUT("Ultrasonic Dummy Output"), +}; + +static const struct snd_soc_dapm_route cs48l32_dapm_routes[] = { + { "IN1LN_1", NULL, "SYSCLK" }, + { "IN1LN_2", NULL, "SYSCLK" }, + { "IN1LP_1", NULL, "SYSCLK" }, + { "IN1LP_2", NULL, "SYSCLK" }, + { "IN1RN_1", NULL, "SYSCLK" }, + { "IN1RN_2", NULL, "SYSCLK" }, + { "IN1RP_1", NULL, "SYSCLK" }, + { "IN1RP_2", NULL, "SYSCLK" }, + + { "IN1_PDMCLK", NULL, "SYSCLK" }, + { "IN1_PDMDATA", NULL, "SYSCLK" }, + { "IN2_PDMCLK", NULL, "SYSCLK" }, + { "IN2_PDMDATA", NULL, "SYSCLK" }, + + { "DSP1 Preloader", NULL, "DSP1MEM" }, + { "DSP1", NULL, "DSP1FREQ" }, + + { "Audio Trace DSP", NULL, "DSP1" }, + { "Voice Ctrl DSP", NULL, "DSP1" }, + + { "VOUT_MIC_REGULATED", NULL, "VOUT_MIC" }, + { "MICBIAS1", NULL, "VOUT_MIC_REGULATED" }, + { "MICBIAS1A", NULL, "MICBIAS1" }, + { "MICBIAS1B", NULL, "MICBIAS1" }, + { "MICBIAS1C", NULL, "MICBIAS1" }, + + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + { "Noise Generator", NULL, "SYSCLK" }, + + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + { "Noise Generator", NULL, "NOISE" }, + + { "ASP1 Capture", NULL, "ASP1TX1" }, + { "ASP1 Capture", NULL, "ASP1TX2" }, + { "ASP1 Capture", NULL, "ASP1TX3" }, + { "ASP1 Capture", NULL, "ASP1TX4" }, + { "ASP1 Capture", NULL, "ASP1TX5" }, + { "ASP1 Capture", NULL, "ASP1TX6" }, + { "ASP1 Capture", NULL, "ASP1TX7" }, + { "ASP1 Capture", NULL, "ASP1TX8" }, + + { "ASP1RX1", NULL, "ASP1 Playback" }, + { "ASP1RX2", NULL, "ASP1 Playback" }, + { "ASP1RX3", NULL, "ASP1 Playback" }, + { "ASP1RX4", NULL, "ASP1 Playback" }, + { "ASP1RX5", NULL, "ASP1 Playback" }, + { "ASP1RX6", NULL, "ASP1 Playback" }, + { "ASP1RX7", NULL, "ASP1 Playback" }, + { "ASP1RX8", NULL, "ASP1 Playback" }, + + { "ASP2 Capture", NULL, "ASP2TX1" }, + { "ASP2 Capture", NULL, "ASP2TX2" }, + { "ASP2 Capture", NULL, "ASP2TX3" }, + { "ASP2 Capture", NULL, "ASP2TX4" }, + + { "ASP2RX1", NULL, "ASP2 Playback" }, + { "ASP2RX2", NULL, "ASP2 Playback" }, + { "ASP2RX3", NULL, "ASP2 Playback" }, + { "ASP2RX4", NULL, "ASP2 Playback" }, + + { "ASP1 Playback", NULL, "SYSCLK" }, + { "ASP2 Playback", NULL, "SYSCLK" }, + + { "ASP1 Capture", NULL, "SYSCLK" }, + { "ASP2 Capture", NULL, "SYSCLK" }, + + { "IN1L Mux", "Analog 1", "IN1LN_1" }, + { "IN1L Mux", "Analog 2", "IN1LN_2" }, + { "IN1L Mux", "Analog 1", "IN1LP_1" }, + { "IN1L Mux", "Analog 2", "IN1LP_2" }, + { "IN1R Mux", "Analog 1", "IN1RN_1" }, + { "IN1R Mux", "Analog 2", "IN1RN_2" }, + { "IN1R Mux", "Analog 1", "IN1RP_1" }, + { "IN1R Mux", "Analog 2", "IN1RP_2" }, + + { "IN1L PGA", NULL, "IN1L Mode" }, + { "IN1R PGA", NULL, "IN1R Mode" }, + + { "IN1L Mode", "Analog", "IN1L Mux" }, + { "IN1R Mode", "Analog", "IN1R Mux" }, + + { "IN1L Mode", "Digital", "IN1_PDMCLK" }, + { "IN1L Mode", "Digital", "IN1_PDMDATA" }, + { "IN1R Mode", "Digital", "IN1_PDMCLK" }, + { "IN1R Mode", "Digital", "IN1_PDMDATA" }, + + { "IN1L PGA", NULL, "VOUT_MIC" }, + { "IN1R PGA", NULL, "VOUT_MIC" }, + + { "IN2L PGA", NULL, "VOUT_MIC" }, + { "IN2R PGA", NULL, "VOUT_MIC" }, + + { "IN2L PGA", NULL, "IN2_PDMCLK" }, + { "IN2R PGA", NULL, "IN2_PDMCLK" }, + { "IN2L PGA", NULL, "IN2_PDMDATA" }, + { "IN2R PGA", NULL, "IN2_PDMDATA" }, + + { "Ultrasonic 1", NULL, "Ultrasonic 1 Input" }, + { "Ultrasonic 2", NULL, "Ultrasonic 2 Input" }, + + { "Ultrasonic 1 Input", "IN1L", "IN1L PGA" }, + { "Ultrasonic 1 Input", "IN1R", "IN1R PGA" }, + { "Ultrasonic 1 Input", "IN2L", "IN2L PGA" }, + { "Ultrasonic 1 Input", "IN2R", "IN2R PGA" }, + + { "Ultrasonic 2 Input", "IN1L", "IN1L PGA" }, + { "Ultrasonic 2 Input", "IN1R", "IN1R PGA" }, + { "Ultrasonic 2 Input", "IN2L", "IN2L PGA" }, + { "Ultrasonic 2 Input", "IN2R", "IN2R PGA" }, + + { "Ultrasonic 1 Detect", "Switch", "Ultrasonic 1 Input" }, + { "Ultrasonic 2 Detect", "Switch", "Ultrasonic 2 Input" }, + + { "Ultrasonic Dummy Output", NULL, "Ultrasonic 1 Detect" }, + { "Ultrasonic Dummy Output", NULL, "Ultrasonic 2 Detect" }, + + CS48L32_MIXER_ROUTES("ASP1TX1", "ASP1TX1"), + CS48L32_MIXER_ROUTES("ASP1TX2", "ASP1TX2"), + CS48L32_MIXER_ROUTES("ASP1TX3", "ASP1TX3"), + CS48L32_MIXER_ROUTES("ASP1TX4", "ASP1TX4"), + CS48L32_MIXER_ROUTES("ASP1TX5", "ASP1TX5"), + CS48L32_MIXER_ROUTES("ASP1TX6", "ASP1TX6"), + CS48L32_MIXER_ROUTES("ASP1TX7", "ASP1TX7"), + CS48L32_MIXER_ROUTES("ASP1TX8", "ASP1TX8"), + + CS48L32_MIXER_ROUTES("ASP2TX1", "ASP2TX1"), + CS48L32_MIXER_ROUTES("ASP2TX2", "ASP2TX2"), + CS48L32_MIXER_ROUTES("ASP2TX3", "ASP2TX3"), + CS48L32_MIXER_ROUTES("ASP2TX4", "ASP2TX4"), + + CS48L32_MIXER_ROUTES("EQ1", "EQ1"), + CS48L32_MIXER_ROUTES("EQ2", "EQ2"), + CS48L32_MIXER_ROUTES("EQ3", "EQ3"), + CS48L32_MIXER_ROUTES("EQ4", "EQ4"), + + CS48L32_MIXER_ROUTES("DRC1L", "DRC1L"), + CS48L32_MIXER_ROUTES("DRC1R", "DRC1R"), + CS48L32_MIXER_ROUTES("DRC2L", "DRC2L"), + CS48L32_MIXER_ROUTES("DRC2R", "DRC2R"), + + CS48L32_MIXER_ROUTES("LHPF1", "LHPF1"), + CS48L32_MIXER_ROUTES("LHPF2", "LHPF2"), + CS48L32_MIXER_ROUTES("LHPF3", "LHPF3"), + CS48L32_MIXER_ROUTES("LHPF4", "LHPF4"), + + CS48L32_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + CS48L32_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + CS48L32_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + CS48L32_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + CS48L32_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + CS48L32_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + CS48L32_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + CS48L32_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + CS48L32_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + CS48L32_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + CS48L32_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + CS48L32_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + CS48L32_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"), + CS48L32_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"), + + CS48L32_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"), + CS48L32_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"), + + CS48L32_DSP_ROUTES_1_8_SYSCLK("DSP1"), + + { "DSP Trigger Out", NULL, "DSP1 Trigger Output" }, + + { "DSP1 Trigger Output", "Switch", "DSP1" }, + + { "AUXPDM1 Analog Input", "IN1L", "IN1L PGA" }, + { "AUXPDM1 Analog Input", "IN1R", "IN1R PGA" }, + + { "AUXPDM2 Analog Input", "IN1L", "IN1L PGA" }, + { "AUXPDM2 Analog Input", "IN1R", "IN1R PGA" }, + + { "AUXPDM1 Input", "Analog", "AUXPDM1 Analog Input" }, + { "AUXPDM1 Input", "IN1 Digital", "IN1L PGA" }, + { "AUXPDM1 Input", "IN1 Digital", "IN1R PGA" }, + { "AUXPDM1 Input", "IN2 Digital", "IN2L PGA" }, + { "AUXPDM1 Input", "IN2 Digital", "IN2R PGA" }, + + { "AUXPDM2 Input", "Analog", "AUXPDM2 Analog Input" }, + { "AUXPDM2 Input", "IN1 Digital", "IN1L PGA" }, + { "AUXPDM2 Input", "IN1 Digital", "IN1R PGA" }, + { "AUXPDM2 Input", "IN2 Digital", "IN2L PGA" }, + { "AUXPDM2 Input", "IN2 Digital", "IN2R PGA" }, + + { "AUXPDM1 Output", "Switch", "AUXPDM1 Input" }, + { "AUXPDM1_CLK", NULL, "AUXPDM1 Output" }, + { "AUXPDM1_DOUT", NULL, "AUXPDM1 Output" }, + + { "AUXPDM2 Output", "Switch", "AUXPDM2 Input" }, + { "AUXPDM2_CLK", NULL, "AUXPDM2 Output" }, + { "AUXPDM2_DOUT", NULL, "AUXPDM2 Output" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" }, + { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" }, + { "DRC1 Activity Output", "Switch", "DRC1L" }, + { "DRC1 Activity Output", "Switch", "DRC1R" }, + { "DRC2 Activity Output", "Switch", "DRC2L" }, + { "DRC2 Activity Output", "Switch", "DRC2R" }, +}; + +static int cs48l32_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + + if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-trace") && + strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-voicectrl")) { + dev_err(cs48l32_codec->core.dev, "No suitable compressed stream for DAI '%s'\n", + snd_soc_rtd_to_codec(rtd, 0)->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&cs48l32_codec->dsp, stream); +} + +static const struct snd_compress_ops cs48l32_compress_ops = { + .open = &cs48l32_compr_open, + .free = &wm_adsp_compr_free, + .set_params = &wm_adsp_compr_set_params, + .get_caps = &wm_adsp_compr_get_caps, + .trigger = &wm_adsp_compr_trigger, + .pointer = &wm_adsp_compr_pointer, + .copy = &wm_adsp_compr_copy, +}; + +static const struct snd_soc_dai_ops cs48l32_compress_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + +static struct snd_soc_dai_driver cs48l32_dai[] = { + { + .name = "cs48l32-asp1", + .id = 1, + .base = CS48L32_ASP1_ENABLES1, + .playback = { + .stream_name = "ASP1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .capture = { + .stream_name = "ASP1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .ops = &cs48l32_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs48l32-asp2", + .id = 2, + .base = CS48L32_ASP2_ENABLES1, + .playback = { + .stream_name = "ASP2 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .capture = { + .stream_name = "ASP2 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .ops = &cs48l32_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs48l32-cpu-trace", + .id = 3, + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .ops = &cs48l32_compress_dai_ops, + }, + { + .name = "cs48l32-dsp-trace", + .id = 4, + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + }, + { + .name = "cs48l32-cpu-voicectrl", + .id = 5, + .capture = { + .stream_name = "Voice Ctrl CPU", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .ops = &cs48l32_compress_dai_ops, + }, + { + .name = "cs48l32-dsp-voicectrl", + .id = 6, + .capture = { + .stream_name = "Voice Ctrl DSP", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + }, +}; + +static int cs48l32_init_inputs(struct snd_soc_component *component) +{ + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs48l32_codec->core.regmap; + unsigned int ana_mode_l, ana_mode_r, dig_mode; + int i; + + /* + * Initialize input modes from the A settings. For muxed inputs the + * B settings will be applied if the mux is changed + */ + switch (cs48l32_codec->in_type[0][0]) { + default: + case CS48L32_IN_TYPE_DIFF: + ana_mode_l = 0; + break; + case CS48L32_IN_TYPE_SE: + ana_mode_l = 1 << CS48L32_INx_SRC_SHIFT; + break; + } + + switch (cs48l32_codec->in_type[1][0]) { + default: + case CS48L32_IN_TYPE_DIFF: + ana_mode_r = 0; + break; + case CS48L32_IN_TYPE_SE: + ana_mode_r = 1 << CS48L32_INx_SRC_SHIFT; + break; + } + + dev_dbg(cs48l32_codec->core.dev, "IN1_1 Analogue mode=#%x,#%x\n", + ana_mode_l, ana_mode_r); + + regmap_update_bits(regmap, + CS48L32_IN1L_CONTROL1, + CS48L32_INx_SRC_MASK, + ana_mode_l); + + regmap_update_bits(regmap, + CS48L32_IN1R_CONTROL1, + CS48L32_INx_SRC_MASK, + ana_mode_r); + + for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) { + dig_mode = cs48l32_codec->pdm_sup[i] << CS48L32_IN1_PDM_SUP_SHIFT; + + dev_dbg(cs48l32_codec->core.dev, "IN%d PDM_SUP=#%x\n", i + 1, dig_mode); + + regmap_update_bits(regmap, + CS48L32_INPUT1_CONTROL1 + (i * 0x40), + CS48L32_IN1_PDM_SUP_MASK, dig_mode); + } + + return 0; +} + +static int cs48l32_init_dai(struct cs48l32_codec *cs48l32_codec, int id) +{ + struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[id]; + + dai_priv->clk = CS48L32_CLK_SYSCLK_1; + dai_priv->constraint = cs48l32_constraint; + + return 0; +} + +static int cs48l32_init_eq(struct cs48l32_codec *cs48l32_codec) +{ + struct regmap *regmap = cs48l32_codec->core.regmap; + unsigned int reg = CS48L32_EQ1_BAND1_COEFF1, mode; + __be16 *data; + int i, ret; + + ret = regmap_read(regmap, CS48L32_EQ_CONTROL2, &mode); + if (ret < 0) { + dev_err(cs48l32_codec->core.dev, "Error reading EQ mode: %d\n", ret); + goto out; + } + + for (i = 0; i < 4; ++i) { + cs48l32_codec->eq_mode[i] = (mode >> i) & 0x1; + + data = &cs48l32_codec->eq_coefficients[i][0]; + ret = regmap_raw_read(regmap, reg + (i * 68), data, + CS48L32_EQ_BLOCK_SZ); + if (ret < 0) { + dev_err(cs48l32_codec->core.dev, + "Error reading EQ coefficients: %d\n", ret); + goto out; + } + } + +out: + return ret; +} + +static int cs48l32_component_probe(struct snd_soc_component *component) +{ + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + int i, ret; + + snd_soc_component_init_regmap(component, cs48l32_codec->core.regmap); + + ret = cs48l32_init_inputs(component); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(cs48l32_dai); i++) + cs48l32_init_dai(cs48l32_codec, i); + + ret = cs48l32_init_eq(cs48l32_codec); + if (ret) + return ret; + + wm_adsp2_component_probe(&cs48l32_codec->dsp, component); + + /* Unmask DSP IRQs */ + regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7, + CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK); + regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9, + CS48L32_DSP1_IRQ0_EINT1_MASK); + + return 0; +} + +static void cs48l32_component_remove(struct snd_soc_component *component) +{ + struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component); + + /* Mask DSP IRQs */ + regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7, + CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK); + regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9, + CS48L32_DSP1_IRQ0_EINT1_MASK); + + wm_adsp2_component_remove(&cs48l32_codec->dsp, component); +} + +static const struct snd_soc_component_driver cs48l32_soc_component_drv = { + .probe = &cs48l32_component_probe, + .remove = &cs48l32_component_remove, + .set_sysclk = &cs48l32_set_sysclk, + .set_pll = &cs48l32_set_fll, + .name = "cs48l32-codec", + .compress_ops = &cs48l32_compress_ops, + .controls = cs48l32_snd_controls, + .num_controls = ARRAY_SIZE(cs48l32_snd_controls), + .dapm_widgets = cs48l32_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs48l32_dapm_widgets), + .dapm_routes = cs48l32_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs48l32_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cs48l32_prop_read_u32_array(struct cs48l32_codec *cs48l32_codec, + const char *propname, + u32 *dest, + int n_max) +{ + struct cs48l32 *cs48l32 = &cs48l32_codec->core; + int ret; + + ret = device_property_read_u32_array(cs48l32->dev, propname, dest, n_max); + if (ret == -EINVAL) + return -ENOENT; + + if (ret < 0) + return dev_err_probe(cs48l32->dev, ret, "%s malformed\n", propname); + + return 0; +} + +static void cs48l32_prop_get_in_type(struct cs48l32_codec *cs48l32_codec) +{ + const char *propname = "cirrus,in-type"; + u32 tmp[CS48L32_MAX_ANALOG_INPUT * CS48L32_MAX_IN_MUX_WAYS]; + int i, in_idx, mux_way_idx, ret; + + static_assert(ARRAY_SIZE(tmp) == + ARRAY_SIZE(cs48l32_codec->in_type) * ARRAY_SIZE(cs48l32_codec->in_type[0])); + + ret = cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp)); + if (ret < 0) + return; + + in_idx = 0; + mux_way_idx = 0; + for (i = 0; i < ARRAY_SIZE(tmp); ++i) { + switch (tmp[i]) { + case CS48L32_IN_TYPE_DIFF: + case CS48L32_IN_TYPE_SE: + cs48l32_codec->in_type[in_idx][mux_way_idx] = tmp[i]; + break; + default: + dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n", + propname, tmp[i]); + break; + } + + /* + * Property array is [mux_way][in_channel]. Swap to + * [in_channel][mux_way] for convenience. + */ + if (++in_idx == ARRAY_SIZE(cs48l32_codec->in_type)) { + in_idx = 0; + ++mux_way_idx; + } + } +} + +static void cs48l32_prop_get_pdm_sup(struct cs48l32_codec *cs48l32_codec) +{ + const char *propname = "cirrus,pdm-sup"; + u32 tmp[CS48L32_MAX_ANALOG_INPUT]; + int i; + + static_assert(ARRAY_SIZE(tmp) == ARRAY_SIZE(cs48l32_codec->pdm_sup)); + + cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp)); + + for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) { + switch (tmp[i]) { + case CS48L32_PDM_SUP_VOUT_MIC: + case CS48L32_PDM_SUP_MICBIAS1: + cs48l32_codec->pdm_sup[i] = tmp[i]; + break; + default: + dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n", + propname, cs48l32_codec->pdm_sup[i]); + break; + } + } +} + +static void cs48l32_handle_properties(struct cs48l32_codec *cs48l32_codec) +{ + cs48l32_prop_get_in_type(cs48l32_codec); + cs48l32_prop_get_pdm_sup(cs48l32_codec); +} + +static int cs48l32_request_interrupt(struct cs48l32_codec *cs48l32_codec) +{ + int irq = cs48l32_codec->core.irq; + int ret; + + if (irq < 1) + return 0; + + /* + * Don't use devm because this must be freed before destroying the + * rest of the driver + */ + ret = request_threaded_irq(irq, NULL, cs48l32_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "cs48l32", cs48l32_codec); + if (ret) + return dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to get IRQ\n"); + + return 0; +} + +static int cs48l32_create_codec_component(struct cs48l32_codec *cs48l32_codec) +{ + struct wm_adsp *dsp; + int ret; + + ASSERT_STRUCT_OFFSET(struct cs48l32_codec, dsp, 0); + static_assert(ARRAY_SIZE(cs48l32_dai) == ARRAY_SIZE(cs48l32_codec->dai)); + + cs48l32_handle_properties(cs48l32_codec); + + dsp = &cs48l32_codec->dsp; + dsp->part = "cs48l32"; + dsp->cs_dsp.num = 1; + dsp->cs_dsp.type = WMFW_HALO; + dsp->cs_dsp.rev = 0; + dsp->cs_dsp.dev = cs48l32_codec->core.dev; + dsp->cs_dsp.regmap = cs48l32_codec->core.regmap; + dsp->cs_dsp.base = CS48L32_DSP1_CLOCK_FREQ; + dsp->cs_dsp.base_sysinfo = CS48L32_DSP1_SYS_INFO_ID; + dsp->cs_dsp.mem = cs48l32_dsp1_regions; + dsp->cs_dsp.num_mems = ARRAY_SIZE(cs48l32_dsp1_regions); + dsp->pre_run = cs48l32_dsp_pre_run; + + ret = wm_halo_init(dsp); + if (ret != 0) + return ret; + + cs48l32_codec->fll.codec = cs48l32_codec; + cs48l32_codec->fll.id = 1; + cs48l32_codec->fll.base = CS48L32_FLL1_CONTROL1; + cs48l32_codec->fll.sts_addr = CS48L32_IRQ1_STS_6; + cs48l32_codec->fll.sts_mask = CS48L32_FLL1_LOCK_STS1_MASK; + cs48l32_init_fll(&cs48l32_codec->fll); + + ret = cs48l32_request_interrupt(cs48l32_codec); + if (ret) + goto err_dsp; + + ret = devm_snd_soc_register_component(cs48l32_codec->core.dev, + &cs48l32_soc_component_drv, + cs48l32_dai, + ARRAY_SIZE(cs48l32_dai)); + if (ret < 0) { + dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to register component\n"); + goto err_dsp; + } + + return 0; + +err_dsp: + wm_adsp2_remove(&cs48l32_codec->dsp); + + return ret; +} + +static int cs48l32_wait_for_boot(struct cs48l32 *cs48l32) +{ + unsigned int val; + int ret; + + ret = regmap_read_poll_timeout(cs48l32->regmap, CS48L32_IRQ1_EINT_2, val, + ((val < 0xffffffff) && (val & CS48L32_BOOT_DONE_EINT1_MASK)), + 1000, CS48L32_BOOT_TIMEOUT_US); + if (ret) { + dev_err(cs48l32->dev, "BOOT_DONE timed out\n"); + return -ETIMEDOUT; + } + + ret = regmap_read(cs48l32->regmap, CS48L32_MCU_CTRL1, &val); + if (ret) { + dev_err(cs48l32->dev, "Failed to read MCU_CTRL1: %d\n", ret); + return ret; + } + + if (val & BIT(CS48L32_MCU_STS_SHIFT)) { + dev_err(cs48l32->dev, "MCU boot failed\n"); + return -EIO; + } + + pm_runtime_mark_last_busy(cs48l32->dev); + + return 0; +} + +static int cs48l32_soft_reset(struct cs48l32 *cs48l32) +{ + int ret; + + ret = regmap_write(cs48l32->regmap, CS48L32_SFT_RESET, CS48L32_SFT_RESET_MAGIC); + if (ret != 0) { + dev_err(cs48l32->dev, "Failed to write soft reset: %d\n", ret); + return ret; + } + + usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000); + + return 0; +} + +static void cs48l32_enable_hard_reset(struct cs48l32 *cs48l32) +{ + if (cs48l32->reset_gpio) + gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 0); +} + +static void cs48l32_disable_hard_reset(struct cs48l32 *cs48l32) +{ + if (cs48l32->reset_gpio) { + gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 1); + usleep_range(CS48L32_HARD_RESET_MIN_US, CS48L32_HARD_RESET_MIN_US + 1000); + } +} + +static int cs48l32_runtime_resume(struct device *dev) +{ + struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev); + struct cs48l32 *cs48l32 = &cs48l32_codec->core; + unsigned int val; + int ret; + + ret = regulator_enable(cs48l32->vdd_d); + if (ret) { + dev_err(cs48l32->dev, "Failed to enable VDD_D: %d\n", ret); + return ret; + } + + usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000); + + regcache_cache_only(cs48l32->regmap, false); + + ret = cs48l32_wait_for_boot(cs48l32); + if (ret) + goto err; + + /* Check whether registers reset during suspend */ + regmap_read(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, &val); + if (!val) + regcache_mark_dirty(cs48l32->regmap); + else + dev_dbg(cs48l32->dev, "Did not reset during suspend\n"); + + ret = regcache_sync(cs48l32->regmap); + if (ret) { + dev_err(cs48l32->dev, "Failed to restore register cache\n"); + goto err; + } + + return 0; + +err: + regcache_cache_only(cs48l32->regmap, true); + regulator_disable(cs48l32->vdd_d); + + return ret; +} + +static int cs48l32_runtime_suspend(struct device *dev) +{ + struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev); + struct cs48l32 *cs48l32 = &cs48l32_codec->core; + + /* Flag to detect if the registers reset during suspend */ + regmap_write(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, 1); + + regcache_cache_only(cs48l32->regmap, true); + regulator_disable(cs48l32->vdd_d); + + return 0; +} + +static const struct dev_pm_ops cs48l32_pm_ops = { + RUNTIME_PM_OPS(cs48l32_runtime_suspend, cs48l32_runtime_resume, NULL) +}; + +static int cs48l32_configure_clk32k(struct cs48l32 *cs48l32) +{ + int ret = 0; + + ret = clk_prepare_enable(cs48l32->mclk1); + if (ret) + return dev_err_probe(cs48l32->dev, ret, "Failed to enable 32k clock\n"); + + ret = regmap_update_bits(cs48l32->regmap, CS48L32_CLOCK32K, + CS48L32_CLK_32K_EN_MASK | CS48L32_CLK_32K_SRC_MASK, + CS48L32_CLK_32K_EN_MASK | CS48L32_32K_MCLK1); + if (ret) { + clk_disable_unprepare(cs48l32->mclk1); + return dev_err_probe(cs48l32->dev, ret, "Failed to init 32k clock\n"); + } + + return 0; +} + +static int cs48l32_get_clocks(struct cs48l32 *cs48l32) +{ + cs48l32->mclk1 = devm_clk_get_optional(cs48l32->dev, "mclk1"); + if (IS_ERR(cs48l32->mclk1)) + return dev_err_probe(cs48l32->dev, PTR_ERR(cs48l32->mclk1), + "Failed to get mclk1\n"); + + return 0; +} + +static int cs48l32_get_reset_gpio(struct cs48l32 *cs48l32) +{ + struct gpio_desc *reset; + + reset = devm_gpiod_get_optional(cs48l32->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset)) + return dev_err_probe(cs48l32->dev, PTR_ERR(reset), "Failed to request /RESET\n"); + + /* ACPI can override the GPIOD_OUT_LOW so ensure it starts low */ + gpiod_set_raw_value_cansleep(reset, 0); + + cs48l32->reset_gpio = reset; + + return 0; +} + +static int cs48l32_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct cs48l32_codec *cs48l32_codec; + struct cs48l32 *cs48l32; + unsigned int hwid, rev, otp_rev; + int i, ret; + + cs48l32_codec = devm_kzalloc(&spi->dev, sizeof(*cs48l32_codec), GFP_KERNEL); + if (!cs48l32_codec) + return -ENOMEM; + + cs48l32 = &cs48l32_codec->core; + cs48l32->dev = dev; + cs48l32->irq = spi->irq; + mutex_init(&cs48l32_codec->rate_lock); + cs48l32_codec->in_vu_reg = CS48L32_INPUT_CONTROL3; + + dev_set_drvdata(cs48l32->dev, cs48l32_codec); + + ret = cs48l32_create_regmap(spi, cs48l32); + if (ret) + return dev_err_probe(&spi->dev, ret, "Failed to allocate regmap\n"); + + regcache_cache_only(cs48l32->regmap, true); + + ret = cs48l32_get_reset_gpio(cs48l32); + if (ret) + return ret; + + ret = cs48l32_get_clocks(cs48l32); + if (ret) + return ret; + + static_assert(ARRAY_SIZE(cs48l32_core_supplies) == ARRAY_SIZE(cs48l32->core_supplies)); + for (i = 0; i < ARRAY_SIZE(cs48l32->core_supplies); i++) + cs48l32->core_supplies[i].supply = cs48l32_core_supplies[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs48l32->core_supplies), + cs48l32->core_supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to request core supplies\n"); + + cs48l32->vdd_d = devm_regulator_get(cs48l32->dev, "vdd-d"); + if (IS_ERR(cs48l32->vdd_d)) + return dev_err_probe(dev, PTR_ERR(cs48l32->vdd_d), "Failed to request vdd-d\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable core supplies\n"); + + ret = regulator_enable(cs48l32->vdd_d); + if (ret) { + dev_err(dev, "Failed to enable vdd-d: %d\n", ret); + goto err_enable; + } + + cs48l32_disable_hard_reset(cs48l32); + + regcache_cache_only(cs48l32->regmap, false); + + /* If we don't have a reset GPIO use a soft reset */ + if (!cs48l32->reset_gpio) { + ret = cs48l32_soft_reset(cs48l32); + if (ret) + goto err_reset; + } + + ret = cs48l32_wait_for_boot(cs48l32); + if (ret) { + dev_err(cs48l32->dev, "Device failed initial boot: %d\n", ret); + goto err_reset; + } + + ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &hwid); + if (ret) { + dev_err(dev, "Failed to read ID register: %d\n", ret); + goto err_reset; + } + hwid &= CS48L32_DEVID_MASK; + + switch (hwid) { + case CS48L32_SILICON_ID: + break; + default: + ret = -ENODEV; + dev_err_probe(cs48l32->dev, ret, "Unknown device ID: %#x\n", hwid); + goto err_reset; + } + + ret = regmap_read(cs48l32->regmap, CS48L32_REVID, &rev); + if (ret) { + dev_err(dev, "Failed to read revision register: %d\n", ret); + goto err_reset; + } + rev &= CS48L32_AREVID_MASK | CS48L32_MTLREVID_MASK; + + ret = regmap_read(cs48l32->regmap, CS48L32_OTPID, &otp_rev); + if (ret) { + dev_err(dev, "Failed to read OTP revision register: %d\n", ret); + goto err_reset; + } + otp_rev &= CS48L32_OTPID_MASK; + + dev_info(dev, "CS48L%x revision %X%u OTP%u\n", hwid & 0xff, + rev >> CS48L32_AREVID_SHIFT, rev & CS48L32_MTLREVID_MASK, otp_rev); + + /* Apply hardware patch */ + ret = cs48l32_apply_patch(cs48l32); + if (ret) { + dev_err(cs48l32->dev, "Failed to apply patch %d\n", ret); + goto err_reset; + } + + /* BOOT_DONE interrupt is unmasked by default, so mask it */ + ret = regmap_set_bits(cs48l32->regmap, CS48L32_IRQ1_MASK_2, CS48L32_BOOT_DONE_EINT1_MASK); + + ret = cs48l32_configure_clk32k(cs48l32); + if (ret) + goto err_reset; + + pm_runtime_set_active(cs48l32->dev); + pm_runtime_set_autosuspend_delay(cs48l32->dev, 100); + pm_runtime_use_autosuspend(cs48l32->dev); + pm_runtime_enable(cs48l32->dev); + + ret = cs48l32_create_codec_component(cs48l32_codec); + if (ret) + goto err_clk32k; + + return 0; + +err_clk32k: + clk_disable_unprepare(cs48l32->mclk1); +err_reset: + cs48l32_enable_hard_reset(cs48l32); + regulator_disable(cs48l32->vdd_d); +err_enable: + regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies); + + return ret; +} + +static void cs48l32_spi_remove(struct spi_device *spi) +{ + struct cs48l32_codec *cs48l32_codec = spi_get_drvdata(spi); + struct cs48l32 *cs48l32 = &cs48l32_codec->core; + + /* Remove IRQ handler before destroying anything else */ + if (cs48l32->irq >= 1) + free_irq(cs48l32->irq, cs48l32_codec); + + pm_runtime_disable(cs48l32->dev); + regulator_disable(cs48l32->vdd_d); + clk_disable_unprepare(cs48l32->mclk1); + cs48l32_enable_hard_reset(cs48l32); + regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies); + + mutex_destroy(&cs48l32_codec->rate_lock); +} + +static const struct of_device_id cs48l32_of_match[] = { + { .compatible = "cirrus,cs48l32", }, + {}, +}; + +static const struct spi_device_id cs48l32_spi_ids[] = { + { "cs48l32", }, + { }, +}; +MODULE_DEVICE_TABLE(spi, cs48l32_spi_ids); + +static struct spi_driver cs48l32_spi_driver = { + .driver = { + .name = "cs48l32", + .pm = pm_ptr(&cs48l32_pm_ops), + .of_match_table = cs48l32_of_match, + }, + .probe = &cs48l32_spi_probe, + .remove = &cs48l32_spi_remove, + .id_table = cs48l32_spi_ids, +}; +module_spi_driver(cs48l32_spi_driver); + +MODULE_DESCRIPTION("CS48L32 ASoC codec driver"); +MODULE_AUTHOR("Stuart Henderson <stuarth@opensource.cirrus.com>"); +MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs48l32.h b/sound/soc/codecs/cs48l32.h new file mode 100644 index 000000000000..c1b4e13feae4 --- /dev/null +++ b/sound/soc/codecs/cs48l32.h @@ -0,0 +1,403 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Cirrus Logic CS48L32 audio DSP. + * + * Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ +#ifndef SND_SOC_CS48L32_H +#define SND_SOC_CS48L32_H + +#include <linux/bits.h> +#include <sound/soc.h> +#include "wm_adsp.h" + +#define CS48L32_SILICON_ID 0x48a32 + +#define CS48L32_32K_MCLK1 0 + +#define CS48L32_SFT_RESET_MAGIC 0x5a000000 +#define CS48L32_SOFT_RESET_US 2000 +#define CS48L32_HARD_RESET_MIN_US 1000 + +#define CS48L32_SEEN_BOOT_DONE BIT(0) +#define CS48L32_BOOT_TIMEOUT_US 25000 + +#define CS48L32_ASP_ENABLES1 0x00 +#define CS48L32_ASP_CONTROL1 0x04 +#define CS48L32_ASP_CONTROL2 0x08 +#define CS48L32_ASP_CONTROL3 0x0c +#define CS48L32_ASP_FRAME_CONTROL1 0x10 +#define CS48L32_ASP_FRAME_CONTROL2 0x14 +#define CS48L32_ASP_FRAME_CONTROL5 0x20 +#define CS48L32_ASP_FRAME_CONTROL6 0x24 +#define CS48L32_ASP_DATA_CONTROL1 0x30 +#define CS48L32_ASP_DATA_CONTROL5 0x40 +#define CS48L32_SYSCLK_RATE_6MHZ 0 +#define CS48L32_SYSCLK_RATE_12MHZ 1 +#define CS48L32_SYSCLK_RATE_24MHZ 2 +#define CS48L32_SYSCLK_RATE_49MHZ 3 +#define CS48L32_SYSCLK_RATE_98MHZ 4 +#define CS48L32_FLLHJ_INT_MAX_N 1023 +#define CS48L32_FLLHJ_INT_MIN_N 1 +#define CS48L32_FLLHJ_FRAC_MAX_N 255 +#define CS48L32_FLLHJ_FRAC_MIN_N 2 +#define CS48L32_FLLHJ_LP_INT_MODE_THRESH 100000 +#define CS48L32_FLLHJ_LOW_THRESH 192000 +#define CS48L32_FLLHJ_MID_THRESH 1152000 +#define CS48L32_FLLHJ_MAX_THRESH 13000000 +#define CS48L32_FLLHJ_LOW_GAINS 0x23f0 +#define CS48L32_FLLHJ_MID_GAINS 0x22f2 +#define CS48L32_FLLHJ_HIGH_GAINS 0x21f0 +#define CS48L32_FLL_MAX_FOUT 50000000 +#define CS48L32_FLL_MAX_REFDIV 8 +#define CS48L32_FLL_CONTROL1_OFFS 0x00 +#define CS48L32_FLL_CONTROL2_OFFS 0x04 +#define CS48L32_FLL_CONTROL3_OFFS 0x08 +#define CS48L32_FLL_CONTROL4_OFFS 0x0c +#define CS48L32_FLL_CONTROL5_OFFS 0x10 +#define CS48L32_FLL_CONTROL6_OFFS 0x14 +#define CS48L32_FLL_DIGITAL_TEST2_OFFS 0x34 +#define CS48L32_FLL_GPIO_CLOCK_OFFS 0xa0 +#define CS48L32_DSP_CLOCK_FREQ_OFFS 0x00000 +#define CS48L32_ASP_FMT_DSP_MODE_A 0 +#define CS48L32_ASP_FMT_DSP_MODE_B 1 +#define CS48L32_ASP_FMT_I2S_MODE 2 +#define CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE 3 +#define CS48L32_HALO_SAMPLE_RATE_RX1 0x00080 +#define CS48L32_HALO_SAMPLE_RATE_TX1 0x00280 +#define CS48L32_HALO_DSP_RATE_MASK 0x1f + +#define CS48L32_PDMCLK_SRC_IN1_PDMCLK 0x0 +#define CS48L32_PDMCLK_SRC_IN2_PDMCLK 0x1 +#define CS48L32_PDMCLK_SRC_IN3_PDMCLK 0x2 +#define CS48L32_PDMCLK_SRC_IN4_PDMCLK 0x3 +#define CS48L32_PDMCLK_SRC_AUXPDM1_CLK 0x8 +#define CS48L32_PDMCLK_SRC_AUXPDM2_CLK 0x9 + +#define CS48L32_MAX_DAI 6 +#define CS48L32_MAX_INPUT 4 +#define CS48L32_MAX_ANALOG_INPUT 2 +#define CS48L32_MAX_IN_MUX_WAYS 2 +#define CS48L32_MAX_ASP 2 + +#define CS48L32_EQ_BLOCK_SZ 60 +#define CS48L32_N_EQ_BLOCKS 4 + +#define CS48L32_DSP_N_RX_CHANNELS 8 +#define CS48L32_DSP_N_TX_CHANNELS 8 + +#define CS48L32_LHPF_MAX_COEFF 4095 +#define CS48L32_EQ_MAX_COEFF 4095 + +#define CS48L32_MIXER_CONTROLS(name, base) \ + SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base, \ + CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs48l32_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 4, \ + CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs48l32_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 8, \ + CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs48l32_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 12, \ + CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs48l32_mixer_tlv) + +#define CS48L32_MUX_ENUM_DECL(name, reg) \ + SOC_VALUE_ENUM_SINGLE_DECL( \ + name, reg, 0, CS48L32_MIXER_SRC_MASK, \ + cs48l32_mixer_texts, cs48l32_mixer_values) + +#define CS48L32_MUX_CTL_DECL(name) \ + const struct snd_kcontrol_new name##_mux = SOC_DAPM_ENUM("Route", name##_enum) + +#define CS48L32_MUX_ENUMS(name, base_reg) \ + static CS48L32_MUX_ENUM_DECL(name##_enum, base_reg); \ + static CS48L32_MUX_CTL_DECL(name) + +#define CS48L32_MIXER_ENUMS(name, base_reg) \ + CS48L32_MUX_ENUMS(name##_in1, base_reg); \ + CS48L32_MUX_ENUMS(name##_in2, base_reg + 4); \ + CS48L32_MUX_ENUMS(name##_in3, base_reg + 8); \ + CS48L32_MUX_ENUMS(name##_in4, base_reg + 12) + +#define CS48L32_MUX(name, ctrl) SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl) + +#define CS48L32_MUX_WIDGETS(name, name_str) CS48L32_MUX(name_str " Input 1", &name##_mux) + +#define CS48L32_MIXER_WIDGETS(name, name_str) \ + CS48L32_MUX(name_str " Input 1", &name##_in1_mux), \ + CS48L32_MUX(name_str " Input 2", &name##_in2_mux), \ + CS48L32_MUX(name_str " Input 3", &name##_in3_mux), \ + CS48L32_MUX(name_str " Input 4", &name##_in4_mux), \ + SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0) + +#define CS48L32_MUX_ROUTES(widget, name) \ + { widget, NULL, name " Input 1" }, \ + CS48L32_MIXER_INPUT_ROUTES(name " Input 1") + +#define CS48L32_MIXER_ROUTES(widget, name) \ + { widget, NULL, name " Mixer" }, \ + { name " Mixer", NULL, name " Input 1" }, \ + { name " Mixer", NULL, name " Input 2" }, \ + { name " Mixer", NULL, name " Input 3" }, \ + { name " Mixer", NULL, name " Input 4" }, \ + CS48L32_MIXER_INPUT_ROUTES(name " Input 1"), \ + CS48L32_MIXER_INPUT_ROUTES(name " Input 2"), \ + CS48L32_MIXER_INPUT_ROUTES(name " Input 3"), \ + CS48L32_MIXER_INPUT_ROUTES(name " Input 4") + +#define CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \ + { name, NULL, name " Preloader" }, \ + { name, NULL, "SYSCLK" }, \ + { name " Preload", NULL, name " Preloader" }, \ + CS48L32_MIXER_ROUTES(name, name "RX1"), \ + CS48L32_MIXER_ROUTES(name, name "RX2"), \ + CS48L32_MIXER_ROUTES(name, name "RX3"), \ + CS48L32_MIXER_ROUTES(name, name "RX4"), \ + CS48L32_MIXER_ROUTES(name, name "RX5"), \ + CS48L32_MIXER_ROUTES(name, name "RX6"), \ + CS48L32_MIXER_ROUTES(name, name "RX7"), \ + CS48L32_MIXER_ROUTES(name, name "RX8") \ + +#define CS48L32_DSP_ROUTES_1_8(name) \ + { name, NULL, "DSPCLK" }, \ + CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \ + +#define CS48L32_RATE_CONTROL(name, domain) SOC_ENUM(name, cs48l32_sample_rate[(domain) - 1]) + +#define CS48L32_RATE_ENUM(name, enum) \ + SOC_ENUM_EXT(name, enum, snd_soc_get_enum_double, cs48l32_rate_put) + +#define CS48L32_DSP_RATE_CONTROL(name, num) \ + SOC_ENUM_EXT(name " Rate", cs48l32_dsp_rate_enum[num], \ + cs48l32_dsp_rate_get, cs48l32_dsp_rate_put) + +#define CS48L32_EQ_COEFF_CONTROL(xname, xreg, xbase, xshift) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = cs48l32_eq_coeff_info, .get = cs48l32_eq_coeff_get, \ + .put = cs48l32_eq_coeff_put, .private_value = \ + (unsigned long)&(struct cs48l32_eq_control) { .reg = xreg,\ + .shift = xshift, .block_base = xbase, .max = 65535 } } + +#define CS48L32_EQ_REG_NAME_PASTER(eq, band, type) \ + CS48L32_ ## eq ## _ ## band ## _ ## type + +#define CS48L32_EQ_BAND_COEFF_CONTROLS(name, band) \ + CS48L32_EQ_COEFF_CONTROL(#name " " #band " A", \ + CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 0), \ + CS48L32_EQ_COEFF_CONTROL(#name " " #band " B", \ + CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 16), \ + CS48L32_EQ_COEFF_CONTROL(#name " " #band " C", \ + CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF2), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 0), \ + CS48L32_EQ_COEFF_CONTROL(#name " " #band " PG", \ + CS48L32_EQ_REG_NAME_PASTER(name, band, PG), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 0) + +#define CS48L32_EQ_COEFF_CONTROLS(name) \ + CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND1), \ + CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND2), \ + CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND3), \ + CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND4), \ + CS48L32_EQ_COEFF_CONTROL(#name " BAND5 A", \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 0), \ + CS48L32_EQ_COEFF_CONTROL(#name " BAND5 B", \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 16), \ + CS48L32_EQ_COEFF_CONTROL(#name " BAND5 PG", \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND5, PG), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 0) + +#define CS48L32_LHPF_CONTROL(xname, xbase) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \ + .put = cs48l32_lhpf_coeff_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) { .base = xbase, \ + .num_regs = 1 }) } + +/* these have a subseq number so they run after SYSCLK and DSPCLK widgets */ +#define CS48L32_DSP_FREQ_WIDGET_EV(name, num, event) \ + SND_SOC_DAPM_SUPPLY_S(name "FREQ", 100, SND_SOC_NOPM, num, 0, \ + event, SND_SOC_DAPM_POST_PMU) + +#define CS48L32_RATES SNDRV_PCM_RATE_KNOT + +#define CS48L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define CS48L32_MIXER_INPUT_ROUTES(name) \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "ASP1RX1", "ASP1RX1" }, \ + { name, "ASP1RX2", "ASP1RX2" }, \ + { name, "ASP1RX3", "ASP1RX3" }, \ + { name, "ASP1RX4", "ASP1RX4" }, \ + { name, "ASP1RX5", "ASP1RX5" }, \ + { name, "ASP1RX6", "ASP1RX6" }, \ + { name, "ASP1RX7", "ASP1RX7" }, \ + { name, "ASP1RX8", "ASP1RX8" }, \ + { name, "ASP2RX1", "ASP2RX1" }, \ + { name, "ASP2RX2", "ASP2RX2" }, \ + { name, "ASP2RX3", "ASP2RX3" }, \ + { name, "ASP2RX4", "ASP2RX4" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC3DEC1", "ISRC3DEC1" }, \ + { name, "ISRC3DEC2", "ISRC3DEC2" }, \ + { name, "ISRC3INT1", "ISRC3INT1" }, \ + { name, "ISRC3INT2", "ISRC3INT2" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "Ultrasonic 1", "Ultrasonic 1" }, \ + { name, "Ultrasonic 2", "Ultrasonic 2" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" }, \ + { name, "DSP1.7", "DSP1" }, \ + { name, "DSP1.8", "DSP1" } + +struct cs48l32_enum { + struct soc_enum mixer_enum; + int val; +}; + +struct cs48l32_eq_control { + unsigned int reg; + unsigned int shift; + unsigned int block_base; + unsigned int max; +}; + +struct cs48l32_dai_priv { + int clk; + struct snd_pcm_hw_constraint_list constraint; +}; + +struct cs48l32_dsp_power_reg_block { + unsigned int start; + unsigned int end; +}; + +struct cs48l32_dsp_power_regs { + const unsigned int *pwd; + unsigned int n_pwd; + const struct cs48l32_dsp_power_reg_block *ext; + unsigned int n_ext; +}; + +struct cs48l32; +struct cs48l32_codec; +struct spi_device; + +struct cs48l32_fll_cfg { + int n; + unsigned int theta; + unsigned int lambda; + int refdiv; + int fratio; + int gain; + int alt_gain; +}; + +struct cs48l32_fll { + struct cs48l32_codec *codec; + int id; + unsigned int base; + + unsigned int sts_addr; + unsigned int sts_mask; + unsigned int fout; + int ref_src; + unsigned int ref_freq; + + struct cs48l32_fll_cfg ref_cfg; +}; + +struct cs48l32_codec { + struct wm_adsp dsp; /* must be first */ + struct cs48l32 core; + int sysclk; + int dspclk; + struct cs48l32_dai_priv dai[CS48L32_MAX_DAI]; + struct cs48l32_fll fll; + + unsigned int in_up_pending; + unsigned int in_vu_reg; + + struct mutex rate_lock; + + u8 dsp_dma_rates[CS48L32_DSP_N_RX_CHANNELS + CS48L32_DSP_N_TX_CHANNELS]; + + u8 in_type[CS48L32_MAX_ANALOG_INPUT][CS48L32_MAX_IN_MUX_WAYS]; + u8 pdm_sup[CS48L32_MAX_ANALOG_INPUT]; + u8 tdm_width[CS48L32_MAX_ASP]; + u8 tdm_slots[CS48L32_MAX_ASP]; + + unsigned int eq_mode[CS48L32_N_EQ_BLOCKS]; + __be16 eq_coefficients[CS48L32_N_EQ_BLOCKS][CS48L32_EQ_BLOCK_SZ / 2]; + + const struct cs48l32_dsp_power_regs *dsp_power_regs; +}; + +#define cs48l32_fll_err(_fll, fmt, ...) \ + dev_err(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) +#define cs48l32_fll_warn(_fll, fmt, ...) \ + dev_warn(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) +#define cs48l32_fll_dbg(_fll, fmt, ...) \ + dev_dbg(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) + +#define cs48l32_asp_err(_dai, fmt, ...) \ + dev_err(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__) +#define cs48l32_asp_warn(_dai, fmt, ...) \ + dev_warn(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__) +#define cs48l32_asp_dbg(_dai, fmt, ...) \ + dev_dbg(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__) + +int cs48l32_apply_patch(struct cs48l32 *cs48l32); +int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32); +int cs48l32_enable_asp1_pins(struct cs48l32_codec *cs48l32_codec); +int cs48l32_enable_asp2_pins(struct cs48l32_codec *cs48l32_codec); +int cs48l32_micvdd_voltage_index(u32 voltage); +int cs48l32_micbias1_voltage_index(u32 voltage); + +#endif diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c new file mode 100644 index 000000000000..decc86c92427 --- /dev/null +++ b/sound/soc/codecs/es8375.c @@ -0,0 +1,793 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * es8375.c -- ES8375 ALSA SoC Audio Codec + * + * Copyright Everest Semiconductor Co., Ltd + * + * Authors: Michael Zhang (zhangyi@everest-semi.com) + */ + +#include <linux/gpio/consumer.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include <sound/soc.h> +#include <linux/acpi.h> +#include "es8375.h" + +struct es8375_priv { + struct regmap *regmap; + struct clk *mclk; + struct regulator_bulk_data core_supply[2]; + unsigned int mclk_freq; + int mastermode; + u8 mclk_src; + u8 vddd; + enum snd_soc_bias_level bias_level; +}; + +static const char * const es8375_core_supplies[] = { + "vddd", + "vdda", +}; + +static const DECLARE_TLV_DB_SCALE(es8375_adc_osr_gain_tlv, -3100, 100, 0); +static const DECLARE_TLV_DB_SCALE(es8375_adc_volume_tlv, -9550, 50, 0); +static const DECLARE_TLV_DB_SCALE(es8375_adc_automute_attn_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(es8375_adc_dmic_volume_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(es8375_dac_volume_tlv, -9550, 50, 0); +static const DECLARE_TLV_DB_SCALE(es8375_dac_vppscale_tlv, -388, 12, 0); +static const DECLARE_TLV_DB_SCALE(es8375_dac_automute_attn_tlv, 0, 400, 0); +static const DECLARE_TLV_DB_SCALE(es8375_automute_ng_tlv, -9600, 600, 0); + +static const char *const es8375_ramprate_txt[] = { + "0.125dB/LRCK", + "0.125dB/2LRCK", + "0.125dB/4LRCK", + "0.125dB/8LRCK", + "0.125dB/16LRCK", + "0.125dB/32LRCK", + "0.125dB/64LRCK", + "0.125dB/128LRCK", + "disable softramp", +}; +static SOC_ENUM_SINGLE_DECL(es8375_adc_ramprate, ES8375_ADC2, + ADC_RAMPRATE_SHIFT_0, es8375_ramprate_txt); +static SOC_ENUM_SINGLE_DECL(es8375_dac_ramprate, ES8375_DAC2, + DAC_RAMPRATE_SHIFT_0, es8375_ramprate_txt); + +static const char *const es8375_automute_ws_txt[] = { + "256 samples", + "512 samples", + "1024 samples", + "2048 samples", + "4096 samples", + "8192 samples", + "16384 samples", + "32768 samples", +}; +static SOC_ENUM_SINGLE_DECL(es8375_adc_automute_ws, ES8375_ADC_AUTOMUTE, + ADC_AUTOMUTE_WS_SHIFT_3, es8375_automute_ws_txt); +static SOC_ENUM_SINGLE_DECL(es8375_dac_automute_ws, ES8375_DAC_AUTOMUTE, + DAC_AUTOMUTE_WS_SHIFT_5, es8375_automute_ws_txt); + +static const char *const es8375_dmic_pol_txt[] = { + "Low", + "High", +}; + +static SOC_ENUM_SINGLE_DECL(es8375_dmic_pol, ES8375_ADC1, + DMIC_POL_SHIFT_4, es8375_dmic_pol_txt); + +static const char *const es8375_adc_hpf_txt[] = { + "Freeze Offset", + "Dynamic HPF", +}; + +static SOC_ENUM_SINGLE_DECL(es8375_adc_hpf, ES8375_HPF1, + ADC_HPF_SHIFT_5, es8375_adc_hpf_txt); + +static const char *const es8375_dmic_mux_txt[] = { + "AMIC", + "DMIC", +}; +static const struct soc_enum es8375_dmic_mux_enum = + SOC_ENUM_SINGLE(ES8375_ADC1, ADC_SRC_SHIFT_7, + ARRAY_SIZE(es8375_dmic_mux_txt), es8375_dmic_mux_txt); + +static const struct snd_kcontrol_new es8375_dmic_mux_controls = + SOC_DAPM_ENUM("ADC MUX", es8375_dmic_mux_enum); + +static const struct snd_kcontrol_new es8375_snd_controls[] = { + SOC_SINGLE_TLV("ADC OSR Volume", ES8375_ADC_OSR_GAIN, + ADC_OSR_GAIN_SHIFT_0, ES8375_ADC_OSR_GAIN_MAX, 0, + es8375_adc_osr_gain_tlv), + SOC_SINGLE("ADC Invert Switch", ES8375_ADC1, ADC_INV_SHIFT_6, 1, 0), + SOC_SINGLE("ADC RAM Clear", ES8375_ADC1, ADC_RAMCLR_SHIFT_5, 1, 0), + SOC_ENUM("DMIC Polarity", es8375_dmic_pol), + SOC_SINGLE_TLV("DMIC Volume", ES8375_ADC1, + DMIC_GAIN_SHIFT_2, ES8375_DMIC_GAIN_MAX, + 0, es8375_adc_dmic_volume_tlv), + SOC_ENUM("ADC Ramp Rate", es8375_adc_ramprate), + SOC_SINGLE_TLV("ADC Volume", ES8375_ADC_VOLUME, + ADC_VOLUME_SHIFT_0, ES8375_ADC_VOLUME_MAX, + 0, es8375_adc_volume_tlv), + SOC_SINGLE("ADC Automute Switch", ES8375_ADC_AUTOMUTE, + ADC_AUTOMUTE_SHIFT_7, 1, 0), + SOC_ENUM("ADC Automute Winsize", es8375_adc_automute_ws), + SOC_SINGLE_TLV("ADC Automute Noise Gate", ES8375_ADC_AUTOMUTE, + ADC_AUTOMUTE_NG_SHIFT_0, ES8375_AUTOMUTE_NG_MAX, + 0, es8375_automute_ng_tlv), + SOC_SINGLE_TLV("ADC Automute Volume", ES8375_ADC_AUTOMUTE_ATTN, + ADC_AUTOMUTE_ATTN_SHIFT_0, ES8375_ADC_AUTOMUTE_ATTN_MAX, + 0, es8375_adc_automute_attn_tlv), + SOC_ENUM("ADC HPF", es8375_adc_hpf), + + SOC_SINGLE("DAC DSM Mute Switch", ES8375_DAC1, DAC_DSMMUTE_SHIFT_7, 1, 0), + SOC_SINGLE("DAC DEM Mute Switch", ES8375_DAC1, DAC_DEMMUTE_SHIFT_6, 1, 0), + SOC_SINGLE("DAC Invert Switch", ES8375_DAC1, DAC_INV_SHIFT_5, 1, 0), + SOC_SINGLE("DAC RAM Clear", ES8375_DAC1, DAC_RAMCLR_SHIFT_4, 1, 0), + SOC_ENUM("DAC Ramp Rate", es8375_dac_ramprate), + SOC_SINGLE_TLV("DAC Volume", ES8375_DAC_VOLUME, + DAC_VOLUME_SHIFT_0, ES8375_DAC_VOLUME_MAX, + 0, es8375_dac_volume_tlv), + SOC_SINGLE_TLV("DAC VPP Scale", ES8375_DAC_VPPSCALE, + DAC_VPPSCALE_SHIFT_0, ES8375_DAC_VPPSCALE_MAX, + 0, es8375_dac_vppscale_tlv), + SOC_SINGLE("DAC Automute Switch", ES8375_DAC_AUTOMUTE1, + DAC_AUTOMUTE_EN_SHIFT_7, 1, 0), + SOC_SINGLE_TLV("DAC Automute Noise Gate", ES8375_DAC_AUTOMUTE1, + DAC_AUTOMUTE_NG_SHIFT_0, ES8375_AUTOMUTE_NG_MAX, + 0, es8375_automute_ng_tlv), + SOC_ENUM("DAC Automute Winsize", es8375_dac_automute_ws), + SOC_SINGLE_TLV("DAC Automute Volume", ES8375_DAC_AUTOMUTE, + DAC_AUTOMUTE_ATTN_SHIFT_0, ES8375_DAC_AUTOMUTE_ATTN_MAX, + 0, es8375_dac_automute_attn_tlv), +}; + +static const struct snd_soc_dapm_widget es8375_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_ADC("Mono ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8375_SDP2, + ES8375_ADC_P2S_MUTE_SHIFT_5, 1), + + SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8375_dmic_mux_controls), + + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8375_SDP, + SND_SOC_NOPM, 0), + SND_SOC_DAPM_DAC("Mono DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route es8375_dapm_routes[] = { + {"ADC MUX", "AMIC", "MIC1"}, + {"ADC MUX", "DMIC", "DMIC"}, + {"PGA", NULL, "ADC MUX"}, + {"Mono ADC", NULL, "PGA"}, + {"AIF1TX", NULL, "Mono ADC"}, + + {"Mono DAC", NULL, "AIF1RX"}, + {"OUT", NULL, "Mono DAC"}, +}; + +struct _coeff_div { + u16 mclk_lrck_ratio; + u32 mclk; + u32 rate; + u8 Reg0x04; + u8 Reg0x05; + u8 Reg0x06; + u8 Reg0x07; + u8 Reg0x08; + u8 Reg0x09; + u8 Reg0x0A; + u8 Reg0x0B; + u8 Reg0x19; + u8 dvdd_vol; + u8 dmic_sel; +}; + +static const struct _coeff_div coeff_div[] = { + {32, 256000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2}, + {32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2}, + {32, 1536000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2}, + {36, 288000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2}, + {36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2}, + {36, 1728000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2}, + {48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2}, + {48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2}, + {48, 2304000, 48000, 0x05, 0x11, 0x53, 0x55, 0x17, 0x20, 0x92, 0x00, 0x28, 2, 2}, + {50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2}, + {50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2}, + {50, 2400000, 48000, 0x05, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2}, + {64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2}, + {64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2}, + {64, 3072000, 48000, 0x05, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2}, + {72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2}, + {72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2}, + {72, 3456000, 48000, 0x05, 0x11, 0x53, 0x33, 0x23, 0x08, 0x92, 0x00, 0x1F, 2, 2}, + {96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2}, + {96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2}, + {96, 4608000, 48000, 0x15, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2}, + {100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2}, + {100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x93, 0x00, 0x27, 2, 2}, + {100, 4800000, 48000, 0x03, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2}, + {128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x93, 0x01, 0x1F, 2, 2}, + {128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2}, + {128, 6144000, 48000, 0x03, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2}, + {144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x93, 0x01, 0x1F, 2, 2}, + {144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2}, + {144, 6912000, 48000, 0x03, 0x00, 0x31, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2}, + {192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x93, 0x02, 0x1F, 2, 2}, + {192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2}, + {192, 9216000, 48000, 0x15, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2}, + {250, 12000000, 48000, 0x25, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2}, + {256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2}, + {256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2}, + {256, 12288000, 48000, 0x0B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2}, + {384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2}, + {384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2}, + {384, 18432000, 48000, 0x13, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2}, + {400, 19200000, 48000, 0x1B, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2}, + {500, 24000000, 48000, 0x23, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2}, + {512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2}, + {512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2}, + {512, 24576000, 48000, 0x1B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2}, + {768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2}, + {768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2}, + {1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, + {1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, + {1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, + {1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2}, + {1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2}, + {1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2}, + {1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2}, + {1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2}, + {2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x1F, 0x1F, 2, 2}, + {2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x23, 0x1F, 2, 2}, + {2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x25, 0x27, 2, 2}, + {3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x2F, 0x1F, 2, 2}, + {32, 3072000, 96000, 0x05, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2}, + {64, 6144000, 96000, 0x03, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2}, + {96, 9216000, 96000, 0x15, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2}, + {128, 12288000, 96000, 0x0B, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x01, 0x37, 2, 2}, +}; + +static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) +{ + int i; + u8 dmic_det, vddd_det; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) { + vddd_det = ~(coeff_div[i].dvdd_vol ^ vddd) & 0x01; + dmic_det = ~(coeff_div[i].dmic_sel ^ dmic) & 0x01; + vddd_det |= ~(coeff_div[i].dvdd_vol % 2) & 0x01; + dmic_det |= ~(coeff_div[i].dmic_sel % 2) & 0x01; + + if (vddd_det && dmic_det) + return i; + } + } + + return -EINVAL; +} + +static int es8375_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 es8375_priv *es8375 = snd_soc_component_get_drvdata(component); + int par_width = params_width(params); + u8 dmic_enable, iface = 0; + unsigned int regv; + int coeff, ret; + + if (es8375->mclk_src == ES8375_BCLK_PIN) { + regmap_update_bits(es8375->regmap, + ES8375_MCLK_SEL, 0x80, 0x80); + + es8375->mclk_freq = 2 * (unsigned int)par_width * params_rate(params); + } + + regmap_read(es8375->regmap, ES8375_ADC1, ®v); + dmic_enable = regv >> 7 & 0x01; + + ret = regulator_get_voltage(es8375->core_supply[ES8375_SUPPLY_VD].consumer); + switch (ret) { + case 1800000 ... 2000000: + es8375->vddd = ES8375_1V8; + break; + case 2500000 ... 3300000: + es8375->vddd = ES8375_3V3; + break; + default: + es8375->vddd = ES8375_3V3; + break; + } + + coeff = get_coeff(es8375->vddd, dmic_enable, es8375->mclk_freq, params_rate(params)); + if (coeff < 0) { + dev_warn(component->dev, "Clock coefficients do not match"); + } + regmap_write(es8375->regmap, ES8375_CLK_MGR4, + coeff_div[coeff].Reg0x04); + regmap_write(es8375->regmap, ES8375_CLK_MGR5, + coeff_div[coeff].Reg0x05); + regmap_write(es8375->regmap, ES8375_CLK_MGR6, + coeff_div[coeff].Reg0x06); + regmap_write(es8375->regmap, ES8375_CLK_MGR7, + coeff_div[coeff].Reg0x07); + regmap_write(es8375->regmap, ES8375_CLK_MGR8, + coeff_div[coeff].Reg0x08); + regmap_write(es8375->regmap, ES8375_CLK_MGR9, + coeff_div[coeff].Reg0x09); + regmap_write(es8375->regmap, ES8375_CLK_MGR10, + coeff_div[coeff].Reg0x0A); + regmap_write(es8375->regmap, ES8375_CLK_MGR11, + coeff_div[coeff].Reg0x0B); + regmap_write(es8375->regmap, ES8375_ADC_OSR_GAIN, + coeff_div[coeff].Reg0x19); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + iface |= 0x0c; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x04; + break; + case SNDRV_PCM_FORMAT_S24_LE: + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface |= 0x10; + break; + } + + regmap_update_bits(es8375->regmap, ES8375_SDP, 0x1c, iface); + + return 0; +} + +static int es8375_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component); + + es8375->mclk_freq = freq; + + return 0; +} + +static int es8375_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component); + unsigned int iface, codeciface; + + regmap_read(es8375->regmap, ES8375_SDP, &codeciface); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFP: + es8375->mastermode = 1; + regmap_update_bits(es8375->regmap, ES8375_RESET1, + 0x80, 0x80); + break; + case SND_SOC_DAIFMT_CBC_CFC: + es8375->mastermode = 0; + regmap_update_bits(es8375->regmap, ES8375_RESET1, + 0x80, 0x00); + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + codeciface &= 0xFC; + break; + case SND_SOC_DAIFMT_RIGHT_J: + return -EINVAL; + case SND_SOC_DAIFMT_LEFT_J: + codeciface &= 0xFC; + codeciface |= 0x01; + break; + case SND_SOC_DAIFMT_DSP_A: + codeciface &= 0xDC; + codeciface |= 0x03; + break; + case SND_SOC_DAIFMT_DSP_B: + codeciface &= 0xDC; + codeciface |= 0x23; + break; + default: + return -EINVAL; + } + + regmap_read(es8375->regmap, ES8375_CLK_MGR3, &iface); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + iface &= 0xFE; + codeciface &= 0xDF; + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x01; + codeciface |= 0x20; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x01; + codeciface &= 0xDF; + break; + case SND_SOC_DAIFMT_NB_IF: + iface &= 0xFE; + codeciface |= 0x20; + break; + default: + return -EINVAL; + } + + regmap_write(es8375->regmap, ES8375_CLK_MGR3, iface); + regmap_write(es8375->regmap, ES8375_SDP, codeciface); + + return 0; +} + +static int es8375_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + ret = clk_prepare_enable(es8375->mclk); + if (ret) { + dev_err(component->dev, "unable to prepare mclk\n"); + return ret; + } + regmap_write(es8375->regmap, ES8375_CSM1, 0xA6); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + regmap_write(es8375->regmap, ES8375_CSM1, 0x96); + clk_disable_unprepare(es8375->mclk); + break; + case SND_SOC_BIAS_OFF: + break; + } + return 0; +} + +static int es8375_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component); + + if (mute) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x40); + else + regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x20); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x00); + else + regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x00); + } + + return 0; +} + +#define es8375_RATES SNDRV_PCM_RATE_8000_96000 + +#define es8375_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops es8375_ops = { + .hw_params = es8375_hw_params, + .mute_stream = es8375_mute, + .set_sysclk = es8375_set_sysclk, + .set_fmt = es8375_set_dai_fmt, +}; + +static struct snd_soc_dai_driver es8375_dai = { + .name = "ES8375 HiFi", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = es8375_RATES, + .formats = es8375_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = es8375_RATES, + .formats = es8375_FORMATS, + }, + .ops = &es8375_ops, + .symmetric_rate = 1, +}; + +static void es8375_init(struct snd_soc_component *component) +{ + struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component); + + regmap_write(es8375->regmap, ES8375_CLK_MGR10, 0x95); + regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x48); + regmap_write(es8375->regmap, ES8375_DIV_SPKCLK, 0x18); + regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x02); + regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x05); + regmap_write(es8375->regmap, ES8375_CSM1, 0x82); + regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x20); + regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x20); + regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x28); + regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xFC); + regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE0); + regmap_write(es8375->regmap, ES8375_VMID_SEL, 0xFE); + regmap_write(es8375->regmap, ES8375_ANALOG1, 0xB8); + regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x03); + regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0x16); + regmap_write(es8375->regmap, ES8375_RESET1, 0x00); + msleep(80); + regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x00); + regmap_write(es8375->regmap, ES8375_CSM1, 0x86); + regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x0B); + regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x00); + regmap_write(es8375->regmap, ES8375_CLK_MGR6, 0x31); + regmap_write(es8375->regmap, ES8375_CLK_MGR7, 0x11); + regmap_write(es8375->regmap, ES8375_CLK_MGR8, 0x1F); + regmap_write(es8375->regmap, ES8375_CLK_MGR9, 0x00); + regmap_write(es8375->regmap, ES8375_ADC_OSR_GAIN, 0x1F); + regmap_write(es8375->regmap, ES8375_ADC2, 0x00); + regmap_write(es8375->regmap, ES8375_DAC2, 0x00); + regmap_write(es8375->regmap, ES8375_DAC_OTP, 0x88); + regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE7); + regmap_write(es8375->regmap, ES8375_ANALOG2, 0xF0); + regmap_write(es8375->regmap, ES8375_ANALOG3, 0x40); + regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0xFE); + + regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x40); + regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x20); +} + +static int es8375_suspend(struct snd_soc_component *component) +{ + struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component); + + regmap_write(es8375->regmap, ES8375_CSM1, 0x96); + regcache_cache_only(es8375->regmap, true); + regcache_mark_dirty(es8375->regmap); + return 0; +} + +static int es8375_resume(struct snd_soc_component *component) +{ + struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component); + unsigned int reg; + + regcache_cache_only(es8375->regmap, false); + regcache_cache_bypass(es8375->regmap, true); + regmap_read(es8375->regmap, ES8375_CLK_MGR2, ®); + regcache_cache_bypass(es8375->regmap, false); + + if (reg == 0x00) + es8375_init(component); + else + es8375_set_bias_level(component, SND_SOC_BIAS_ON); + + regcache_sync(es8375->regmap); + + return 0; +} + +static int es8375_codec_probe(struct snd_soc_component *component) +{ + struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component); + + es8375->mastermode = 0; + + es8375_init(component); + + return 0; +} + +static bool es8375_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ES8375_CHIP_VERSION: + case ES8375_CHIP_ID0: + case ES8375_CHIP_ID1: + case ES8375_SPK_OFFSET: + case ES8375_FLAGS2: + return false; + default: + return true; + } +} + +static struct regmap_config es8375_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ES8375_REG_MAX, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, + .writeable_reg = es8375_writeable_register, +}; + +static struct snd_soc_component_driver es8375_codec_driver = { + .probe = es8375_codec_probe, + .suspend = es8375_suspend, + .resume = es8375_resume, + .set_bias_level = es8375_set_bias_level, + .controls = es8375_snd_controls, + .num_controls = ARRAY_SIZE(es8375_snd_controls), + .dapm_widgets = es8375_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8375_dapm_widgets), + .dapm_routes = es8375_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8375_dapm_routes), + + .idle_bias_on = 1, + .suspend_bias_off = 1, +}; + +static int es8375_read_device_properities(struct device *dev, struct es8375_priv *es8375) +{ + int ret, i; + + ret = device_property_read_u8(dev, "everest,mclk-src", &es8375->mclk_src); + if (ret != 0) + es8375->mclk_src = ES8375_MCLK_SOURCE; + dev_dbg(dev, "mclk-src %x", es8375->mclk_src); + + for (i = 0; i < ARRAY_SIZE(es8375_core_supplies); i++) + es8375->core_supply[i].supply = es8375_core_supplies[i]; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8375_core_supplies), es8375->core_supply); + if (ret) { + dev_err(dev, "Failed to request core supplies %d\n", ret); + return ret; + } + + es8375->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(es8375->mclk)) + return dev_err_probe(dev, PTR_ERR(es8375->mclk), "unable to get mclk\n"); + + if (!es8375->mclk) + dev_warn(dev, "assuming static mclk\n"); + + ret = clk_prepare_enable(es8375->mclk); + if (ret) { + dev_err(dev, "unable to enable mclk\n"); + return ret; + } + ret = regulator_bulk_enable(ARRAY_SIZE(es8375_core_supplies), es8375->core_supply); + if (ret) { + dev_err(dev, "Failed to enable core supplies: %d\n", ret); + clk_disable_unprepare(es8375->mclk); + return ret; + } + + return 0; +} + +static int es8375_i2c_probe(struct i2c_client *i2c_client) +{ + struct es8375_priv *es8375; + struct device *dev = &i2c_client->dev; + int ret; + unsigned int val; + + es8375 = devm_kzalloc(&i2c_client->dev, sizeof(*es8375), GFP_KERNEL); + if (!es8375) + return -ENOMEM; + + es8375->regmap = devm_regmap_init_i2c(i2c_client, + &es8375_regmap_config); + if (IS_ERR(es8375->regmap)) + return dev_err_probe(&i2c_client->dev, PTR_ERR(es8375->regmap), + "regmap_init() failed\n"); + + i2c_set_clientdata(i2c_client, es8375); + + ret = regmap_read(es8375->regmap, ES8375_CHIP_ID1, &val); + if (ret < 0) { + dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n", + i2c_client->addr); + return ret; + } + + if (val != 0x83) { + dev_err(&i2c_client->dev, "device at addr %X is not an es8375\n", + i2c_client->addr); + return -ENODEV; + } + + ret = regmap_read(es8375->regmap, ES8375_CHIP_ID0, &val); + if (val != 0x75) { + dev_err(&i2c_client->dev, "device at addr %X is not an es8375\n", + i2c_client->addr); + return -ENODEV; + } + + ret = es8375_read_device_properities(dev, es8375); + if (ret != 0) { + dev_err(&i2c_client->dev, "get an error from dts info %X\n", ret); + return ret; + } + + return devm_snd_soc_register_component(&i2c_client->dev, &es8375_codec_driver, + &es8375_dai, 1); +} + +static void es8375_i2c_shutdown(struct i2c_client *i2c) +{ + struct es8375_priv *es8375; + + es8375 = i2c_get_clientdata(i2c); + + regmap_write(es8375->regmap, ES8375_CSM1, 0x3C); + regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x48); + regmap_write(es8375->regmap, ES8375_CSM2, 0x80); + regmap_write(es8375->regmap, ES8375_CSM1, 0x3E); + regmap_write(es8375->regmap, ES8375_CLK_MGR10, 0x15); + regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x0C); + regmap_write(es8375->regmap, ES8375_RESET1, 0x00); + regmap_write(es8375->regmap, ES8375_CSM2, 0x00); + + regulator_bulk_disable(ARRAY_SIZE(es8375_core_supplies), es8375->core_supply); + clk_disable_unprepare(es8375->mclk); +} + +static const struct i2c_device_id es8375_id[] = { + {"es8375"}, + { } +}; +MODULE_DEVICE_TABLE(i2c, es8375_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id es8375_acpi_match[] = { + {"ESSX8375", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, es8375_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id es8375_of_match[] = { + {.compatible = "everest,es8375",}, + {} +}; + +MODULE_DEVICE_TABLE(of, es8375_of_match); +#endif + +static struct i2c_driver es8375_i2c_driver = { + .driver = { + .name = "es8375", + .of_match_table = of_match_ptr(es8375_of_match), + .acpi_match_table = ACPI_PTR(es8375_acpi_match), + }, + .shutdown = es8375_i2c_shutdown, + .probe = es8375_i2c_probe, + .id_table = es8375_id, +}; +module_i2c_driver(es8375_i2c_driver); + +MODULE_DESCRIPTION("ASoC ES8375 driver"); +MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8375.h b/sound/soc/codecs/es8375.h new file mode 100644 index 000000000000..11e3ceec9b68 --- /dev/null +++ b/sound/soc/codecs/es8375.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +* ES8375.h -- ES8375 ALSA SoC Audio Codec +* +* Authors: +* +* Based on ES8375.h by Michael Zhang +*/ +#ifndef _ES8375_H +#define _ES8375_H + +// Registors +#define ES8375_RESET1 0x00 +#define ES8375_MCLK_SEL 0x01 +#define ES8375_CLK_MGR2 0x02 +#define ES8375_CLK_MGR3 0x03 +#define ES8375_CLK_MGR4 0x04 +#define ES8375_CLK_MGR5 0x05 +#define ES8375_CLK_MGR6 0x06 +#define ES8375_CLK_MGR7 0x07 +#define ES8375_CLK_MGR8 0x08 +#define ES8375_CLK_MGR9 0x09 +#define ES8375_CLK_MGR10 0x0A +#define ES8375_CLK_MGR11 0x0B +#define ES8375_CLK_MGR12 0x0C +#define ES8375_DIV_SPKCLK 0x0E +#define ES8375_CSM1 0x0F +#define ES8375_CSM2 0x10 +#define ES8375_VMID_CHARGE2 0x11 +#define ES8375_VMID_CHARGE3 0x12 +#define ES8375_SDP 0x15 +#define ES8375_SDP2 0x16 +#define ES8375_ADC1 0x17 +#define ES8375_ADC2 0x18 +#define ES8375_ADC_OSR_GAIN 0x19 +#define ES8375_ADC_VOLUME 0x1A +#define ES8375_ADC_AUTOMUTE 0x1B +#define ES8375_ADC_AUTOMUTE_ATTN 0x1C +#define ES8375_HPF1 0x1D +#define ES8375_DAC1 0x1F +#define ES8375_DAC2 0x20 +#define ES8375_DAC_VOLUME 0x21 +#define ES8375_DAC_VPPSCALE 0x22 +#define ES8375_DAC_AUTOMUTE1 0x23 +#define ES8375_DAC_AUTOMUTE 0x24 +#define ES8375_DAC_CAL 0x25 +#define ES8375_DAC_OTP 0x27 +#define ES8375_ANALOG_SPK1 0x28 +#define ES8375_ANALOG_SPK2 0x29 +#define ES8375_VMID_SEL 0x2D +#define ES8375_ANALOG1 0x2E +#define ES8375_ANALOG2 0x32 +#define ES8375_ANALOG3 0x37 +#define ES8375_ADC2DAC_CLKTRI 0xF8 +#define ES8375_SYS_CTRL2 0xF9 +#define ES8375_FLAGS2 0xFB +#define ES8375_SPK_OFFSET 0xFC +#define ES8375_CHIP_ID1 0xFD +#define ES8375_CHIP_ID0 0xFE +#define ES8375_CHIP_VERSION 0xFF + +// Bit Shifts +#define ADC_OSR_GAIN_SHIFT_0 0 +#define ADC_RAMPRATE_SHIFT_0 0 +#define ADC_VOLUME_SHIFT_0 0 +#define ADC_AUTOMUTE_NG_SHIFT_0 0 +#define ADC_AUTOMUTE_ATTN_SHIFT_0 0 +#define DAC_RAMPRATE_SHIFT_0 0 +#define DAC_VOLUME_SHIFT_0 0 +#define DAC_VPPSCALE_SHIFT_0 0 +#define DAC_AUTOMUTE_NG_SHIFT_0 0 +#define DAC_AUTOMUTE_ATTN_SHIFT_0 0 +#define DMIC_GAIN_SHIFT_2 2 +#define ADC_AUTOMUTE_WS_SHIFT_3 3 +#define DMIC_POL_SHIFT_4 4 +#define DAC_RAMCLR_SHIFT_4 4 +#define ES8375_EN_MODL_SHIFT_4 4 +#define ADC_RAMCLR_SHIFT_5 5 +#define ADC_HPF_SHIFT_5 5 +#define DAC_INV_SHIFT_5 5 +#define DAC_AUTOMUTE_WS_SHIFT_5 5 +#define ES8375_EN_PGAL_SHIFT_5 5 +#define ES8375_ADC_P2S_MUTE_SHIFT_5 5 +#define ADC_INV_SHIFT_6 6 +#define DAC_DEMMUTE_SHIFT_6 6 +#define ES8375_DAC_S2P_MUTE_SHIFT_6 6 +#define ADC_SRC_SHIFT_7 7 +#define ADC_AUTOMUTE_SHIFT_7 7 +#define DAC_DSMMUTE_SHIFT_7 7 +#define DAC_AUTOMUTE_EN_SHIFT_7 7 + +// Function values +#define ES8375_ADC_OSR_GAIN_MAX 0x3F +#define ES8375_DMIC_GAIN_MAX 0x04 +#define ES8375_ADC_AUTOMUTE_ATTN_MAX 0x1F +#define ES8375_AUTOMUTE_NG_MAX 0x07 +#define ES8375_ADC_VOLUME_MAX 0xFF +#define ES8375_DAC_VOLUME_MAX 0xFF +#define ES8375_DAC_VPPSCALE_MAX 0x3F +#define ES8375_DAC_AUTOMUTE_ATTN_MAX 0x17 +#define ES8375_REG_MAX 0xFF + +enum ES8375_supplies { + ES8375_SUPPLY_VD = 0, + ES8375_SUPPLY_VA, +}; + +// Properties +#define ES8375_3V3 1 +#define ES8375_1V8 0 + +#define ES8375_MCLK_PIN 0 +#define ES8375_BCLK_PIN 1 +#define ES8375_MCLK_SOURCE ES8375_MCLK_PIN + +#define DMIC_POSITIVE_EDGE 0 +#define DMIC_NEGATIVE_EDGE 1 +#define DMIC_POL DMIC_POSITIVE_EDGE + +#define PA_SHUTDOWN 0 +#define PA_ENABLE 1 + +#endif diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c new file mode 100644 index 000000000000..ba1763f36f17 --- /dev/null +++ b/sound/soc/codecs/es8389.c @@ -0,0 +1,962 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * es8389.c -- ES8389 ALSA SoC Audio Codec + * + * Copyright Everest Semiconductor Co., Ltd + * + * Authors: Michael Zhang (zhangyi@everest-semi.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include <sound/soc.h> + +#include "es8389.h" + + +/* codec private data */ + +struct es8389_private { + struct regmap *regmap; + struct clk *mclk; + unsigned int sysclk; + int mastermode; + + u8 mclk_src; + enum snd_soc_bias_level bias_level; +}; + +static bool es8389_volatile_register(struct device *dev, + unsigned int reg) +{ + if ((reg <= 0xff)) + return true; + else + return false; +} + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -9550, 50, 0); +static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, 0, 300, 0); +static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0); +static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0); +static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0); + +static int es8389_dmic_set(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 es8389_private *es8389 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + bool changed1, changed2; + + val = ucontrol->value.integer.value[0]; + if (val > 1) + return -EINVAL; + + if (val) { + regmap_update_bits_check(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0xC0, &changed1); + regmap_update_bits_check(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x03, &changed2); + } else { + regmap_update_bits_check(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0x00, &changed1); + regmap_update_bits_check(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x00, &changed2); + } + + if (changed1 & changed2) + return snd_soc_dapm_mux_update_power(dapm, kcontrol, val, e, NULL); + else + return 0; +} + +static const char *const alc[] = { + "ALC OFF", + "ADCR ALC ON", + "ADCL ALC ON", + "ADCL & ADCL ALC ON", +}; + +static const char *const ramprate[] = { + "0.125db/1 LRCK", + "0.125db/4 LRCK", + "0.125db/8 LRCK", + "0.125db/16 LRCK", + "0.125db/32 LRCK", + "0.125db/64 LRCK", + "0.125db/128 LRCK", + "0.125db/256 LRCK", + "0.125db/512 LRCK", + "0.125db/1024 LRCK", + "0.125db/2048 LRCK", + "0.125db/4096 LRCK", + "0.125db/8192 LRCK", + "0.125db/16384 LRCK", + "0.125db/32768 LRCK", + "0.125db/65536 LRCK", +}; + +static const char *const winsize[] = { + "2 LRCK", + "4 LRCK", + "8 LRCK", + "16 LRCK", + "32 LRCK", + "64 LRCK", + "128 LRCK", + "256 LRCK", + "512 LRCK", + "1024 LRCK", + "2048 LRCK", + "4096 LRCK", + "8192 LRCK", + "16384 LRCK", + "32768 LRCK", + "65536 LRCK", +}; + +static const struct soc_enum alc_enable = + SOC_ENUM_SINGLE(ES8389_ALC_ON, 5, 4, alc); +static const struct soc_enum alc_ramprate = + SOC_ENUM_SINGLE(ES8389_ALC_CTL, 4, 16, ramprate); +static const struct soc_enum alc_winsize = + SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize); + +static const char *const es8389_outl_mux_txt[] = { + "Normal", + "DAC2 channel to DAC1 channel", +}; + +static const char *const es8389_outr_mux_txt[] = { + "Normal", + "DAC1 channel to DAC2 channel", +}; + +static const char *const es8389_dmic_mux_txt[] = { + "AMIC", + "DMIC", +}; + +static const char *const es8389_pga1_texts[] = { + "DifferentialL", "Line 1P", "Line 2P" +}; + +static const char *const es8389_pga2_texts[] = { + "DifferentialR", "Line 2N", "Line 1N" +}; + +static const unsigned int es8389_pga_values[] = { + 1, 5, 6 +}; + +static const struct soc_enum es8389_outl_mux_enum = + SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5, + ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt); + +static const struct snd_kcontrol_new es8389_outl_mux_controls = + SOC_DAPM_ENUM("OUTL MUX", es8389_outl_mux_enum); + +static const struct soc_enum es8389_outr_mux_enum = + SOC_ENUM_SINGLE(ES8389_DAC_MIX, 4, + ARRAY_SIZE(es8389_outr_mux_txt), es8389_outr_mux_txt); + +static const struct snd_kcontrol_new es8389_outr_mux_controls = + SOC_DAPM_ENUM("OUTR MUX", es8389_outr_mux_enum); + +static SOC_ENUM_SINGLE_DECL( + es8389_dmic_mux_enum, ES8389_DMIC_EN, 6, es8389_dmic_mux_txt); + +static const struct soc_enum es8389_pgal_enum = + SOC_VALUE_ENUM_SINGLE(ES8389_MIC1_GAIN, 4, 7, + ARRAY_SIZE(es8389_pga1_texts), es8389_pga1_texts, + es8389_pga_values); + +static const struct soc_enum es8389_pgar_enum = + SOC_VALUE_ENUM_SINGLE(ES8389_MIC2_GAIN, 4, 7, + ARRAY_SIZE(es8389_pga2_texts), es8389_pga2_texts, + es8389_pga_values); + +static const struct snd_kcontrol_new es8389_dmic_mux_controls = + SOC_DAPM_ENUM_EXT("ADC MUX", es8389_dmic_mux_enum, + snd_soc_dapm_get_enum_double, es8389_dmic_set); + +static const struct snd_kcontrol_new es8389_left_mixer_controls[] = { + SOC_DAPM_SINGLE("DACR DACL Mixer", ES8389_DAC_MIX, 3, 1, 0), +}; + +static const struct snd_kcontrol_new es8389_right_mixer_controls[] = { + SOC_DAPM_SINGLE("DACL DACR Mixer", ES8389_DAC_MIX, 2, 1, 0), +}; + +static const struct snd_kcontrol_new es8389_leftadc_mixer_controls[] = { + SOC_DAPM_SINGLE("ADCL DACL Mixer", ES8389_DAC_MIX, 1, 1, 0), +}; + +static const struct snd_kcontrol_new es8389_rightadc_mixer_controls[] = { + SOC_DAPM_SINGLE("ADCR DACR Mixer", ES8389_DAC_MIX, 0, 1, 0), +}; + +static const struct snd_kcontrol_new es8389_adc_mixer_controls[] = { + SOC_DAPM_SINGLE("DACL ADCL Mixer", ES8389_ADC_RESET, 7, 1, 0), + SOC_DAPM_SINGLE("DACR ADCR Mixer", ES8389_ADC_RESET, 6, 1, 0), +}; + +static const struct snd_kcontrol_new es8389_snd_controls[] = { + SOC_SINGLE_TLV("ADCL Capture Volume", ES8389_ADCL_VOL, 0, 0xFF, 0, adc_vol_tlv), + SOC_SINGLE_TLV("ADCR Capture Volume", ES8389_ADCR_VOL, 0, 0xFF, 0, adc_vol_tlv), + SOC_SINGLE_TLV("ADCL PGA Volume", ES8389_MIC1_GAIN, 0, 0x0E, 0, pga_vol_tlv), + SOC_SINGLE_TLV("ADCR PGA Volume", ES8389_MIC2_GAIN, 0, 0x0E, 0, pga_vol_tlv), + + SOC_ENUM("PGAL Select", es8389_pgal_enum), + SOC_ENUM("PGAR Select", es8389_pgar_enum), + SOC_ENUM("ALC Capture Switch", alc_enable), + SOC_SINGLE_TLV("ALC Capture Target Level", ES8389_ALC_TARGET, + 0, 0x0f, 0, alc_target_tlv), + SOC_SINGLE_TLV("ALC Capture Max Gain", ES8389_ALC_GAIN, + 0, 0x0f, 0, alc_max_level), + SOC_ENUM("ADC Ramp Rate", alc_ramprate), + SOC_ENUM("ALC Capture Winsize", alc_winsize), + SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0), + SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv), + SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0), + + SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv), + SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv), + SOC_DOUBLE("DAC OUTPUT Invert Switch", ES8389_DAC_INV, 5, 6, 1, 0), + SOC_SINGLE_TLV("ADC2DAC Mixer Volume", ES8389_MIX_VOL, 0, 0x7F, 0, mix_vol_tlv), +}; + +static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = { + /*Input Side*/ + SND_SOC_DAPM_INPUT("INPUT1"), + SND_SOC_DAPM_INPUT("INPUT2"), + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_PGA("PGAL", SND_SOC_NOPM, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGAR", SND_SOC_NOPM, 4, 0, NULL, 0), + + /*ADCs*/ + SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S IN", "I2S Playback", 0, SND_SOC_NOPM, 0, 0), + + /*DACs*/ + SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + + /*Output Side*/ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + + /* Digital Interface */ + SND_SOC_DAPM_PGA("IF DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF DACL1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF DACR1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF DACL2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF DACR2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF DACL3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF DACR3", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MIXER("IF DACL Mixer", SND_SOC_NOPM, 0, 0, + &es8389_left_mixer_controls[0], + ARRAY_SIZE(es8389_left_mixer_controls)), + SND_SOC_DAPM_MIXER("IF DACR Mixer", SND_SOC_NOPM, 0, 0, + &es8389_right_mixer_controls[0], + ARRAY_SIZE(es8389_right_mixer_controls)), + SND_SOC_DAPM_MIXER("IF ADCDACL Mixer", SND_SOC_NOPM, 0, 0, + &es8389_leftadc_mixer_controls[0], + ARRAY_SIZE(es8389_leftadc_mixer_controls)), + SND_SOC_DAPM_MIXER("IF ADCDACR Mixer", SND_SOC_NOPM, 0, 0, + &es8389_rightadc_mixer_controls[0], + ARRAY_SIZE(es8389_rightadc_mixer_controls)), + + SND_SOC_DAPM_MIXER("ADC Mixer", SND_SOC_NOPM, 0, 0, + &es8389_adc_mixer_controls[0], + ARRAY_SIZE(es8389_adc_mixer_controls)), + SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls), + + SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls), + SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls), +}; + + +static const struct snd_soc_dapm_route es8389_dapm_routes[] = { + {"PGAL", NULL, "INPUT1"}, + {"PGAR", NULL, "INPUT2"}, + + {"ADCL", NULL, "PGAL"}, + {"ADCR", NULL, "PGAR"}, + + {"ADC Mixer", "DACL ADCL Mixer", "DACL"}, + {"ADC Mixer", "DACR ADCR Mixer", "DACR"}, + {"ADC Mixer", NULL, "ADCL"}, + {"ADC Mixer", NULL, "ADCR"}, + + {"ADC MUX", "AMIC", "ADC Mixer"}, + {"ADC MUX", "DMIC", "DMIC"}, + + {"I2S OUT", NULL, "ADC MUX"}, + + {"DACL", NULL, "I2S IN"}, + {"DACR", NULL, "I2S IN"}, + + {"IF DACL1", NULL, "DACL"}, + {"IF DACR1", NULL, "DACR"}, + {"IF DACL2", NULL, "DACL"}, + {"IF DACR2", NULL, "DACR"}, + {"IF DACL3", NULL, "DACL"}, + {"IF DACR3", NULL, "DACR"}, + + {"IF DACL Mixer", NULL, "IF DACL2"}, + {"IF DACL Mixer", "DACR DACL Mixer", "IF DACR1"}, + {"IF DACR Mixer", NULL, "IF DACR2"}, + {"IF DACR Mixer", "DACL DACR Mixer", "IF DACL1"}, + + {"IF ADCDACL Mixer", NULL, "IF DACL Mixer"}, + {"IF ADCDACL Mixer", "ADCL DACL Mixer", "IF DACL3"}, + {"IF ADCDACR Mixer", NULL, "IF DACR Mixer"}, + {"IF ADCDACR Mixer", "ADCR DACR Mixer", "IF DACR3"}, + + {"OUTL MUX", "Normal", "IF ADCDACL Mixer"}, + {"OUTL MUX", "DAC2 channel to DAC1 channel", "IF ADCDACR Mixer"}, + {"OUTR MUX", "Normal", "IF ADCDACR Mixer"}, + {"OUTR MUX", "DAC1 channel to DAC2 channel", "IF ADCDACL Mixer"}, + + {"HPOL", NULL, "OUTL MUX"}, + {"HPOR", NULL, "OUTR MUX"}, + +}; + +struct _coeff_div { + u16 fs; + u32 mclk; + u32 rate; + u8 Reg0x04; + u8 Reg0x05; + u8 Reg0x06; + u8 Reg0x07; + u8 Reg0x08; + u8 Reg0x09; + u8 Reg0x0A; + u8 Reg0x0F; + u8 Reg0x11; + u8 Reg0x21; + u8 Reg0x22; + u8 Reg0x26; + u8 Reg0x30; + u8 Reg0x41; + u8 Reg0x42; + u8 Reg0x43; + u8 Reg0xF0; + u8 Reg0xF1; + u8 Reg0x16; + u8 Reg0x18; + u8 Reg0x19; +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07}, + {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E}, + {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14}, + {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B}, + {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28}, + {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25}, + {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28}, + {128, 12288000, 96000, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28}, + {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28}, + {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F}, + + {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x90, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07}, + {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07}, + {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xD0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07}, +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return -EINVAL; +} + +/* + * if PLL not be used, use internal clk1 for mclk,otherwise, use internal clk2 for PLL source. + */ +static int es8389_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + es8389->sysclk = freq; + + return 0; +} + +static int es8389_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; + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + regmap_update_bits(es8389->regmap, ES8389_PTDM_SLOT, + ES8389_TDM_SLOT, (slots << ES8389_TDM_SHIFT)); + regmap_update_bits(es8389->regmap, ES8389_DAC_RAMP, + ES8389_TDM_SLOT, (slots << ES8389_TDM_SHIFT)); + + return 0; +} + +static int es8389_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + u8 state = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFP: + regmap_update_bits(es8389->regmap, ES8389_MASTER_MODE, + ES8389_MASTER_MODE_EN, ES8389_MASTER_MODE_EN); + es8389->mastermode = 1; + break; + case SND_SOC_DAIFMT_CBC_CFC: + es8389->mastermode = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + state |= ES8389_DAIFMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dev_err(component->dev, "component driver does not support right justified\n"); + return -EINVAL; + case SND_SOC_DAIFMT_LEFT_J: + state |= ES8389_DAIFMT_LEFT_J; + break; + case SND_SOC_DAIFMT_DSP_A: + state |= ES8389_DAIFMT_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + state |= ES8389_DAIFMT_DSP_B; + break; + default: + break; + } + regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, ES8389_DAIFMT_MASK, state); + regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, ES8389_DAIFMT_MASK, state); + + return 0; +} + +static int es8389_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 es8389_private *es8389 = snd_soc_component_get_drvdata(component); + int coeff; + u8 state = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + state |= ES8389_S16_LE; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + state |= ES8389_S20_3_LE; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + state |= ES8389_S18_LE; + break; + case SNDRV_PCM_FORMAT_S24_LE: + state |= ES8389_S24_LE; + break; + case SNDRV_PCM_FORMAT_S32_LE: + state |= ES8389_S32_LE; + break; + default: + return -EINVAL; + } + + regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, ES8389_DATA_LEN_MASK, state); + regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, ES8389_DATA_LEN_MASK, state); + + if (es8389->mclk_src == ES8389_SCLK_PIN) { + regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK, + ES8389_MCLK_SOURCE, es8389->mclk_src); + es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params); + } + + coeff = get_coeff(es8389->sysclk, params_rate(params)); + if (coeff >= 0) { + regmap_write(es8389->regmap, ES8389_CLK_DIV1, coeff_div[coeff].Reg0x04); + regmap_write(es8389->regmap, ES8389_CLK_MUL, coeff_div[coeff].Reg0x05); + regmap_write(es8389->regmap, ES8389_CLK_MUX1, coeff_div[coeff].Reg0x06); + regmap_write(es8389->regmap, ES8389_CLK_MUX2, coeff_div[coeff].Reg0x07); + regmap_write(es8389->regmap, ES8389_CLK_CTL1, coeff_div[coeff].Reg0x08); + regmap_write(es8389->regmap, ES8389_CLK_CTL2, coeff_div[coeff].Reg0x09); + regmap_write(es8389->regmap, ES8389_CLK_CTL3, coeff_div[coeff].Reg0x0A); + regmap_update_bits(es8389->regmap, ES8389_OSC_CLK, + 0xC0, coeff_div[coeff].Reg0x0F); + regmap_write(es8389->regmap, ES8389_CLK_DIV2, coeff_div[coeff].Reg0x11); + regmap_write(es8389->regmap, ES8389_ADC_OSR, coeff_div[coeff].Reg0x21); + regmap_write(es8389->regmap, ES8389_ADC_DSP, coeff_div[coeff].Reg0x22); + regmap_write(es8389->regmap, ES8389_OSR_VOL, coeff_div[coeff].Reg0x26); + regmap_update_bits(es8389->regmap, ES8389_SYSTEM30, + 0xC0, coeff_div[coeff].Reg0x30); + regmap_write(es8389->regmap, ES8389_DAC_DSM_OSR, coeff_div[coeff].Reg0x41); + regmap_write(es8389->regmap, ES8389_DAC_DSP_OSR, coeff_div[coeff].Reg0x42); + regmap_update_bits(es8389->regmap, ES8389_DAC_MISC, + 0x81, coeff_div[coeff].Reg0x43); + regmap_update_bits(es8389->regmap, ES8389_CHIP_MISC, + 0x72, coeff_div[coeff].Reg0xF0); + regmap_write(es8389->regmap, ES8389_CSM_STATE1, coeff_div[coeff].Reg0xF1); + regmap_write(es8389->regmap, ES8389_SYSTEM16, coeff_div[coeff].Reg0x16); + regmap_write(es8389->regmap, ES8389_SYSTEM18, coeff_div[coeff].Reg0x18); + regmap_write(es8389->regmap, ES8389_SYSTEM19, coeff_div[coeff].Reg0x19); + } else { + dev_warn(component->dev, "Clock coefficients do not match"); + } + + return 0; +} + +static int es8389_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int ret; + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + ret = clk_prepare_enable(es8389->mclk); + if (ret) + return ret; + + regmap_update_bits(es8389->regmap, ES8389_HPSW, 0x20, 0x20); + regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xD9); + regmap_write(es8389->regmap, ES8389_ADC_EN, 0x8F); + regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4); + regmap_write(es8389->regmap, ES8389_RESET, 0x01); + regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a); + usleep_range(70000, 72000); + regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04); + regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4); + usleep_range(70000, 72000); + regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59); + regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00); + regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00); + regmap_write(es8389->regmap, ES8389_RESET, 0x7E); + regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80); + usleep_range(8000, 8500); + regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00); + + clk_disable_unprepare(es8389->mclk); + break; + case SND_SOC_BIAS_OFF: + break; + } + return 0; +} + + + +static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + if (mute) { + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, + 0x03, 0x03); + } else { + regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, + 0x03, 0x03); + } + } else { + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, + 0x03, 0x00); + } else { + regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, + 0x03, 0x00); + } + } + + return 0; +} + +#define es8389_RATES SNDRV_PCM_RATE_8000_96000 + +#define es8389_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops es8389_ops = { + .hw_params = es8389_pcm_hw_params, + .set_fmt = es8389_set_dai_fmt, + .set_sysclk = es8389_set_dai_sysclk, + .set_tdm_slot = es8389_set_tdm_slot, + .mute_stream = es8389_mute, +}; + +static struct snd_soc_dai_driver es8389_dai = { + .name = "ES8389 HiFi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = es8389_RATES, + .formats = es8389_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = es8389_RATES, + .formats = es8389_FORMATS, + }, + .ops = &es8389_ops, + .symmetric_rate = 1, +}; + +static void es8389_init(struct snd_soc_component *component) +{ + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00); + regmap_write(es8389->regmap, ES8389_RESET, 0x7E); + regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38); + regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64); + regmap_write(es8389->regmap, ES8389_ADC_HPF2, 0x04); + regmap_write(es8389->regmap, ES8389_DAC_INV, 0x03); + + regmap_write(es8389->regmap, ES8389_VMID, 0x2A); + regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xC9); + regmap_write(es8389->regmap, ES8389_ANA_VSEL, 0x4F); + regmap_write(es8389->regmap, ES8389_ANA_CTL2, 0x06); + regmap_write(es8389->regmap, ES8389_LOW_POWER1, 0x00); + regmap_write(es8389->regmap, ES8389_DMIC_EN, 0x16); + + regmap_write(es8389->regmap, ES8389_PGA_SW, 0xAA); + regmap_write(es8389->regmap, ES8389_MOD_SW1, 0x66); + regmap_write(es8389->regmap, ES8389_MOD_SW2, 0x99); + regmap_write(es8389->regmap, ES8389_ADC_MODE, (0x00 | ES8389_TDM_MODE)); + regmap_update_bits(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0x00); + regmap_update_bits(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x00); + + regmap_update_bits(es8389->regmap, ES8389_MIC1_GAIN, + ES8389_MIC_SEL_MASK, ES8389_MIC_DEFAULT); + regmap_update_bits(es8389->regmap, ES8389_MIC2_GAIN, + ES8389_MIC_SEL_MASK, ES8389_MIC_DEFAULT); + regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xC4); + regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x08); + regmap_write(es8389->regmap, ES8389_CSM_STATE1, 0x00); + regmap_write(es8389->regmap, ES8389_SYSTEM12, 0x01); + regmap_write(es8389->regmap, ES8389_SYSTEM13, 0x01); + regmap_write(es8389->regmap, ES8389_SYSTEM14, 0x01); + regmap_write(es8389->regmap, ES8389_SYSTEM15, 0x01); + regmap_write(es8389->regmap, ES8389_SYSTEM16, 0x35); + regmap_write(es8389->regmap, ES8389_SYSTEM17, 0x09); + regmap_write(es8389->regmap, ES8389_SYSTEM18, 0x91); + regmap_write(es8389->regmap, ES8389_SYSTEM19, 0x28); + regmap_write(es8389->regmap, ES8389_SYSTEM1A, 0x01); + regmap_write(es8389->regmap, ES8389_SYSTEM1B, 0x01); + regmap_write(es8389->regmap, ES8389_SYSTEM1C, 0x11); + + regmap_write(es8389->regmap, ES8389_CHIP_MISC, 0x13); + regmap_write(es8389->regmap, ES8389_MASTER_CLK, 0x00); + regmap_write(es8389->regmap, ES8389_CLK_DIV1, 0x00); + regmap_write(es8389->regmap, ES8389_CLK_MUL, 0x10); + regmap_write(es8389->regmap, ES8389_CLK_MUX1, 0x00); + regmap_write(es8389->regmap, ES8389_CLK_MUX2, 0xC0); + regmap_write(es8389->regmap, ES8389_CLK_CTL1, 0x00); + regmap_write(es8389->regmap, ES8389_CLK_CTL2, 0xC0); + regmap_write(es8389->regmap, ES8389_CLK_CTL3, 0x80); + regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04); + regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01); + regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00); + regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00); + regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F); + regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F); + regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0); + regmap_write(es8389->regmap, ES8389_SYSTEM30, 0xF4); + regmap_write(es8389->regmap, ES8389_DAC_DSM_OSR, 0x7F); + regmap_write(es8389->regmap, ES8389_DAC_DSP_OSR, 0x7F); + regmap_write(es8389->regmap, ES8389_DAC_MISC, 0x10); + regmap_write(es8389->regmap, ES8389_DAC_RAMP, 0x0F); + regmap_write(es8389->regmap, ES8389_SYSTEM4C, 0xC0); + regmap_write(es8389->regmap, ES8389_RESET, 0x00); + regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC1); + regmap_write(es8389->regmap, ES8389_RESET, 0x01); + regmap_write(es8389->regmap, ES8389_DAC_RESET, 0x02); + + regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, 0x03, 0x03); + regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, 0x03, 0x03); +} + +static int es8389_suspend(struct snd_soc_component *component) +{ + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); + regcache_cache_only(es8389->regmap, true); + regcache_mark_dirty(es8389->regmap); + + return 0; +} + +static int es8389_resume(struct snd_soc_component *component) +{ + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + unsigned int regv; + + regcache_cache_only(es8389->regmap, false); + regcache_cache_bypass(es8389->regmap, true); + regmap_read(es8389->regmap, ES8389_RESET, ®v); + regcache_cache_bypass(es8389->regmap, false); + + if (regv == 0xff) + es8389_init(component); + else + es8389_set_bias_level(component, SND_SOC_BIAS_ON); + + regcache_sync(es8389->regmap); + + return 0; +} + +static int es8389_probe(struct snd_soc_component *component) +{ + int ret; + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + ret = device_property_read_u8(component->dev, "everest,mclk-src", &es8389->mclk_src); + if (ret != 0) { + dev_dbg(component->dev, "mclk-src return %d", ret); + es8389->mclk_src = ES8389_MCLK_SOURCE; + } + + es8389->mclk = devm_clk_get(component->dev, "mclk"); + if (IS_ERR(es8389->mclk)) + return dev_err_probe(component->dev, PTR_ERR(es8389->mclk), + "ES8389 is unable to get mclk\n"); + + if (!es8389->mclk) + dev_err(component->dev, "%s, assuming static mclk\n", __func__); + + ret = clk_prepare_enable(es8389->mclk); + if (ret) { + dev_err(component->dev, "%s, unable to enable mclk\n", __func__); + return ret; + } + + es8389_init(component); + es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); + + return 0; +} + +static void es8389_remove(struct snd_soc_component *component) +{ + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x28); + regmap_write(es8389->regmap, ES8389_HPSW, 0x00); + regmap_write(es8389->regmap, ES8389_VMID, 0x00); + regmap_write(es8389->regmap, ES8389_RESET, 0x00); + regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xCC); + usleep_range(500000, 550000);//500MS + regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0x00); + regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08); + regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1); + regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00); + +} + +static const struct snd_soc_component_driver soc_codec_dev_es8389 = { + .probe = es8389_probe, + .remove = es8389_remove, + .suspend = es8389_suspend, + .resume = es8389_resume, + .set_bias_level = es8389_set_bias_level, + + .controls = es8389_snd_controls, + .num_controls = ARRAY_SIZE(es8389_snd_controls), + .dapm_widgets = es8389_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8389_dapm_widgets), + .dapm_routes = es8389_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8389_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, +}; + +static const struct regmap_config es8389_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = ES8389_MAX_REGISTER, + + .volatile_reg = es8389_volatile_register, + .cache_type = REGCACHE_MAPLE, +}; + +static void es8389_i2c_shutdown(struct i2c_client *i2c) +{ + struct es8389_private *es8389; + + es8389 = i2c_get_clientdata(i2c); + + regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x28); + regmap_write(es8389->regmap, ES8389_HPSW, 0x00); + regmap_write(es8389->regmap, ES8389_VMID, 0x00); + regmap_write(es8389->regmap, ES8389_RESET, 0x00); + regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xCC); + usleep_range(500000, 550000);//500MS + regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0x00); + regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08); + regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1); + regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00); +} + +static int es8389_i2c_probe(struct i2c_client *i2c_client) +{ + struct es8389_private *es8389; + int ret; + + es8389 = devm_kzalloc(&i2c_client->dev, sizeof(*es8389), GFP_KERNEL); + if (es8389 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c_client, es8389); + es8389->regmap = devm_regmap_init_i2c(i2c_client, &es8389_regmap); + if (IS_ERR(es8389->regmap)) + return dev_err_probe(&i2c_client->dev, PTR_ERR(es8389->regmap), + "regmap_init() failed\n"); + + ret = devm_snd_soc_register_component(&i2c_client->dev, + &soc_codec_dev_es8389, + &es8389_dai, + 1); + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id es8389_if_dt_ids[] = { + { .compatible = "everest,es8389", }, + { } +}; +MODULE_DEVICE_TABLE(of, es8389_if_dt_ids); +#endif + +static const struct i2c_device_id es8389_i2c_id[] = { + {"es8389"}, + { } +}; +MODULE_DEVICE_TABLE(i2c, es8389_i2c_id); + +static struct i2c_driver es8389_i2c_driver = { + .driver = { + .name = "es8389", + .of_match_table = of_match_ptr(es8389_if_dt_ids), + }, + .shutdown = es8389_i2c_shutdown, + .probe = es8389_i2c_probe, + .id_table = es8389_i2c_id, +}; +module_i2c_driver(es8389_i2c_driver); + +MODULE_DESCRIPTION("ASoC es8389 driver"); +MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h new file mode 100644 index 000000000000..123d1e4b2d53 --- /dev/null +++ b/sound/soc/codecs/es8389.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +* ES8389.h -- ES8389 ALSA SoC Audio Codec +* +* Authors: +* +* Based on ES8374.h by Michael Zhang +*/ + +#ifndef _ES8389_H +#define _ES8389_H + +/* +* ES8389_REGISTER NAME_REG_REGISTER ADDRESS +*/ +#define ES8389_RESET 0x00 /*reset digital,csm,clock manager etc.*/ + +/* +* Clock Scheme Register definition +*/ +#define ES8389_MASTER_MODE 0x01 +#define ES8389_MASTER_CLK 0x02 +#define ES8389_CLK_OFF1 0x03 +#define ES8389_CLK_DIV1 0x04 +#define ES8389_CLK_MUL 0x05 +#define ES8389_CLK_MUX1 0x06 +#define ES8389_CLK_MUX2 0x07 +#define ES8389_CLK_CTL1 0x08 +#define ES8389_CLK_CTL2 0x09 +#define ES8389_CLK_CTL3 0x0A +#define ES8389_SCLK_DIV 0x0B +#define ES8389_LRCK_DIV1 0x0C +#define ES8389_LRCK_DIV2 0x0D +#define ES8389_CLK_OFF2 0x0E +#define ES8389_OSC_CLK 0x0F +#define ES8389_CSM_JUMP 0x10 +#define ES8389_CLK_DIV2 0x11 +#define ES8389_SYSTEM12 0x12 +#define ES8389_SYSTEM13 0x13 +#define ES8389_SYSTEM14 0x14 +#define ES8389_SYSTEM15 0x15 +#define ES8389_SYSTEM16 0x16 +#define ES8389_SYSTEM17 0x17 +#define ES8389_SYSTEM18 0x18 +#define ES8389_SYSTEM19 0x19 +#define ES8389_SYSTEM1A 0x1A +#define ES8389_SYSTEM1B 0x1B +#define ES8389_SYSTEM1C 0x1C +#define ES8389_ADC_FORMAT_MUTE 0x20 +#define ES8389_ADC_OSR 0x21 +#define ES8389_ADC_DSP 0x22 +#define ES8389_ADC_MODE 0x23 +#define ES8389_ADC_HPF1 0x24 +#define ES8389_ADC_HPF2 0x25 +#define ES8389_OSR_VOL 0x26 +#define ES8389_ADCL_VOL 0x27 +#define ES8389_ADCR_VOL 0x28 +#define ES8389_ALC_CTL 0x29 +#define ES8389_PTDM_SLOT 0x2A +#define ES8389_ALC_ON 0x2B +#define ES8389_ALC_TARGET 0x2C +#define ES8389_ALC_GAIN 0x2D +#define ES8389_SYSTEM2E 0x2E +#define ES8389_ADC_MUTE 0x2F +#define ES8389_SYSTEM30 0x30 +#define ES8389_ADC_RESET 0x31 +#define ES8389_DAC_FORMAT_MUTE 0x40 +#define ES8389_DAC_DSM_OSR 0x41 +#define ES8389_DAC_DSP_OSR 0x42 +#define ES8389_DAC_MISC 0x43 +#define ES8389_DAC_MIX 0x44 +#define ES8389_DAC_INV 0x45 +#define ES8389_DACL_VOL 0x46 +#define ES8389_DACR_VOL 0x47 +#define ES8389_MIX_VOL 0x48 +#define ES8389_DAC_RAMP 0x49 +#define ES8389_SYSTEM4C 0x4C +#define ES8389_DAC_RESET 0x4D +#define ES8389_VMID 0x60 +#define ES8389_ANA_CTL1 0x61 +#define ES8389_ANA_VSEL 0x62 +#define ES8389_ANA_CTL2 0x63 +#define ES8389_ADC_EN 0x64 +#define ES8389_HPSW 0x69 +#define ES8389_LOW_POWER1 0x6B +#define ES8389_LOW_POWER2 0x6C +#define ES8389_DMIC_EN 0x6D +#define ES8389_PGA_SW 0x6E +#define ES8389_MOD_SW1 0x6F +#define ES8389_MOD_SW2 0x70 +#define ES8389_MOD_SW3 0x71 +#define ES8389_MIC1_GAIN 0x72 +#define ES8389_MIC2_GAIN 0x73 + +#define ES8389_CHIP_MISC 0xF0 +#define ES8389_CSM_STATE1 0xF1 +#define ES8389_PULL_DOWN 0xF2 +#define ES8389_ISO_CTL 0xF3 +#define ES8389_CSM_STATE2 0xF4 + +#define ES8389_CHIP_ID0 0xFD +#define ES8389_CHIP_ID1 0xFE + +#define ES8389_MAX_REGISTER 0xFF + +#define ES8389_MIC_SEL_MASK (7 << 4) +#define ES8389_MIC_DEFAULT (1 << 4) + +#define ES8389_MASTER_MODE_EN (1 << 0) + +#define ES8389_TDM_OFF (0 << 0) +#define ES8389_STDM_ON (1 << 7) +#define ES8389_PTDM_ON (1 << 6) + +#define ES8389_TDM_MODE ES8389_TDM_OFF +#define ES8389_TDM_SLOT (0x70 << 0) +#define ES8389_TDM_SHIFT 4 + +#define ES8389_MCLK_SOURCE (1 << 6) +#define ES8389_MCLK_PIN (1 << 6) +#define ES8389_SCLK_PIN (0 << 6) + +/* ES8389_FMT */ +#define ES8389_S24_LE (0 << 5) +#define ES8389_S20_3_LE (1 << 5) +#define ES8389_S18_LE (2 << 5) +#define ES8389_S16_LE (3 << 5) +#define ES8389_S32_LE (4 << 5) +#define ES8389_DATA_LEN_MASK (7 << 5) + +#define ES8389_DAIFMT_MASK (7 << 2) +#define ES8389_DAIFMT_I2S 0 +#define ES8389_DAIFMT_LEFT_J (1 << 2) +#define ES8389_DAIFMT_DSP_A (1 << 3) +#define ES8389_DAIFMT_DSP_B (3 << 3) + +#define ES8389_STATE_ON (13 << 0) +#define ES8389_STATE_STANDBY (7 << 0) + +#endif diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 17019b1d680b..31121f9c18c9 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -842,12 +842,28 @@ static void print_eld_info(struct snd_info_entry *entry, static int hdmi_dai_proc_new(struct hdmi_codec_priv *hcp, struct snd_soc_dai *dai) { + struct snd_soc_component *component = dai->component; + struct snd_soc_card *card = component->card; + struct snd_soc_dai *d; + struct snd_soc_pcm_runtime *rtd; struct snd_info_entry *entry; char name[32]; - int err; + int err, i, id = 0; - snprintf(name, sizeof(name), "eld#%d", dai->id); - err = snd_card_proc_new(dai->component->card->snd_card, name, &entry); + /* + * To avoid duplicate proc entry, find its rtd and use rtd->id + * instead of dai->id + */ + for_each_card_rtds(card, rtd) { + for_each_rtd_dais(rtd, i, d) + if (d == dai) { + id = rtd->id; + goto found; + } + } +found: + snprintf(name, sizeof(name), "eld#%d", id); + err = snd_card_proc_new(card->snd_card, name, &entry); if (err < 0) return err; @@ -1010,6 +1026,7 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { .startup = hdmi_codec_startup, .shutdown = hdmi_codec_shutdown, .hw_params = hdmi_codec_hw_params, + .prepare = hdmi_codec_prepare, .mute_stream = hdmi_codec_mute, .pcm_new = hdmi_codec_pcm_new, }; diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c index cb7a68c799f8..55e90604bbaa 100644 --- a/sound/soc/codecs/idt821034.c +++ b/sound/soc/codecs/idt821034.c @@ -957,7 +957,8 @@ static const struct snd_soc_component_driver idt821034_component_driver = { #define IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(_offset) (((_offset) / 5) % 4) #define IDT821034_GPIO_OFFSET_TO_SLIC_MASK(_offset) BIT((_offset) % 5) -static void idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) +static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, + int val) { u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); @@ -973,12 +974,14 @@ static void idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, in else slic_raw &= ~mask; ret = idt821034_write_slic_raw(idt821034, ch, slic_raw); - if (ret) { + + mutex_unlock(&idt821034->mutex); + + if (ret) dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n", offset, ch, mask, ret); - } - mutex_unlock(&idt821034->mutex); + return ret; } static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset) @@ -1054,7 +1057,9 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off u8 slic_conf; int ret; - idt821034_chip_gpio_set(c, offset, val); + ret = idt821034_chip_gpio_set(c, offset, val); + if (ret) + return ret; mutex_lock(&idt821034->mutex); @@ -1112,7 +1117,7 @@ static int idt821034_gpio_init(struct idt821034 *idt821034) idt821034->gpio_chip.direction_input = idt821034_chip_direction_input; idt821034->gpio_chip.direction_output = idt821034_chip_direction_output; idt821034->gpio_chip.get = idt821034_chip_gpio_get; - idt821034->gpio_chip.set = idt821034_chip_gpio_set; + idt821034->gpio_chip.set_rv = idt821034_chip_gpio_set; idt821034->gpio_chip.can_sleep = true; return devm_gpiochip_add_data(&idt821034->spi->dev, &idt821034->gpio_chip, diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index b261fa373e65..c1fb71cfb5d0 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -63,6 +63,10 @@ #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_TX_SPKR_PROT_PCM_RATE_16K 1 +#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_24K 2 +#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_32K 3 +#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_48K 4 #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) @@ -407,6 +411,7 @@ struct wsa_macro { int ear_spkr_gain; int spkr_gain_offset; int spkr_mode; + u32 pcm_rate_vi; int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX]; int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX]; struct regmap *regmap; @@ -1280,6 +1285,7 @@ static int wsa_macro_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); int ret; switch (substream->stream) { @@ -1292,6 +1298,11 @@ static int wsa_macro_hw_params(struct snd_pcm_substream *substream, return ret; } break; + case SNDRV_PCM_STREAM_CAPTURE: + if (dai->id == WSA_MACRO_AIF_VI) + wsa->pcm_rate_vi = params_rate(params); + + break; default: break; } @@ -1448,35 +1459,11 @@ static void wsa_macro_mclk_enable(struct wsa_macro *wsa, bool mclk_enable) } } -static int wsa_macro_mclk_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static void wsa_macro_enable_disable_vi_sense(struct snd_soc_component *component, bool enable, + u32 tx_reg0, u32 tx_reg1, u32 val) { - 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 */ + if (enable) { + /* 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); @@ -1485,10 +1472,10 @@ static int wsa_macro_enable_vi_feedback(struct snd_soc_dapm_widget *w, 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); + val); snd_soc_component_update_bits(component, tx_reg1, CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK, - CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K); + val); snd_soc_component_update_bits(component, tx_reg0, CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK, CDC_WSA_TX_SPKR_PROT_CLK_ENABLE); @@ -1501,9 +1488,7 @@ static int wsa_macro_enable_vi_feedback(struct snd_soc_dapm_widget *w, 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 */ + } else { snd_soc_component_update_bits(component, tx_reg0, CDC_WSA_TX_SPKR_PROT_RESET_MASK, CDC_WSA_TX_SPKR_PROT_RESET); @@ -1516,6 +1501,72 @@ static int wsa_macro_enable_vi_feedback(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(component, tx_reg1, CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK, CDC_WSA_TX_SPKR_PROT_CLK_DISABLE); + } +} + +static void wsa_macro_enable_disable_vi_feedback(struct snd_soc_component *component, + bool enable, u32 rate) +{ + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + if (test_bit(WSA_MACRO_TX0, &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) + wsa_macro_enable_disable_vi_sense(component, enable, + CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + CDC_WSA_TX1_SPKR_PROT_PATH_CTL, rate); + + if (test_bit(WSA_MACRO_TX1, &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) + wsa_macro_enable_disable_vi_sense(component, enable, + CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + CDC_WSA_TX3_SPKR_PROT_PATH_CTL, rate); +} + +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 rate_val; + + switch (wsa->pcm_rate_vi) { + case 8000: + rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K; + break; + case 16000: + rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_16K; + break; + case 24000: + rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_24K; + break; + case 32000: + rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_32K; + break; + case 48000: + rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_48K; + break; + default: + rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Enable V&I sensing */ + wsa_macro_enable_disable_vi_feedback(component, true, rate_val); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable V&I sensing */ + wsa_macro_enable_disable_vi_feedback(component, false, rate_val); break; } diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index b2bd2f172ae7..75af12231d1d 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -1642,8 +1642,7 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) } ret = pcmdev_profile_ctrl_add(pcm_dev); out: - if (fw_entry) - release_firmware(fw_entry); + release_firmware(fw_entry); mutex_unlock(&pcm_dev->codec_lock); return ret; diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index a989cfe058f0..b8905c03445e 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -1726,7 +1726,8 @@ end: return ret; } -static void peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) +static int peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, + int val) { struct peb2466 *peb2466 = gpiochip_get_data(c); unsigned int xr_reg; @@ -1740,14 +1741,14 @@ static void peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int */ dev_warn(&peb2466->spi->dev, "cannot set gpio %d (read-only)\n", offset); - return; + return -EINVAL; } ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask); if (ret) { dev_err(&peb2466->spi->dev, "cannot set gpio %d (%d)\n", offset, ret); - return; + return ret; } ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, val ? mask : 0); @@ -1755,6 +1756,8 @@ static void peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int dev_err(&peb2466->spi->dev, "set gpio %d (0x%x, 0x%x) failed (%d)\n", offset, xr_reg, mask, ret); } + + return ret; } static int peb2466_chip_gpio_get(struct gpio_chip *c, unsigned int offset) @@ -1879,7 +1882,9 @@ static int peb2466_chip_direction_output(struct gpio_chip *c, unsigned int offse return -EINVAL; } - peb2466_chip_gpio_set(c, offset, val); + ret = peb2466_chip_gpio_set(c, offset, val); + if (ret) + return ret; if (offset < 16) { /* SOx_{0,1} */ @@ -1940,7 +1945,7 @@ static int peb2466_gpio_init(struct peb2466 *peb2466) peb2466->gpio.gpio_chip.direction_input = peb2466_chip_direction_input; peb2466->gpio.gpio_chip.direction_output = peb2466_chip_direction_output; peb2466->gpio.gpio_chip.get = peb2466_chip_gpio_get; - peb2466->gpio.gpio_chip.set = peb2466_chip_gpio_set; + peb2466->gpio.gpio_chip.set_rv = peb2466_chip_gpio_set; peb2466->gpio.gpio_chip.can_sleep = true; return devm_gpiochip_add_data(&peb2466->spi->dev, &peb2466->gpio.gpio_chip, diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 343e3bcef0ca..dba78efadc85 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -4286,7 +4286,7 @@ static void rt5645_i2c_remove(struct i2c_client *i2c) * Since the rt5645_btn_check_callback() can queue jack_detect_work, * the timer need to be delted first */ - del_timer_sync(&rt5645->btn_check_timer); + timer_delete_sync(&rt5645->btn_check_timer); cancel_delayed_work_sync(&rt5645->jack_detect_work); cancel_delayed_work_sync(&rt5645->rcclock_work); @@ -4318,7 +4318,7 @@ static int rt5645_sys_suspend(struct device *dev) { struct rt5645_priv *rt5645 = dev_get_drvdata(dev); - del_timer_sync(&rt5645->btn_check_timer); + timer_delete_sync(&rt5645->btn_check_timer); cancel_delayed_work_sync(&rt5645->jack_detect_work); cancel_delayed_work_sync(&rt5645->rcclock_work); diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index e0d1991cffdb..b16b2c66e754 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -31,9 +31,7 @@ #include "rl6231.h" #include "rt5665.h" -#define RT5665_NUM_SUPPLIES 3 - -static const char *rt5665_supply_names[RT5665_NUM_SUPPLIES] = { +static const char * const rt5665_supply_names[] = { "AVDD", "MICVDD", "VBAT", @@ -46,7 +44,6 @@ struct rt5665_priv { struct gpio_desc *gpiod_ldo1_en; struct gpio_desc *gpiod_reset; struct snd_soc_jack *hs_jack; - struct regulator_bulk_data supplies[RT5665_NUM_SUPPLIES]; struct delayed_work jack_detect_work; struct delayed_work calibrate_work; struct delayed_work jd_check_work; @@ -1025,102 +1022,6 @@ static int rt5665_mono_vol_put(struct snd_kcontrol *kcontrol, return ret; } -/** - * rt5665_sel_asrc_clk_src - select ASRC clock source for a set of filters - * @component: SoC audio component device. - * @filter_mask: mask of filters. - * @clk_src: clock source - * - * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5665 can - * only support standard 32fs or 64fs i2s format, ASRC should be enabled to - * support special i2s clock format such as Intel's 100fs(100 * sampling rate). - * ASRC function will track i2s clock and generate a corresponding system clock - * for codec. This function provides an API to select the clock source for a - * set of filters specified by the mask. And the codec driver will turn on ASRC - * for these filters if ASRC is selected as their clock source. - */ -int rt5665_sel_asrc_clk_src(struct snd_soc_component *component, - unsigned int filter_mask, unsigned int clk_src) -{ - unsigned int asrc2_mask = 0; - unsigned int asrc2_value = 0; - unsigned int asrc3_mask = 0; - unsigned int asrc3_value = 0; - - switch (clk_src) { - case RT5665_CLK_SEL_SYS: - case RT5665_CLK_SEL_I2S1_ASRC: - case RT5665_CLK_SEL_I2S2_ASRC: - case RT5665_CLK_SEL_I2S3_ASRC: - case RT5665_CLK_SEL_SYS2: - case RT5665_CLK_SEL_SYS3: - case RT5665_CLK_SEL_SYS4: - break; - - default: - return -EINVAL; - } - - if (filter_mask & RT5665_DA_STEREO1_FILTER) { - asrc2_mask |= RT5665_DA_STO1_CLK_SEL_MASK; - asrc2_value = (asrc2_value & ~RT5665_DA_STO1_CLK_SEL_MASK) - | (clk_src << RT5665_DA_STO1_CLK_SEL_SFT); - } - - if (filter_mask & RT5665_DA_STEREO2_FILTER) { - asrc2_mask |= RT5665_DA_STO2_CLK_SEL_MASK; - asrc2_value = (asrc2_value & ~RT5665_DA_STO2_CLK_SEL_MASK) - | (clk_src << RT5665_DA_STO2_CLK_SEL_SFT); - } - - if (filter_mask & RT5665_DA_MONO_L_FILTER) { - asrc2_mask |= RT5665_DA_MONOL_CLK_SEL_MASK; - asrc2_value = (asrc2_value & ~RT5665_DA_MONOL_CLK_SEL_MASK) - | (clk_src << RT5665_DA_MONOL_CLK_SEL_SFT); - } - - if (filter_mask & RT5665_DA_MONO_R_FILTER) { - asrc2_mask |= RT5665_DA_MONOR_CLK_SEL_MASK; - asrc2_value = (asrc2_value & ~RT5665_DA_MONOR_CLK_SEL_MASK) - | (clk_src << RT5665_DA_MONOR_CLK_SEL_SFT); - } - - if (filter_mask & RT5665_AD_STEREO1_FILTER) { - asrc3_mask |= RT5665_AD_STO1_CLK_SEL_MASK; - asrc3_value = (asrc2_value & ~RT5665_AD_STO1_CLK_SEL_MASK) - | (clk_src << RT5665_AD_STO1_CLK_SEL_SFT); - } - - if (filter_mask & RT5665_AD_STEREO2_FILTER) { - asrc3_mask |= RT5665_AD_STO2_CLK_SEL_MASK; - asrc3_value = (asrc2_value & ~RT5665_AD_STO2_CLK_SEL_MASK) - | (clk_src << RT5665_AD_STO2_CLK_SEL_SFT); - } - - if (filter_mask & RT5665_AD_MONO_L_FILTER) { - asrc3_mask |= RT5665_AD_MONOL_CLK_SEL_MASK; - asrc3_value = (asrc3_value & ~RT5665_AD_MONOL_CLK_SEL_MASK) - | (clk_src << RT5665_AD_MONOL_CLK_SEL_SFT); - } - - if (filter_mask & RT5665_AD_MONO_R_FILTER) { - asrc3_mask |= RT5665_AD_MONOR_CLK_SEL_MASK; - asrc3_value = (asrc3_value & ~RT5665_AD_MONOR_CLK_SEL_MASK) - | (clk_src << RT5665_AD_MONOR_CLK_SEL_SFT); - } - - if (asrc2_mask) - snd_soc_component_update_bits(component, RT5665_ASRC_2, - asrc2_mask, asrc2_value); - - if (asrc3_mask) - snd_soc_component_update_bits(component, RT5665_ASRC_3, - asrc3_mask, asrc3_value); - - return 0; -} -EXPORT_SYMBOL_GPL(rt5665_sel_asrc_clk_src); - static int rt5665_button_detect(struct snd_soc_component *component) { int btn_type, val; @@ -4471,8 +4372,6 @@ static void rt5665_remove(struct snd_soc_component *component) struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); regmap_write(rt5665->regmap, RT5665_RESET, 0); - - regulator_bulk_disable(ARRAY_SIZE(rt5665->supplies), rt5665->supplies); } #ifdef CONFIG_PM @@ -4758,7 +4657,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c) { struct rt5665_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5665_priv *rt5665; - int i, ret; + int ret; unsigned int val; rt5665 = devm_kzalloc(&i2c->dev, sizeof(struct rt5665_priv), @@ -4774,24 +4673,13 @@ static int rt5665_i2c_probe(struct i2c_client *i2c) else rt5665_parse_dt(rt5665, &i2c->dev); - for (i = 0; i < ARRAY_SIZE(rt5665->supplies); i++) - rt5665->supplies[i].supply = rt5665_supply_names[i]; - - ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5665->supplies), - rt5665->supplies); + ret = devm_regulator_bulk_get_enable(&i2c->dev, ARRAY_SIZE(rt5665_supply_names), + rt5665_supply_names); if (ret != 0) { dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); return ret; } - ret = regulator_bulk_enable(ARRAY_SIZE(rt5665->supplies), - rt5665->supplies); - if (ret != 0) { - dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); - return ret; - } - - rt5665->gpiod_ldo1_en = devm_gpiod_get_optional(&i2c->dev, "realtek,ldo1-en", GPIOD_OUT_HIGH); diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h index 12ab28e5f10d..089e4078d37a 100644 --- a/sound/soc/codecs/rt5665.h +++ b/sound/soc/codecs/rt5665.h @@ -1999,7 +1999,4 @@ enum { RT5665_CLK_SEL_SYS4, }; -int rt5665_sel_asrc_clk_src(struct snd_soc_component *component, - unsigned int filter_mask, unsigned int clk_src); - #endif /* __RT5665_H__ */ diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index f626453f332b..8442dd09cfaf 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -799,49 +799,6 @@ static void rt5668_reset(struct regmap *regmap) regmap_write(regmap, RT5668_RESET, 0); regmap_write(regmap, RT5668_I2C_MODE, 1); } -/** - * rt5668_sel_asrc_clk_src - select ASRC clock source for a set of filters - * @component: SoC audio component device. - * @filter_mask: mask of filters. - * @clk_src: clock source - * - * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5668 can - * only support standard 32fs or 64fs i2s format, ASRC should be enabled to - * support special i2s clock format such as Intel's 100fs(100 * sampling rate). - * ASRC function will track i2s clock and generate a corresponding system clock - * for codec. This function provides an API to select the clock source for a - * set of filters specified by the mask. And the component driver will turn on - * ASRC for these filters if ASRC is selected as their clock source. - */ -int rt5668_sel_asrc_clk_src(struct snd_soc_component *component, - unsigned int filter_mask, unsigned int clk_src) -{ - - switch (clk_src) { - case RT5668_CLK_SEL_SYS: - case RT5668_CLK_SEL_I2S1_ASRC: - case RT5668_CLK_SEL_I2S2_ASRC: - break; - - default: - return -EINVAL; - } - - if (filter_mask & RT5668_DA_STEREO1_FILTER) { - snd_soc_component_update_bits(component, RT5668_PLL_TRACK_2, - RT5668_FILTER_CLK_SEL_MASK, - clk_src << RT5668_FILTER_CLK_SEL_SFT); - } - - if (filter_mask & RT5668_AD_STEREO1_FILTER) { - snd_soc_component_update_bits(component, RT5668_PLL_TRACK_3, - RT5668_FILTER_CLK_SEL_MASK, - clk_src << RT5668_FILTER_CLK_SEL_SFT); - } - - return 0; -} -EXPORT_SYMBOL_GPL(rt5668_sel_asrc_clk_src); static int rt5668_button_detect(struct snd_soc_component *component) { diff --git a/sound/soc/codecs/rt5668.h b/sound/soc/codecs/rt5668.h index 6b851ddcc58a..b34a61d2109c 100644 --- a/sound/soc/codecs/rt5668.h +++ b/sound/soc/codecs/rt5668.h @@ -1309,7 +1309,4 @@ enum { RT5668_CLK_SEL_I2S2_ASRC, }; -int rt5668_sel_asrc_clk_src(struct snd_soc_component *component, - unsigned int filter_mask, unsigned int clk_src); - #endif /* __RT5668_H__ */ diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index abe0a5a95770..885edcf0a3a5 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -365,8 +365,8 @@ static void rt5677_spi_copy_work(struct work_struct *work) new_bytes -= copy_bytes; } - delay = bytes_to_frames(runtime, period_bytes) / (runtime->rate / 1000); - schedule_delayed_work(&rt5677_dsp->copy_work, msecs_to_jiffies(delay)); + delay = bytes_to_frames(runtime, period_bytes) / runtime->rate; + schedule_delayed_work(&rt5677_dsp->copy_work, secs_to_jiffies(delay)); done: mutex_unlock(&rt5677_dsp->dma_lock); } diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 6e4774148566..69a0fb8d7f77 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4725,13 +4725,14 @@ static int rt5677_update_gpio_bits(struct rt5677_priv *rt5677, unsigned offset, } #ifdef CONFIG_GPIOLIB -static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int rt5677_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct rt5677_priv *rt5677 = gpiochip_get_data(chip); int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO; int m = RT5677_GPIOx_OUT_MASK; - rt5677_update_gpio_bits(rt5677, offset, m, level); + return rt5677_update_gpio_bits(rt5677, offset, m, level); } static int rt5677_gpio_direction_out(struct gpio_chip *chip, @@ -4834,7 +4835,7 @@ static const struct gpio_chip rt5677_template_chip = { .label = RT5677_DRV_NAME, .owner = THIS_MODULE, .direction_output = rt5677_gpio_direction_out, - .set = rt5677_gpio_set, + .set_rv = rt5677_gpio_set, .direction_input = rt5677_gpio_direction_in, .get = rt5677_gpio_get, .to_irq = rt5677_to_irq, diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index db011da63bd9..4d044dfa3136 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -263,12 +263,8 @@ static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, if (!adc_vol_flag) /* boost gain */ ctl = regvalue / 0x0a00; - else { /* ADC gain */ - if (adc_vol_flag) - ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); - else - ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset); - } + else /* ADC gain */ + ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); ucontrol->value.integer.value[i] = ctl; } diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 19d99b9d4ab2..570c2af1245d 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -1065,12 +1065,8 @@ static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, if (!adc_vol_flag) /* boost gain */ ctl = regvalue / 0x0a00; - else { /* ADC gain */ - if (adc_vol_flag) - ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); - else - ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset); - } + else /* ADC gain */ + ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); ucontrol->value.integer.value[i] = ctl; } diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index 11e2e8f68a98..609ca0d6c83a 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -24,6 +24,7 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) case 0x2f50: case 0x2f54: case 0x2f58 ... 0x2f5d: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_SELECTED_MODE, 0): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, @@ -42,8 +43,12 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) RT722_SDCA_CTL_FU_MUTE, CH_R): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, + RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, + RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, @@ -56,6 +61,9 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) RT722_SDCA_CTL_VENDOR_DEF, 0): case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, + RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0): case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, @@ -70,6 +78,9 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) RT722_SDCA_CTL_VENDOR_DEF, CH_08): case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, + RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0): case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: @@ -150,11 +161,18 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) switch (reg) { case 0x2f01: case 0x2f54: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0): case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0): case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: case 0x2000000: case 0x200000d: @@ -166,6 +184,8 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) case 0x2000084: case 0x2000086: case 0x3110000: + case 0x5800003: + case 0x5810000: return true; default: return false; diff --git a/sound/soc/codecs/rt722-sdca-sdw.h b/sound/soc/codecs/rt722-sdca-sdw.h index 80b014456940..c5dd472a2c00 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.h +++ b/sound/soc/codecs/rt722-sdca-sdw.h @@ -34,6 +34,7 @@ static const struct reg_default rt722_sdca_reg_defaults[] = { { 0x200003c, 0xc214 }, { 0x2000046, 0x8004 }, { 0x5810000, 0x702d }, + { 0x6100000, 0x0201 }, { 0x6100006, 0x0005 }, { 0x6100010, 0x2630 }, { 0x6100011, 0x152f }, diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index f093ce841b3f..ac9588284a95 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -842,6 +842,7 @@ static int rt722_sdca_fu113_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: rt722->fu1e_dapm_mute = false; rt722_sdca_set_fu1e_capture_ctl(rt722); + usleep_range(150000, 160000); break; case SND_SOC_DAPM_PRE_PMD: rt722->fu1e_dapm_mute = true; @@ -871,6 +872,28 @@ static int rt722_sdca_fu36_event(struct snd_soc_dapm_widget *w, return 0; } +static void rt722_pde_transition_delay(struct rt722_sdca_priv *rt722, unsigned char func, + unsigned char entity, unsigned char ps) +{ + unsigned int delay = 1000, val; + + pm_runtime_mark_last_busy(&rt722->slave->dev); + + /* waiting for Actual PDE becomes to PS0/PS3 */ + while (delay) { + regmap_read(rt722->regmap, + SDW_SDCA_CTL(func, entity, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0), &val); + if (val == ps) + break; + + usleep_range(1000, 1500); + delay--; + } + if (!delay) { + dev_warn(&rt722->slave->dev, "%s PDE to %s is NOT ready", __func__, ps?"PS3":"PS0"); + } +} + static int rt722_sdca_pde47_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -884,11 +907,13 @@ static int rt722_sdca_pde47_event(struct snd_soc_dapm_widget *w, regmap_write(rt722->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, ps0); break; case SND_SOC_DAPM_PRE_PMD: regmap_write(rt722->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, ps3); break; } return 0; @@ -907,11 +932,13 @@ static int rt722_sdca_pde23_event(struct snd_soc_dapm_widget *w, regmap_write(rt722->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + rt722_pde_transition_delay(rt722, FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, ps0); break; case SND_SOC_DAPM_PRE_PMD: regmap_write(rt722->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + rt722_pde_transition_delay(rt722, FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, ps3); break; } return 0; @@ -930,11 +957,13 @@ static int rt722_sdca_pde11_event(struct snd_soc_dapm_widget *w, regmap_write(rt722->regmap, SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + rt722_pde_transition_delay(rt722, FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, ps0); break; case SND_SOC_DAPM_PRE_PMD: regmap_write(rt722->regmap, SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + rt722_pde_transition_delay(rt722, FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, ps3); break; } return 0; @@ -953,11 +982,13 @@ static int rt722_sdca_pde12_event(struct snd_soc_dapm_widget *w, regmap_write(rt722->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, ps0); break; case SND_SOC_DAPM_PRE_PMD: regmap_write(rt722->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, ps3); break; } return 0; @@ -1278,6 +1309,8 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave rt722->slave = slave; rt722->regmap = regmap; + regcache_cache_only(rt722->regmap, true); + mutex_init(&rt722->calibrate_mutex); mutex_init(&rt722->disable_irq_lock); @@ -1302,140 +1335,183 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722) { - /* Set AD07 power entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, - RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a29); - /* Set AD10 power entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, - RT722_ADC10_PDE_FLOAT_CTL, 0x2a00); - /* Set DMIC1/DMIC2 power entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, - RT722_DMIC1_2_PDE_FLOAT_CTL, 0x2a2a); - /* Set DMIC2 IT entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, - RT722_DMIC_ENT_FLOAT_CTL, 0x2626); - /* Set AD10 FU entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, - RT722_ADC_ENT_FLOAT_CTL, 0x1e00); - /* Set DMIC2 FU entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, - RT722_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515); - /* Set AD10 FU channel floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, - RT722_ADC_VOL_CH_FLOAT_CTL, 0x0304); - /* Set DMIC2 FU channel floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, - RT722_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304); - /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2āb00 */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, - RT722_HDA_LEGACY_CONFIG_CTL0, 0x0000); - /* Enable vf707_r12_05/vf707_r13_05 */ - regmap_write(rt722->regmap, - SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, - RT722_SDCA_CTL_VENDOR_DEF, 0), 0x01); - /* Fine tune PDE2A latency */ - regmap_write(rt722->regmap, 0x2f5c, 0x25); + unsigned int mic_func_status; + struct device *dev = &rt722->slave->dev; + + regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &mic_func_status); + dev_dbg(dev, "%s mic func_status=0x%x\n", __func__, mic_func_status); + + if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) { + /* Set AD07 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a29); + /* Set AD10 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC10_PDE_FLOAT_CTL, 0x2a00); + /* Set DMIC1/DMIC2 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC1_2_PDE_FLOAT_CTL, 0x2a2a); + /* Set DMIC2 IT entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_ENT_FLOAT_CTL, 0x2626); + /* Set AD10 FU entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC_ENT_FLOAT_CTL, 0x1e00); + /* Set DMIC2 FU entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515); + /* Set AD10 FU channel floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC_VOL_CH_FLOAT_CTL, 0x0304); + /* Set DMIC2 FU channel floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304); + /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2āb00 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_CONFIG_CTL0, 0x0000); + /* Enable vf707_r12_05/vf707_r13_05 */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, + RT722_SDCA_CTL_VENDOR_DEF, 0), 0x01); + /* Fine tune PDE2A latency */ + regmap_write(rt722->regmap, 0x2f5c, 0x25); + /* PHYtiming TDZ/TZD control */ + regmap_write(rt722->regmap, 0x2f03, 0x06); + + /* clear flag */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); + } } static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722) { - /* Set DVQ=01 */ - rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, - 0xc215); - /* Reset dc_cal_top */ - rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, - 0x702c); - /* W1C Trigger Calibration */ - rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, - 0xf02d); - /* Set DAC02/ClassD power entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_AMP_PDE_FLOAT_CTL, - 0x2323); - /* Set EAPD high */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_EAPD_CTL, - 0x0002); - /* Enable vf707_r14 */ - regmap_write(rt722->regmap, - SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, - RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04); + unsigned int amp_func_status; + struct device *dev = &rt722->slave->dev; + + regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &_func_status); + dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status); + + if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) { + /* Set DVQ=01 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, + 0xc215); + /* Reset dc_cal_top */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, + 0x702c); + /* W1C Trigger Calibration */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, + 0xf02d); + /* Set DAC02/ClassD power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_AMP_PDE_FLOAT_CTL, + 0x2323); + /* Set EAPD high */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_EAPD_CTL, + 0x0002); + /* Enable vf707_r14 */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, + RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04); + + /* clear flag */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); + } } static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) { int loop_check, chk_cnt = 100, ret; unsigned int calib_status = 0; + unsigned int jack_func_status; + struct device *dev = &rt722->slave->dev; + + regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status); + dev_dbg(dev, "%s jack func_status=0x%x\n", __func__, jack_func_status); + + if ((jack_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) { + /* Config analog bias */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_ANALOG_BIAS_CTL3, + 0xa081); + /* GE related settings */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2, + 0xa009); + /* Button A, B, C, D bypass mode */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4, + 0xcf00); + /* HID1 slot enable */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL5, + 0x000f); + /* Report ID for HID1 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL0, + 0x1100); + /* OSC/OOC for slot 2, 3 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL7, + 0x0c12); + /* Set JD de-bounce clock control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_JD_CTRL1, + 0x7002); + /* Set DVQ=01 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, + 0xc215); + /* FSM switch to calibration manual mode */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_FSM_CTL, + 0x4100); + /* W1C Trigger DC calibration (HP) */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DAC_DC_CALI_CTL3, + 0x008d); + /* check HP calibration FSM status */ + for (loop_check = 0; loop_check < chk_cnt; loop_check++) { + usleep_range(10000, 11000); + ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI, + RT722_DAC_DC_CALI_CTL3, &calib_status); + if (ret < 0) + dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret); + if ((calib_status & 0x0040) == 0x0) + break; + } - /* Config analog bias */ - rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_ANALOG_BIAS_CTL3, - 0xa081); - /* GE related settings */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2, - 0xa009); - /* Button A, B, C, D bypass mode */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4, - 0xcf00); - /* HID1 slot enable */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL5, - 0x000f); - /* Report ID for HID1 */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL0, - 0x1100); - /* OSC/OOC for slot 2, 3 */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL7, - 0x0c12); - /* Set JD de-bounce clock control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_JD_CTRL1, - 0x7002); - /* Set DVQ=01 */ - rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, - 0xc215); - /* FSM switch to calibration manual mode */ - rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_FSM_CTL, - 0x4100); - /* W1C Trigger DC calibration (HP) */ - rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DAC_DC_CALI_CTL3, - 0x008d); - /* check HP calibration FSM status */ - for (loop_check = 0; loop_check < chk_cnt; loop_check++) { - usleep_range(10000, 11000); - ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI, - RT722_DAC_DC_CALI_CTL3, &calib_status); - if (ret < 0) - dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret); - if ((calib_status & 0x0040) == 0x0) - break; + if (loop_check == chk_cnt) + dev_dbg(&rt722->slave->dev, "%s, calibration time-out!\n", __func__); + + /* Set ADC09 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL, + 0x2a12); + /* Set MIC2 and LINE1 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_MIC2_LINE2_PDE_FLOAT_CTL, + 0x3429); + /* Set ET41h and LINE2 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ET41_LINE2_PDE_FLOAT_CTL, + 0x4112); + /* Set DAC03 and HP power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL, + 0x4040); + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ENT_FLOAT_CTRL_1, + 0x4141); + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_FLOAT_CTRL_1, + 0x0101); + /* Fine tune PDE40 latency */ + regmap_write(rt722->regmap, 0x2f58, 0x07); + regmap_write(rt722->regmap, 0x2f03, 0x06); + /* MIC VRefo */ + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_COMBO_JACK_AUTO_CTL1, 0x0200, 0x0200); + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_VREFO_GAT, 0x4000, 0x4000); + /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4, + 0x0010); + + /* clear flag */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); } - - if (loop_check == chk_cnt) - dev_dbg(&rt722->slave->dev, "%s, calibration time-out!\n", __func__); - - /* Set ADC09 power entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL, - 0x2a12); - /* Set MIC2 and LINE1 power entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_MIC2_LINE2_PDE_FLOAT_CTL, - 0x3429); - /* Set ET41h and LINE2 power entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ET41_LINE2_PDE_FLOAT_CTL, - 0x4112); - /* Set DAC03 and HP power entity floating control */ - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL, - 0x4040); - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ENT_FLOAT_CTRL_1, - 0x4141); - rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_FLOAT_CTRL_1, - 0x0101); - /* Fine tune PDE40 latency */ - regmap_write(rt722->regmap, 0x2f58, 0x07); - regmap_write(rt722->regmap, 0x2f03, 0x06); - /* MIC VRefo */ - rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, - RT722_COMBO_JACK_AUTO_CTL1, 0x0200, 0x0200); - rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, - RT722_VREFO_GAT, 0x4000, 0x4000); - /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ - rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4, - 0x0010); } int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) @@ -1447,8 +1523,8 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt722->hw_init) return 0; + regcache_cache_only(rt722->regmap, false); if (rt722->first_hw_init) { - regcache_cache_only(rt722->regmap, false); regcache_cache_bypass(rt722->regmap, true); } else { /* diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h index 04c3b4232ef3..3c383705dd3c 100644 --- a/sound/soc/codecs/rt722-sdca.h +++ b/sound/soc/codecs/rt722-sdca.h @@ -183,6 +183,7 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_SDCA_ENT_PLATFORM_FU44 0x44 #define RT722_SDCA_ENT_XU03 0x03 #define RT722_SDCA_ENT_XU0D 0x0d +#define RT722_SDCA_ENT0 0x00 /* RT722 SDCA control */ #define RT722_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 @@ -197,6 +198,8 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_SDCA_CTL_REQ_POWER_STATE 0x01 #define RT722_SDCA_CTL_VENDOR_DEF 0x30 #define RT722_SDCA_CTL_FU_CH_GAIN 0x0b +#define RT722_SDCA_CTL_FUNC_STATUS 0x10 +#define RT722_SDCA_CTL_ACTUAL_POWER_STATE 0x10 /* RT722 SDCA channel */ #define CH_L 0x01 @@ -215,6 +218,9 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_SDCA_RATE_96000HZ 0x0b #define RT722_SDCA_RATE_192000HZ 0x0d +/* Function_Status */ +#define FUNCTION_NEEDS_INITIALIZATION BIT(5) + enum { RT722_AIF1, /* For headset mic and headphone */ RT722_AIF2, /* For speaker */ diff --git a/sound/soc/codecs/rt9123.c b/sound/soc/codecs/rt9123.c new file mode 100644 index 000000000000..242e8c975a62 --- /dev/null +++ b/sound/soc/codecs/rt9123.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt9123.c -- RT9123 (SW I2C Mode) ALSA SoC Codec driver +// +// Author: ChiYuan Huang <cy_huang@richtek.com> + +#include <linux/acpi.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/byteorder/generic.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#define RT9123_REG_AMPCTRL 0x01 +#define RT9123_REG_I2SOPT 0x02 +#define RT9123_REG_TDMRX 0x03 +#define RT9123_REG_SILVOLEN 0x04 +#define RT9123_REG_VOLGAIN 0x12 +#define RT9123_REG_ANAFLAG 0x36 +#define RT9123_REG_COMBOID 0xF7 + +#define RT9123_MASK_SWRST BIT(15) +#define RT9123_MASK_SWMUTE BIT(14) +#define RT9123_MASK_AMPON BIT(12) +#define RT9123_MASK_AUDBIT GENMASK(14, 12) +#define RT9123_MASK_AUDFMT GENMASK(11, 8) +#define RT9123_MASK_TDMRXLOC GENMASK(4, 0) +#define RT9123_MASK_VENID GENMASK(15, 4) + +#define RT9123_FIXED_VENID 0x340 + +struct rt9123_priv { + struct gpio_desc *enable; + unsigned int dai_fmt; + int tdm_slots; + int tdm_slot_width; +}; + +static int rt9123_enable_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 device *dev = comp->dev; + unsigned int enable; + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + enable = 1; + break; + case SND_SOC_DAPM_POST_PMD: + enable = 0; + break; + default: + return -EINVAL; + } + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + /* AMPON bit is located in volatile RG, use pm_runtime to guarantee the RG access */ + snd_soc_component_write_field(comp, RT9123_REG_AMPCTRL, RT9123_MASK_AMPON, enable); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static const struct snd_soc_dapm_widget rt9123_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt9123_enable_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route rt9123_dapm_routes[] = { + { "Amp Drv", NULL, "HiFi Playback" }, + { "SPK", NULL, "Amp Drv" }, +}; + +static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0); +static const DECLARE_TLV_DB_RANGE(ana_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-1200, 0, 0), + 1, 9, TLV_DB_SCALE_ITEM(0, 150, 0), + 10, 10, TLV_DB_SCALE_ITEM(1400, 0, 0)); +static const char * const pwmfreq_text[] = { "300KHz", "325KHz", "350KHz", "375KHz" }; +static const struct soc_enum rt9123_pwm_freq_enum = + SOC_ENUM_SINGLE(RT9123_REG_AMPCTRL, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text); +static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" }; +static const struct soc_enum rt9123_i2sch_select_enum = + SOC_ENUM_SINGLE(RT9123_REG_I2SOPT, 4, ARRAY_SIZE(i2sch_text), i2sch_text); + +static int rt9123_kcontrol_name_comp(struct snd_kcontrol *kcontrol, const char *s) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + const char *kctlname = kcontrol->id.name; + + if (comp && comp->name_prefix) + kctlname += strlen(comp->name_prefix) + 1; + + return strcmp(kctlname, s); +} + +static int rt9123_xhandler_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct device *dev = comp->dev; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + /* + * Since the RG bitfield for 'Speaker Volume' and 'PWM Frequency Select' are located in + * volatile RG address, special handling here with pm runtime API to guarantee RG read + * operation. + */ + if (rt9123_kcontrol_name_comp(kcontrol, "Speaker Volume") == 0) + ret = snd_soc_get_volsw(kcontrol, ucontrol); + else + ret = snd_soc_get_enum_double(kcontrol, ucontrol); + + if (ret < 0) + dev_err(dev, "Failed to get control (%d)\n", ret); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; +} + +static int rt9123_xhandler_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct device *dev = comp->dev; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + /* + * Since the RG bitfield for 'Speaker Volume' and 'PWM Frequency Select' are located in + * volatile RG address, special handling here with pm runtime API to guarantee RG write + * operation. + */ + if (rt9123_kcontrol_name_comp(kcontrol, "Speaker Volume") == 0) + ret = snd_soc_put_volsw(kcontrol, ucontrol); + else + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + + if (ret < 0) + dev_err(dev, "Failed to put control (%d)\n", ret); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; +} + +static const struct snd_kcontrol_new rt9123_controls[] = { + SOC_SINGLE_TLV("Master Volume", RT9123_REG_VOLGAIN, 2, 511, 1, dig_tlv), + SOC_SINGLE_EXT_TLV("Speaker Volume", RT9123_REG_AMPCTRL, 0, 10, 0, rt9123_xhandler_get, + rt9123_xhandler_put, ana_tlv), + SOC_ENUM_EXT("PWM Frequency Select", rt9123_pwm_freq_enum, rt9123_xhandler_get, + rt9123_xhandler_put), + SOC_ENUM("I2S CH Select", rt9123_i2sch_select_enum), + SOC_SINGLE("Silence Detect Switch", RT9123_REG_SILVOLEN, 14, 1, 0), +}; + +static const struct snd_soc_component_driver rt9123_comp_driver = { + .controls = rt9123_controls, + .num_controls = ARRAY_SIZE(rt9123_controls), + .dapm_widgets = rt9123_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt9123_dapm_widgets), + .dapm_routes = rt9123_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt9123_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int rt9123_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai); + + rt9123->dai_fmt = fmt; + return 0; +} + +static int rt9123_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *comp = dai->component; + struct device *dev = dai->dev; + unsigned int rx_loc; + + dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots, + slot_width, tx_mask, rx_mask); + + if (slots <= 0 || slot_width <= 0 || slots % 2 || slot_width % 8 || + slots * slot_width > 256) { + dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width); + return -EINVAL; + } + + if (!rx_mask || hweight_long(rx_mask) > 1 || ffs(rx_mask) > slots) { + dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots); + return -EINVAL; + } + + /* Configure rx channel data location */ + rx_loc = (ffs(rx_mask) - 1) * slot_width / 8; + snd_soc_component_write_field(comp, RT9123_REG_TDMRX, RT9123_MASK_TDMRXLOC, rx_loc); + + rt9123->tdm_slots = slots; + rt9123->tdm_slot_width = slot_width; + + return 0; +} + +static int rt9123_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *param, struct snd_soc_dai *dai) +{ + struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *comp = dai->component; + unsigned int fmtval, width, slot_width; + struct device *dev = dai->dev; + unsigned int audfmt, audbit; + + fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rt9123->dai_fmt); + if (rt9123->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && fmtval != SND_SOC_DAIFMT_DSP_B) { + dev_err(dev, "TDM only can support DSP_A or DSP_B format\n"); + return -EINVAL; + } + + switch (fmtval) { + case SND_SOC_DAIFMT_I2S: + audfmt = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + audfmt = 1; + break; + case SND_SOC_DAIFMT_RIGHT_J: + audfmt = 2; + break; + case SND_SOC_DAIFMT_DSP_B: + audfmt = rt9123->tdm_slots ? 4 : 3; + break; + case SND_SOC_DAIFMT_DSP_A: + audfmt = rt9123->tdm_slots ? 12 : 11; + break; + default: + dev_err(dev, "Unsupported format %d\n", fmtval); + return -EINVAL; + } + + switch (width = params_width(param)) { + case 16: + audbit = 0; + break; + case 20: + audbit = 1; + break; + case 24: + audbit = 2; + break; + case 32: + audbit = 3; + break; + case 8: + audbit = 4; + break; + default: + dev_err(dev, "Unsupported width %d\n", width); + return -EINVAL; + } + + slot_width = params_physical_width(param); + if (rt9123->tdm_slots && slot_width > rt9123->tdm_slot_width) { + dev_err(dev, "Slot width is larger than TDM slot width\n"); + return -EINVAL; + } + + snd_soc_component_write_field(comp, RT9123_REG_I2SOPT, RT9123_MASK_AUDFMT, audfmt); + snd_soc_component_write_field(comp, RT9123_REG_I2SOPT, RT9123_MASK_AUDBIT, audbit); + + return 0; +} + +static const struct snd_soc_dai_ops rt9123_dai_ops = { + .set_fmt = rt9123_dai_set_format, + .set_tdm_slot = rt9123_dai_set_tdm_slot, + .hw_params = rt9123_dai_hw_params, +}; + +static struct snd_soc_dai_driver rt9123_dai_driver = { + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_24000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rt9123_dai_ops, +}; + +static bool rt9123_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00 ... 0x05: + case 0x12 ... 0x13: + case 0x20 ... 0x21: + case 0x36: + return true; + default: + return false; + } +} + +static bool rt9123_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x01 ... 0x05: + case 0x12 ... 0x13: + case 0x20 ... 0x21: + return true; + default: + return false; + } +} + +static bool rt9123_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x01: + case 0x20: + case 0x36: + return true; + default: + return false; + } +} + +static const struct regmap_config rt9123_regmap_config = { + .name = "rt9123", + .reg_bits = 8, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_BIG, + .readable_reg = rt9123_readable_reg, + .writeable_reg = rt9123_writeable_reg, + .volatile_reg = rt9123_volatile_reg, + .cache_type = REGCACHE_MAPLE, + .num_reg_defaults_raw = RT9123_REG_ANAFLAG + 1, +}; + +static int rt9123_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct rt9123_priv *rt9123; + struct regmap *regmap; + __be16 value; + u16 venid; + int ret; + + rt9123 = devm_kzalloc(dev, sizeof(*rt9123), GFP_KERNEL); + if (!rt9123) + return -ENOMEM; + + rt9123->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(rt9123->enable)) + return PTR_ERR(rt9123->enable); + else if (rt9123->enable) + usleep_range(250, 350); + else + dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n"); + + /* Check vendor id information */ + ret = i2c_smbus_read_i2c_block_data(i2c, RT9123_REG_COMBOID, sizeof(value), (u8 *)&value); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read vendor-id\n"); + + venid = be16_to_cpu(value); + if ((venid & RT9123_MASK_VENID) != RT9123_FIXED_VENID) + return dev_err_probe(dev, -ENODEV, "Incorrect vendor-id 0x%04x\n", venid); + + /* Trigger RG reset before regmap init cache */ + value = cpu_to_be16(RT9123_MASK_SWRST); + ret = i2c_smbus_write_i2c_block_data(i2c, RT9123_REG_AMPCTRL, sizeof(value), (u8 *)&value); + if (ret) + return dev_err_probe(dev, ret, "Failed to trigger RG reset\n"); + + /* Need to wait 10ms for the reset to complete */ + usleep_range(10000, 11000); + + regmap = devm_regmap_init_i2c(i2c, &rt9123_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n"); + + i2c_set_clientdata(i2c, rt9123); + + pm_runtime_set_autosuspend_delay(dev, 500); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm runtime\n"); + + return devm_snd_soc_register_component(dev, &rt9123_comp_driver, &rt9123_dai_driver, 1); +} + +#ifdef CONFIG_PM +static int rt9123_runtime_suspend(struct device *dev) +{ + struct rt9123_priv *rt9123 = dev_get_drvdata(dev); + struct regmap *regmap = dev_get_regmap(dev, NULL); + + if (rt9123->enable) { + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + gpiod_set_value(rt9123->enable, 0); + } + + return 0; +} + +static int rt9123_runtime_resume(struct device *dev) +{ + struct rt9123_priv *rt9123 = dev_get_drvdata(dev); + struct regmap *regmap = dev_get_regmap(dev, NULL); + int ret; + + if (rt9123->enable) { + gpiod_set_value(rt9123->enable, 1); + usleep_range(250, 350); + + regcache_cache_only(regmap, false); + ret = regcache_sync(regmap); + if (ret) + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops rt9123_dev_pm_ops = { + SET_RUNTIME_PM_OPS(rt9123_runtime_suspend, rt9123_runtime_resume, NULL) +}; + +#ifdef CONFIG_OF +static const struct of_device_id rt9123_device_id[] = { + { .compatible = "richtek,rt9123" }, + {} +}; +MODULE_DEVICE_TABLE(of, rt9123_device_id); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt9123_acpi_match[] = { + { "RT9123", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, rt9123_acpi_match); +#endif + +static struct i2c_driver rt9123_i2c_driver = { + .driver = { + .name = "rt9123", + .of_match_table = of_match_ptr(rt9123_device_id), + .acpi_match_table = ACPI_PTR(rt9123_acpi_match), + .pm = pm_ptr(&rt9123_dev_pm_ops), + }, + .probe = rt9123_i2c_probe, +}; +module_i2c_driver(rt9123_i2c_driver); + +MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); +MODULE_DESCRIPTION("ASoC rt9123 Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt9123p.c b/sound/soc/codecs/rt9123p.c new file mode 100644 index 000000000000..d509659e735b --- /dev/null +++ b/sound/soc/codecs/rt9123p.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt9123p.c -- RT9123 (HW Mode) ALSA SoC Codec driver +// +// Author: ChiYuan Huang <cy_huang@richtek.com> + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/soc-dapm.h> + +struct rt9123p_priv { + struct gpio_desc *enable; + unsigned int enable_delay; + int enable_switch; +}; + +static int rt9123p_daiops_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *comp = dai->component; + struct rt9123p_priv *rt9123p = snd_soc_component_get_drvdata(comp); + + if (!rt9123p->enable) + return 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + mdelay(rt9123p->enable_delay); + if (rt9123p->enable_switch) { + gpiod_set_value(rt9123p->enable, 1); + dev_dbg(comp->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(rt9123p->enable, 0); + dev_dbg(comp->dev, "set enable to 0"); + break; + default: + break; + } + + return 0; +} + +static int rt9123p_enable_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 rt9123p_priv *rt9123p = snd_soc_component_get_drvdata(comp); + + if (event & SND_SOC_DAPM_POST_PMU) + rt9123p->enable_switch = 1; + else if (event & SND_SOC_DAPM_POST_PMD) + rt9123p->enable_switch = 0; + + return 0; +} + +static const struct snd_soc_dapm_widget rt9123p_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt9123p_enable_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route rt9123p_dapm_routes[] = { + {"Amp Drv", NULL, "HiFi Playback"}, + {"SPK", NULL, "Amp Drv"}, +}; + +static const struct snd_soc_component_driver rt9123p_comp_driver = { + .dapm_widgets = rt9123p_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt9123p_dapm_widgets), + .dapm_routes = rt9123p_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt9123p_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_dai_ops rt9123p_dai_ops = { + .trigger = rt9123p_daiops_trigger, +}; + +static struct snd_soc_dai_driver rt9123p_dai_driver = { + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_24000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rt9123p_dai_ops, +}; + +static int rt9123p_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rt9123p_priv *rt9123p; + int ret; + + rt9123p = devm_kzalloc(dev, sizeof(*rt9123p), GFP_KERNEL); + if (!rt9123p) + return -ENOMEM; + + rt9123p->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(rt9123p->enable)) + return PTR_ERR(rt9123p->enable); + + ret = device_property_read_u32(dev, "enable-delay-ms", &rt9123p->enable_delay); + if (ret) { + rt9123p->enable_delay = 0; + dev_dbg(dev, "no optional property 'enable-delay-ms' found, default: no delay\n"); + } + + platform_set_drvdata(pdev, rt9123p); + + return devm_snd_soc_register_component(dev, &rt9123p_comp_driver, &rt9123p_dai_driver, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt9123p_device_id[] = { + { .compatible = "richtek,rt9123p" }, + {} +}; +MODULE_DEVICE_TABLE(of, rt9123p_device_id); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt9123p_acpi_match[] = { + { "RT9123P", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, rt9123p_acpi_match); +#endif + +static struct platform_driver rt9123p_platform_driver = { + .driver = { + .name = "rt9123p", + .of_match_table = of_match_ptr(rt9123p_device_id), + .acpi_match_table = ACPI_PTR(rt9123p_acpi_match), + }, + .probe = rt9123p_platform_probe, +}; +module_platform_driver(rt9123p_platform_driver); + +MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); +MODULE_DESCRIPTION("ASoC rt9123p Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c index f5c303d4bb62..b3d401ada176 100644 --- a/sound/soc/codecs/sma1307.c +++ b/sound/soc/codecs/sma1307.c @@ -8,7 +8,6 @@ #include <linux/firmware.h> #include <linux/i2c.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <sound/pcm_params.h> #include <sound/tlv.h> @@ -1705,7 +1704,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *file) { const struct firmware *fw; - int *data, size, offset, num_mode; + int size, offset, num_mode; int ret; ret = request_firmware(&fw, file, sma1307->dev); @@ -1722,7 +1721,7 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil return; } - data = kzalloc(fw->size, GFP_KERNEL); + int *data __free(kfree) = kzalloc(fw->size, GFP_KERNEL); if (!data) { release_firmware(fw); sma1307->set.status = false; @@ -1742,7 +1741,6 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil sma1307->set.header_size, GFP_KERNEL); if (!sma1307->set.header) { - kfree(data); sma1307->set.status = false; return; } @@ -1763,8 +1761,6 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil = devm_kzalloc(sma1307->dev, sma1307->set.def_size * sizeof(int), GFP_KERNEL); if (!sma1307->set.def) { - kfree(data); - kfree(sma1307->set.header); sma1307->set.status = false; return; } @@ -1782,9 +1778,6 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil sma1307->set.mode_size * 2 * sizeof(int), GFP_KERNEL); if (!sma1307->set.mode_set[i]) { - kfree(data); - kfree(sma1307->set.header); - kfree(sma1307->set.def); for (int j = 0; j < i; j++) kfree(sma1307->set.mode_set[j]); sma1307->set.status = false; @@ -1799,7 +1792,6 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil } } - kfree(data); sma1307->set.status = true; } diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index bd8848ea1ec2..24d4b643917d 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -983,8 +983,7 @@ static int sta32x_probe(struct snd_soc_component *component) err_regulator_bulk_disable: regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); err_clk_disable_unprepare: - if (sta32x->xti_clk) - clk_disable_unprepare(sta32x->xti_clk); + clk_disable_unprepare(sta32x->xti_clk); return ret; } @@ -995,8 +994,7 @@ static void sta32x_remove(struct snd_soc_component *component) sta32x_watchdog_stop(sta32x); regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); - if (sta32x->xti_clk) - clk_disable_unprepare(sta32x->xti_clk); + clk_disable_unprepare(sta32x->xti_clk); } static const struct snd_soc_component_driver sta32x_component = { diff --git a/sound/soc/codecs/tas2764-quirks.h b/sound/soc/codecs/tas2764-quirks.h new file mode 100644 index 000000000000..7a62b3ba5b40 --- /dev/null +++ b/sound/soc/codecs/tas2764-quirks.h @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __TAS2764_QUIRKS__ +#define __TAS2764_QUIRKS__ + +#include <linux/regmap.h> + +#include "tas2764.h" + +/* Bitmask of enabled Apple quirks */ +#define ENABLED_APPLE_QUIRKS 0x3f + +/* + * Disable noise gate and flip down reserved bit in NS_CFG0 + */ +#define TAS2764_NOISE_GATE_DISABLE BIT(0) + +static const struct reg_sequence tas2764_noise_gate_dis_seq[] = { + REG_SEQ0(TAS2764_REG(0x0, 0x35), 0xb0) +}; + +/* + * CONV_VBAT_PVDD_MODE=1 + */ +#define TAS2764_CONV_VBAT_PVDD_MODE BIT(1) + +static const struct reg_sequence tas2764_conv_vbat_pvdd_mode_seq[] = { + REG_SEQ0(TAS2764_REG(0x0, 0x6b), 0x41) +}; + +/* + * Reset of DAC modulator when DSP is OFF + */ +#define TAS2764_DMOD_RST BIT(2) + +static const struct reg_sequence tas2764_dmod_rst_seq[] = { + REG_SEQ0(TAS2764_REG(0x0, 0x76), 0x0) +}; + +/* + * Unknown 0x133/0x137 writes (maybe TDM related) + */ +#define TAS2764_UNK_SEQ0 BIT(3) + +static const struct reg_sequence tas2764_unk_seq0[] = { + REG_SEQ0(TAS2764_REG(0x1, 0x33), 0x80), + REG_SEQ0(TAS2764_REG(0x1, 0x37), 0x3a), +}; + +/* + * Unknown 0x614 - 0x61f writes + */ +#define TAS2764_APPLE_UNK_SEQ1 BIT(4) + +static const struct reg_sequence tas2764_unk_seq1[] = { + REG_SEQ0(TAS2764_REG(0x6, 0x14), 0x0), + REG_SEQ0(TAS2764_REG(0x6, 0x15), 0x13), + REG_SEQ0(TAS2764_REG(0x6, 0x16), 0x52), + REG_SEQ0(TAS2764_REG(0x6, 0x17), 0x0), + REG_SEQ0(TAS2764_REG(0x6, 0x18), 0xe4), + REG_SEQ0(TAS2764_REG(0x6, 0x19), 0xc), + REG_SEQ0(TAS2764_REG(0x6, 0x16), 0xaa), + REG_SEQ0(TAS2764_REG(0x6, 0x1b), 0x0), + REG_SEQ0(TAS2764_REG(0x6, 0x1c), 0x12), + REG_SEQ0(TAS2764_REG(0x6, 0x1d), 0xa0), + REG_SEQ0(TAS2764_REG(0x6, 0x1e), 0xd8), + REG_SEQ0(TAS2764_REG(0x6, 0x1f), 0x0), +}; + +/* + * Unknown writes in the 0xfd page (with secondary paging inside) + */ +#define TAS2764_APPLE_UNK_SEQ2 BIT(5) + +static const struct reg_sequence tas2764_unk_seq2[] = { + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd), + REG_SEQ0(TAS2764_REG(0xfd, 0x6c), 0x2), + REG_SEQ0(TAS2764_REG(0xfd, 0x6d), 0xf), + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0), +}; + +/* + * Disable 'Thermal Threshold 1' + */ +#define TAS2764_THERMAL_TH1_DISABLE BIT(6) + +static const struct reg_sequence tas2764_thermal_th1_dis_seq[] = { + REG_SEQ0(TAS2764_REG(0x1, 0x47), 0x2), +}; + +/* + * Imitate Apple's shutdown dance + */ +#define TAS2764_SHUTDOWN_DANCE BIT(7) + +static const struct reg_sequence tas2764_shutdown_dance_init_seq[] = { + /* + * SDZ_MODE=01 (immediate) + * + * We want the shutdown to happen under the influence of + * the magic writes in the 0xfdXX region, so make sure + * the shutdown is immediate and there's no grace period + * followed by the codec part. + */ + REG_SEQ0(TAS2764_REG(0x0, 0x7), 0x60), +}; + +static const struct reg_sequence tas2764_pre_shutdown_seq[] = { + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd), /* switch hidden page */ + REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x4), /* do write (unknown semantics) */ + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0), /* switch hidden page back */ +}; + +static const struct reg_sequence tas2764_post_shutdown_seq[] = { + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd), + REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x0), /* revert write from pre sequence */ + REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0), +}; + +static int tas2764_do_quirky_pwr_ctrl_change(struct tas2764_priv *tas2764, + unsigned int target) +{ + unsigned int curr; + int ret; + + curr = snd_soc_component_read_field(tas2764->component, + TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK); + + if (target == curr) + return 0; + + /* Handle power state transition to shutdown */ + if (target == TAS2764_PWR_CTRL_SHUTDOWN && + (curr == TAS2764_PWR_CTRL_MUTE || curr == TAS2764_PWR_CTRL_ACTIVE)) { + ret = regmap_multi_reg_write(tas2764->regmap, tas2764_pre_shutdown_seq, + ARRAY_SIZE(tas2764_pre_shutdown_seq)); + if (!ret) + ret = snd_soc_component_update_bits(tas2764->component, + TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_SHUTDOWN); + if (!ret) + ret = regmap_multi_reg_write(tas2764->regmap, + tas2764_post_shutdown_seq, + ARRAY_SIZE(tas2764_post_shutdown_seq)); + } + + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, target); + + return ret; +} + +/* + * Via devicetree (TODO): + * - switch from spread spectrum to class-D switching + * - disable edge control + * - set BOP settings (the BOP config bits *and* BOP_SRC) + */ + +/* + * Other setup TODOs: + * - DVC ramp rate + */ + +static const struct tas2764_quirk_init_sequence { + const struct reg_sequence *seq; + int len; +} tas2764_quirk_init_sequences[] = { + { tas2764_noise_gate_dis_seq, ARRAY_SIZE(tas2764_noise_gate_dis_seq) }, + { tas2764_dmod_rst_seq, ARRAY_SIZE(tas2764_dmod_rst_seq) }, + { tas2764_conv_vbat_pvdd_mode_seq, ARRAY_SIZE(tas2764_conv_vbat_pvdd_mode_seq) }, + { tas2764_unk_seq0, ARRAY_SIZE(tas2764_unk_seq0) }, + { tas2764_unk_seq1, ARRAY_SIZE(tas2764_unk_seq1) }, + { tas2764_unk_seq2, ARRAY_SIZE(tas2764_unk_seq2) }, + { tas2764_thermal_th1_dis_seq, ARRAY_SIZE(tas2764_thermal_th1_dis_seq) }, + { tas2764_shutdown_dance_init_seq, ARRAY_SIZE(tas2764_shutdown_dance_init_seq) }, +}; + +#endif /* __TAS2764_QUIRKS__ */ diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 08aa7ee34256..36e25e48b354 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -8,6 +8,7 @@ #include <linux/err.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/hwmon.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/gpio/consumer.h> @@ -45,6 +46,8 @@ struct tas2764_priv { bool unmuted; }; +#include "tas2764-quirks.h" + static const char *tas2764_int_ltch0_msgs[8] = { "fault: over temperature", /* INT_LTCH0 & BIT(0) */ "fault: over current", @@ -122,6 +125,9 @@ static int tas2764_update_pwr_ctrl(struct tas2764_priv *tas2764) else val = TAS2764_PWR_CTRL_SHUTDOWN; + if (ENABLED_APPLE_QUIRKS & TAS2764_SHUTDOWN_DANCE) + return tas2764_do_quirky_pwr_ctrl_change(tas2764, val); + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, TAS2764_PWR_CTRL_MASK, val); if (ret < 0) @@ -546,6 +552,106 @@ static uint8_t sn012776_bop_presets[] = { 0x06, 0x3e, 0x37, 0x30, 0xff, 0xe6 }; +static const struct regmap_config tas2764_i2c_regmap; + +static int tas2764_apply_init_quirks(struct tas2764_priv *tas2764) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(tas2764_quirk_init_sequences); i++) { + const struct tas2764_quirk_init_sequence *init_seq = + &tas2764_quirk_init_sequences[i]; + + if (!init_seq->seq) + continue; + + if (!(BIT(i) & ENABLED_APPLE_QUIRKS)) + continue; + + ret = regmap_multi_reg_write(tas2764->regmap, init_seq->seq, + init_seq->len); + + if (ret < 0) + return ret; + } + + return 0; +} + +static int tas2764_read_die_temp(struct tas2764_priv *tas2764, long *result) +{ + int ret, reg; + + ret = regmap_read(tas2764->regmap, TAS2764_TEMP, ®); + if (ret) + return ret; + /* + * As per datasheet, subtract 93 from raw value to get degrees + * Celsius. hwmon wants millidegrees. + * + * NOTE: The chip will initialise the TAS2764_TEMP register to + * 2.6 *C to avoid triggering temperature protection. Since the + * ADC is powered down during software shutdown, this value will + * persist until the chip is fully powered up (e.g. the PCM it's + * attached to is opened). The ADC will power down again when + * the chip is put back into software shutdown, with the last + * value sampled persisting in the ADC's register. + */ + *result = (reg - 93) * 1000; + return 0; +} + +static umode_t tas2764_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + break; + } + + return 0; +} + +static int tas2764_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct tas2764_priv *tas2764 = dev_get_drvdata(dev); + int ret; + + switch (attr) { + case hwmon_temp_input: + ret = tas2764_read_die_temp(tas2764, val); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static const struct hwmon_channel_info *const tas2764_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops tas2764_hwmon_ops = { + .is_visible = tas2764_hwmon_is_visible, + .read = tas2764_hwmon_read, +}; + +static const struct hwmon_chip_info tas2764_hwmon_chip_info = { + .ops = &tas2764_hwmon_ops, + .info = tas2764_hwmon_info, +}; + static int tas2764_codec_probe(struct snd_soc_component *component) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); @@ -559,9 +665,10 @@ static int tas2764_codec_probe(struct snd_soc_component *component) } tas2764_reset(tas2764); + regmap_reinit_cache(tas2764->regmap, &tas2764_i2c_regmap); if (tas2764->irq) { - ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0xff); + ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0x00); if (ret < 0) return ret; @@ -614,6 +721,13 @@ static int tas2764_codec_probe(struct snd_soc_component *component) if (ret < 0) return ret; } + + /* Apply all enabled Apple quirks */ + ret = tas2764_apply_init_quirks(tas2764); + + if (ret < 0) + return ret; + break; default: break; @@ -682,7 +796,7 @@ static const struct reg_default tas2764_reg_defaults[] = { static const struct regmap_range_cfg tas2764_regmap_ranges[] = { { .range_min = 0, - .range_max = 1 * 128, + .range_max = 0xffff, .selector_reg = TAS2764_PAGE, .selector_mask = 0xff, .selector_shift = 0, @@ -698,6 +812,9 @@ static bool tas2764_volatile_register(struct device *dev, unsigned int reg) case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4: case TAS2764_INT_CLK_CFG: return true; + case TAS2764_REG(0xf0, 0x0) ... TAS2764_REG(0xff, 0x0): + /* TI's undocumented registers for the application of quirks */ + return true; default: return false; } @@ -712,7 +829,7 @@ static const struct regmap_config tas2764_i2c_regmap = { .cache_type = REGCACHE_RBTREE, .ranges = tas2764_regmap_ranges, .num_ranges = ARRAY_SIZE(tas2764_regmap_ranges), - .max_register = 1 * 128, + .max_register = 0xffff, }; static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) @@ -759,7 +876,7 @@ static int tas2764_i2c_probe(struct i2c_client *client) if (!tas2764) return -ENOMEM; - tas2764->devid = (enum tas2764_devid)of_device_get_match_data(&client->dev); + tas2764->devid = (kernel_ulong_t)of_device_get_match_data(&client->dev); tas2764->dev = &client->dev; tas2764->irq = client->irq; @@ -783,6 +900,20 @@ static int tas2764_i2c_probe(struct i2c_client *client) } } + if (IS_REACHABLE(CONFIG_HWMON)) { + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2764", + tas2764, + &tas2764_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon)) { + return dev_err_probe(&client->dev, PTR_ERR(hwmon), + "Failed to register temp sensor\n"); + } + } + + return devm_snd_soc_register_component(tas2764->dev, &soc_component_driver_tas2764, tas2764_dai_driver, diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 3251dc0106e0..538290ed3d92 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -117,6 +117,9 @@ #define TAS2764_INT_LTCH3 TAS2764_REG(0x0, 0x50) #define TAS2764_INT_LTCH4 TAS2764_REG(0x0, 0x51) +/* Readout Registers */ +#define TAS2764_TEMP TAS2764_REG(0x0, 0x56) + /* Clock/IRQ Settings */ #define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c) #define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2) diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 7f219df8be70..6f878b01716f 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -12,6 +12,7 @@ #include <linux/err.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/hwmon.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/gpio/consumer.h> @@ -156,11 +157,37 @@ static const struct snd_kcontrol_new isense_switch = static const struct snd_kcontrol_new vsense_switch = SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 2, 1, 1); +static int sense_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 tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + + /* + * Powering up ISENSE/VSENSE requires a trip through the shutdown state. + * Do that here to ensure that our changes are applied properly, otherwise + * we might end up with non-functional IVSENSE if playback started earlier, + * which would break software speaker protection. + */ + switch (event) { + case SND_SOC_DAPM_PRE_REG: + return snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); + case SND_SOC_DAPM_POST_REG: + return tas2770_update_pwr_ctrl(tas2770); + default: + return 0; + } +} + static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2770_asi1_mux), - SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch), - SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch), + SND_SOC_DAPM_SWITCH_E("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch, + sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH_E("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch, + sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_OUTPUT("OUT"), @@ -214,6 +241,19 @@ static int tas2770_set_ivsense_transmit(struct tas2770_priv *tas2770, return 0; } +static int tas2770_set_pdm_transmit(struct tas2770_priv *tas2770, int slot) +{ + struct snd_soc_component *component = tas2770->component; + int ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG7, + TAS2770_TDM_CFG_REG7_PDM_MASK | + TAS2770_TDM_CFG_REG7_50_MASK, + TAS2770_TDM_CFG_REG7_PDM_ENABLE | + slot); + return ret; +} + static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) { int ret; @@ -491,6 +531,88 @@ static struct snd_soc_dai_driver tas2770_dai_driver[] = { }, }; +static int tas2770_read_die_temp(struct tas2770_priv *tas2770, long *result) +{ + int ret = 0; + int reading, msb, lsb; + + ret = regmap_read(tas2770->regmap, TAS2770_TEMP_MSB, &msb); + if (ret) + return ret; + + ret = regmap_read(tas2770->regmap, TAS2770_TEMP_LSB, &lsb); + if (ret) + return ret; + + reading = (msb << 4) | (lsb >> 4); + + /* + * As per datasheet: divide register by 16 and subtract 93 to get + * degrees Celsius. hwmon requires millidegrees. Let's avoid rounding + * errors by subtracting 93 * 16 then multiplying by 1000 / 16. + * + * NOTE: The ADC registers are initialised to 0 on reset. This means + * that the temperature will read -93 *C until the chip is brought out + * of software shutdown (e.g. the PCM it's attached to is opened). The + * ADC is also shut down in software shutdown/low-power mode, so the + * value read back from its registers will be the last value sampled + * before entering software shutdown. + */ + *result = (reading - (93 * 16)) * (1000 / 16); + return 0; +} + +static umode_t tas2770_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + break; + } + + return 0; +} + +static int tas2770_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct tas2770_priv *tas2770 = dev_get_drvdata(dev); + int ret; + + switch (attr) { + case hwmon_temp_input: + ret = tas2770_read_die_temp(tas2770, val); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static const struct hwmon_channel_info *const tas2770_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops tas2770_hwmon_ops = { + .is_visible = tas2770_hwmon_is_visible, + .read = tas2770_hwmon_read, +}; + +static const struct hwmon_chip_info tas2770_hwmon_chip_info = { + .ops = &tas2770_hwmon_ops, + .info = tas2770_hwmon_info, +}; + static const struct regmap_config tas2770_i2c_regmap; static int tas2770_codec_probe(struct snd_soc_component *component) @@ -517,6 +639,13 @@ static int tas2770_codec_probe(struct snd_soc_component *component) return ret; } + if (tas2770->pdm_slot != -1) { + ret = tas2770_set_pdm_transmit(tas2770, tas2770->pdm_slot); + + if (ret < 0) + return ret; + } + return 0; } @@ -656,6 +785,11 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) tas2770->v_sense_slot = -1; } + rc = fwnode_property_read_u32(dev->fwnode, "ti,pdm-slot-no", + &tas2770->pdm_slot); + if (rc) + tas2770->pdm_slot = -1; + tas2770->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); if (IS_ERR(tas2770->sdz_gpio)) { if (PTR_ERR(tas2770->sdz_gpio) == -EPROBE_DEFER) @@ -707,6 +841,19 @@ static int tas2770_i2c_probe(struct i2c_client *client) } } + if (IS_REACHABLE(CONFIG_HWMON)) { + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2770", + tas2770, + &tas2770_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon)) { + return dev_err_probe(&client->dev, PTR_ERR(hwmon), + "Failed to register temp sensor\n"); + } + } + result = tas2770_register_codec(tas2770); if (result) dev_err(tas2770->dev, "Register codec failed.\n"); diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h index f75f40781ab1..3fd2e7003c50 100644 --- a/sound/soc/codecs/tas2770.h +++ b/sound/soc/codecs/tas2770.h @@ -77,6 +77,11 @@ #define TAS2770_TDM_CFG_REG6_ISNS_MASK BIT(6) #define TAS2770_TDM_CFG_REG6_ISNS_ENABLE BIT(6) #define TAS2770_TDM_CFG_REG6_50_MASK GENMASK(5, 0) + /* TDM Configuration Reg10 */ +#define TAS2770_TDM_CFG_REG7 TAS2770_REG(0X0, 0x11) +#define TAS2770_TDM_CFG_REG7_PDM_MASK BIT(6) +#define TAS2770_TDM_CFG_REG7_PDM_ENABLE BIT(6) +#define TAS2770_TDM_CFG_REG7_50_MASK GENMASK(5, 0) /* Brown Out Prevention Reg0 */ #define TAS2770_BO_PRV_REG0 TAS2770_REG(0X0, 0x1B) /* Interrupt MASK Reg0 */ @@ -138,6 +143,7 @@ struct tas2770_priv { struct device *dev; int v_sense_slot; int i_sense_slot; + int pdm_slot; bool dac_powered; bool unmuted; }; diff --git a/sound/soc/codecs/tas2781-comlib-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c new file mode 100644 index 000000000000..c078bb0a8437 --- /dev/null +++ b/sound/soc/codecs/tas2781-comlib-i2c.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers based on I2C +// +// Copyright 2025 Texas Instruments, Inc. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tas2781.h> +#include <sound/tas2781-comlib-i2c.h> + +static const struct regmap_range_cfg tasdevice_ranges[] = { + { + .range_min = 0, + .range_max = 256 * 128, + .selector_reg = TASDEVICE_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config tasdevice_regmap = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .ranges = tasdevice_ranges, + .num_ranges = ARRAY_SIZE(tasdevice_ranges), + .max_register = 256 * 128, +}; + +static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv, + unsigned short chn, int book) +{ + struct i2c_client *client = (struct i2c_client *)tas_priv->client; + int ret = 0; + + if (chn < tas_priv->ndev) { + struct tasdevice *tasdev = &tas_priv->tasdevice[chn]; + struct regmap *map = tas_priv->regmap; + + if (client->addr != tasdev->dev_addr) { + client->addr = tasdev->dev_addr; + /* All tas2781s share the same regmap, clear the page + * inside regmap once switching to another tas2781. + * Register 0 at any pages and any books inside tas2781 + * is the same one for page-switching. + */ + ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d channel:%d\n", + __func__, ret, chn); + goto out; + } + } + + if (tasdev->cur_book != book) { + ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + tasdev->cur_book = book; + } + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} + +int tasdev_chn_switch(struct tasdevice_priv *tas_priv, + unsigned short chn) +{ + struct i2c_client *client = (struct i2c_client *)tas_priv->client; + struct tasdevice *tasdev = &tas_priv->tasdevice[chn]; + struct regmap *map = tas_priv->regmap; + int ret; + + if (client->addr != tasdev->dev_addr) { + client->addr = tasdev->dev_addr; + /* All devices share the same regmap, clear the page + * inside regmap once switching to another device. + * Register 0 at any pages and any books inside tas2781 + * is the same one for page-switching. + */ + ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + return ret; + } + return 1; + } + return 0; +} +EXPORT_SYMBOL_GPL(tasdev_chn_switch); + +int tasdevice_dev_update_bits( + struct tasdevice_priv *tas_priv, unsigned short chn, + unsigned int reg, unsigned int mask, unsigned int value) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tas_priv->change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_update_bits(map, TASDEVICE_PGRG(reg), + mask, value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + ret = -EINVAL; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits); + +struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c) +{ + struct tasdevice_priv *tas_priv; + + tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL); + if (!tas_priv) + return NULL; + tas_priv->dev = &i2c->dev; + tas_priv->client = (void *)i2c; + + return tas_priv; +} +EXPORT_SYMBOL_GPL(tasdevice_kzalloc); + +int tasdevice_init(struct tasdevice_priv *tas_priv) +{ + int ret = 0; + int i; + + tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client, + &tasdevice_regmap); + if (IS_ERR(tas_priv->regmap)) { + ret = PTR_ERR(tas_priv->regmap); + dev_err(tas_priv->dev, "Failed to allocate register map: %d\n", + ret); + goto out; + } + + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + tas_priv->isspi = false; + + for (i = 0; i < tas_priv->ndev; i++) { + tas_priv->tasdevice[i].cur_book = -1; + tas_priv->tasdevice[i].cur_prog = -1; + tas_priv->tasdevice[i].cur_conf = -1; + } + + tas_priv->update_bits = tasdevice_dev_update_bits; + tas_priv->change_chn_book = tasdevice_change_chn_book; + tas_priv->dev_read = tasdevice_dev_read; + tas_priv->dev_bulk_read = tasdevice_dev_bulk_read; + + mutex_init(&tas_priv->codec_lock); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_init); + +static int tasdevice_clamp(int val, int max, unsigned int invert) +{ + if (val > max) + val = max; + if (invert) + val = max - val; + if (val < 0) + val = 0; + return val; +} + +int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask; + int max = mc->max; + int err_cnt = 0; + int val, i, ret; + + mask = (1 << fls(max)) - 1; + mask <<= mc->shift; + val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); + for (i = 0; i < tas_priv->ndev; i++) { + ret = tasdevice_dev_update_bits(tas_priv, i, + mc->reg, mask, (unsigned int)(val << mc->shift)); + if (!ret) + continue; + err_cnt++; + dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i); + } + + /* All the devices set error, return 0 */ + return (err_cnt == tas_priv->ndev) ? 0 : 1; +} +EXPORT_SYMBOL_GPL(tasdevice_amp_putvol); + +int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask = 0; + int max = mc->max; + int ret = 0; + int val; + + /* Read the primary device */ + ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__); + goto out; + } + + mask = (1 << fls(max)) - 1; + mask <<= mc->shift; + val = (val & mask) >> mc->shift; + val = tasdevice_clamp(val, max, invert); + ucontrol->value.integer.value[0] = val; + +out: + return ret; + +} +EXPORT_SYMBOL_GPL(tasdevice_amp_getvol); + +int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int ret, val; + + /* Read the primary device as the whole */ + ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get digital vol error\n", + __func__); + goto out; + } + + val = tasdevice_clamp(val, max, invert); + ucontrol->value.integer.value[0] = val; + +out: + return ret; + +} +EXPORT_SYMBOL_GPL(tasdevice_digital_getvol); + +int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int err_cnt = 0; + int ret; + int val, i; + + val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); + + for (i = 0; i < tas_priv->ndev; i++) { + ret = tasdevice_dev_write(tas_priv, i, mc->reg, + (unsigned int)val); + if (!ret) + continue; + err_cnt++; + dev_err(tas_priv->dev, + "set digital vol err in dev %d\n", i); + } + + /* All the devices set error, return 0 */ + return (err_cnt == tas_priv->ndev) ? 0 : 1; + +} +EXPORT_SYMBOL_GPL(tasdevice_digital_putvol); + +void tasdevice_reset(struct tasdevice_priv *tas_dev) +{ + int ret, i; + + if (tas_dev->reset) { + gpiod_set_value_cansleep(tas_dev->reset, 0); + usleep_range(500, 1000); + gpiod_set_value_cansleep(tas_dev->reset, 1); + } else { + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_write(tas_dev, i, + TASDEVICE_REG_SWRESET, + TASDEVICE_REG_SWRESET_RESET); + if (ret < 0) + dev_err(tas_dev->dev, + "dev %d swreset fail, %d\n", + i, ret); + } + } + usleep_range(1000, 1050); +} +EXPORT_SYMBOL_GPL(tasdevice_reset); + +int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, + struct module *module, + void (*cont)(const struct firmware *fw, void *context)) +{ + int ret = 0; + + /* Codec Lock Hold to ensure that codec_probe and firmware parsing and + * loading do not simultaneously execute. + */ + mutex_lock(&tas_priv->codec_lock); + + if (tas_priv->name_prefix) + scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin", + tas_priv->name_prefix, tas_priv->dev_name, + tas_priv->ndev); + else + scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin", + tas_priv->dev_name, tas_priv->ndev); + crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); + tas_priv->codec = codec; + ret = request_firmware_nowait(module, FW_ACTION_UEVENT, + tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv, + cont); + if (ret) + dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", + ret); + + /* Codec Lock Release*/ + mutex_unlock(&tas_priv->codec_lock); + return ret; +} +EXPORT_SYMBOL_GPL(tascodec_init); + +MODULE_DESCRIPTION("TAS2781 common library for I2C"); +MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c index 1e0b3aa95749..4cec9f8a00af 100644 --- a/sound/soc/codecs/tas2781-comlib.c +++ b/sound/soc/codecs/tas2781-comlib.c @@ -2,14 +2,14 @@ // // TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers // -// Copyright 2023 - 2024 Texas Instruments, Inc. +// Copyright 2023 - 2025 Texas Instruments, Inc. // // Author: Shenghao Ding <shenghao-ding@ti.com> #include <linux/crc8.h> +#include <linux/dev_printk.h> #include <linux/firmware.h> #include <linux/gpio/consumer.h> -#include <linux/i2c.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> @@ -17,67 +17,24 @@ #include <linux/of_irq.h> #include <linux/regmap.h> #include <linux/slab.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> #include <sound/tas2781.h> -#define TASDEVICE_CRC8_POLYNOMIAL 0x4d - -static const struct regmap_range_cfg tasdevice_ranges[] = { - { - .range_min = 0, - .range_max = 256 * 128, - .selector_reg = TASDEVICE_PAGE_SELECT, - .selector_mask = 0xff, - .selector_shift = 0, - .window_start = 0, - .window_len = 128, - }, -}; - -static const struct regmap_config tasdevice_regmap = { - .reg_bits = 8, - .val_bits = 8, - .cache_type = REGCACHE_NONE, - .ranges = tasdevice_ranges, - .num_ranges = ARRAY_SIZE(tasdevice_ranges), - .max_register = 256 * 128, -}; - -static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv, - unsigned short chn, int book) +int tasdevice_dev_read(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned int *val) { - struct i2c_client *client = (struct i2c_client *)tas_priv->client; int ret = 0; if (chn < tas_priv->ndev) { - struct tasdevice *tasdev = &tas_priv->tasdevice[chn]; struct regmap *map = tas_priv->regmap; - if (client->addr != tasdev->dev_addr) { - client->addr = tasdev->dev_addr; - /* All tas2781s share the same regmap, clear the page - * inside regmap once switching to another tas2781. - * Register 0 at any pages and any books inside tas2781 - * is the same one for page-switching. - */ - ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0); - if (ret < 0) { - dev_err(tas_priv->dev, "%s, E=%d channel:%d\n", - __func__, ret, chn); - goto out; - } - } + ret = tas_priv->change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; - if (tasdev->cur_book != book) { - ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book); - if (ret < 0) { - dev_err(tas_priv->dev, "%s, E=%d\n", - __func__, ret); - goto out; - } - tasdev->cur_book = book; - } + ret = regmap_read(map, TASDEVICE_PGRG(reg), val); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); } else { ret = -EINVAL; dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, @@ -87,59 +44,33 @@ static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv, out: return ret; } +EXPORT_SYMBOL_GPL(tasdevice_dev_read); -int tasdev_chn_switch(struct tasdevice_priv *tas_priv, - unsigned short chn) -{ - struct i2c_client *client = (struct i2c_client *)tas_priv->client; - struct tasdevice *tasdev = &tas_priv->tasdevice[chn]; - struct regmap *map = tas_priv->regmap; - int ret; - - if (client->addr != tasdev->dev_addr) { - client->addr = tasdev->dev_addr; - /* All devices share the same regmap, clear the page - * inside regmap once switching to another device. - * Register 0 at any pages and any books inside tas2781 - * is the same one for page-switching. - */ - ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0); - if (ret < 0) { - dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); - return ret; - } - return 1; - } - return 0; -} -EXPORT_SYMBOL_GPL(tasdev_chn_switch); - -int tasdevice_dev_read(struct tasdevice_priv *tas_priv, - unsigned short chn, unsigned int reg, unsigned int *val) +int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned char *data, + unsigned int len) { int ret = 0; if (chn < tas_priv->ndev) { struct regmap *map = tas_priv->regmap; - ret = tasdevice_change_chn_book(tas_priv, chn, + ret = tas_priv->change_chn_book(tas_priv, chn, TASDEVICE_BOOK_ID(reg)); if (ret < 0) goto out; - ret = regmap_read(map, TASDEVICE_PGRG(reg), val); + ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len); if (ret < 0) dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); - } else { - ret = -EINVAL; + } else dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, chn); - } out: return ret; } -EXPORT_SYMBOL_GPL(tasdevice_dev_read); +EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read); int tasdevice_dev_write(struct tasdevice_priv *tas_priv, unsigned short chn, unsigned int reg, unsigned int value) @@ -149,7 +80,7 @@ int tasdevice_dev_write(struct tasdevice_priv *tas_priv, if (chn < tas_priv->ndev) { struct regmap *map = tas_priv->regmap; - ret = tasdevice_change_chn_book(tas_priv, chn, + ret = tas_priv->change_chn_book(tas_priv, chn, TASDEVICE_BOOK_ID(reg)); if (ret < 0) goto out; @@ -179,7 +110,7 @@ int tasdevice_dev_bulk_write( if (chn < tas_priv->ndev) { struct regmap *map = tas_priv->regmap; - ret = tasdevice_change_chn_book(tas_priv, chn, + ret = tas_priv->change_chn_book(tas_priv, chn, TASDEVICE_BOOK_ID(reg)); if (ret < 0) goto out; @@ -199,161 +130,6 @@ out: } EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write); -int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv, - unsigned short chn, unsigned int reg, unsigned char *data, - unsigned int len) -{ - int ret = 0; - - if (chn < tas_priv->ndev) { - struct regmap *map = tas_priv->regmap; - - ret = tasdevice_change_chn_book(tas_priv, chn, - TASDEVICE_BOOK_ID(reg)); - if (ret < 0) - goto out; - - ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len); - if (ret < 0) - dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); - } else - dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, - chn); - -out: - return ret; -} -EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read); - -int tasdevice_dev_update_bits( - struct tasdevice_priv *tas_priv, unsigned short chn, - unsigned int reg, unsigned int mask, unsigned int value) -{ - int ret = 0; - - if (chn < tas_priv->ndev) { - struct regmap *map = tas_priv->regmap; - - ret = tasdevice_change_chn_book(tas_priv, chn, - TASDEVICE_BOOK_ID(reg)); - if (ret < 0) - goto out; - - ret = regmap_update_bits(map, TASDEVICE_PGRG(reg), - mask, value); - if (ret < 0) - dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); - } else { - dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, - chn); - ret = -EINVAL; - } - -out: - return ret; -} -EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits); - -struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c) -{ - struct tasdevice_priv *tas_priv; - - tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL); - if (!tas_priv) - return NULL; - tas_priv->dev = &i2c->dev; - tas_priv->client = (void *)i2c; - - return tas_priv; -} -EXPORT_SYMBOL_GPL(tasdevice_kzalloc); - -void tasdevice_reset(struct tasdevice_priv *tas_dev) -{ - int ret, i; - - if (tas_dev->reset) { - gpiod_set_value_cansleep(tas_dev->reset, 0); - usleep_range(500, 1000); - gpiod_set_value_cansleep(tas_dev->reset, 1); - } else { - for (i = 0; i < tas_dev->ndev; i++) { - ret = tasdevice_dev_write(tas_dev, i, - TASDEVICE_REG_SWRESET, - TASDEVICE_REG_SWRESET_RESET); - if (ret < 0) - dev_err(tas_dev->dev, - "dev %d swreset fail, %d\n", - i, ret); - } - } - usleep_range(1000, 1050); -} -EXPORT_SYMBOL_GPL(tasdevice_reset); - -int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, - struct module *module, - void (*cont)(const struct firmware *fw, void *context)) -{ - int ret = 0; - - /* Codec Lock Hold to ensure that codec_probe and firmware parsing and - * loading do not simultaneously execute. - */ - mutex_lock(&tas_priv->codec_lock); - - if (tas_priv->name_prefix) - scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin", - tas_priv->name_prefix, tas_priv->dev_name, - tas_priv->ndev); - else - scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin", - tas_priv->dev_name, tas_priv->ndev); - crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); - tas_priv->codec = codec; - ret = request_firmware_nowait(module, FW_ACTION_UEVENT, - tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv, - cont); - if (ret) - dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", - ret); - - /* Codec Lock Release*/ - mutex_unlock(&tas_priv->codec_lock); - return ret; -} -EXPORT_SYMBOL_GPL(tascodec_init); - -int tasdevice_init(struct tasdevice_priv *tas_priv) -{ - int ret = 0; - int i; - - tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client, - &tasdevice_regmap); - if (IS_ERR(tas_priv->regmap)) { - ret = PTR_ERR(tas_priv->regmap); - dev_err(tas_priv->dev, "Failed to allocate register map: %d\n", - ret); - goto out; - } - - tas_priv->cur_prog = -1; - tas_priv->cur_conf = -1; - - for (i = 0; i < tas_priv->ndev; i++) { - tas_priv->tasdevice[i].cur_book = -1; - tas_priv->tasdevice[i].cur_prog = -1; - tas_priv->tasdevice[i].cur_conf = -1; - } - - mutex_init(&tas_priv->codec_lock); - -out: - return ret; -} -EXPORT_SYMBOL_GPL(tasdevice_init); - static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog) { struct tasdevice_data *tas_dt; @@ -440,137 +216,6 @@ void tasdevice_remove(struct tasdevice_priv *tas_priv) } EXPORT_SYMBOL_GPL(tasdevice_remove); -int tasdevice_save_calibration(struct tasdevice_priv *tas_priv) -{ - if (tas_priv->save_calibration) - return tas_priv->save_calibration(tas_priv); - return -EINVAL; -} -EXPORT_SYMBOL_GPL(tasdevice_save_calibration); - -void tasdevice_apply_calibration(struct tasdevice_priv *tas_priv) -{ - if (tas_priv->apply_calibration && tas_priv->cali_data.total_sz) - tas_priv->apply_calibration(tas_priv); -} -EXPORT_SYMBOL_GPL(tasdevice_apply_calibration); - -static int tasdevice_clamp(int val, int max, unsigned int invert) -{ - if (val > max) - val = max; - if (invert) - val = max - val; - if (val < 0) - val = 0; - return val; -} - -int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv, - struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) -{ - unsigned int invert = mc->invert; - unsigned char mask; - int max = mc->max; - int err_cnt = 0; - int val, i, ret; - - mask = (1 << fls(max)) - 1; - mask <<= mc->shift; - val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); - for (i = 0; i < tas_priv->ndev; i++) { - ret = tasdevice_dev_update_bits(tas_priv, i, - mc->reg, mask, (unsigned int)(val << mc->shift)); - if (!ret) - continue; - err_cnt++; - dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i); - } - - /* All the devices set error, return 0 */ - return (err_cnt == tas_priv->ndev) ? 0 : 1; -} -EXPORT_SYMBOL_GPL(tasdevice_amp_putvol); - -int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv, - struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) -{ - unsigned int invert = mc->invert; - unsigned char mask = 0; - int max = mc->max; - int ret = 0; - int val; - - /* Read the primary device */ - ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); - if (ret) { - dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__); - goto out; - } - - mask = (1 << fls(max)) - 1; - mask <<= mc->shift; - val = (val & mask) >> mc->shift; - val = tasdevice_clamp(val, max, invert); - ucontrol->value.integer.value[0] = val; - -out: - return ret; - -} -EXPORT_SYMBOL_GPL(tasdevice_amp_getvol); - -int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv, - struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) -{ - unsigned int invert = mc->invert; - int max = mc->max; - int err_cnt = 0; - int ret; - int val, i; - - val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); - - for (i = 0; i < tas_priv->ndev; i++) { - ret = tasdevice_dev_write(tas_priv, i, mc->reg, - (unsigned int)val); - if (!ret) - continue; - err_cnt++; - dev_err(tas_priv->dev, - "set digital vol err in dev %d\n", i); - } - - /* All the devices set error, return 0 */ - return (err_cnt == tas_priv->ndev) ? 0 : 1; - -} -EXPORT_SYMBOL_GPL(tasdevice_digital_putvol); - -int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv, - struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) -{ - unsigned int invert = mc->invert; - int max = mc->max; - int ret, val; - - /* Read the primary device as the whole */ - ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); - if (ret) { - dev_err(tas_priv->dev, "%s, get digital vol error\n", - __func__); - goto out; - } - - val = tasdevice_clamp(val, max, invert); - ucontrol->value.integer.value[0] = val; - -out: - return ret; - -} -EXPORT_SYMBOL_GPL(tasdevice_digital_getvol); - MODULE_DESCRIPTION("TAS2781 common library"); MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 13a197468193..c9c1e608ddb7 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -50,6 +50,11 @@ #define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG #define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG +#define TASDEVICE_CMD_SING_W 0x1 +#define TASDEVICE_CMD_BURST 0x2 +#define TASDEVICE_CMD_DELAY 0x3 +#define TASDEVICE_CMD_FIELD_W 0x4 + #define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5 #define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64 #define TASDEVICE_MAXCONFIG_NUM_KERNEL 10 @@ -803,8 +808,13 @@ static int tasdevice_process_block(void *context, unsigned char *data, chn = idx - 1; chnend = idx; } else { - chn = 0; - chnend = tas_priv->ndev; + if (tas_priv->isspi) { + chn = tas_priv->index; + chnend = chn + 1; + } else { + chn = 0; + chnend = tas_priv->ndev; + } } for (; chn < chnend; chn++) { @@ -896,7 +906,7 @@ static int tasdevice_process_block(void *context, unsigned char *data, is_err = true; break; } - rc = tasdevice_dev_update_bits(tas_priv, chn, + rc = tas_priv->update_bits(tas_priv, chn, TASDEVICE_REG(data[subblk_offset + 2], data[subblk_offset + 3], data[subblk_offset + 4]), @@ -1461,7 +1471,7 @@ static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice, goto end; } - ret = tasdevice_dev_bulk_read(tasdevice, chn, + ret = tasdevice->dev_bulk_read(tasdevice, chn, TASDEVICE_REG(book, page, crc_data.offset), nBuf1, crc_data.len); if (ret < 0) @@ -1511,7 +1521,7 @@ static int do_singlereg_checksum(struct tasdevice_priv *tasdevice, in = check_yram(&crc_data, book, page, reg, 1); if (!in) goto end; - ret = tasdevice_dev_read(tasdevice, chl, + ret = tasdevice->dev_read(tasdevice, chl, TASDEVICE_REG(book, page, reg), &nData1); if (ret < 0) goto end; @@ -1615,7 +1625,7 @@ static int tasdev_block_chksum(struct tasdevice_priv *tas_priv, unsigned int nr_value; int ret; - ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_CHECKSUM_REG, + ret = tas_priv->dev_read(tas_priv, chn, TASDEVICE_CHECKSUM_REG, &nr_value); if (ret < 0) { dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn); @@ -2074,8 +2084,7 @@ int tas2781_load_calibration(void *context, char *file_name, } out: - if (fw_entry) - release_firmware(fw_entry); + release_firmware(fw_entry); return ret; } diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index b950914b7d48..c40d8f754d89 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -14,6 +14,9 @@ // #include <linux/crc8.h> +#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C +#include <linux/debugfs.h> +#endif #include <linux/firmware.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> @@ -28,6 +31,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/tas2781.h> +#include <sound/tas2781-comlib-i2c.h> #include <sound/tlv.h> #include <sound/tas2563-tlv.h> #include <sound/tas2781-tlv.h> @@ -1422,10 +1426,150 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) nctrls < i ? nctrls : i); } +#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C +/* + * This debugfs node is a bridge to the acoustic tuning application + * tool which can tune the chips' acoustic effect. + * + * package structure for PPC3 communications: + * Pkg len (1 byte) + * Pkg id (1 byte, 'r' or 'w') + * Dev id (1 byte, i2c address) + * Book id (1 byte) + * Page id (1 byte) + * Reg id (1 byte) + * switch (pkg id) { + * case 'w': + * 1 byte, length of data to read + * case 'r': + * data payload (1~128 bytes) + * } + */ +static ssize_t acoustic_ctl_read(struct file *file, char __user *to, + size_t count, loff_t *ppos) +{ + struct snd_soc_component *comp = file->private_data; + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp); + struct acoustic_data *p = &tas_priv->acou_data; + int ret = -1; + + if (p->id == 'r' && p->len == count && count <= sizeof(*p)) + ret = simple_read_from_buffer(to, count, ppos, p, p->len); + else + dev_err(tas_priv->dev, "Not ready for get.\n"); + return ret; +} + +static ssize_t acoustic_ctl_write(struct file *file, + const char __user *from, size_t count, loff_t *ppos) +{ + struct snd_soc_component *comp = file->private_data; + struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); + struct acoustic_data *p = &priv->acou_data; + unsigned int max_pkg_len = sizeof(*p); + unsigned char *src; + int j, len, reg, val; + unsigned short chn; + int ret = -1; + + if (count > sizeof(*p)) { + dev_err(priv->dev, "count(%u) is larger than max(%u).\n", + (unsigned int)count, max_pkg_len); + return ret; + } + + src = memdup_user(from, count); + if (IS_ERR(src)) + return PTR_ERR(src); + + if (src[0] > max_pkg_len && src[0] != count) { + dev_err(priv->dev, "pkg(%u), max(%u), count(%u) dismatch.\n", + src[0], max_pkg_len, (unsigned int)count); + ret = 0; + goto exit; + } + + switch (src[1]) { + case 'r': + /* length of data to read */ + len = src[6]; + break; + case 'w': + /* Skip 6 bytes for package type and register address */ + len = src[0] - 6; + break; + default: + dev_err(priv->dev, "%s Wrong code %02x.\n", __func__, src[1]); + ret = 0; + goto exit; + } + + if (len < 1) { + dev_err(priv->dev, "pkg fmt invalid %02x.\n", len); + ret = 0; + goto exit; + } + + for (j = 0; j < priv->ndev; j++) + if (src[2] == priv->tasdevice[j].dev_addr) { + chn = j; + break; + } + if (j >= priv->ndev) { + dev_err(priv->dev, "no such device 0x%02x.\n", src[2]); + ret = 0; + goto exit; + } + + reg = TASDEVICE_REG(src[3], src[4], src[5]); + + guard(mutex)(&priv->codec_lock); + + if (src[1] == 'w') { + if (len > 1) + ret = tasdevice_dev_bulk_write(priv, chn, reg, + &src[6], len); + else + ret = tasdevice_dev_write(priv, chn, reg, src[6]); + } else { + struct acoustic_data *p = &priv->acou_data; + + memcpy(p, src, 6); + if (len > 1) { + ret = tasdevice_dev_bulk_read(priv, chn, reg, + p->data, len); + } else { + ret = tasdevice_dev_read(priv, chn, reg, &val); + p->data[0] = val; + } + p->len = len + 6; + } + + if (ret) + dev_err(priv->dev, "i2c communication error.\n"); + else + ret = count; +exit: + kfree(src); + return ret; +} + +static const struct file_operations acoustic_ctl_fops = { + .open = simple_open, + .read = acoustic_ctl_read, + .write = acoustic_ctl_write, +}; +#endif + static void tasdevice_fw_ready(const struct firmware *fmw, void *context) { struct tasdevice_priv *tas_priv = context; +#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C + struct snd_soc_component *comp = tas_priv->codec; + struct dentry *debugfs_root = comp->debugfs_root; + char *acoustic_debugfs_node; +#endif int ret = 0; int i; @@ -1499,14 +1643,24 @@ static void tasdevice_fw_ready(const struct firmware *fmw, tasdevice_prmg_load(tas_priv, 0); tas_priv->cur_prog = 0; + +#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C + if (tas_priv->name_prefix) + acoustic_debugfs_node = devm_kasprintf(tas_priv->dev, + GFP_KERNEL, "%s_acoustic_ctl", tas_priv->name_prefix); + else + acoustic_debugfs_node = devm_kstrdup(tas_priv->dev, + "acoustic_ctl", GFP_KERNEL); + debugfs_create_file(acoustic_debugfs_node, 0644, debugfs_root, + comp, &acoustic_ctl_fops); +#endif out: if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) { /* If DSP FW fail, DSP kcontrol won't be created. */ tasdevice_dsp_remove(tas_priv); } mutex_unlock(&tas_priv->codec_lock); - if (fmw) - release_firmware(fmw); + release_firmware(fmw); } static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index 191e067ed1c9..1035ba17dc5d 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1015,10 +1015,10 @@ static int adc3xxx_gpio_direction_out(struct gpio_chip *chip, * so we set the output mode and output value in the same call. Hence * .set in practice does the same thing as .direction_out . */ -static void adc3xxx_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int adc3xxx_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { - (void) adc3xxx_gpio_direction_out(chip, offset, value); + return adc3xxx_gpio_direction_out(chip, offset, value); } /* Even though we only support GPIO output for now, some GPIO clients @@ -1052,7 +1052,7 @@ static const struct gpio_chip adc3xxx_gpio_chip = { .owner = THIS_MODULE, .request = adc3xxx_gpio_request, .direction_output = adc3xxx_gpio_direction_out, - .set = adc3xxx_gpio_set, + .set_rv = adc3xxx_gpio_set, .get = adc3xxx_gpio_get, .can_sleep = 1, }; diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 56e795a00e22..f1649df19738 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1818,10 +1818,8 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(aic3x->supplies), aic3x->supplies); - if (ret) { - dev_err(dev, "Failed to request supplies: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request supplies\n"); aic3x_configure_ocmv(dev, aic3x); diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index b5472fa1bdda..38cc000891ea 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -7,19 +7,17 @@ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> */ -#include <linux/module.h> -#include <linux/errno.h> #include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> -#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> -#include <sound/tpa6130a2-plat.h> #include <sound/soc.h> #include <sound/tlv.h> -#include <linux/of.h> -#include <linux/of_gpio.h> -#include <linux/regmap.h> #include "tpa6130a2.h" @@ -33,7 +31,7 @@ struct tpa6130a2_data { struct device *dev; struct regmap *regmap; struct regulator *supply; - int power_gpio; + struct gpio_desc *power_gpio; enum tpa_model id; }; @@ -49,8 +47,7 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) return ret; } /* Power on */ - if (data->power_gpio >= 0) - gpio_set_value(data->power_gpio, 1); + gpiod_set_value(data->power_gpio, 1); /* Sync registers */ regcache_cache_only(data->regmap, false); @@ -59,8 +56,7 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) dev_err(data->dev, "Failed to sync registers: %d\n", ret); regcache_cache_only(data->regmap, true); - if (data->power_gpio >= 0) - gpio_set_value(data->power_gpio, 0); + gpiod_set_value(data->power_gpio, 0); ret2 = regulator_disable(data->supply); if (ret2 != 0) dev_err(data->dev, @@ -76,8 +72,7 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) regcache_cache_only(data->regmap, true); /* Power off */ - if (data->power_gpio >= 0) - gpio_set_value(data->power_gpio, 0); + gpiod_set_value(data->power_gpio, 0); ret = regulator_disable(data->supply); if (ret != 0) { @@ -209,18 +204,10 @@ static const struct regmap_config tpa6130a2_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static const struct i2c_device_id tpa6130a2_id[] = { - { "tpa6130a2", TPA6130A2 }, - { "tpa6140a2", TPA6140A2 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tpa6130a2_id); - static int tpa6130a2_probe(struct i2c_client *client) { struct device *dev; struct tpa6130a2_data *data; - struct tpa6130a2_platform_data *pdata = client->dev.platform_data; struct device_node *np = client->dev.of_node; const char *regulator; unsigned int version; @@ -238,10 +225,13 @@ static int tpa6130a2_probe(struct i2c_client *client) if (IS_ERR(data->regmap)) return PTR_ERR(data->regmap); - if (pdata) { - data->power_gpio = pdata->power_gpio; - } else if (np) { - data->power_gpio = of_get_named_gpio(np, "power-gpio", 0); + if (np) { + data->power_gpio = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(data->power_gpio)) { + return dev_err_probe(dev, PTR_ERR(data->power_gpio), + "Failed to request power GPIO\n"); + } + gpiod_set_consumer_name(data->power_gpio, "tpa6130a2 enable"); } else { dev_err(dev, "Platform data not set\n"); dump_stack(); @@ -252,17 +242,6 @@ static int tpa6130a2_probe(struct i2c_client *client) data->id = (uintptr_t)i2c_get_match_data(client); - if (data->power_gpio >= 0) { - ret = devm_gpio_request(dev, data->power_gpio, - "tpa6130a2 enable"); - if (ret < 0) { - dev_err(dev, "Failed to request power GPIO (%d)\n", - data->power_gpio); - return ret; - } - gpio_direction_output(data->power_gpio, 0); - } - switch (data->id) { default: dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n", @@ -318,7 +297,6 @@ static struct i2c_driver tpa6130a2_i2c_driver = { .of_match_table = of_match_ptr(tpa6130a2_of_match), }, .probe = tpa6130a2_probe, - .id_table = tpa6130a2_id, }; module_i2c_driver(tpa6130a2_i2c_driver); diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 609886461805..92194579e15b 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -5,28 +5,25 @@ * Author: Steve Sakoman, <steve@sakoman.com> */ +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/mfd/twl.h> +#include <linux/mfd/twl4030-audio.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/delay.h> +#include <linux/of.h> #include <linux/pm.h> -#include <linux/i2c.h> #include <linux/platform_device.h> -#include <linux/of.h> -#include <linux/of_gpio.h> -#include <linux/mfd/twl.h> #include <linux/slab.h> -#include <linux/gpio.h> #include <sound/core.h> +#include <sound/initval.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/initval.h> #include <sound/tlv.h> -/* Register descriptions are here */ -#include <linux/mfd/twl4030-audio.h> - /* TWL4030 PMBR1 Register */ #define TWL4030_PMBR1_REG 0x0D /* TWL4030 PMBR1 Register GPIO6 mux bits */ @@ -39,7 +36,7 @@ struct twl4030_board_params { unsigned int ramp_delay_value; unsigned int offset_cncl_path; unsigned int hs_extmute:1; - int hs_extmute_gpio; + struct gpio_desc *hs_extmute_gpio; }; /* codec private data */ @@ -213,8 +210,7 @@ twl4030_get_board_param_values(struct twl4030_board_params *board_params, if (!of_property_read_u32(node, "ti,hs_extmute", &value)) board_params->hs_extmute = value; - board_params->hs_extmute_gpio = of_get_named_gpio(node, "ti,hs_extmute_gpio", 0); - if (gpio_is_valid(board_params->hs_extmute_gpio)) + if (of_property_present(node, "ti,hs_extmute_gpio")) board_params->hs_extmute = 1; } @@ -242,7 +238,7 @@ twl4030_get_board_params(struct snd_soc_component *component) return board_params; } -static void twl4030_init_chip(struct snd_soc_component *component) +static int twl4030_init_chip(struct snd_soc_component *component) { struct twl4030_board_params *board_params; struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); @@ -252,24 +248,20 @@ static void twl4030_init_chip(struct snd_soc_component *component) board_params = twl4030_get_board_params(component); if (board_params && board_params->hs_extmute) { - if (gpio_is_valid(board_params->hs_extmute_gpio)) { - int ret; - - if (!board_params->hs_extmute_gpio) - dev_warn(component->dev, - "Extmute GPIO is 0 is this correct?\n"); - - ret = gpio_request_one(board_params->hs_extmute_gpio, - GPIOF_OUT_INIT_LOW, - "hs_extmute"); - if (ret) { - dev_err(component->dev, - "Failed to get hs_extmute GPIO\n"); - board_params->hs_extmute_gpio = -1; - } + board_params->hs_extmute_gpio = devm_gpiod_get_optional(component->dev, + "ti,hs_extmute", + GPIOD_OUT_LOW); + if (IS_ERR(board_params->hs_extmute_gpio)) + return dev_err_probe(component->dev, PTR_ERR(board_params->hs_extmute_gpio), + "Failed to get hs_extmute GPIO\n"); + + if (board_params->hs_extmute_gpio) { + gpiod_set_consumer_name(board_params->hs_extmute_gpio, "hs_extmute"); } else { u8 pin_mux; + dev_info(component->dev, "use TWL4030 GPIO6\n"); + /* Set TWL4030 GPIO6 as EXTMUTE signal */ twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux, TWL4030_PMBR1_REG); @@ -297,7 +289,7 @@ static void twl4030_init_chip(struct snd_soc_component *component) /* Machine dependent setup */ if (!board_params) - return; + return 0; twl4030->board_params = board_params; @@ -332,6 +324,8 @@ static void twl4030_init_chip(struct snd_soc_component *component) TWL4030_CNCL_OFFSET_START)); twl4030_codec_enable(component, 0); + + return 0; } static void twl4030_apll_enable(struct snd_soc_component *component, int enable) @@ -714,8 +708,8 @@ static void headset_ramp(struct snd_soc_component *component, int ramp) /* Enable external mute control, this dramatically reduces * the pop-noise */ if (board_params && board_params->hs_extmute) { - if (gpio_is_valid(board_params->hs_extmute_gpio)) { - gpio_set_value(board_params->hs_extmute_gpio, 1); + if (board_params->hs_extmute_gpio) { + gpiod_set_value(board_params->hs_extmute_gpio, 1); } else { hs_pop |= TWL4030_EXTMUTE; twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop); @@ -750,8 +744,8 @@ static void headset_ramp(struct snd_soc_component *component, int ramp) /* Disable external mute */ if (board_params && board_params->hs_extmute) { - if (gpio_is_valid(board_params->hs_extmute_gpio)) { - gpio_set_value(board_params->hs_extmute_gpio, 0); + if (board_params->hs_extmute_gpio) { + gpiod_set_value(board_params->hs_extmute_gpio, 0); } else { hs_pop &= ~TWL4030_EXTMUTE; twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop); @@ -2168,24 +2162,11 @@ static int twl4030_soc_probe(struct snd_soc_component *component) /* Set the defaults, and power up the codec */ twl4030->sysclk = twl4030_audio_get_mclk() / 1000; - twl4030_init_chip(component); - - return 0; -} - -static void twl4030_soc_remove(struct snd_soc_component *component) -{ - struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); - struct twl4030_board_params *board_params = twl4030->board_params; - - if (board_params && board_params->hs_extmute && - gpio_is_valid(board_params->hs_extmute_gpio)) - gpio_free(board_params->hs_extmute_gpio); + return twl4030_init_chip(component); } static const struct snd_soc_component_driver soc_component_dev_twl4030 = { .probe = twl4030_soc_probe, - .remove = twl4030_soc_remove, .read = twl4030_read, .write = twl4030_write, .set_bias_level = twl4030_set_bias_level, diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index d589a212b768..4b7c3d6080a1 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -1260,7 +1260,7 @@ correct_plug_type: if (pt_gnd_mic_swap_cnt == mbhc->swap_thr) { /* US_EU gpio present, flip switch */ if (mbhc->cfg->swap_gnd_mic) { - if (mbhc->cfg->swap_gnd_mic(component, true)) + if (mbhc->cfg->swap_gnd_mic(component)) continue; } } diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index b977e8f87d7c..a5d52b9643f5 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -194,7 +194,7 @@ struct wcd_mbhc_config { int num_btn; bool mono_stero_detection; bool typec_analog_mux; - bool (*swap_gnd_mic)(struct snd_soc_component *component, bool active); + bool (*swap_gnd_mic)(struct snd_soc_component *component); bool hs_ext_micbias; bool gnd_det_en; uint32_t linein_th; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 7cef43bb2a88..8ee4360aff92 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -17,7 +17,7 @@ #include <sound/soc.h> #include <sound/pcm_params.h> #include <sound/soc-dapm.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/of.h> #include <linux/of_irq.h> #include <sound/tlv.h> @@ -331,7 +331,7 @@ struct wcd9335_codec { int comp_enabled[COMPANDER_MAX]; int intr1; - int reset_gpio; + struct gpio_desc *reset_gpio; struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY]; unsigned int rx_port_value[WCD9335_RX_MAX]; @@ -4975,12 +4975,11 @@ static const struct regmap_irq_chip wcd9335_regmap_irq1_chip = { static int wcd9335_parse_dt(struct wcd9335_codec *wcd) { struct device *dev = wcd->dev; - struct device_node *np = dev->of_node; int ret; - wcd->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); - if (wcd->reset_gpio < 0) - return dev_err_probe(dev, wcd->reset_gpio, "Reset GPIO missing from DT\n"); + wcd->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(wcd->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(wcd->reset_gpio), "Reset GPIO missing from DT\n"); wcd->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(wcd->mclk)) @@ -5023,9 +5022,9 @@ static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) */ usleep_range(600, 650); - gpio_direction_output(wcd->reset_gpio, 0); + gpiod_set_value(wcd->reset_gpio, 1); msleep(20); - gpio_set_value(wcd->reset_gpio, 1); + gpiod_set_value(wcd->reset_gpio, 0); msleep(20); return 0; diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index dd2045a5d26d..3b1a1518e764 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -2656,7 +2656,7 @@ static void wcd937x_dt_parse_micbias_info(struct device *dev, struct wcd937x_pri dev_warn(dev, "Micbias3 DT property not found\n"); } -static bool wcd937x_swap_gnd_mic(struct snd_soc_component *component, bool active) +static bool wcd937x_swap_gnd_mic(struct snd_soc_component *component) { int value; struct wcd937x_priv *wcd937x; diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index cabddadc90ef..e822cc145250 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1225,7 +1225,7 @@ static int wcd9380_probe(struct sdw_slave *pdev, if (!wcd) return -ENOMEM; - /** + /* * Port map index starts with 0, however the data port for this codec * are from index 1 */ diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 1ae498c32391..d9b61eab029a 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -11,7 +11,6 @@ #include <linux/pm_runtime.h> #include <linux/component.h> #include <sound/tlv.h> -#include <linux/of_gpio.h> #include <linux/of.h> #include <sound/jack.h> #include <sound/pcm.h> @@ -19,6 +18,7 @@ #include <linux/regmap.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <linux/mux/consumer.h> #include <linux/regulator/consumer.h> #include "wcd-clsh-v2.h" @@ -171,8 +171,10 @@ struct wcd938x_priv { int flyback_cur_det_disable; int ear_rx_path; int variant; - int reset_gpio; + struct gpio_desc *reset_gpio; struct gpio_desc *us_euro_gpio; + struct mux_control *us_euro_mux; + unsigned int mux_state; u32 micb1_mv; u32 micb2_mv; u32 micb3_mv; @@ -183,6 +185,7 @@ struct wcd938x_priv { bool comp1_enable; bool comp2_enable; bool ldoh; + bool mux_setup_done; }; static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); @@ -3230,17 +3233,28 @@ static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_pri dev_info(dev, "%s: Micbias4 DT property not found\n", __func__); } -static bool wcd938x_swap_gnd_mic(struct snd_soc_component *component, bool active) +static bool wcd938x_swap_gnd_mic(struct snd_soc_component *component) { - int value; - - struct wcd938x_priv *wcd938x; + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + int ret; - wcd938x = snd_soc_component_get_drvdata(component); + if (wcd938x->us_euro_mux) { + if (wcd938x->mux_setup_done) + mux_control_deselect(wcd938x->us_euro_mux); - value = gpiod_get_value(wcd938x->us_euro_gpio); + ret = mux_control_try_select(wcd938x->us_euro_mux, !wcd938x->mux_state); + if (ret) { + dev_err(dev, "Error (%d) Unable to select us/euro mux state\n", ret); + wcd938x->mux_setup_done = false; + return false; + } + wcd938x->mux_setup_done = true; + } else { + gpiod_set_value(wcd938x->us_euro_gpio, !wcd938x->mux_state); + } - gpiod_set_value(wcd938x->us_euro_gpio, !value); + wcd938x->mux_state = !wcd938x->mux_state; return true; } @@ -3251,16 +3265,30 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device struct wcd_mbhc_config *cfg = &wcd938x->mbhc_cfg; int ret; - wcd938x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0); - if (wcd938x->reset_gpio < 0) - return dev_err_probe(dev, wcd938x->reset_gpio, + wcd938x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(wcd938x->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(wcd938x->reset_gpio), "Failed to get reset gpio\n"); - wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro", - GPIOD_OUT_LOW); - if (IS_ERR(wcd938x->us_euro_gpio)) - return dev_err_probe(dev, PTR_ERR(wcd938x->us_euro_gpio), - "us-euro swap Control GPIO not found\n"); + if (of_property_present(dev->of_node, "mux-controls")) { + wcd938x->us_euro_mux = devm_mux_control_get(dev, NULL); + if (IS_ERR(wcd938x->us_euro_mux)) { + ret = PTR_ERR(wcd938x->us_euro_mux); + return dev_err_probe(dev, ret, "failed to get mux control\n"); + } + + ret = mux_control_try_select(wcd938x->us_euro_mux, wcd938x->mux_state); + if (ret) { + dev_err(dev, "Error (%d) Unable to select us/euro mux state\n", ret); + return ret; + } + wcd938x->mux_setup_done = true; + } else { + wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro", GPIOD_OUT_LOW); + if (IS_ERR(wcd938x->us_euro_gpio)) + return dev_err_probe(dev, PTR_ERR(wcd938x->us_euro_gpio), + "us-euro swap Control GPIO not found\n"); + } cfg->swap_gnd_mic = wcd938x_swap_gnd_mic; @@ -3297,10 +3325,10 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device static int wcd938x_reset(struct wcd938x_priv *wcd938x) { - gpio_direction_output(wcd938x->reset_gpio, 0); + gpiod_set_value(wcd938x->reset_gpio, 1); /* 20us sleep required after pulling the reset gpio to LOW */ usleep_range(20, 30); - gpio_set_value(wcd938x->reset_gpio, 1); + gpiod_set_value(wcd938x->reset_gpio, 0); /* 20us sleep required after pulling the reset gpio to HIGH */ usleep_range(20, 30); @@ -3576,6 +3604,9 @@ static void wcd938x_remove(struct platform_device *pdev) pm_runtime_set_suspended(dev); pm_runtime_dont_use_autosuspend(dev); + if (wcd938x->us_euro_mux && wcd938x->mux_setup_done) + mux_control_deselect(wcd938x->us_euro_mux); + regulator_bulk_disable(WCD938X_MAX_SUPPLY, wcd938x->supplies); regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies); } diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 0a87a79772da..067d23c7ecf9 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -15,7 +15,6 @@ #include <linux/pm_runtime.h> #include <linux/component.h> #include <sound/tlv.h> -#include <linux/of_gpio.h> #include <linux/of_graph.h> #include <linux/of.h> #include <sound/jack.h> @@ -201,7 +200,7 @@ struct wcd939x_priv { u32 hph_mode; u32 tx_mode[TX_ADC_MAX]; int variant; - int reset_gpio; + struct gpio_desc *reset_gpio; u32 micb1_mv; u32 micb2_mv; u32 micb3_mv; @@ -3215,7 +3214,7 @@ static void wcd939x_dt_parse_micbias_info(struct device *dev, struct wcd939x_pri } #if IS_ENABLED(CONFIG_TYPEC) -static bool wcd939x_swap_gnd_mic(struct snd_soc_component *component, bool active) +static bool wcd939x_swap_gnd_mic(struct snd_soc_component *component) { struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); @@ -3239,10 +3238,11 @@ static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device #endif /* CONFIG_TYPEC */ int ret; - wcd939x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0); - if (wcd939x->reset_gpio < 0) - return dev_err_probe(dev, wcd939x->reset_gpio, - "Failed to get reset gpio\n"); + wcd939x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(wcd939x->reset_gpio)) { + ret = PTR_ERR(wcd939x->reset_gpio); + return dev_err_probe(dev, ret, "Failed to get reset gpio\n"); + } wcd939x->supplies[0].supply = "vdd-rxtx"; wcd939x->supplies[1].supply = "vdd-io"; @@ -3290,10 +3290,10 @@ static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device static int wcd939x_reset(struct wcd939x_priv *wcd939x) { - gpio_direction_output(wcd939x->reset_gpio, 0); + gpiod_set_value(wcd939x->reset_gpio, 1); /* 20us sleep required after pulling the reset gpio to LOW */ usleep_range(20, 30); - gpio_set_value(wcd939x->reset_gpio, 1); + gpiod_set_value(wcd939x->reset_gpio, 0); /* 20us sleep required after pulling the reset gpio to HIGH */ usleep_range(20, 30); diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index d9e5762324df..fb5ed4ba7f60 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2236,12 +2236,14 @@ static irqreturn_t wm5100_edge_irq(int irq, void *data) } #ifdef CONFIG_GPIOLIB -static void wm5100_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm5100_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct wm5100_priv *wm5100 = gpiochip_get_data(chip); - regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset, - WM5100_GP1_LVL, !!value << WM5100_GP1_LVL_SHIFT); + return regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset, + WM5100_GP1_LVL, + !!value << WM5100_GP1_LVL_SHIFT); } static int wm5100_gpio_direction_out(struct gpio_chip *chip, @@ -2288,7 +2290,7 @@ static const struct gpio_chip wm5100_template_chip = { .label = "wm5100", .owner = THIS_MODULE, .direction_output = wm5100_gpio_direction_out, - .set = wm5100_gpio_set, + .set_rv = wm5100_gpio_set, .direction_input = wm5100_gpio_direction_in, .get = wm5100_gpio_get, .can_sleep = 1, diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 03902909f27e..2ed9f493d507 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1825,13 +1825,15 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip, return 0; } -static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm8903_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct wm8903_priv *wm8903 = gpiochip_get_data(chip); - regmap_update_bits(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, - WM8903_GP1_LVL_MASK, - !!value << WM8903_GP1_LVL_SHIFT); + return regmap_update_bits(wm8903->regmap, + WM8903_GPIO_CONTROL_1 + offset, + WM8903_GP1_LVL_MASK, + !!value << WM8903_GP1_LVL_SHIFT); } static const struct gpio_chip wm8903_template_chip = { @@ -1841,7 +1843,7 @@ static const struct gpio_chip wm8903_template_chip = { .direction_input = wm8903_gpio_direction_in, .get = wm8903_gpio_get, .direction_output = wm8903_gpio_direction_out, - .set = wm8903_gpio_set, + .set_rv = wm8903_gpio_set, .can_sleep = 1, }; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 68f746626c33..d69aa8b15629 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3407,13 +3407,16 @@ static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset) return 0; } -static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm8962_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct wm8962_priv *wm8962 = gpiochip_get_data(chip); struct snd_soc_component *component = wm8962->component; - snd_soc_component_update_bits(component, WM8962_GPIO_BASE + offset, - WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT); + return snd_soc_component_update_bits(component, + WM8962_GPIO_BASE + offset, + WM8962_GP2_LVL, + !!value << WM8962_GP2_LVL_SHIFT); } static int wm8962_gpio_direction_out(struct gpio_chip *chip, @@ -3439,7 +3442,7 @@ static const struct gpio_chip wm8962_template_chip = { .owner = THIS_MODULE, .request = wm8962_gpio_request, .direction_output = wm8962_gpio_direction_out, - .set = wm8962_gpio_set, + .set_rv = wm8962_gpio_set, .can_sleep = 1, }; diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index c2af8d7ecdd8..e364d0da9044 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2136,12 +2136,14 @@ static int wm8996_set_fll(struct snd_soc_component *component, int fll_id, int s } #ifdef CONFIG_GPIOLIB -static void wm8996_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm8996_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct wm8996_priv *wm8996 = gpiochip_get_data(chip); - regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset, - WM8996_GP1_LVL, !!value << WM8996_GP1_LVL_SHIFT); + return regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset, + WM8996_GP1_LVL, + !!value << WM8996_GP1_LVL_SHIFT); } static int wm8996_gpio_direction_out(struct gpio_chip *chip, @@ -2184,7 +2186,7 @@ static const struct gpio_chip wm8996_template_chip = { .label = "wm8996", .owner = THIS_MODULE, .direction_output = wm8996_gpio_direction_out, - .set = wm8996_gpio_set, + .set_rv = wm8996_gpio_set, .direction_input = wm8996_gpio_direction_in, .get = wm8996_gpio_get, .can_sleep = 1, diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 91c8697c29c3..3c580faab3b7 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -8,6 +8,7 @@ */ #include <linux/array_size.h> +#include <linux/cleanup.h> #include <linux/ctype.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -19,7 +20,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> -#include <linux/vmalloc.h> +#include <linux/string.h> #include <linux/workqueue.h> #include <linux/debugfs.h> #include <sound/core.h> @@ -415,21 +416,12 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, (struct soc_bytes_ext *)kctl->private_value; struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; - void *scratch; - int ret = 0; + void *scratch __free(kvfree) = vmemdup_user(bytes, size); - scratch = vmalloc(size); - if (!scratch) - return -ENOMEM; + if (IS_ERR(scratch)) + return PTR_ERR(scratch); - if (copy_from_user(scratch, bytes, size)) - ret = -EFAULT; - else - ret = cs_dsp_coeff_lock_and_write_ctrl(cs_ctl, 0, scratch, size); - - vfree(scratch); - - return ret; + return cs_dsp_coeff_lock_and_write_ctrl(cs_ctl, 0, scratch, size); } static int wm_coeff_put_acked(struct snd_kcontrol *kctl, @@ -718,12 +710,10 @@ static void wm_adsp_release_firmware_files(struct wm_adsp *dsp, const struct firmware *coeff_firmware, char *coeff_filename) { - if (wmfw_firmware) - release_firmware(wmfw_firmware); + release_firmware(wmfw_firmware); kfree(wmfw_filename); - if (coeff_firmware) - release_firmware(coeff_firmware); + release_firmware(coeff_firmware); kfree(coeff_filename); } @@ -785,7 +775,7 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, return ret; } -static const char *cirrus_dir = "cirrus/"; +static const char * const cirrus_dir = "cirrus/"; static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, const struct firmware **wmfw_firmware, char **wmfw_filename, diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index d259e1d4d83d..f04d99c66f33 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -568,7 +568,7 @@ static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = { }, [WSA883X_PORT_VISENSE] = { .num = WSA883X_PORT_VISENSE + 1, - .ch_mask = 0x3, + .ch_mask = 0x1, }, }; @@ -1584,7 +1584,7 @@ static int wsa883x_probe(struct sdw_slave *pdev, wsa883x->sconfig.type = SDW_STREAM_PDM; mutex_init(&wsa883x->sp_lock); - /** + /* * Port map index starts with 0, however the data port for this codec * are from index 1 */ diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index 8051483aa1ac..fd6ebc25fe89 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -891,7 +891,7 @@ static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = { }, [WSA884X_PORT_VISENSE] = { .num = WSA884X_PORT_VISENSE + 1, - .ch_mask = 0x3, + .ch_mask = 0x1, }, [WSA884X_PORT_CPS] = { .num = WSA884X_PORT_CPS + 1, @@ -2085,7 +2085,7 @@ static int wsa884x_probe(struct sdw_slave *pdev, wsa884x->sconfig.direction = SDW_DATA_DIR_RX; wsa884x->sconfig.type = SDW_STREAM_PDM; - /** + /* * Port map index starts with 0, however the data port for this codec * are from index 1 */ diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c index 28c92d90299e..180d45a349ac 100644 --- a/sound/soc/codecs/zl38060.c +++ b/sound/soc/codecs/zl38060.c @@ -387,12 +387,12 @@ static const struct snd_soc_component_driver zl38_component_dev = { .endianness = 1, }; -static void chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) +static int chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) { struct regmap *regmap = gpiochip_get_data(c); unsigned int mask = BIT(offset); - regmap_update_bits(regmap, REG_GPIO_DAT, mask, val ? mask : 0); + return regmap_update_bits(regmap, REG_GPIO_DAT, mask, val ? mask : 0); } static int chip_gpio_get(struct gpio_chip *c, unsigned int offset) @@ -422,8 +422,12 @@ chip_direction_output(struct gpio_chip *c, unsigned int offset, int val) { struct regmap *regmap = gpiochip_get_data(c); unsigned int mask = BIT(offset); + int ret; + + ret = chip_gpio_set(c, offset, val); + if (ret) + return ret; - chip_gpio_set(c, offset, val); return regmap_update_bits(regmap, REG_GPIO_DIR, mask, mask); } @@ -436,7 +440,7 @@ static const struct gpio_chip template_chip = { .direction_input = chip_direction_input, .direction_output = chip_direction_output, .get = chip_gpio_get, - .set = chip_gpio_set, + .set_rv = chip_gpio_set, .can_sleep = true, }; |