diff options
Diffstat (limited to 'sound/soc/codecs')
56 files changed, 8906 insertions, 388 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 59f9742e9ff4..f78ea2f86fa6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -114,6 +114,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ES8328_I2C imply SND_SOC_ES7134 imply SND_SOC_ES7241 + imply SND_SOC_FRAMER imply SND_SOC_GTM601 imply SND_SOC_HDAC_HDMI imply SND_SOC_HDAC_HDA @@ -276,6 +277,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_WCD9335 imply SND_SOC_WCD934X imply SND_SOC_WCD938X_SDW + imply SND_SOC_WCD939X_SDW imply SND_SOC_LPASS_MACRO_COMMON imply SND_SOC_LPASS_RX_MACRO imply SND_SOC_LPASS_TX_MACRO @@ -727,6 +729,22 @@ config SND_SOC_CROS_EC_CODEC If you say yes here you will get support for the ChromeOS Embedded Controller's Audio Codec. +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 + default KUNIT_ALL_TESTS + select SND_SOC_CS_AMP_LIB + help + This builds KUnit tests for the Cirrus Logic common + amplifier library. + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in + Documentation/dev-tools/kunit/. + If in doubt, say "N". + config SND_SOC_CS35L32 tristate "Cirrus Logic CS35L32 CODEC" depends on I2C @@ -795,6 +813,7 @@ config SND_SOC_CS35L56 tristate config SND_SOC_CS35L56_SHARED + select SND_SOC_CS_AMP_LIB tristate config SND_SOC_CS35L56_I2C @@ -1101,6 +1120,20 @@ config SND_SOC_ES8328_SPI depends on SPI_MASTER select SND_SOC_ES8328 +config SND_SOC_FRAMER + tristate "Framer codec" + depends on GENERIC_FRAMER + help + Enable support for the framer codec. + The framer codec uses the generic framer infrastructure to transport + some audio data over an analog E1/T1/J1 line. + This codec allows to use some of the time slots available on the TDM + bus on which the framer is connected to transport the audio data. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-framer. + + config SND_SOC_GTM601 tristate 'GTM601 UMTS modem audio codec' @@ -2059,6 +2092,25 @@ config SND_SOC_WCD938X_SDW The WCD9380/9385 is a audio codec IC Integrated in Qualcomm SoCs like SM8250. +config SND_SOC_WCD939X + depends on SND_SOC_WCD939X_SDW + tristate + depends on SOUNDWIRE || !SOUNDWIRE + depends on TYPEC || !TYPEC + select SND_SOC_WCD_CLASSH + +config SND_SOC_WCD939X_SDW + tristate "WCD9390/WCD9395 Codec - SDW" + depends on TYPEC || !TYPEC + select SND_SOC_WCD939X + select SND_SOC_WCD_MBHC + select REGMAP_IRQ + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + help + The WCD9390/9395 is a audio codec IC Integrated in + Qualcomm SoCs like SM8650. + config SND_SOC_WL1273 tristate @@ -2300,7 +2352,6 @@ config SND_SOC_WSA881X tristate "WSA881X Codec" depends on SOUNDWIRE select REGMAP_SOUNDWIRE - tristate help This enables support for Qualcomm WSA8810/WSA8815 Class-D Smart Speaker Amplifier. @@ -2309,7 +2360,6 @@ config SND_SOC_WSA883X tristate "WSA883X Codec" depends on SOUNDWIRE select REGMAP_SOUNDWIRE - tristate help This enables support for Qualcomm WSA8830/WSA8835 Class-D Smart Speaker Amplifier. @@ -2318,7 +2368,6 @@ config SND_SOC_WSA884X tristate "WSA884X Codec" depends on SOUNDWIRE select REGMAP_SOUNDWIRE - tristate help This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D Smart Speaker Amplifier. diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f53baa2b9565..7c075539dc47 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -59,6 +59,8 @@ snd-soc-chv3-codec-objs := chv3-codec.o snd-soc-cpcap-objs := cpcap.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cros-ec-codec-objs := cros_ec_codec.o +snd-soc-cs-amp-lib-objs := cs-amp-lib.o +snd-soc-cs-amp-lib-test-objs := cs-amp-lib-test.o snd-soc-cs35l32-objs := cs35l32.o snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o @@ -122,6 +124,7 @@ snd-soc-es8326-objs := es8326.o snd-soc-es8328-objs := es8328.o snd-soc-es8328-i2c-objs := es8328-i2c.o snd-soc-es8328-spi-objs := es8328-spi.o +snd-soc-framer-objs := framer-codec.o snd-soc-gtm601-objs := gtm601.o snd-soc-hdac-hdmi-objs := hdac_hdmi.o snd-soc-hdac-hda-objs := hdac_hda.o @@ -313,6 +316,8 @@ snd-soc-wcd9335-objs := wcd9335.o snd-soc-wcd934x-objs := wcd934x.o snd-soc-wcd938x-objs := wcd938x.o snd-soc-wcd938x-sdw-objs := wcd938x-sdw.o +snd-soc-wcd939x-objs := wcd939x.o +snd-soc-wcd939x-sdw-objs := wcd939x-sdw.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o @@ -449,6 +454,8 @@ obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o +obj-$(CONFIG_SND_SOC_CS_AMP_LIB) += snd-soc-cs-amp-lib.o +obj-$(CONFIG_SND_SOC_CS_AMP_LIB_TEST) += snd-soc-cs-amp-lib-test.o obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o @@ -512,6 +519,7 @@ 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_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 obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o @@ -703,6 +711,11 @@ ifdef CONFIG_SND_SOC_WCD938X_SDW # avoid link failure by forcing sdw code built-in when needed obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x-sdw.o endif +obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x.o +ifdef CONFIG_SND_SOC_WCD939X_SDW +# avoid link failure by forcing sdw code built-in when needed +obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o +endif obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c new file mode 100644 index 000000000000..15f991b2e16e --- /dev/null +++ b/sound/soc/codecs/cs-amp-lib-test.c @@ -0,0 +1,709 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// KUnit test for the Cirrus common amplifier library. +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <kunit/test.h> +#include <kunit/static_stub.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/platform_device.h> +#include <linux/random.h> +#include <sound/cs-amp-lib.h> + +struct cs_amp_lib_test_priv { + struct platform_device amp_pdev; + + struct cirrus_amp_efi_data *cal_blob; + struct list_head ctl_write_list; +}; + +struct cs_amp_lib_test_ctl_write_entry { + struct list_head list; + unsigned int value; + char name[16]; +}; + +struct cs_amp_lib_test_param { + int num_amps; + int amp_index; +}; + +static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + unsigned int blob_size; + + blob_size = offsetof(struct cirrus_amp_efi_data, data) + + sizeof(struct cirrus_amp_cal_data) * num_amps; + + priv->cal_blob = kunit_kzalloc(test, blob_size, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob); + + 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); +} + +static u64 cs_amp_lib_test_get_target_uid(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + u64 uid; + + uid = priv->cal_blob->data[param->amp_index].calTarget[1]; + uid <<= 32; + uid |= priv->cal_blob->data[param->amp_index].calTarget[0]; + + return uid; +} + +/* Redirected get_efi_variable to simulate that the file is too short */ +static efi_status_t cs_amp_lib_test_get_efi_variable_nohead(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + if (!buf) { + *size = offsetof(struct cirrus_amp_efi_data, data) - 1; + return EFI_BUFFER_TOO_SMALL; + } + + return EFI_NOT_FOUND; +} + +/* Should return -EOVERFLOW if the header is larger than the EFI data */ +static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(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); + KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* Redirected get_efi_variable to simulate that the count is larger than the file */ +static efi_status_t cs_amp_lib_test_get_efi_variable_bad_count(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + struct kunit *test = kunit_get_current_test(); + struct cs_amp_lib_test_priv *priv = test->priv; + + if (!buf) { + /* + * Return a size that is shorter than required for the + * declared number of entries. + */ + *size = priv->cal_blob->size - 1; + return EFI_BUFFER_TOO_SMALL; + } + + memcpy(buf, priv->cal_blob, priv->cal_blob->size - 1); + + return EFI_SUCCESS; +} + +/* Should return -EOVERFLOW if the entry count is larger than the EFI data */ +static void cs_amp_lib_test_cal_count_too_big_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(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); + KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* Redirected get_efi_variable to simulate that the variable not found */ +static efi_status_t cs_amp_lib_test_get_efi_variable_none(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + return EFI_NOT_FOUND; +} + +/* If EFI doesn't contain a cal data variable the result should be -ENOENT */ +static void cs_amp_lib_test_no_cal_data_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(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); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* Redirected get_efi_variable to simulate reading a cal data blob */ +static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + static const efi_char16_t expected_name[] = L"CirrusSmartAmpCalibrationData"; + static const efi_guid_t expected_guid = + EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3); + struct kunit *test = kunit_get_current_test(); + struct cs_amp_lib_test_priv *priv = test->priv; + + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size); + + KUNIT_EXPECT_MEMEQ(test, name, expected_name, sizeof(expected_name)); + KUNIT_EXPECT_MEMEQ(test, guid, &expected_guid, sizeof(expected_guid)); + + if (!buf) { + *size = priv->cal_blob->size; + return EFI_BUFFER_TOO_SMALL; + } + + KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small"); + + memcpy(buf, priv->cal_blob, priv->cal_blob->size); + + return EFI_SUCCESS; +} + +/* Get cal data block for a given amp, matched by target UID. */ +static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + u64 target_uid; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + 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); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTarget[0], target_uid & 0xFFFFFFFFULL); + KUNIT_EXPECT_EQ(test, result_data.calTarget[1], target_uid >> 32); + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* Get cal data block for a given amp index without checking target UID. */ +static void cs_amp_lib_test_get_efi_cal_by_index_unchecked_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(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, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* Get cal data block for a given amp index with checked target UID. */ +static void cs_amp_lib_test_get_efi_cal_by_index_checked_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + u64 target_uid; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + 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, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* + * Get cal data block for a given amp index with checked target UID. + * The UID does not match so the result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + u64 target_uid; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + /* 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, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * Get cal data block for a given amp, where the cal data does not + * specify calTarget so the lookup falls back to using the index + */ +static void cs_amp_lib_test_get_efi_cal_by_index_fallback_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + static const u64 bad_target_uid = 0xBADCA100BABABABAULL; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Make all the target values zero so they are ignored */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] = 0; + priv->cal_blob->data[i].calTarget[1] = 0; + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(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, bad_target_uid, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* + * If the target UID isn't present in the cal data, and there isn't an + * index to fall back do, the result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + static const u64 bad_target_uid = 0xBADCA100BABABABAULL; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Make all the target values != bad_target_uid */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL); + priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32); + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(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, bad_target_uid, -1, + &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * If the target UID isn't present in the cal data, and the index is + * out of range, the result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + static const u64 bad_target_uid = 0xBADCA100BABABABAULL; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Make all the target values != bad_target_uid */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL); + priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32); + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(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, bad_target_uid, 99, + &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * If the target UID isn't given, and the index is out of range, the + * result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(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, 99, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* If neither the target UID or the index is given the result should be -ENOENT. */ +static void cs_amp_lib_test_get_efi_cal_no_uid_no_index_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(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); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * If the UID is passed as 0 this must not match an entry with an + * unpopulated calTarget + */ +static void cs_amp_lib_test_get_efi_cal_zero_not_matched_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Make all the target values zero so they are ignored */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] = 0; + priv->cal_blob->data[i].calTarget[1] = 0; + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(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); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +static const struct cirrus_amp_cal_controls cs_amp_lib_test_calibration_controls = { + .alg_id = 0x9f210, + .mem_region = WMFW_ADSP2_YM, + .ambient = "CAL_AMBIENT", + .calr = "CAL_R", + .status = "CAL_STATUS", + .checksum = "CAL_CHECKSUM", +}; + +static int cs_amp_lib_test_write_cal_coeff(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const char *ctl_name, u32 val) +{ + struct kunit *test = kunit_get_current_test(); + struct cs_amp_lib_test_priv *priv = test->priv; + struct cs_amp_lib_test_ctl_write_entry *entry; + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctl_name); + KUNIT_EXPECT_PTR_EQ(test, controls, &cs_amp_lib_test_calibration_controls); + + entry = kunit_kzalloc(test, sizeof(*entry), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, entry); + + INIT_LIST_HEAD(&entry->list); + strscpy(entry->name, ctl_name, sizeof(entry->name)); + entry->value = val; + + list_add_tail(&entry->list, &priv->ctl_write_list); + + return 0; +} + +static void cs_amp_lib_test_write_cal_data_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cs_amp_lib_test_ctl_write_entry *entry; + struct cirrus_amp_cal_data data; + struct cs_dsp *dsp; + int ret; + + dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp); + dsp->dev = &priv->amp_pdev.dev; + + get_random_bytes(&data, sizeof(data)); + + /* Redirect calls to write firmware controls */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->write_cal_coeff, + cs_amp_lib_test_write_cal_coeff); + + ret = cs_amp_write_cal_coeffs(dsp, &cs_amp_lib_test_calibration_controls, &data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->write_cal_coeff); + + KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->ctl_write_list), 4); + + /* Checksum control must be written last */ + entry = list_last_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.checksum); + KUNIT_EXPECT_EQ(test, entry->value, data.calR + 1); + list_del(&entry->list); + + entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.ambient); + KUNIT_EXPECT_EQ(test, entry->value, data.calAmbient); + list_del(&entry->list); + + entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.calr); + KUNIT_EXPECT_EQ(test, entry->value, data.calR); + list_del(&entry->list); + + entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.status); + 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); + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + test->priv = priv; + 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"); + + 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 }, + + { .num_amps = 3, .amp_index = 0 }, + { .num_amps = 3, .amp_index = 1 }, + { .num_amps = 3, .amp_index = 2 }, + + { .num_amps = 4, .amp_index = 0 }, + { .num_amps = 4, .amp_index = 1 }, + { .num_amps = 4, .amp_index = 2 }, + { .num_amps = 4, .amp_index = 3 }, + + { .num_amps = 5, .amp_index = 0 }, + { .num_amps = 5, .amp_index = 1 }, + { .num_amps = 5, .amp_index = 2 }, + { .num_amps = 5, .amp_index = 3 }, + { .num_amps = 5, .amp_index = 4 }, + + { .num_amps = 6, .amp_index = 0 }, + { .num_amps = 6, .amp_index = 1 }, + { .num_amps = 6, .amp_index = 2 }, + { .num_amps = 6, .amp_index = 3 }, + { .num_amps = 6, .amp_index = 4 }, + { .num_amps = 6, .amp_index = 5 }, + + { .num_amps = 8, .amp_index = 0 }, + { .num_amps = 8, .amp_index = 1 }, + { .num_amps = 8, .amp_index = 2 }, + { .num_amps = 8, .amp_index = 3 }, + { .num_amps = 8, .amp_index = 4 }, + { .num_amps = 8, .amp_index = 5 }, + { .num_amps = 8, .amp_index = 6 }, + { .num_amps = 8, .amp_index = 7 }, +}; + +static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_param *param, + char *desc) +{ + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "num_amps:%d amp_index:%d", + param->num_amps, param->amp_index); +} + +KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases, + cs_amp_lib_test_get_cal_param_desc); + +static struct kunit_case cs_amp_lib_test_cases[] = { + /* Tests for getting calibration data from EFI */ + KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test), + KUNIT_CASE(cs_amp_lib_test_cal_count_too_big_test), + KUNIT_CASE(cs_amp_lib_test_no_cal_data_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_checked_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_fallback_test, + cs_amp_lib_test_get_cal_gen_params), + + /* Tests for writing calibration data */ + KUNIT_CASE(cs_amp_lib_test_write_cal_data_test), + + { } /* terminator */ +}; + +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, +}; + +kunit_test_suite(cs_amp_lib_test_suite); + +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); +MODULE_DESCRIPTION("KUnit test for Cirrus Logic amplifier library"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c new file mode 100644 index 000000000000..01ef4db5407d --- /dev/null +++ b/sound/soc/codecs/cs-amp-lib.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Common code for Cirrus Logic Smart Amplifiers +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <asm/byteorder.h> +#include <kunit/static_stub.h> +#include <linux/dev_printk.h> +#include <linux/efi.h> +#include <linux/firmware/cirrus/cs_dsp.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <sound/cs-amp-lib.h> + +#define CS_AMP_CAL_GUID \ + EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3) + +#define CS_AMP_CAL_NAME L"CirrusSmartAmpCalibrationData" + +static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const char *ctl_name, u32 val) +{ + struct cs_dsp_coeff_ctl *cs_ctl; + __be32 beval = cpu_to_be32(val); + int ret; + + KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); + + if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { + mutex_lock(&dsp->pwr_lock); + cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); + mutex_unlock(&dsp->pwr_lock); + + if (ret < 0) { + dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); + return ret; + } + + return 0; + } + + return -ENODEV; +} + +static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const struct cirrus_amp_cal_data *data) +{ + int ret; + + dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n", + data->calAmbient, data->calStatus, data->calR); + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1); + if (ret) + return ret; + + return 0; +} + +/** + * cs_amp_write_cal_coeffs - Write calibration data to firmware controls. + * @dsp: Pointer to struct cs_dsp. + * @controls: Pointer to definition of firmware controls to be written. + * @data: Pointer to calibration data. + * + * Returns: 0 on success, else negative error value. + */ +int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const struct cirrus_amp_cal_data *data) +{ + if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + return _cs_amp_write_cal_coeffs(dsp, controls, data); + else + return -ENODEV; +} +EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, SND_SOC_CS_AMP_LIB); + +static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + u32 attr; + + KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid, size, buf); + + if (IS_ENABLED(CONFIG_EFI)) + return efi.get_variable(name, guid, &attr, size, buf); + + return EFI_NOT_FOUND; +} + +static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev) +{ + struct cirrus_amp_efi_data *efi_data; + unsigned long data_size = 0; + u8 *data; + efi_status_t status; + int ret; + + /* Get real size of UEFI variable */ + status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, NULL); + if (status != EFI_BUFFER_TOO_SMALL) + return ERR_PTR(-ENOENT); + + if (data_size < sizeof(*efi_data)) { + dev_err(dev, "EFI cal variable truncated\n"); + return ERR_PTR(-EOVERFLOW); + } + + /* Get variable contents into buffer */ + data = kmalloc(data_size, GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, data); + if (status != EFI_SUCCESS) { + ret = -EINVAL; + goto err; + } + + efi_data = (struct cirrus_amp_efi_data *)data; + 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) { + dev_err(dev, "EFI cal variable truncated\n"); + ret = -EOVERFLOW; + goto err; + } + + return efi_data; + +err: + kfree(data); + dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret); + + return ERR_PTR(ret); +} + +static u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data) +{ + return ((u64)data->calTarget[1] << 32) | data->calTarget[0]; +} + +static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, + struct cirrus_amp_cal_data *out_data) +{ + struct cirrus_amp_efi_data *efi_data; + struct cirrus_amp_cal_data *cal = NULL; + int i, ret; + + efi_data = cs_amp_get_cal_efi_buffer(dev); + if (IS_ERR(efi_data)) + return PTR_ERR(efi_data); + + if (target_uid) { + for (i = 0; i < efi_data->count; ++i) { + u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]); + + /* Skip entries with unpopulated silicon ID */ + if (cal_target == 0) + continue; + + if (cal_target == target_uid) { + cal = &efi_data->data[i]; + break; + } + } + } + + if (!cal && (amp_index >= 0) && (amp_index < efi_data->count)) { + u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]); + + /* + * Treat unpopulated cal_target as a wildcard. + * If target_uid != 0 we can only get here if cal_target == 0 + * or it didn't match any cal_target value. + * If target_uid == 0 it is a wildcard. + */ + if ((cal_target == 0) || (target_uid == 0)) + cal = &efi_data->data[amp_index]; + else + dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index); + } + + if (cal) { + memcpy(out_data, cal, sizeof(*out_data)); + ret = 0; + } else { + dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid); + ret = -ENOENT; + } + + kfree(efi_data); + + return ret; +} + +/** + * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI. + * @dev: struct device of the caller. + * @target_uid: UID to match, or zero to ignore UID matching. + * @amp_index: Entry index to use, or -1 to prevent lookup by index. + * @out_data: struct cirrus_amp_cal_data where the entry will be copied. + * + * This function can perform 3 types of lookup: + * + * (target_uid > 0, amp_index >= 0) + * UID search with fallback to using the array index. + * Search the calibration data for a non-zero calTarget that matches + * target_uid, and if found return that entry. Else, if the entry at + * [amp_index] has calTarget == 0, return that entry. Else fail. + * + * (target_uid > 0, amp_index < 0) + * UID search only. + * Search the calibration data for a non-zero calTarget that matches + * target_uid, and if found return that entry. Else fail. + * + * (target_uid == 0, amp_index >= 0) + * Array index fetch only. + * Return the entry at [amp_index]. + * + * An array lookup will be skipped if amp_index exceeds the number of + * entries in the calibration array, and in this case the return will + * be -ENOENT. An out-of-range amp_index does not prevent matching by + * target_uid - it has the same effect as passing amp_index < 0. + * + * If the EFI data is too short to be a valid entry, or the entry count + * in the EFI data overflows the actual length of the data, this function + * returns -EOVERFLOW. + * + * Return: 0 if the entry was found, -ENOENT if no entry was found, + * -EOVERFLOW if the EFI file is corrupt, else other error value. + */ +int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, + struct cirrus_amp_cal_data *out_data) +{ + if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data); + else + return -ENOENT; +} +EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, SND_SOC_CS_AMP_LIB); + +static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = { + .get_efi_variable = cs_amp_get_efi_variable, + .write_cal_coeff = cs_amp_write_cal_coeff, +}; + +const struct cs_amp_test_hooks * const cs_amp_test_hooks = + PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs); +EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, SND_SOC_CS_AMP_LIB); + +MODULE_DESCRIPTION("Cirrus Logic amplifier library"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(FW_CS_DSP); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index ab960a1c171e..14a5f86019aa 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -161,6 +161,20 @@ static const struct regmap_bus cs35l56_regmap_bus_sdw = { .val_format_endian_default = REGMAP_ENDIAN_BIG, }; +static int cs35l56_sdw_set_cal_index(struct cs35l56_private *cs35l56) +{ + int ret; + + /* SoundWire UniqueId is used to index the calibration array */ + ret = sdw_read_no_pm(cs35l56->sdw_peripheral, SDW_SCP_DEVID_0); + if (ret < 0) + return ret; + + cs35l56->base.cal_index = ret & 0xf; + + return 0; +} + static void cs35l56_sdw_init(struct sdw_slave *peripheral) { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); @@ -168,6 +182,12 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) pm_runtime_get_noresume(cs35l56->base.dev); + if (cs35l56->base.cal_index < 0) { + ret = cs35l56_sdw_set_cal_index(cs35l56); + if (ret < 0) + goto out; + } + regcache_cache_only(cs35l56->base.regmap, false); ret = cs35l56_init(cs35l56); @@ -366,7 +386,7 @@ static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral, dev_dbg(cs35l56->base.dev, "%s: sclk=%u c=%u r=%u\n", __func__, sclk, params->col, params->row); - if (cs35l56->base.rev < 0xb0) + if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev < 0xb0)) return cs35l56_a1_kick_divider(cs35l56, peripheral); return 0; @@ -543,6 +563,7 @@ 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), {}, }; MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index cb4e83126b08..08cac58e3ab2 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -5,10 +5,12 @@ // Copyright (C) 2023 Cirrus Logic, Inc. and // Cirrus Logic International Semiconductor Ltd. +#include <linux/firmware/cirrus/wmfw.h> #include <linux/gpio/consumer.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/types.h> +#include <sound/cs-amp-lib.h> #include "cs35l56.h" @@ -36,6 +38,8 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED); static const struct reg_default cs35l56_reg_defaults[] = { + /* no defaults for OTP_MEM - first read populates cache */ + { CS35L56_ASP1_ENABLES1, 0x00000000 }, { CS35L56_ASP1_CONTROL1, 0x00000028 }, { CS35L56_ASP1_CONTROL2, 0x18180200 }, @@ -91,6 +95,9 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg) case CS35L56_BLOCK_ENABLES2: case CS35L56_REFCLK_INPUT: case CS35L56_GLOBAL_SAMPLE_RATE: + case CS35L56_OTP_MEM_53: + case CS35L56_OTP_MEM_54: + case CS35L56_OTP_MEM_55: case CS35L56_ASP1_ENABLES1: case CS35L56_ASP1_CONTROL1: case CS35L56_ASP1_CONTROL2: @@ -629,6 +636,82 @@ void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_ds } EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED); +struct cs35l56_pte { + u8 x; + u8 wafer_id; + u8 pte[2]; + u8 lot[3]; + u8 y; + u8 unused[3]; + u8 dvs; +} __packed; +static_assert((sizeof(struct cs35l56_pte) % sizeof(u32)) == 0); + +static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid) +{ + struct cs35l56_pte pte; + u64 unique_id; + int ret; + + ret = regmap_raw_read(cs35l56_base->regmap, CS35L56_OTP_MEM_53, &pte, sizeof(pte)); + if (ret) { + dev_err(cs35l56_base->dev, "Failed to read OTP: %d\n", ret); + return ret; + } + + unique_id = (u32)pte.lot[2] | ((u32)pte.lot[1] << 8) | ((u32)pte.lot[0] << 16); + unique_id <<= 32; + 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; +} + +/* Firmware calibration controls */ +const struct cirrus_amp_cal_controls cs35l56_calibration_controls = { + .alg_id = 0x9f210, + .mem_region = WMFW_ADSP2_YM, + .ambient = "CAL_AMBIENT", + .calr = "CAL_R", + .status = "CAL_STATUS", + .checksum = "CAL_CHECKSUM", +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_calibration_controls, SND_SOC_CS35L56_SHARED); + +int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base) +{ + u64 silicon_uid; + int ret; + + /* Driver can't apply calibration to a secured part, so skip */ + if (cs35l56_base->secured) + return 0; + + ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid); + if (ret < 0) + return ret; + + ret = cs_amp_get_efi_calibration_data(cs35l56_base->dev, silicon_uid, + cs35l56_base->cal_index, + &cs35l56_base->cal_data); + + /* Only return an error status if probe should be aborted */ + if ((ret == -ENOENT) || (ret == -EOVERFLOW)) + return 0; + + if (ret < 0) + return ret; + + cs35l56_base->cal_data_valid = true; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_get_calibration, SND_SOC_CS35L56_SHARED); + int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, bool *fw_missing, unsigned int *fw_version) { @@ -693,13 +776,17 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) devid &= CS35L56_DEVID_MASK; switch (devid) { + case 0x35A54: case 0x35A56: + case 0x35A57: break; default: dev_err(cs35l56_base->dev, "Unknown device %x\n", devid); return ret; } + cs35l56_base->type = devid & 0xFF; + ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured); if (ret) { dev_err(cs35l56_base->dev, "Get Secure status failed\n"); @@ -720,8 +807,8 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) if (ret) return ret; - dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n", - cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid, + dev_info(cs35l56_base->dev, "Cirrus Logic CS35L%02X%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n", + cs35l56_base->type, cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid, fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing); /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ @@ -923,3 +1010,4 @@ 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); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 6dd0319bc843..8d2f021fb362 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -23,6 +23,7 @@ #include <linux/soundwire/sdw.h> #include <linux/types.h> #include <linux/workqueue.h> +#include <sound/cs-amp-lib.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -802,16 +803,44 @@ static struct snd_soc_dai_driver cs35l56_dai[] = { } }; +static int cs35l56_write_cal(struct cs35l56_private *cs35l56) +{ + int ret; + + if (cs35l56->base.secured || !cs35l56->base.cal_data_valid) + return -ENODATA; + + ret = wm_adsp_run(&cs35l56->dsp); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp, + &cs35l56_calibration_controls, + &cs35l56->base.cal_data); + + wm_adsp_stop(&cs35l56->dsp); + + if (ret == 0) + dev_info(cs35l56->base.dev, "Calibration applied\n"); + + return ret; +} + static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56) { int ret; /* Use wm_adsp to load and apply the firmware patch and coefficient files */ ret = wm_adsp_power_up(&cs35l56->dsp, true); - if (ret) + if (ret) { dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); - else - cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); + return; + } + + cs35l56_write_cal(cs35l56); + + /* Always REINIT after applying patch or coefficients */ + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); } static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing) @@ -874,6 +903,9 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing CS35L56_FIRMWARE_MISSING); cs35l56->base.fw_patched = true; + if (cs35l56_write_cal(cs35l56) == 0) + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); + err_unlock: mutex_unlock(&cs35l56->base.irq_lock); err: @@ -972,6 +1004,10 @@ static int cs35l56_component_probe(struct snd_soc_component *component) return -ENODEV; } + cs35l56->dsp.part = kasprintf(GFP_KERNEL, "cs35l%02x", cs35l56->base.type); + if (!cs35l56->dsp.part) + return -ENOMEM; + cs35l56->component = component; wm_adsp2_component_probe(&cs35l56->dsp, component); @@ -1002,6 +1038,9 @@ static void cs35l56_component_remove(struct snd_soc_component *component) wm_adsp2_component_remove(&cs35l56->dsp, component); + kfree(cs35l56->dsp.part); + cs35l56->dsp.part = NULL; + kfree(cs35l56->dsp.fwf_name); cs35l56->dsp.fwf_name = NULL; @@ -1221,7 +1260,12 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56) dsp = &cs35l56->dsp; cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp); - dsp->part = "cs35l56"; + + /* + * dsp->part is filled in later as it is based on the DEVID. In a + * SoundWire system that cannot be read until enumeration has occurred + * and the device has attached. + */ dsp->fw = 12; dsp->wmfw_optional = true; @@ -1356,6 +1400,7 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) init_completion(&cs35l56->init_completion); mutex_init(&cs35l56->base.irq_lock); + cs35l56->base.cal_index = -1; cs35l56->speaker_id = -ENOENT; dev_set_drvdata(cs35l56->base.dev, cs35l56); @@ -1457,6 +1502,10 @@ int cs35l56_init(struct cs35l56_private *cs35l56) if (ret) return ret; + ret = cs35l56_get_calibration(&cs35l56->base); + if (ret) + return ret; + if (!cs35l56->base.reset_gpio) { dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n"); cs35l56->soft_resetting = true; @@ -1541,6 +1590,7 @@ EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = { MODULE_DESCRIPTION("ASoC CS35L56 driver"); MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 2d11c5125f73..60d366e53526 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -2195,7 +2195,6 @@ int cs42l42_suspend(struct device *dev) /* Discharge FILT+ */ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2, CS42L42_DISCHARGE_FILT_MASK, CS42L42_DISCHARGE_FILT_MASK); - msleep(CS42L42_FILT_DISCHARGE_TIME_MS); regcache_cache_only(cs42l42->regmap, true); gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 24a598f2ed9a..901b9dbcf585 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -6,29 +6,35 @@ // Cirrus Logic International Semiconductor Ltd. #include <linux/build_bug.h> +#include <linux/completion.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/irq.h> #include <linux/jiffies.h> #include <linux/mfd/cs42l43.h> #include <linux/mfd/cs42l43-regs.h> +#include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/property.h> +#include <linux/regmap.h> +#include <linux/time.h> +#include <linux/workqueue.h> #include <sound/control.h> #include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc-component.h> +#include <sound/soc-jack.h> #include <sound/soc.h> #include "cs42l43.h" static const unsigned int cs42l43_accdet_us[] = { - 20, 100, 1000, 10000, 50000, 75000, 100000, 200000 + 20, 100, 1000, 10000, 50000, 75000, 100000, 200000, }; static const unsigned int cs42l43_accdet_db_ms[] = { - 0, 125, 250, 500, 750, 1000, 1250, 1500 + 0, 125, 250, 500, 750, 1000, 1250, 1500, }; static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 }; @@ -101,8 +107,13 @@ int cs42l43_set_jack(struct snd_soc_component *component, goto error; } - device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", - priv->buttons, ret); + ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", + priv->buttons, ret); + if (ret < 0) { + dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n", + ret); + goto error; + } } else { priv->buttons[0] = 70; priv->buttons[1] = 185; @@ -637,7 +648,7 @@ static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic) static int cs42l43_run_type_detect(struct cs42l43_codec *priv) { struct cs42l43 *cs42l43 = priv->core; - int timeout_ms = ((2 * priv->detect_us) / 1000) + 200; + int timeout_ms = ((2 * priv->detect_us) / USEC_PER_MSEC) + 200; unsigned int type = 0xff; unsigned long time_left; @@ -846,6 +857,9 @@ static const char * const cs42l43_jack_text[] = { "Line-In", "Microphone", "Optical", }; +static_assert(ARRAY_SIZE(cs42l43_jack_override_modes) == + ARRAY_SIZE(cs42l43_jack_text) - 1); + SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text); int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -868,9 +882,6 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int override = ucontrol->value.integer.value[0]; - BUILD_BUG_ON(ARRAY_SIZE(cs42l43_jack_override_modes) != - ARRAY_SIZE(cs42l43_jack_text) - 1); - if (override >= e->items) return -EINVAL; diff --git a/sound/soc/codecs/cs42l43-sdw.c b/sound/soc/codecs/cs42l43-sdw.c index 388f95853b69..60c00c05da05 100644 --- a/sound/soc/codecs/cs42l43-sdw.c +++ b/sound/soc/codecs/cs42l43-sdw.c @@ -9,6 +9,7 @@ #include <linux/mfd/cs42l43.h> #include <linux/mfd/cs42l43-regs.h> #include <linux/module.h> +#include <linux/soundwire/sdw.h> #include <sound/pcm.h> #include <sound/sdw.h> #include <sound/soc-component.h> diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index a97ccb512deb..860d5cda67bf 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -6,17 +6,27 @@ // Cirrus Logic International Semiconductor Ltd. #include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/device.h> #include <linux/err.h> #include <linux/errno.h> +#include <linux/find.h> #include <linux/gcd.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/jiffies.h> #include <linux/mfd/cs42l43.h> #include <linux/mfd/cs42l43-regs.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> #include <linux/string.h> +#include <linux/workqueue.h> #include <sound/control.h> +#include <sound/cs42l43.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc-component.h> @@ -539,23 +549,22 @@ static int cs42l43_asp_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned int mask, int *slots) +static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned long mask, + int *slots, unsigned int nslots) { - int i; + int i = 0; + int slot; - for (i = 0; i < CS42L43_ASP_MAX_CHANNELS; ++i) { - int slot = ffs(mask) - 1; - - if (slot < 0) + for_each_set_bit(slot, &mask, BITS_PER_TYPE(mask)) { + if (i == nslots) { + dev_warn(priv->dev, "Too many channels in TDM mask: %lx\n", + mask); return; + } - slots[i] = slot; - - mask &= ~(1 << slot); + slots[i++] = slot; } - if (mask) - dev_warn(priv->dev, "Too many channels in TDM mask\n"); } static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, @@ -572,8 +581,10 @@ static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mas rx_mask = CS42L43_DEFAULT_SLOTS; } - cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots); - cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots); + cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots, + ARRAY_SIZE(priv->tx_slots)); + cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots, + ARRAY_SIZE(priv->rx_slots)); return 0; } @@ -1051,12 +1062,10 @@ static int cs42l43_decim_get(struct snd_kcontrol *kcontrol, int ret; ret = cs42l43_shutter_get(priv, CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT); - if (ret < 0) - return ret; + if (ret > 0) + ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol); else if (!ret) ucontrol->value.integer.value[0] = ret; - else - ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol); return ret; } @@ -1069,12 +1078,10 @@ static int cs42l43_spk_get(struct snd_kcontrol *kcontrol, int ret; ret = cs42l43_shutter_get(priv, CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT); - if (ret < 0) - return ret; + if (ret > 0) + ret = snd_soc_get_volsw(kcontrol, ucontrol); else if (!ret) ucontrol->value.integer.value[0] = ret; - else - ret = snd_soc_get_volsw(kcontrol, ucontrol); return ret; } @@ -1331,10 +1338,9 @@ static int cs42l43_enable_pll(struct cs42l43_codec *priv) dev_dbg(priv->dev, "Enabling PLL at %uHz\n", freq); - while (freq > cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq) { - div++; - freq /= 2; - } + div = fls(freq) - + fls(cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq); + freq >>= div; if (div <= CS42L43_PLL_REFCLK_DIV_MASK) { int i; @@ -2094,8 +2100,10 @@ static int cs42l43_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, cs42l43->regmap); - cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots); - cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots); + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots, + ARRAY_SIZE(priv->tx_slots)); + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots, + ARRAY_SIZE(priv->rx_slots)); priv->component = component; priv->constraint = cs42l43_constraint; @@ -2103,10 +2111,28 @@ static int cs42l43_component_probe(struct snd_soc_component *component) return 0; } +static void cs42l43_component_remove(struct snd_soc_component *component) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + cs42l43_set_jack(priv->component, NULL, NULL); + + cancel_delayed_work_sync(&priv->bias_sense_timeout); + cancel_delayed_work_sync(&priv->tip_sense_work); + cancel_delayed_work_sync(&priv->button_press_work); + cancel_work_sync(&priv->button_release_work); + + cancel_work_sync(&priv->hp_ilimit_work); + cancel_delayed_work_sync(&priv->hp_ilimit_clear_work); + + priv->component = NULL; +} + static const struct snd_soc_component_driver cs42l43_component_drv = { .name = "cs42l43-codec", .probe = cs42l43_component_probe, + .remove = cs42l43_component_remove, .set_sysclk = cs42l43_set_sysclk, .set_jack = cs42l43_set_jack, @@ -2387,7 +2413,7 @@ MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); static struct platform_driver cs42l43_codec_driver = { .driver = { .name = "cs42l43-codec", - .pm = &cs42l43_codec_pm_ops, + .pm = pm_ptr(&cs42l43_codec_pm_ops), }, .probe = cs42l43_codec_probe, diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index 125e36861d5d..9924c13e1eb5 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -6,19 +6,14 @@ * Cirrus Logic International Semiconductor Ltd. */ -#include <linux/clk.h> +#ifndef CS42L43_ASOC_INT_H +#define CS42L43_ASOC_INT_H + #include <linux/completion.h> -#include <linux/device.h> #include <linux/mutex.h> -#include <linux/regmap.h> -#include <linux/soundwire/sdw.h> #include <linux/types.h> -#include <sound/cs42l43.h> +#include <linux/workqueue.h> #include <sound/pcm.h> -#include <sound/soc-jack.h> - -#ifndef CS42L43_ASOC_INT_H -#define CS42L43_ASOC_INT_H #define CS42L43_INTERNAL_SYSCLK 24576000 #define CS42L43_DEFAULT_SLOTS 0x3F @@ -37,6 +32,14 @@ #define CS42L43_N_BUTTONS 6 +struct clk; +struct device; + +struct snd_soc_component; +struct snd_soc_jack; + +struct cs42l43; + struct cs42l43_codec { struct device *dev; struct cs42l43 *core; diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 0e5c527687a2..369c62078780 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -2101,18 +2101,14 @@ static int da7213_probe(struct snd_soc_component *component) pm_runtime_put_sync(component->dev); /* Check if MCLK provided */ - da7213->mclk = devm_clk_get(component->dev, "mclk"); - if (IS_ERR(da7213->mclk)) { - if (PTR_ERR(da7213->mclk) != -ENOENT) - return PTR_ERR(da7213->mclk); - else - da7213->mclk = NULL; - } else { + da7213->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(da7213->mclk)) + return PTR_ERR(da7213->mclk); + if (da7213->mclk) /* Do automatic PLL handling assuming fixed clock until * set_pll() has been called. This makes the codec usable * with the simple-audio-card driver. */ da7213->fixed_clk_auto_pll = true; - } /* Default infinite tone gen, start/stop by Kcontrol */ snd_soc_component_write(component, DA7213_TONE_GEN_CYCLES, DA7213_BEEP_CYCLES_MASK); diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index cbcd02ec6ba4..15289dadafea 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -31,11 +31,11 @@ struct es8326_priv { * while enabling or disabling or during an irq. */ struct mutex lock; - u8 mic1_src; - u8 mic2_src; u8 jack_pol; u8 interrupt_src; u8 interrupt_clk; + u8 hpl_vol; + u8 hpr_vol; bool jd_inverted; unsigned int sysclk; @@ -121,6 +121,72 @@ static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol, return 0; } +static int es8326_hplvol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8326->hpl_vol; + + return 0; +} + +static int es8326_hplvol_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int hp_vol; + + hp_vol = ucontrol->value.integer.value[0]; + if (hp_vol > 5) + return -EINVAL; + if (es8326->hpl_vol != hp_vol) { + es8326->hpl_vol = hp_vol; + if (hp_vol >= 3) + hp_vol++; + regmap_update_bits(es8326->regmap, ES8326_HP_VOL, + 0x70, (hp_vol << 4)); + return 1; + } + + return 0; +} + +static int es8326_hprvol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8326->hpr_vol; + + return 0; +} + +static int es8326_hprvol_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int hp_vol; + + hp_vol = ucontrol->value.integer.value[0]; + if (hp_vol > 5) + return -EINVAL; + if (es8326->hpr_vol != hp_vol) { + es8326->hpr_vol = hp_vol; + if (hp_vol >= 3) + hp_vol++; + regmap_update_bits(es8326->regmap, ES8326_HP_VOL, + 0x07, hp_vol); + return 1; + } + + return 0; +} + static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0); @@ -151,15 +217,24 @@ static const char *const winsize[] = { static const char *const dacpol_txt[] = { "Normal", "R Invert", "L Invert", "L + R Invert" }; +static const char *const hp_spkvol_switch[] = { + "HPVOL: HPL+HPL, SPKVOL: HPL+HPL", + "HPVOL: HPL+HPR, SPKVOL: HPL+HPR", + "HPVOL: HPL+HPL, SPKVOL: SPKL+SPKR", + "HPVOL: HPL+HPR, SPKVOL: SPKL+SPKR", +}; + static const struct soc_enum dacpol = SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt); static const struct soc_enum alc_winsize = SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize); static const struct soc_enum drc_winsize = SOC_ENUM_SINGLE(ES8326_DRC_WINSIZE, 4, 16, winsize); +static const struct soc_enum hpvol_spkvol_switch = + SOC_ENUM_SINGLE(ES8326_HP_MISC, 6, 4, hp_spkvol_switch); static const struct snd_kcontrol_new es8326_snd_controls[] = { - SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv), SOC_ENUM("Playback Polarity", dacpol), SOC_SINGLE_TLV("DAC Ramp Rate", ES8326_DAC_RAMPRATE, 0, 0x0f, 0, softramp_rate), SOC_SINGLE_TLV("DRC Recovery Level", ES8326_DRC_RECOVERY, 0, 4, 0, drc_recovery_tlv), @@ -182,6 +257,17 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = { es8326_crosstalk1_get, es8326_crosstalk1_set), SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0, es8326_crosstalk2_get, es8326_crosstalk2_set), + SOC_SINGLE_EXT("HPL Volume", SND_SOC_NOPM, 0, 5, 0, + es8326_hplvol_get, es8326_hplvol_set), + SOC_SINGLE_EXT("HPR Volume", SND_SOC_NOPM, 0, 5, 0, + es8326_hprvol_get, es8326_hprvol_set), + + SOC_SINGLE_TLV("HPL Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("HPR Playback Volume", ES8326_DACR_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("SPKL Playback Volume", ES8326_SPKL_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("SPKR Playback Volume", ES8326_SPKR_VOL, 0, 0xbf, 0, dac_vol_tlv), + + SOC_ENUM("HPVol SPKVol Switch", hpvol_spkvol_switch), }; static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { @@ -972,6 +1058,8 @@ static int es8326_resume(struct snd_soc_component *component) es8326->jack_remove_retry = 0; es8326->hp = 0; + es8326->hpl_vol = 0x03; + es8326->hpr_vol = 0x03; return 0; } @@ -1002,20 +1090,6 @@ static int es8326_probe(struct snd_soc_component *component) es8326->jd_inverted = device_property_read_bool(component->dev, "everest,jack-detect-inverted"); - ret = device_property_read_u8(component->dev, "everest,mic1-src", &es8326->mic1_src); - if (ret != 0) { - dev_dbg(component->dev, "mic1-src return %d", ret); - es8326->mic1_src = ES8326_ADC_AMIC; - } - dev_dbg(component->dev, "mic1-src %x", es8326->mic1_src); - - ret = device_property_read_u8(component->dev, "everest,mic2-src", &es8326->mic2_src); - if (ret != 0) { - dev_dbg(component->dev, "mic2-src return %d", ret); - es8326->mic2_src = ES8326_ADC_DMIC; - } - dev_dbg(component->dev, "mic2-src %x", es8326->mic2_src); - ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol); if (ret != 0) { dev_dbg(component->dev, "jack-pol return %d", ret); @@ -1035,7 +1109,7 @@ static int es8326_probe(struct snd_soc_component *component) &es8326->interrupt_clk); if (ret != 0) { dev_dbg(component->dev, "interrupt-clk return %d", ret); - es8326->interrupt_clk = 0x45; + es8326->interrupt_clk = 0x00; } dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk); diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index 4234bbb900c4..ee12caef8105 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -69,7 +69,7 @@ #define ES8326_DAC_DSM 0x4D #define ES8326_DAC_RAMPRATE 0x4E #define ES8326_DAC_VPPSCALE 0x4F -#define ES8326_DAC_VOL 0x50 +#define ES8326_DACL_VOL 0x50 #define ES8326_DRC_RECOVERY 0x53 #define ES8326_DRC_WINSIZE 0x54 #define ES8326_DAC_CROSSTALK 0x55 @@ -81,6 +81,9 @@ #define ES8326_SDINOUT23_IO 0x5B #define ES8326_JACK_PULSE 0x5C +#define ES8326_DACR_VOL 0xF4 +#define ES8326_SPKL_VOL 0xF5 +#define ES8326_SPKR_VOL 0xF6 #define ES8326_HP_MISC 0xF7 #define ES8326_CTIA_OMTP_STA 0xF8 #define ES8326_PULLUP_CTL 0xF9 diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c new file mode 100644 index 000000000000..e5fcde9ee308 --- /dev/null +++ b/sound/soc/codecs/framer-codec.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Framer ALSA SoC driver +// +// Copyright 2023 CS GROUP France +// +// Author: Herve Codina <herve.codina@bootlin.com> + +#include <linux/clk.h> +#include <linux/framer/framer.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define FRAMER_NB_CHANNEL 32 +#define FRAMER_JACK_MASK (SND_JACK_LINEIN | SND_JACK_LINEOUT) + +struct framer_codec { + struct framer *framer; + struct device *dev; + struct snd_soc_jack jack; + struct notifier_block nb; + struct work_struct carrier_work; + int max_chan_playback; + int max_chan_capture; +}; + +static int framer_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + switch (width) { + case 0: + /* Not set -> default 8 */ + case 8: + break; + default: + dev_err(dai->dev, "tdm slot width %d not supported\n", width); + return -EINVAL; + } + + framer->max_chan_playback = hweight32(tx_mask); + if (framer->max_chan_playback > FRAMER_NB_CHANNEL) { + dev_err(dai->dev, "too many tx slots defined (mask = 0x%x) supported max %d\n", + tx_mask, FRAMER_NB_CHANNEL); + return -EINVAL; + } + + framer->max_chan_capture = hweight32(rx_mask); + if (framer->max_chan_capture > FRAMER_NB_CHANNEL) { + dev_err(dai->dev, "too many rx slots defined (mask = 0x%x) supported max %d\n", + rx_mask, FRAMER_NB_CHANNEL); + return -EINVAL; + } + + return 0; +} + +/* + * The constraints for format/channel is to match with the number of 8bit + * time-slots available. + */ +static int framer_dai_hw_rule_channels_by_format(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, + unsigned int nb_ts) +{ + struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_pcm_format_t format = params_format(params); + struct snd_interval ch = {0}; + int width; + + width = snd_pcm_format_physical_width(format); + if (width == 8 || width == 16 || width == 32 || width == 64) { + ch.max = nb_ts * 8 / width; + } else { + dev_err(dai->dev, "format physical width %d not supported\n", width); + return -EINVAL; + } + + ch.min = ch.max ? 1 : 0; + + return snd_interval_refine(c, &ch); +} + +static int framer_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_playback); +} + +static int framer_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_capture); +} + +static int framer_dai_hw_rule_format_by_channels(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, + unsigned int nb_ts) +{ + struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + unsigned int channels = params_channels(params); + unsigned int slot_width; + snd_pcm_format_t format; + struct snd_mask f_new; + + if (!channels || channels > nb_ts) { + dev_err(dai->dev, "channels %u not supported\n", nb_ts); + return -EINVAL; + } + + slot_width = (nb_ts / channels) * 8; + + snd_mask_none(&f_new); + pcm_for_each_format(format) { + if (snd_mask_test_format(f_old, format)) { + if (snd_pcm_format_physical_width(format) <= slot_width) + snd_mask_set_format(&f_new, format); + } + } + + return snd_mask_refine(f_old, &f_new); +} + +static int framer_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_playback); +} + +static int framer_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_capture); +} + +static u64 framer_formats(u8 nb_ts) +{ + unsigned int format_width; + unsigned int chan_width; + snd_pcm_format_t format; + u64 formats_mask; + + if (!nb_ts) + return 0; + + formats_mask = 0; + chan_width = nb_ts * 8; + pcm_for_each_format(format) { + /* Support physical width multiple of 8bit */ + format_width = snd_pcm_format_physical_width(format); + if (format_width == 0 || format_width % 8) + continue; + + /* + * And support physical width that can fit N times in the + * channel + */ + if (format_width > chan_width || chan_width % format_width) + continue; + + formats_mask |= pcm_format_to_bits(format); + } + return formats_mask; +} + +static int framer_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + snd_pcm_hw_rule_func_t hw_rule_channels_by_format; + snd_pcm_hw_rule_func_t hw_rule_format_by_channels; + unsigned int frame_bits; + u64 format; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + format = framer_formats(framer->max_chan_capture); + hw_rule_channels_by_format = framer_dai_hw_rule_capture_channels_by_format; + hw_rule_format_by_channels = framer_dai_hw_rule_capture_format_by_channels; + frame_bits = framer->max_chan_capture * 8; + } else { + format = framer_formats(framer->max_chan_playback); + hw_rule_channels_by_format = framer_dai_hw_rule_playback_channels_by_format; + hw_rule_format_by_channels = framer_dai_hw_rule_playback_format_by_channels; + frame_bits = framer->max_chan_playback * 8; + } + + ret = snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, format); + if (ret) { + dev_err(dai->dev, "Failed to add format constraint (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels_by_format, dai, + SNDRV_PCM_HW_PARAM_FORMAT, -1); + if (ret) { + dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format_by_channels, dai, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret) { + dev_err(dai->dev, "Failed to add format rule (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_FRAME_BITS, + frame_bits); + if (ret < 0) { + dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret); + return ret; + } + + return 0; +} + +static u64 framer_dai_formats[] = { + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops framer_dai_ops = { + .startup = framer_dai_startup, + .set_tdm_slot = framer_dai_set_tdm_slot, + .auto_selectable_formats = framer_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(framer_dai_formats), +}; + +static struct snd_soc_dai_driver framer_dai_driver = { + .name = "framer", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = FRAMER_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = U64_MAX, /* Will be refined on DAI .startup() */ + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = FRAMER_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = U64_MAX, /* Will be refined on DAI .startup() */ + }, + .ops = &framer_dai_ops, +}; + +static void framer_carrier_work(struct work_struct *work) +{ + struct framer_codec *framer = container_of(work, struct framer_codec, carrier_work); + struct framer_status framer_status; + int jack_status; + int ret; + + ret = framer_get_status(framer->framer, &framer_status); + if (ret) { + dev_err(framer->dev, "get framer status failed (%d)\n", ret); + return; + } + + jack_status = framer_status.link_is_on ? FRAMER_JACK_MASK : 0; + snd_soc_jack_report(&framer->jack, jack_status, FRAMER_JACK_MASK); +} + +static int framer_carrier_notifier(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct framer_codec *framer = container_of(nb, struct framer_codec, nb); + + switch (action) { + case FRAMER_EVENT_STATUS: + queue_work(system_power_efficient_wq, &framer->carrier_work); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static int framer_component_probe(struct snd_soc_component *component) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(component); + struct framer_status status; + char *name; + int ret; + + INIT_WORK(&framer->carrier_work, framer_carrier_work); + + name = "carrier"; + if (component->name_prefix) { + name = kasprintf(GFP_KERNEL, "%s carrier", component->name_prefix); + if (!name) + return -ENOMEM; + } + + ret = snd_soc_card_jack_new(component->card, name, FRAMER_JACK_MASK, &framer->jack); + if (component->name_prefix) + kfree(name); /* A copy is done by snd_soc_card_jack_new */ + if (ret) { + dev_err(component->dev, "Cannot create jack\n"); + return ret; + } + + ret = framer_init(framer->framer); + if (ret) { + dev_err(component->dev, "framer init failed (%d)\n", ret); + return ret; + } + + ret = framer_power_on(framer->framer); + if (ret) { + dev_err(component->dev, "framer power-on failed (%d)\n", ret); + goto framer_exit; + } + + /* Be sure that get_status is supported */ + ret = framer_get_status(framer->framer, &status); + if (ret) { + dev_err(component->dev, "get framer status failed (%d)\n", ret); + goto framer_power_off; + } + + framer->nb.notifier_call = framer_carrier_notifier; + ret = framer_notifier_register(framer->framer, &framer->nb); + if (ret) { + dev_err(component->dev, "Cannot register event notifier\n"); + goto framer_power_off; + } + + /* Queue work to set the initial value */ + queue_work(system_power_efficient_wq, &framer->carrier_work); + + return 0; + +framer_power_off: + framer_power_off(framer->framer); +framer_exit: + framer_exit(framer->framer); + return ret; +} + +static void framer_component_remove(struct snd_soc_component *component) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(component); + + framer_notifier_unregister(framer->framer, &framer->nb); + cancel_work_sync(&framer->carrier_work); + framer_power_off(framer->framer); + framer_exit(framer->framer); +} + +static const struct snd_soc_component_driver framer_component_driver = { + .probe = framer_component_probe, + .remove = framer_component_remove, + .endianness = 1, +}; + +static int framer_codec_probe(struct platform_device *pdev) +{ + struct framer_codec *framer; + + framer = devm_kzalloc(&pdev->dev, sizeof(*framer), GFP_KERNEL); + if (!framer) + return -ENOMEM; + + framer->dev = &pdev->dev; + + /* Get framer from parents node */ + framer->framer = devm_framer_get(&pdev->dev, NULL); + if (IS_ERR(framer->framer)) + return dev_err_probe(&pdev->dev, PTR_ERR(framer->framer), "get framer failed\n"); + + platform_set_drvdata(pdev, framer); + + return devm_snd_soc_register_component(&pdev->dev, &framer_component_driver, + &framer_dai_driver, 1); +} + +static struct platform_driver framer_codec_driver = { + .driver = { + .name = "framer-codec", + }, + .probe = framer_codec_probe, +}; +module_platform_driver(framer_codec_driver); + +MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); +MODULE_DESCRIPTION("FRAMER ALSA SoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index d2117e36ddd1..5a58723dc0e9 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -198,19 +198,19 @@ static int hda_codec_probe(struct snd_soc_component *component) ret = snd_hda_codec_device_new(codec->bus, component->card->snd_card, hdev->addr, codec, false); if (ret < 0) { - dev_err(&hdev->dev, "create hda codec failed: %d\n", ret); + dev_err(&hdev->dev, "codec create failed: %d\n", ret); goto device_new_err; } ret = snd_hda_codec_set_name(codec, codec->preset->name); if (ret < 0) { - dev_err(&hdev->dev, "name failed %s\n", codec->preset->name); + dev_err(&hdev->dev, "set name: %s failed: %d\n", codec->preset->name, ret); goto err; } ret = snd_hdac_regmap_init(&codec->core); if (ret < 0) { - dev_err(&hdev->dev, "regmap init failed\n"); + dev_err(&hdev->dev, "regmap init failed: %d\n", ret); goto err; } @@ -223,13 +223,13 @@ static int hda_codec_probe(struct snd_soc_component *component) ret = patch(codec); if (ret < 0) { - dev_err(&hdev->dev, "patch failed %d\n", ret); + dev_err(&hdev->dev, "codec init failed: %d\n", ret); goto err; } ret = snd_hda_codec_parse_pcms(codec); if (ret < 0) { - dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret); + dev_err(&hdev->dev, "unable to map pcms to dai: %d\n", ret); goto parse_pcms_err; } @@ -350,6 +350,11 @@ static int hda_hdev_attach(struct hdac_device *hdev) struct hda_codec *codec = dev_to_hda_codec(&hdev->dev); struct snd_soc_component_driver *comp_drv; + if (hda_codec_is_display(codec) && !hdev->bus->audio_component) { + dev_dbg(&hdev->dev, "no i915, skip registration for 0x%08x\n", hdev->vendor_id); + return -ENODEV; + } + comp_drv = devm_kzalloc(&hdev->dev, sizeof(*comp_drv), GFP_KERNEL); if (!comp_drv) return -ENOMEM; diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h index d3684c7ab930..d98718b3dc4b 100644 --- a/sound/soc/codecs/lpass-macro-common.h +++ b/sound/soc/codecs/lpass-macro-common.h @@ -11,6 +11,13 @@ /* The soundwire block should be internally reset at probe */ #define LPASS_MACRO_FLAG_RESET_SWR BIT(1) +enum lpass_version { + LPASS_VER_9_0_0, + LPASS_VER_9_2_0, + LPASS_VER_10_0_0, + LPASS_VER_11_0_0, +}; + struct lpass_macro { struct device *macro_pd; struct device *dcodec_pd; diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 124c2e144f33..c98b0b747a92 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -38,6 +38,8 @@ #define CDC_TX_TOP_CSR_I2S_RESET (0x00AC) #define CDC_TX_TOP_CSR_SWR_DMICn_CTL(n) (0x00C0 + n * 0x4) #define CDC_TX_TOP_CSR_SWR_DMIC0_CTL (0x00C0) +/* Default divider for AMIC and DMIC clock: DIV2 */ +#define CDC_TX_SWR_MIC_CLK_DEFAULT 0 #define CDC_TX_SWR_DMIC_CLK_SEL_MASK GENMASK(3, 1) #define CDC_TX_TOP_CSR_SWR_DMIC1_CTL (0x00C4) #define CDC_TX_TOP_CSR_SWR_DMIC2_CTL (0x00C8) @@ -253,8 +255,18 @@ struct hpf_work { struct delayed_work dwork; }; +struct tx_macro_data { + unsigned int flags; + unsigned int ver; + const struct snd_soc_dapm_widget *extra_widgets; + size_t extra_widgets_num; + const struct snd_soc_dapm_route *extra_routes; + size_t extra_routes_num; +}; + struct tx_macro { struct device *dev; + const struct tx_macro_data *data; struct snd_soc_component *component; struct hpf_work tx_hpf_work[NUM_DECIMATORS]; struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS]; @@ -270,7 +282,6 @@ struct tx_macro { struct clk_hw hw; bool dec_active[NUM_DECIMATORS]; int tx_mclk_users; - u16 dmic_clk_div; bool bcs_enable; int dec_mode[NUM_DECIMATORS]; struct lpass_macro *pds; @@ -431,6 +442,8 @@ static bool tx_is_volatile_register(struct device *dev, unsigned int reg) case CDC_TX_TOP_CSR_SWR_DMIC1_CTL: case CDC_TX_TOP_CSR_SWR_DMIC2_CTL: case CDC_TX_TOP_CSR_SWR_DMIC3_CTL: + case CDC_TX_TOP_CSR_SWR_AMIC0_CTL: + case CDC_TX_TOP_CSR_SWR_AMIC1_CTL: return true; } return false; @@ -635,13 +648,18 @@ exit: return 0; } -static bool is_amic_enabled(struct snd_soc_component *component, u8 decimator) +static bool is_amic_enabled(struct snd_soc_component *component, + struct tx_macro *tx, u8 decimator) { u16 adc_mux_reg, adc_reg, adc_n; adc_mux_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG1(decimator); if (snd_soc_component_read(component, adc_mux_reg) & SWR_MIC) { + if (tx->data->ver > LPASS_VER_9_0_0) + return true; + + /* else: LPASS <= v9.0.0 */ adc_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG0(decimator); adc_n = snd_soc_component_read_field(component, adc_reg, CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK); @@ -670,7 +688,7 @@ static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work) dec_cfg_reg = CDC_TXn_TX_PATH_CFG0(hpf_work->decimator); hpf_gate_reg = CDC_TXn_TX_PATH_SEC2(hpf_work->decimator); - if (is_amic_enabled(component, hpf_work->decimator)) { + if (is_amic_enabled(component, tx, hpf_work->decimator)) { snd_soc_component_write_field(component, dec_cfg_reg, CDC_TXn_HPF_CUT_FREQ_MASK, @@ -734,16 +752,61 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, return 0; } +static void tx_macro_update_smic_sel_v9(struct snd_soc_component *component, + struct snd_soc_dapm_widget *widget, + struct tx_macro *tx, u16 mic_sel_reg, + unsigned int val) +{ + unsigned int dmic; + u16 dmic_clk_reg; + + if (val < 5) { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 0); + } else { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 1); + dmic = TX_ADC_TO_DMIC(val); + dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); + snd_soc_component_write_field(component, dmic_clk_reg, + CDC_TX_SWR_DMIC_CLK_SEL_MASK, + CDC_TX_SWR_MIC_CLK_DEFAULT); + } +} + +static void tx_macro_update_smic_sel_v9_2(struct snd_soc_component *component, + struct snd_soc_dapm_widget *widget, + struct tx_macro *tx, u16 mic_sel_reg, + unsigned int val) +{ + unsigned int dmic; + u16 dmic_clk_reg; + + if (widget->shift) { + /* MSM DMIC */ + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 1); + + dmic = TX_ADC_TO_DMIC(val); + dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); + snd_soc_component_write_field(component, dmic_clk_reg, + CDC_TX_SWR_DMIC_CLK_SEL_MASK, + CDC_TX_SWR_MIC_CLK_DEFAULT); + } else { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 0); + } +} + static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, dmic; - u16 mic_sel_reg; - u16 dmic_clk_reg; struct tx_macro *tx = snd_soc_component_get_drvdata(component); + unsigned int val; + u16 mic_sel_reg; val = ucontrol->value.enumerated.item[0]; if (val >= e->items) @@ -780,21 +843,15 @@ static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, } if (val != 0) { - if (widget->shift) { /* MSM DMIC */ - snd_soc_component_write_field(component, mic_sel_reg, - CDC_TXn_ADC_DMIC_SEL_MASK, 1); - } else if (val < 5) { - snd_soc_component_write_field(component, mic_sel_reg, - CDC_TXn_ADC_DMIC_SEL_MASK, 0); - } else { + if (widget->shift) /* MSM DMIC */ snd_soc_component_write_field(component, mic_sel_reg, CDC_TXn_ADC_DMIC_SEL_MASK, 1); - dmic = TX_ADC_TO_DMIC(val); - dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); - snd_soc_component_write_field(component, dmic_clk_reg, - CDC_TX_SWR_DMIC_CLK_SEL_MASK, - tx->dmic_clk_div); - } + else if (tx->data->ver <= LPASS_VER_9_0_0) + tx_macro_update_smic_sel_v9(component, widget, tx, + mic_sel_reg, val); + else + tx_macro_update_smic_sel_v9_2(component, widget, tx, + mic_sel_reg, val); } return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); @@ -882,7 +939,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, snd_soc_component_write_field(component, dmic_clk_reg, CDC_TX_SWR_DMIC_CLK_SEL_MASK, - tx->dmic_clk_div); + CDC_TX_SWR_MIC_CLK_DEFAULT); } } snd_soc_component_write_field(component, dec_cfg_reg, @@ -895,7 +952,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: snd_soc_component_write_field(component, tx_vol_ctl_reg, CDC_TXn_CLK_EN_MASK, 0x1); - if (!is_amic_enabled(component, decimator)) { + if (!is_amic_enabled(component, tx, decimator)) { snd_soc_component_update_bits(component, hpf_gate_reg, 0x01, 0x00); /* Minimum 1 clk cycle delay is required as per HW spec */ usleep_range(1000, 1010); @@ -911,7 +968,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, CDC_TXn_HPF_CUT_FREQ_MASK, CF_MIN_3DB_150HZ); - if (is_amic_enabled(component, decimator)) { + if (is_amic_enabled(component, tx, decimator)) { hpf_delay = TX_MACRO_AMIC_HPF_DELAY_MS; unmute_delay = TX_MACRO_AMIC_UNMUTE_DELAY_MS; } @@ -927,7 +984,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, CDC_TXn_HPF_F_CHANGE_MASK | CDC_TXn_HPF_ZERO_GATE_MASK, 0x02); - if (!is_amic_enabled(component, decimator)) + if (!is_amic_enabled(component, tx, decimator)) snd_soc_component_update_bits(component, hpf_gate_reg, CDC_TXn_HPF_F_CHANGE_MASK | CDC_TXn_HPF_ZERO_GATE_MASK, @@ -964,7 +1021,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, component, dec_cfg_reg, CDC_TXn_HPF_CUT_FREQ_MASK, hpf_cut_off_freq); - if (is_amic_enabled(component, decimator)) + if (is_amic_enabled(component, tx, decimator)) snd_soc_component_update_bits(component, hpf_gate_reg, CDC_TXn_HPF_F_CHANGE_MASK | @@ -1235,53 +1292,6 @@ static const struct snd_kcontrol_new tx_dec5_mux = SOC_DAPM_ENUM("tx_dec5", tx_d static const struct snd_kcontrol_new tx_dec6_mux = SOC_DAPM_ENUM("tx_dec6", tx_dec6_enum); static const struct snd_kcontrol_new tx_dec7_mux = SOC_DAPM_ENUM("tx_dec7", tx_dec7_enum); -static const char * const smic_mux_text[] = { - "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0", - "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4", - "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7" -}; - -static SOC_ENUM_SINGLE_DECL(tx_smic0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG0, - 0, smic_mux_text); - -static const struct snd_kcontrol_new tx_smic0_mux = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic1_mux = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic2_mux = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic3_mux = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic4_mux = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic5_mux = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic6_mux = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic7_mux = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); - static const char * const dmic_mux_text[] = { "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6", "DMIC7" @@ -1427,15 +1437,6 @@ static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = { SND_SOC_DAPM_MIXER("TX_AIF3_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF3_CAP, 0, tx_aif3_cap_mixer, ARRAY_SIZE(tx_aif3_cap_mixer)), - SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux), - SND_SOC_DAPM_MUX("TX DMIC MUX0", SND_SOC_NOPM, 4, 0, &tx_dmic0_mux), SND_SOC_DAPM_MUX("TX DMIC MUX1", SND_SOC_NOPM, 4, 0, &tx_dmic1_mux), SND_SOC_DAPM_MUX("TX DMIC MUX2", SND_SOC_NOPM, 4, 0, &tx_dmic2_mux), @@ -1445,18 +1446,6 @@ static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = { SND_SOC_DAPM_MUX("TX DMIC MUX6", SND_SOC_NOPM, 4, 0, &tx_dmic6_mux), SND_SOC_DAPM_MUX("TX DMIC MUX7", SND_SOC_NOPM, 4, 0, &tx_dmic7_mux), - SND_SOC_DAPM_INPUT("TX SWR_ADC0"), - SND_SOC_DAPM_INPUT("TX SWR_ADC1"), - SND_SOC_DAPM_INPUT("TX SWR_ADC2"), - SND_SOC_DAPM_INPUT("TX SWR_ADC3"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC0"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC1"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC2"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC3"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC4"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC5"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC6"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC7"), SND_SOC_DAPM_INPUT("TX DMIC0"), SND_SOC_DAPM_INPUT("TX DMIC1"), SND_SOC_DAPM_INPUT("TX DMIC2"), @@ -1578,6 +1567,150 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX DMIC MUX0", "DMIC6", "TX DMIC6"}, {"TX DMIC MUX0", "DMIC7", "TX DMIC7"}, + {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"}, + {"TX DMIC MUX1", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX1", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX1", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX1", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX1", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX1", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX1", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX1", "DMIC7", "TX DMIC7"}, + + {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"}, + {"TX DMIC MUX2", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX2", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX2", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX2", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX2", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX2", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX2", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX2", "DMIC7", "TX DMIC7"}, + + {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"}, + {"TX DMIC MUX3", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX3", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX3", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX3", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX3", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX3", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX3", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX3", "DMIC7", "TX DMIC7"}, + + {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"}, + {"TX DMIC MUX4", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX4", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX4", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX4", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX4", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX4", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX4", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX4", "DMIC7", "TX DMIC7"}, + + {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"}, + {"TX DMIC MUX5", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX5", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX5", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX5", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX5", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX5", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX5", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX5", "DMIC7", "TX DMIC7"}, + + {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"}, + {"TX DMIC MUX6", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX6", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX6", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX6", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX6", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX6", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX6", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX6", "DMIC7", "TX DMIC7"}, + + {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"}, + {"TX DMIC MUX7", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX7", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX7", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX7", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX7", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX7", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX7", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX7", "DMIC7", "TX DMIC7"}, +}; + +/* Controls and routes specific to LPASS <= v9.0.0 */ +static const char * const smic_mux_text_v9[] = { + "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0", + "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4", + "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7" +}; + +static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9, CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9, CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9, CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9, CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9, CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9, CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9, CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9, CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 0, smic_mux_text_v9); + +static const struct snd_kcontrol_new tx_smic0_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic1_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic2_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic3_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic4_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic5_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic6_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic7_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); + +static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9[] = { + SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9), + + SND_SOC_DAPM_INPUT("TX SWR_ADC0"), + SND_SOC_DAPM_INPUT("TX SWR_ADC1"), + SND_SOC_DAPM_INPUT("TX SWR_ADC2"), + SND_SOC_DAPM_INPUT("TX SWR_ADC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC0"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC1"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC2"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC4"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC5"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC6"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC7"), +}; + +static const struct snd_soc_dapm_route tx_audio_map_v9[] = { {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"}, {"TX SMIC MUX0", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX0", "ADC0", "TX SWR_ADC0"}, @@ -1593,16 +1726,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX0", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX0", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"}, - {"TX DMIC MUX1", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX1", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX1", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX1", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX1", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX1", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX1", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX1", "DMIC7", "TX DMIC7"}, - {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"}, {"TX SMIC MUX1", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX1", "ADC0", "TX SWR_ADC0"}, @@ -1618,16 +1741,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX1", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX1", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"}, - {"TX DMIC MUX2", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX2", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX2", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX2", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX2", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX2", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX2", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX2", "DMIC7", "TX DMIC7"}, - {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"}, {"TX SMIC MUX2", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX2", "ADC0", "TX SWR_ADC0"}, @@ -1643,16 +1756,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX2", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX2", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"}, - {"TX DMIC MUX3", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX3", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX3", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX3", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX3", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX3", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX3", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX3", "DMIC7", "TX DMIC7"}, - {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"}, {"TX SMIC MUX3", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX3", "ADC0", "TX SWR_ADC0"}, @@ -1668,16 +1771,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX3", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX3", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"}, - {"TX DMIC MUX4", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX4", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX4", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX4", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX4", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX4", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX4", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX4", "DMIC7", "TX DMIC7"}, - {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"}, {"TX SMIC MUX4", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX4", "ADC0", "TX SWR_ADC0"}, @@ -1693,16 +1786,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX4", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX4", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"}, - {"TX DMIC MUX5", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX5", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX5", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX5", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX5", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX5", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX5", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX5", "DMIC7", "TX DMIC7"}, - {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"}, {"TX SMIC MUX5", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX5", "ADC0", "TX SWR_ADC0"}, @@ -1718,16 +1801,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX5", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX5", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"}, - {"TX DMIC MUX6", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX6", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX6", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX6", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX6", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX6", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX6", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX6", "DMIC7", "TX DMIC7"}, - {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"}, {"TX SMIC MUX6", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX6", "ADC0", "TX SWR_ADC0"}, @@ -1743,16 +1816,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX6", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX6", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"}, - {"TX DMIC MUX7", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX7", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX7", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX7", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX7", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX7", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX7", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX7", "DMIC7", "TX DMIC7"}, - {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"}, {"TX SMIC MUX7", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX7", "ADC0", "TX SWR_ADC0"}, @@ -1769,6 +1832,200 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX7", "SWR_DMIC7", "TX SWR_DMIC7"}, }; +/* Controls and routes specific to LPASS >= v9.2.0 */ +static const char * const smic_mux_text_v9_2[] = { + "ZERO", "SWR_MIC0", "SWR_MIC1", "SWR_MIC2", "SWR_MIC3", + "SWR_MIC4", "SWR_MIC5", "SWR_MIC6", "SWR_MIC7", + "SWR_MIC8", "SWR_MIC9", "SWR_MIC10", "SWR_MIC11" +}; + +static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 0, smic_mux_text_v9_2); + +static const struct snd_kcontrol_new tx_smic0_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic1_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic2_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic3_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic4_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic5_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic6_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic7_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); + +static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9_2[] = { + SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9_2), + + SND_SOC_DAPM_INPUT("TX SWR_INPUT0"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT1"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT2"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT3"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT4"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT5"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT6"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT7"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT8"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT9"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT10"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT11"), +}; + +static const struct snd_soc_dapm_route tx_audio_map_v9_2[] = { + {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"}, + {"TX SMIC MUX0", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX0", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX0", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX0", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX0", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX0", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX0", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX0", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX0", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX0", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX0", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX0", "SWR_MIC10", "TX SWR_INPUT11"}, + {"TX SMIC MUX0", "SWR_MIC11", "TX SWR_INPUT10"}, + + {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"}, + {"TX SMIC MUX1", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX1", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX1", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX1", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX1", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX1", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX1", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX1", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX1", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX1", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX1", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX1", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX1", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"}, + {"TX SMIC MUX2", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX2", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX2", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX2", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX2", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX2", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX2", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX2", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX2", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX2", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX2", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX2", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX2", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"}, + {"TX SMIC MUX3", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX3", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX3", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX3", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX3", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX3", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX3", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX3", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX3", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX3", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX3", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX3", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX3", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"}, + {"TX SMIC MUX4", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX4", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX4", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX4", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX4", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX4", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX4", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX4", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX4", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX4", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX4", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX4", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX4", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"}, + {"TX SMIC MUX5", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX5", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX5", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX5", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX5", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX5", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX5", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX5", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX5", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX5", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX5", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX5", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX5", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"}, + {"TX SMIC MUX6", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX6", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX6", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX6", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX6", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX6", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX6", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX6", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX6", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX6", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX6", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX6", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX6", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"}, + {"TX SMIC MUX7", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX7", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX7", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX7", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX7", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX7", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX7", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX7", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX7", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX7", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX7", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX7", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX7", "SWR_MIC11", "TX SWR_INPUT11"}, +}; + static const struct snd_kcontrol_new tx_macro_snd_controls[] = { SOC_SINGLE_S8_TLV("TX_DEC0 Volume", CDC_TX0_TX_VOL_CTL, @@ -1823,10 +2080,41 @@ static const struct snd_kcontrol_new tx_macro_snd_controls[] = { tx_macro_get_bcs, tx_macro_set_bcs), }; +static int tx_macro_component_extend(struct snd_soc_component *comp) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(comp); + struct tx_macro *tx = snd_soc_component_get_drvdata(comp); + int ret; + + if (tx->data->extra_widgets_num) { + ret = snd_soc_dapm_new_controls(dapm, tx->data->extra_widgets, + tx->data->extra_widgets_num); + if (ret) { + dev_err(tx->dev, "failed to add extra widgets: %d\n", ret); + return ret; + } + } + + if (tx->data->extra_routes_num) { + ret = snd_soc_dapm_add_routes(dapm, tx->data->extra_routes, + tx->data->extra_routes_num); + if (ret) { + dev_err(tx->dev, "failed to add extra routes: %d\n", ret); + return ret; + } + } + + return 0; +} + static int tx_macro_component_probe(struct snd_soc_component *comp) { struct tx_macro *tx = snd_soc_component_get_drvdata(comp); - int i; + int i, ret; + + ret = tx_macro_component_extend(comp); + if (ret) + return ret; snd_soc_component_init_regmap(comp, tx->regmap); @@ -1848,8 +2136,10 @@ static int tx_macro_component_probe(struct snd_soc_component *comp) snd_soc_component_update_bits(comp, CDC_TX0_TX_PATH_SEC7, 0x3F, 0x0A); /* Enable swr mic0 and mic1 clock */ - snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0xFF, 0x00); - snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0xFF, 0x00); + snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, + CDC_TX_SWR_MIC_CLK_DEFAULT); + snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, + CDC_TX_SWR_MIC_CLK_DEFAULT); return 0; } @@ -1954,17 +2244,16 @@ static int tx_macro_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - kernel_ulong_t flags; struct tx_macro *tx; void __iomem *base; int ret, reg; - flags = (kernel_ulong_t)device_get_match_data(dev); - tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL); if (!tx) return -ENOMEM; + tx->data = device_get_match_data(dev); + tx->macro = devm_clk_get_optional(dev, "macro"); if (IS_ERR(tx->macro)) return dev_err_probe(dev, PTR_ERR(tx->macro), "unable to get macro clock\n"); @@ -1977,7 +2266,7 @@ static int tx_macro_probe(struct platform_device *pdev) if (IS_ERR(tx->mclk)) return dev_err_probe(dev, PTR_ERR(tx->mclk), "unable to get mclk clock\n"); - if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { + if (tx->data->flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { tx->npl = devm_clk_get(dev, "npl"); if (IS_ERR(tx->npl)) return dev_err_probe(dev, PTR_ERR(tx->npl), "unable to get npl clock\n"); @@ -2052,7 +2341,7 @@ static int tx_macro_probe(struct platform_device *pdev) /* reset soundwire block */ - if (flags & LPASS_MACRO_FLAG_RESET_SWR) + if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR) regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE); @@ -2060,7 +2349,7 @@ static int tx_macro_probe(struct platform_device *pdev) CDC_TX_SWR_CLK_EN_MASK, CDC_TX_SWR_CLK_ENABLE); - if (flags & LPASS_MACRO_FLAG_RESET_SWR) + if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR) regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_RESET_MASK, 0x0); @@ -2164,25 +2453,75 @@ static const struct dev_pm_ops tx_macro_pm_ops = { SET_RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL) }; +static const struct tx_macro_data lpass_ver_9 = { + .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK | + LPASS_MACRO_FLAG_RESET_SWR, + .ver = LPASS_VER_9_0_0, + .extra_widgets = tx_macro_dapm_widgets_v9, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9), + .extra_routes = tx_audio_map_v9, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9), +}; + +static const struct tx_macro_data lpass_ver_9_2 = { + .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK | + LPASS_MACRO_FLAG_RESET_SWR, + .ver = LPASS_VER_9_2_0, + .extra_widgets = tx_macro_dapm_widgets_v9_2, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2), + .extra_routes = tx_audio_map_v9_2, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2), +}; + +static const struct tx_macro_data lpass_ver_10_sm6115 = { + .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + .ver = LPASS_VER_10_0_0, + .extra_widgets = tx_macro_dapm_widgets_v9_2, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2), + .extra_routes = tx_audio_map_v9_2, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2), +}; + +static const struct tx_macro_data lpass_ver_11 = { + .flags = LPASS_MACRO_FLAG_RESET_SWR, + .ver = LPASS_VER_11_0_0, + .extra_widgets = tx_macro_dapm_widgets_v9_2, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2), + .extra_routes = tx_audio_map_v9_2, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2), +}; + static const struct of_device_id tx_macro_dt_match[] = { { + /* + * The block is actually LPASS v9.4, but keep LPASS v9 match + * data and audio widgets, due to compatibility reasons. + * Microphones are working on SC7280 fine, so apparently the fix + * is not necessary. + */ .compatible = "qcom,sc7280-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + .data = &lpass_ver_9, }, { .compatible = "qcom,sm6115-lpass-tx-macro", - .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + .data = &lpass_ver_10_sm6115, }, { .compatible = "qcom,sm8250-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + .data = &lpass_ver_9, }, { .compatible = "qcom,sm8450-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + .data = &lpass_ver_9_2, }, { .compatible = "qcom,sm8550-lpass-tx-macro", - .data = (void *)LPASS_MACRO_FLAG_RESET_SWR, + .data = &lpass_ver_11, }, { .compatible = "qcom,sc8280xp-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + /* + * The block is actually LPASS v9.3, but keep LPASS v9 match + * data and audio widgets, due to compatibility reasons. + * Microphones are working on SC8280xp fine, so apparently the + * fix is not necessary. + */ + .data = &lpass_ver_9, }, { } }; diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index b71ef03c4aef..6eceeff10bf6 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -201,10 +201,12 @@ struct va_macro { unsigned long active_ch_cnt[VA_MACRO_MAX_DAIS]; u16 dmic_clk_div; bool has_swr_master; + bool has_npl_clk; int dec_mode[VA_MACRO_NUM_DECIMATORS]; struct regmap *regmap; struct clk *mclk; + struct clk *npl; struct clk *macro; struct clk *dcodec; struct clk *fsgen; @@ -225,14 +227,22 @@ struct va_macro { struct va_macro_data { bool has_swr_master; + bool has_npl_clk; }; static const struct va_macro_data sm8250_va_data = { .has_swr_master = false, + .has_npl_clk = false, }; static const struct va_macro_data sm8450_va_data = { .has_swr_master = true, + .has_npl_clk = true, +}; + +static const struct va_macro_data sm8550_va_data = { + .has_swr_master = true, + .has_npl_clk = false, }; static bool va_is_volatile_register(struct device *dev, unsigned int reg) @@ -1332,6 +1342,12 @@ static int fsgen_gate_enable(struct clk_hw *hw) struct regmap *regmap = va->regmap; int ret; + if (va->has_swr_master) { + ret = clk_prepare_enable(va->mclk); + if (ret) + return ret; + } + ret = va_macro_mclk_enable(va, true); if (va->has_swr_master) regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, @@ -1350,6 +1366,8 @@ static void fsgen_gate_disable(struct clk_hw *hw) CDC_VA_SWR_CLK_EN_MASK, 0x0); va_macro_mclk_enable(va, false); + if (va->has_swr_master) + clk_disable_unprepare(va->mclk); } static int fsgen_gate_is_enabled(struct clk_hw *hw) @@ -1378,6 +1396,9 @@ static int va_macro_register_fsgen_output(struct va_macro *va) struct clk_init_data init; int ret; + if (va->has_npl_clk) + parent = va->npl; + parent_clk_name = __clk_get_name(parent); of_property_read_string(np, "clock-output-names", &clk_name); @@ -1500,10 +1521,21 @@ static int va_macro_probe(struct platform_device *pdev) data = of_device_get_match_data(dev); va->has_swr_master = data->has_swr_master; + va->has_npl_clk = data->has_npl_clk; /* mclk rate */ clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); + if (va->has_npl_clk) { + va->npl = devm_clk_get(dev, "npl"); + if (IS_ERR(va->npl)) { + ret = PTR_ERR(va->npl); + goto err; + } + + clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); + } + ret = clk_prepare_enable(va->macro); if (ret) goto err; @@ -1516,6 +1548,12 @@ static int va_macro_probe(struct platform_device *pdev) if (ret) goto err_mclk; + if (va->has_npl_clk) { + ret = clk_prepare_enable(va->npl); + if (ret) + goto err_npl; + } + if (va->has_swr_master) { /* Set default CLK div to 1 */ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0, @@ -1564,6 +1602,9 @@ static int va_macro_probe(struct platform_device *pdev) return 0; err_clkout: + if (va->has_npl_clk) + clk_disable_unprepare(va->npl); +err_npl: clk_disable_unprepare(va->mclk); err_mclk: clk_disable_unprepare(va->dcodec); @@ -1579,6 +1620,9 @@ static void va_macro_remove(struct platform_device *pdev) { struct va_macro *va = dev_get_drvdata(&pdev->dev); + if (va->has_npl_clk) + clk_disable_unprepare(va->npl); + clk_disable_unprepare(va->mclk); clk_disable_unprepare(va->dcodec); clk_disable_unprepare(va->macro); @@ -1593,6 +1637,9 @@ static int __maybe_unused va_macro_runtime_suspend(struct device *dev) regcache_cache_only(va->regmap, true); regcache_mark_dirty(va->regmap); + if (va->has_npl_clk) + clk_disable_unprepare(va->npl); + clk_disable_unprepare(va->mclk); return 0; @@ -1609,6 +1656,15 @@ static int __maybe_unused va_macro_runtime_resume(struct device *dev) return ret; } + if (va->has_npl_clk) { + ret = clk_prepare_enable(va->npl); + if (ret) { + clk_disable_unprepare(va->mclk); + dev_err(va->dev, "unable to prepare npl\n"); + return ret; + } + } + regcache_cache_only(va->regmap, false); regcache_sync(va->regmap); @@ -1624,6 +1680,7 @@ static const struct of_device_id va_macro_dt_match[] = { { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data }, + { .compatible = "qcom,sm8550-lpass-va-macro", .data = &sm8550_va_data }, { .compatible = "qcom,sc8280xp-lpass-va-macro", .data = &sm8450_va_data }, {} }; diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index a2cca3436c68..950105e5bffd 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -314,7 +314,7 @@ static int max98363_update_status(struct sdw_slave *slave, return max98363_io_init(slave); } -static struct sdw_slave_ops max98363_slave_ops = { +static const struct sdw_slave_ops max98363_slave_ops = { .read_prop = max98363_read_prop, .update_status = max98363_update_status, }; diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index b5cb951af570..383e551f3bc7 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -821,7 +821,7 @@ static int max98373_bus_config(struct sdw_slave *slave, * slave_ops: callbacks for get_clock_stop_mode, clock_stop and * port_prep are not defined for now */ -static struct sdw_slave_ops max98373_slave_ops = { +static const struct sdw_slave_ops max98373_slave_ops = { .read_prop = max98373_read_prop, .update_status = max98373_update_status, .bus_config = max98373_bus_config, diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index f66417a0f29f..22251fb2fa1f 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -26,7 +26,6 @@ #include <sound/tlv.h> #include "nau8540.h" - #define NAU_FREF_MAX 13500000 #define NAU_FVCO_MAX 100000000 #define NAU_FVCO_MIN 90000000 @@ -230,6 +229,49 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new digital_ch1_mux = SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum); +static int nau8540_fepga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2, + NAU8540_ACDC_CTL_MASK, NAU8540_ACDC_CTL_MIC1P_VREF | + NAU8540_ACDC_CTL_MIC1N_VREF | NAU8540_ACDC_CTL_MIC2P_VREF | + NAU8540_ACDC_CTL_MIC2N_VREF | NAU8540_ACDC_CTL_MIC3P_VREF | + NAU8540_ACDC_CTL_MIC3N_VREF | NAU8540_ACDC_CTL_MIC4P_VREF | + NAU8540_ACDC_CTL_MIC4N_VREF); + break; + default: + break; + } + return 0; +} + +static int nau8540_precharge_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE, + NAU8540_DISCHRG_EN, NAU8540_DISCHRG_EN); + msleep(40); + regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE, + NAU8540_DISCHRG_EN, 0); + regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2, + NAU8540_ACDC_CTL_MASK, 0); + break; + default: + break; + } + return 0; +} + static int adc_power_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -237,8 +279,10 @@ static int adc_power_control(struct snd_soc_dapm_widget *w, struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); if (SND_SOC_DAPM_EVENT_ON(event)) { - msleep(300); + msleep(160); /* DO12 and DO34 pad output enable */ + regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT, + NAU8540_ADC_ALL_EN, NAU8540_ADC_ALL_EN); regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, NAU8540_I2S_DO12_TRI, 0); regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, @@ -248,6 +292,8 @@ static int adc_power_control(struct snd_soc_dapm_widget *w, NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI); regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI); + regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT, + NAU8540_ADC_ALL_EN, 0); } return 0; } @@ -274,28 +320,26 @@ static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MIC3"), SND_SOC_DAPM_INPUT("MIC4"), - SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0), - SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0), - SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0), - SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0), - - SND_SOC_DAPM_ADC_E("ADC1", NULL, - NAU8540_REG_POWER_MANAGEMENT, 0, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_ADC_E("ADC2", NULL, - NAU8540_REG_POWER_MANAGEMENT, 1, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_ADC_E("ADC3", NULL, - NAU8540_REG_POWER_MANAGEMENT, 2, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_ADC_E("ADC4", NULL, - NAU8540_REG_POWER_MANAGEMENT, 3, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - - SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Frontend PGA1", 0, NAU8540_REG_PWR, 12, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Frontend PGA2", 0, NAU8540_REG_PWR, 13, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Frontend PGA3", 0, NAU8540_REG_PWR, 14, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Frontend PGA4", 0, NAU8540_REG_PWR, 15, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_PGA_S("Precharge", 1, SND_SOC_NOPM, 0, 0, + nau8540_precharge_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_PGA_S("ADC CH1", 2, NAU8540_REG_ANALOG_PWR, 0, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("ADC CH2", 2, NAU8540_REG_ANALOG_PWR, 1, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("ADC CH3", 2, NAU8540_REG_ANALOG_PWR, 2, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("ADC CH4", 2, NAU8540_REG_ANALOG_PWR, 3, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_MUX("Digital CH4 Mux", SND_SOC_NOPM, 0, 0, &digital_ch4_mux), @@ -316,20 +360,20 @@ static const struct snd_soc_dapm_route nau8540_dapm_routes[] = { {"Frontend PGA3", NULL, "MIC3"}, {"Frontend PGA4", NULL, "MIC4"}, - {"ADC1", NULL, "Frontend PGA1"}, - {"ADC2", NULL, "Frontend PGA2"}, - {"ADC3", NULL, "Frontend PGA3"}, - {"ADC4", NULL, "Frontend PGA4"}, + {"Precharge", NULL, "Frontend PGA1"}, + {"Precharge", NULL, "Frontend PGA2"}, + {"Precharge", NULL, "Frontend PGA3"}, + {"Precharge", NULL, "Frontend PGA4"}, - {"ADC CH1", NULL, "ADC1"}, - {"ADC CH2", NULL, "ADC2"}, - {"ADC CH3", NULL, "ADC3"}, - {"ADC CH4", NULL, "ADC4"}, + {"ADC CH1", NULL, "Precharge"}, + {"ADC CH2", NULL, "Precharge"}, + {"ADC CH3", NULL, "Precharge"}, + {"ADC CH4", NULL, "Precharge"}, - {"ADC1", NULL, "MICBIAS1"}, - {"ADC2", NULL, "MICBIAS1"}, - {"ADC3", NULL, "MICBIAS2"}, - {"ADC4", NULL, "MICBIAS2"}, + {"ADC CH1", NULL, "MICBIAS1"}, + {"ADC CH2", NULL, "MICBIAS1"}, + {"ADC CH3", NULL, "MICBIAS2"}, + {"ADC CH4", NULL, "MICBIAS2"}, {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"}, {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"}, diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h index 2ce6063d462b..762bb93b06fd 100644 --- a/sound/soc/codecs/nau8540.h +++ b/sound/soc/codecs/nau8540.h @@ -78,6 +78,7 @@ /* POWER_MANAGEMENT (0x01) */ +#define NAU8540_ADC_ALL_EN 0xf #define NAU8540_ADC4_EN (0x1 << 3) #define NAU8540_ADC3_EN (0x1 << 2) #define NAU8540_ADC2_EN (0x1 << 1) @@ -202,6 +203,7 @@ /* REFERENCE (0x68) */ #define NAU8540_PRECHARGE_DIS (0x1 << 13) #define NAU8540_GLOBAL_BIAS_EN (0x1 << 12) +#define NAU8540_DISCHRG_EN (0x1 << 11) /* FEPGA1 (0x69) */ #define NAU8540_FEPGA1_MODCH2_SHT_SFT 7 @@ -214,7 +216,16 @@ #define NAU8540_FEPGA2_MODCH4_SHT (0x1 << NAU8540_FEPGA2_MODCH4_SHT_SFT) #define NAU8540_FEPGA2_MODCH3_SHT_SFT 3 #define NAU8540_FEPGA2_MODCH3_SHT (0x1 << NAU8540_FEPGA2_MODCH3_SHT_SFT) - +#define NAU8540_ACDC_CTL_SFT 8 +#define NAU8540_ACDC_CTL_MASK (0xff << NAU8540_ACDC_CTL_SFT) +#define NAU8540_ACDC_CTL_MIC4N_VREF (0x1 << 15) +#define NAU8540_ACDC_CTL_MIC4P_VREF (0x1 << 14) +#define NAU8540_ACDC_CTL_MIC3N_VREF (0x1 << 13) +#define NAU8540_ACDC_CTL_MIC3P_VREF (0x1 << 12) +#define NAU8540_ACDC_CTL_MIC2N_VREF (0x1 << 11) +#define NAU8540_ACDC_CTL_MIC2P_VREF (0x1 << 10) +#define NAU8540_ACDC_CTL_MIC1N_VREF (0x1 << 9) +#define NAU8540_ACDC_CTL_MIC1P_VREF (0x1 << 8) /* System Clock Source */ enum { diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 5cb0de648bd3..cd30ad649bae 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -2836,16 +2836,12 @@ static int nau8825_read_device_properties(struct device *dev, if (nau8825->adc_delay < 125 || nau8825->adc_delay > 500) dev_warn(dev, "Please set the suitable delay time!\n"); - nau8825->mclk = devm_clk_get(dev, "mclk"); - if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) { - return -EPROBE_DEFER; - } else if (PTR_ERR(nau8825->mclk) == -ENOENT) { + nau8825->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(nau8825->mclk)) + return PTR_ERR(nau8825->mclk); + if (!nau8825->mclk) /* The MCLK is managed externally or not used at all */ - nau8825->mclk = NULL; dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally"); - } else if (IS_ERR(nau8825->mclk)) { - return -EINVAL; - } return 0; } diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index 7295f44c77eb..4dbbd8bdaaac 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -520,7 +520,7 @@ static const struct snd_soc_dapm_route rt1017_sdca_dapm_routes[] = { { "DP2TX", NULL, "V Sense" }, }; -static struct sdw_slave_ops rt1017_sdca_slave_ops = { +static const struct sdw_slave_ops rt1017_sdca_slave_ops = { .read_prop = rt1017_sdca_read_prop, .update_status = rt1017_sdca_update_status, }; diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 9a33e3776b55..6e7843484250 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1192,7 +1192,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274); if (ret != 0) { dev_err(&i2c->dev, - "Failed to reguest IRQ: %d\n", ret); + "Failed to request IRQ: %d\n", ret); return ret; } } diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 981155b046fd..f8994f4968c5 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1237,7 +1237,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286); if (ret != 0) { dev_err(&i2c->dev, - "Failed to reguest IRQ: %d\n", ret); + "Failed to request IRQ: %d\n", ret); return ret; } } diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index ad3783ade1b5..03d9839a5de3 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1284,7 +1284,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298); if (ret != 0) { dev_err(&i2c->dev, - "Failed to reguest IRQ: %d\n", ret); + "Failed to request IRQ: %d\n", ret); return ret; } } diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 3ee6d85268ba..f475c8cfadae 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -279,7 +279,7 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component) rt5514_dsp); if (ret) dev_err(&rt5514_spi->dev, - "%s Failed to reguest IRQ: %d\n", __func__, + "%s Failed to request IRQ: %d\n", __func__, ret); else device_init_wakeup(rt5514_dsp->dev, true); diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 43fc7814fdde..a8cdc3d6994d 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1054,9 +1054,6 @@ static int rt5514_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_PREPARE: - if (IS_ERR(rt5514->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5514->mclk); } else { @@ -1097,9 +1094,9 @@ static int rt5514_probe(struct snd_soc_component *component) struct platform_device *pdev = container_of(component->dev, struct platform_device, dev); - rt5514->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5514->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5514->mclk)) + return PTR_ERR(rt5514->mclk); if (rt5514->pdata.dsp_calib_clk_name) { rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev, diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index c13108b51eaf..e7aa60e73961 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1174,9 +1174,6 @@ static int rt5616_set_bias_level(struct snd_soc_component *component, * away from ON. Disable the clock in that case, otherwise * enable it. */ - if (IS_ERR(rt5616->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5616->mclk); } else { @@ -1225,9 +1222,9 @@ static int rt5616_probe(struct snd_soc_component *component) struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); /* Check if MCLK provided */ - rt5616->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5616->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5616->mclk)) + return PTR_ERR(rt5616->mclk); rt5616->component = component; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index e8cdc166bdaa..174872ef35d2 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1949,9 +1949,6 @@ static int rt5640_set_bias_level(struct snd_soc_component *component, * away from ON. Disable the clock in that case, otherwise * enable it. */ - if (IS_ERR(rt5640->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5640->mclk); } else { @@ -2661,9 +2658,9 @@ static int rt5640_probe(struct snd_soc_component *component) u32 val; /* Check if MCLK provided */ - rt5640->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5640->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5640->mclk)) + return PTR_ERR(rt5640->mclk); rt5640->component = component; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 20191a4473c2..e3ba04484813 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -4223,7 +4223,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5645", rt5645); if (ret) { - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); goto err_enable; } } diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 0cee4fd1c84b..33a34bd0b405 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -2261,7 +2261,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651); if (ret) { - dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n", + dev_warn(&i2c->dev, "Failed to request IRQ %d: %d\n", rt5651->irq, ret); rt5651->irq = -ENXIO; } diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index a061028a16d8..fb094c0fe740 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -4292,7 +4292,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c) rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5659", rt5659); if (ret) - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); /* Enable IRQ output for GPIO1 pin any way */ regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1, diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 0cecfd602415..d5c2f0f2df98 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1079,9 +1079,6 @@ static int rt5660_set_bias_level(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5660_GEN_CTRL1, RT5660_DIG_GATE_CTRL, RT5660_DIG_GATE_CTRL); - if (IS_ERR(rt5660->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5660->mclk); } else { @@ -1277,9 +1274,9 @@ static int rt5660_i2c_probe(struct i2c_client *i2c) return -ENOMEM; /* Check if MCLK provided */ - rt5660->mclk = devm_clk_get(&i2c->dev, "mclk"); - if (PTR_ERR(rt5660->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5660->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(rt5660->mclk)) + return PTR_ERR(rt5660->mclk); i2c_set_clientdata(i2c, rt5660); diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 9550492605ac..161dcb3915f9 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3692,7 +3692,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5663", rt5663); if (ret) { - dev_err(&i2c->dev, "%s Failed to reguest IRQ: %d\n", + dev_err(&i2c->dev, "%s Failed to request IRQ: %d\n", __func__, ret); goto err_enable; } diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index a39de4a7df00..6f778c8f0832 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4929,7 +4929,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c) rt5665_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5665", rt5665); if (ret) - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index 4623b3e62487..6d8e228ccb57 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2580,7 +2580,7 @@ static int rt5668_i2c_probe(struct i2c_client *i2c) rt5668_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5668", rt5668); if (ret) - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index fbad1ed06626..62f26ce9d476 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -266,7 +266,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c) if (!ret) rt5682->irq = i2c->irq; else - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } #ifdef CONFIG_COMMON_CLK diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 3322056bbb3b..12741668fdb3 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -3283,7 +3283,7 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c) if (!ret) rt5682s->irq = i2c->irq; else - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } return devm_snd_soc_register_component(&i2c->dev, &rt5682s_soc_component_dev, diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index ba08d03e717c..0926b26619bd 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -944,7 +944,7 @@ static const struct dev_pm_ops rt712_sdca_dmic_pm = { }; -static struct sdw_slave_ops rt712_sdca_dmic_slave_ops = { +static const struct sdw_slave_ops rt712_sdca_dmic_slave_ops = { .read_prop = rt712_sdca_dmic_read_prop, .update_status = rt712_sdca_dmic_update_status, }; diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 6b644a89c589..01ac555cd79b 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -331,7 +331,7 @@ io_error: return ret; } -static struct sdw_slave_ops rt712_sdca_slave_ops = { +static const struct sdw_slave_ops rt712_sdca_slave_ops = { .read_prop = rt712_sdca_read_prop, .interrupt_callback = rt712_sdca_interrupt_callback, .update_status = rt712_sdca_update_status, diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index e24b9cbdc10c..eb76f4c675b6 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -362,7 +362,7 @@ io_error: return ret; } -static struct sdw_slave_ops rt722_sdca_slave_ops = { +static const struct sdw_slave_ops rt722_sdca_slave_ops = { .read_prop = rt722_sdca_read_prop, .interrupt_callback = rt722_sdca_interrupt_callback, .update_status = rt722_sdca_update_status, diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c index 5d0e5348b361..3aa81514dad7 100644 --- a/sound/soc/codecs/tas2781-comlib.c +++ b/sound/soc/codecs/tas2781-comlib.c @@ -408,7 +408,6 @@ void tasdevice_remove(struct tasdevice_priv *tas_priv) { if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) gpio_free(tas_priv->irq_info.irq_gpio); - kfree(tas_priv->acpi_subsystem_id); mutex_destroy(&tas_priv->codec_lock); } EXPORT_SYMBOL_GPL(tasdevice_remove); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 85e14ff61769..45760fe19523 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -1531,7 +1531,7 @@ static int tasdev_load_blk(struct tasdevice_priv *tas_priv, unsigned int sleep_time; unsigned int len; unsigned int nr_cmds; - unsigned char *data = block->data; + unsigned char *data; unsigned char crc_chksum = 0; unsigned char offset; unsigned char book; diff --git a/sound/soc/codecs/wcd-clsh-v2.h b/sound/soc/codecs/wcd-clsh-v2.h index 4e3653058275..eeb9bc5b01e2 100644 --- a/sound/soc/codecs/wcd-clsh-v2.h +++ b/sound/soc/codecs/wcd-clsh-v2.h @@ -47,6 +47,7 @@ enum wcd_codec_version { /* New CLSH after this */ WCD937X = 2, WCD938X = 3, + WCD939X = 4, }; struct wcd_clsh_ctrl; diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 5da1934527f3..0e6218ed0e5e 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -16,6 +16,7 @@ #define HS_DETECT_PLUG_TIME_MS (3 * 1000) #define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 #define GND_MIC_SWAP_THRESHOLD 4 +#define GND_MIC_USBC_SWAP_THRESHOLD 2 #define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 #define HPHL_CROSS_CONN_THRESHOLD 100 #define HS_VREF_MIN_VAL 1400 @@ -52,12 +53,15 @@ struct wcd_mbhc { struct wcd_mbhc_field *fields; /* Delayed work to report long button press */ struct delayed_work mbhc_btn_dwork; + /* Work to handle plug report */ + struct work_struct mbhc_plug_detect_work; /* Work to correct accessory type */ struct work_struct correct_plug_swch; struct mutex lock; int buttons_pressed; u32 hph_status; /* track headhpone status */ u8 current_plug; + unsigned int swap_thr; bool is_btn_press; bool in_swch_irq_handler; bool hs_detect_work_stop; @@ -506,14 +510,13 @@ static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc) } } -static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) +static void mbhc_plug_detect_fn(struct work_struct *work) { - struct snd_soc_component *component; + struct wcd_mbhc *mbhc = container_of(work, struct wcd_mbhc, mbhc_plug_detect_work); + struct snd_soc_component *component = mbhc->component; enum snd_jack_types jack_type; - struct wcd_mbhc *mbhc = data; bool detection_type; - component = mbhc->component; mutex_lock(&mbhc->lock); mbhc->in_swch_irq_handler = true; @@ -576,9 +579,51 @@ static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) exit: mbhc->in_swch_irq_handler = false; mutex_unlock(&mbhc->lock); +} + +static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + + if (!mbhc->cfg->typec_analog_mux) + schedule_work(&mbhc->mbhc_plug_detect_work); + return IRQ_HANDLED; } +int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc) +{ + + if (!mbhc || !mbhc->cfg->typec_analog_mux) + return -EINVAL; + + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(mbhc->component, false); + + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, 0); + + schedule_work(&mbhc->mbhc_plug_detect_work); + + return 0; +} +EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_unplug); + +int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc) +{ + if (!mbhc || !mbhc->cfg->typec_analog_mux) + return -EINVAL; + + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(mbhc->component, true); + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1); + + schedule_work(&mbhc->mbhc_plug_detect_work); + + return 0; +} +EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_plug); + static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc) { int mask = 0; @@ -725,14 +770,23 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mutex_lock(&mbhc->lock); - /* enable HS detection */ + if (mbhc->cfg->typec_analog_mux) + mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD; + else + mbhc->swap_thr = GND_MIC_SWAP_THRESHOLD; + + /* setup HS detection */ if (mbhc->mbhc_cb->hph_pull_up_control_v2) mbhc->mbhc_cb->hph_pull_up_control_v2(component, - HS_PULLUP_I_DEFAULT); + mbhc->cfg->typec_analog_mux ? + HS_PULLUP_I_OFF : HS_PULLUP_I_DEFAULT); else if (mbhc->mbhc_cb->hph_pull_up_control) - mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT); + mbhc->mbhc_cb->hph_pull_up_control(component, + mbhc->cfg->typec_analog_mux ? + I_OFF : I_DEFAULT); else - wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3); + wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, + mbhc->cfg->typec_analog_mux ? 0 : 3); wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh); wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh); @@ -741,10 +795,18 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true); wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1); - wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1); + /* Plug detect is triggered manually if analog goes through USBCC */ + if (mbhc->cfg->typec_analog_mux) + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0); + else + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1); - /* Insertion debounce set to 96ms */ - wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6); + if (mbhc->cfg->typec_analog_mux) + /* Insertion debounce set to 48ms */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 4); + else + /* Insertion debounce set to 96ms */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6); /* Button Debounce set to 16ms */ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2); @@ -753,7 +815,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mbhc->mbhc_cb->mbhc_bias(component, true); /* enable MBHC clock */ if (mbhc->mbhc_cb->clk_setup) - mbhc->mbhc_cb->clk_setup(component, true); + mbhc->mbhc_cb->clk_setup(component, + mbhc->cfg->typec_analog_mux ? false : true); /* program HS_VREF value */ wcd_program_hs_vref(mbhc); @@ -1115,7 +1178,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) do { cross_conn = wcd_check_cross_conn(mbhc); try++; - } while (try < GND_MIC_SWAP_THRESHOLD); + } while (try < mbhc->swap_thr); if (cross_conn > 0) { plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; @@ -1183,7 +1246,7 @@ correct_plug_type: cross_conn = wcd_check_cross_conn(mbhc); if (cross_conn > 0) { /* cross-connection */ pt_gnd_mic_swap_cnt++; - if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD) + if (pt_gnd_mic_swap_cnt < mbhc->swap_thr) continue; else plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; @@ -1194,7 +1257,7 @@ correct_plug_type: } else /* Error if (cross_conn < 0) */ continue; - if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) { + 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)) @@ -1473,6 +1536,7 @@ struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, mutex_init(&mbhc->lock); INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); + INIT_WORK(&mbhc->mbhc_plug_detect_work, mbhc_plug_detect_fn); ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL, wcd_mbhc_mech_plug_detect_irq, @@ -1562,6 +1626,7 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) mutex_lock(&mbhc->lock); wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + cancel_work_sync(&mbhc->mbhc_plug_detect_work); mutex_unlock(&mbhc->lock); kfree(mbhc); diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index 006118f3e81f..df68e99c81a3 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -193,6 +193,7 @@ struct wcd_mbhc_config { int v_hs_max; int num_btn; bool mono_stero_detection; + bool typec_analog_mux; bool (*swap_gnd_mic)(struct snd_soc_component *component, bool active); bool hs_ext_micbias; bool gnd_det_en; @@ -273,6 +274,8 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg, void wcd_mbhc_stop(struct wcd_mbhc *mbhc); void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type); int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc); +int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc); +int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc); struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c new file mode 100644 index 000000000000..8acb5651c5bc --- /dev/null +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -0,0 +1,1551 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Linaro Limited + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/component.h> +#include <linux/pm_runtime.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include "wcd939x.h" + +#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) + +static struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { + WCD_SDW_CH(WCD939X_HPH_L, WCD939X_HPH_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_HPH_R, WCD939X_HPH_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_CLSH, WCD939X_CLSH_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_COMP_L, WCD939X_COMP_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_COMP_R, WCD939X_COMP_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_LO, WCD939X_LO_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DSD_L, WCD939X_DSD_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DSD_R, WCD939X_DSD_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_HIFI_PCM_L, WCD939X_HIFI_PCM_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_HIFI_PCM_R, WCD939X_HIFI_PCM_PORT, BIT(1)), +}; + +static struct wcd939x_sdw_ch_info wcd939x_sdw_tx_ch_info[] = { + WCD_SDW_CH(WCD939X_ADC1, WCD939X_ADC_1_4_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_ADC2, WCD939X_ADC_1_4_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_ADC3, WCD939X_ADC_1_4_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_ADC4, WCD939X_ADC_1_4_PORT, BIT(3)), + WCD_SDW_CH(WCD939X_DMIC0, WCD939X_DMIC_0_3_MBHC_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DMIC1, WCD939X_DMIC_0_3_MBHC_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_MBHC, WCD939X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_DMIC2, WCD939X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_DMIC3, WCD939X_DMIC_0_3_MBHC_PORT, BIT(3)), + WCD_SDW_CH(WCD939X_DMIC4, WCD939X_DMIC_3_7_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DMIC5, WCD939X_DMIC_3_7_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_DMIC6, WCD939X_DMIC_3_7_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_DMIC7, WCD939X_DMIC_3_7_PORT, BIT(3)), +}; + +static struct sdw_dpn_prop wcd939x_rx_dpn_prop[WCD939X_MAX_RX_SWR_PORTS] = { + { + .num = WCD939X_HPH_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_CLSH_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_COMP_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_LO_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_DSD_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_HIFI_PCM_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + } +}; + +static struct sdw_dpn_prop wcd939x_tx_dpn_prop[WCD939X_MAX_TX_SWR_PORTS] = { + { + .num = WCD939X_ADC_1_4_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_ADC_DMIC_1_2_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_DMIC_0_3_MBHC_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_DMIC_3_7_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + } +}; + +struct device *wcd939x_sdw_device_get(struct device_node *np) +{ + return bus_find_device_by_of_node(&sdw_bus_type, np); +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_device_get); + +unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev) +{ + return FIELD_GET(SDW_SCP_STAT_CURR_BANK, + sdw_read(sdev, SDW_SCP_CTRL)); +} +EXPORT_SYMBOL_GPL(wcd939x_swr_get_current_bank); + +int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS]; + unsigned long ch_mask; + int i, j; + + wcd->sconfig.ch_count = 1; + wcd->active_ports = 0; + for (i = 0; i < WCD939X_MAX_SWR_PORTS; i++) { + ch_mask = wcd->port_config[i].ch_mask; + + if (!ch_mask) + continue; + + for_each_set_bit(j, &ch_mask, 4) + wcd->sconfig.ch_count++; + + port_config[wcd->active_ports] = wcd->port_config[i]; + wcd->active_ports++; + } + + wcd->sconfig.bps = 1; + wcd->sconfig.frame_rate = params_rate(params); + if (wcd->is_tx) + wcd->sconfig.direction = SDW_DATA_DIR_TX; + else + wcd->sconfig.direction = SDW_DATA_DIR_RX; + + wcd->sconfig.type = SDW_STREAM_PCM; + + return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig, &port_config[0], + wcd->active_ports, wcd->sruntime); +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_hw_params); + +int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + sdw_stream_remove_slave(wcd->sdev, wcd->sruntime); + + return 0; +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_free); + +int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd, + struct snd_soc_dai *dai, void *stream, + int direction) +{ + wcd->sruntime = stream; + + return 0; +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_set_sdw_stream); + +struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd) +{ + if (wcd->regmap) + return wcd->regmap; + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(wcd939x_swr_get_regmap); + +static int wcd9390_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev); + + if (wcd->regmap && status == SDW_SLAVE_ATTACHED) { + /* Write out any cached changes that happened between probe and attach */ + regcache_cache_only(wcd->regmap, false); + return regcache_sync(wcd->regmap); + } + + return 0; +} + +static int wcd9390_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), + 0x01); + + return 0; +} + +/* + * Handle Soundwire out-of-band interrupt event by triggering + * the first irq of the slave_irq irq domain, which then will + * be handled by the regmap_irq threaded irq. + * Looping is to ensure no interrupts were missed in the process. + */ +static int wcd9390_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev); + struct irq_domain *slave_irq = wcd->slave_irq; + u32 sts1, sts2, sts3; + + do { + handle_nested_irq(irq_find_mapping(slave_irq, 0)); + regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_0, &sts1); + regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_1, &sts2); + regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_2, &sts3); + + } while (sts1 || sts2 || sts3); + + return IRQ_HANDLED; +} + +static const struct reg_default wcd939x_defaults[] = { + /* Default values except for Read-Only & Volatile registers */ + { WCD939X_ANA_PAGE, 0x00 }, + { WCD939X_ANA_BIAS, 0x00 }, + { WCD939X_ANA_RX_SUPPLIES, 0x00 }, + { WCD939X_ANA_HPH, 0x0c }, + { WCD939X_ANA_EAR, 0x00 }, + { WCD939X_ANA_EAR_COMPANDER_CTL, 0x02 }, + { WCD939X_ANA_TX_CH1, 0x20 }, + { WCD939X_ANA_TX_CH2, 0x00 }, + { WCD939X_ANA_TX_CH3, 0x20 }, + { WCD939X_ANA_TX_CH4, 0x00 }, + { WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00 }, + { WCD939X_ANA_MICB3_DSP_EN_LOGIC, 0x00 }, + { WCD939X_ANA_MBHC_MECH, 0x39 }, + { WCD939X_ANA_MBHC_ELECT, 0x08 }, + { WCD939X_ANA_MBHC_ZDET, 0x00 }, + { WCD939X_ANA_MBHC_BTN0, 0x00 }, + { WCD939X_ANA_MBHC_BTN1, 0x10 }, + { WCD939X_ANA_MBHC_BTN2, 0x20 }, + { WCD939X_ANA_MBHC_BTN3, 0x30 }, + { WCD939X_ANA_MBHC_BTN4, 0x40 }, + { WCD939X_ANA_MBHC_BTN5, 0x50 }, + { WCD939X_ANA_MBHC_BTN6, 0x60 }, + { WCD939X_ANA_MBHC_BTN7, 0x70 }, + { WCD939X_ANA_MICB1, 0x10 }, + { WCD939X_ANA_MICB2, 0x10 }, + { WCD939X_ANA_MICB2_RAMP, 0x00 }, + { WCD939X_ANA_MICB3, 0x00 }, + { WCD939X_ANA_MICB4, 0x00 }, + { WCD939X_BIAS_CTL, 0x2a }, + { WCD939X_BIAS_VBG_FINE_ADJ, 0x55 }, + { WCD939X_LDOL_VDDCX_ADJUST, 0x01 }, + { WCD939X_LDOL_DISABLE_LDOL, 0x00 }, + { WCD939X_MBHC_CTL_CLK, 0x00 }, + { WCD939X_MBHC_CTL_ANA, 0x00 }, + { WCD939X_MBHC_ZDET_VNEG_CTL, 0x00 }, + { WCD939X_MBHC_ZDET_BIAS_CTL, 0x46 }, + { WCD939X_MBHC_CTL_BCS, 0x00 }, + { WCD939X_MBHC_TEST_CTL, 0x00 }, + { WCD939X_LDOH_MODE, 0x2b }, + { WCD939X_LDOH_BIAS, 0x68 }, + { WCD939X_LDOH_STB_LOADS, 0x00 }, + { WCD939X_LDOH_SLOWRAMP, 0x50 }, + { WCD939X_MICB1_TEST_CTL_1, 0x1a }, + { WCD939X_MICB1_TEST_CTL_2, 0x00 }, + { WCD939X_MICB1_TEST_CTL_3, 0xa4 }, + { WCD939X_MICB2_TEST_CTL_1, 0x1a }, + { WCD939X_MICB2_TEST_CTL_2, 0x00 }, + { WCD939X_MICB2_TEST_CTL_3, 0x24 }, + { WCD939X_MICB3_TEST_CTL_1, 0x9a }, + { WCD939X_MICB3_TEST_CTL_2, 0x80 }, + { WCD939X_MICB3_TEST_CTL_3, 0x24 }, + { WCD939X_MICB4_TEST_CTL_1, 0x1a }, + { WCD939X_MICB4_TEST_CTL_2, 0x80 }, + { WCD939X_MICB4_TEST_CTL_3, 0x24 }, + { WCD939X_TX_COM_ADC_VCM, 0x39 }, + { WCD939X_TX_COM_BIAS_ATEST, 0xe0 }, + { WCD939X_TX_COM_SPARE1, 0x00 }, + { WCD939X_TX_COM_SPARE2, 0x00 }, + { WCD939X_TX_COM_TXFE_DIV_CTL, 0x22 }, + { WCD939X_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD939X_TX_COM_SPARE3, 0x00 }, + { WCD939X_TX_COM_SPARE4, 0x00 }, + { WCD939X_TX_1_2_TEST_EN, 0xcc }, + { WCD939X_TX_1_2_ADC_IB, 0xe9 }, + { WCD939X_TX_1_2_ATEST_REFCTL, 0x0b }, + { WCD939X_TX_1_2_TEST_CTL, 0x38 }, + { WCD939X_TX_1_2_TEST_BLK_EN1, 0xff }, + { WCD939X_TX_1_2_TXFE1_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_TEST_EN, 0xcc }, + { WCD939X_TX_3_4_ADC_IB, 0xe9 }, + { WCD939X_TX_3_4_ATEST_REFCTL, 0x0b }, + { WCD939X_TX_3_4_TEST_CTL, 0x38 }, + { WCD939X_TX_3_4_TEST_BLK_EN3, 0xff }, + { WCD939X_TX_3_4_TXFE3_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_TEST_BLK_EN2, 0xfb }, + { WCD939X_TX_3_4_TXFE2_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_SPARE1, 0x00 }, + { WCD939X_TX_3_4_TEST_BLK_EN4, 0xfb }, + { WCD939X_TX_3_4_TXFE4_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_SPARE2, 0x00 }, + { WCD939X_CLASSH_MODE_1, 0x40 }, + { WCD939X_CLASSH_MODE_2, 0x3a }, + { WCD939X_CLASSH_MODE_3, 0xf0 }, + { WCD939X_CLASSH_CTRL_VCL_1, 0x7c }, + { WCD939X_CLASSH_CTRL_VCL_2, 0x82 }, + { WCD939X_CLASSH_CTRL_CCL_1, 0x31 }, + { WCD939X_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD939X_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD939X_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD939X_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD939X_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD939X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD939X_CLASSH_SPARE, 0x80 }, + { WCD939X_FLYBACK_EN, 0x4e }, + { WCD939X_FLYBACK_VNEG_CTRL_1, 0x0b }, + { WCD939X_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD939X_FLYBACK_VNEG_CTRL_3, 0x14 }, + { WCD939X_FLYBACK_VNEG_CTRL_4, 0xdb }, + { WCD939X_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD939X_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD939X_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD939X_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD939X_FLYBACK_VNEG_CTRL_9, 0x66 }, + { WCD939X_FLYBACK_VNEGDAC_CTRL_1, 0xed }, + { WCD939X_FLYBACK_VNEGDAC_CTRL_2, 0xf8 }, + { WCD939X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 }, + { WCD939X_FLYBACK_CTRL_1, 0x65 }, + { WCD939X_FLYBACK_TEST_CTL, 0x02 }, + { WCD939X_RX_AUX_SW_CTL, 0x00 }, + { WCD939X_RX_PA_AUX_IN_CONN, 0x01 }, + { WCD939X_RX_TIMER_DIV, 0x32 }, + { WCD939X_RX_OCP_CTL, 0x1f }, + { WCD939X_RX_OCP_COUNT, 0x77 }, + { WCD939X_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD939X_RX_BIAS_EAR_AMP, 0xaa }, + { WCD939X_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD939X_RX_BIAS_HPH_PA, 0xaa }, + { WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2, 0xca }, + { WCD939X_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD939X_RX_BIAS_HPH_CNP1, 0x82 }, + { WCD939X_RX_BIAS_HPH_LOWPOWER, 0x82 }, + { WCD939X_RX_BIAS_AUX_DAC, 0xa0 }, + { WCD939X_RX_BIAS_AUX_AMP, 0xaa }, + { WCD939X_RX_BIAS_VNEGDAC_BLEEDER, 0x50 }, + { WCD939X_RX_BIAS_MISC, 0x00 }, + { WCD939X_RX_BIAS_BUCK_RST, 0x08 }, + { WCD939X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD939X_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD939X_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD939X_RX_BIAS_FLYB_MID_RST, 0x14 }, + { WCD939X_HPH_CNP_EN, 0x80 }, + { WCD939X_HPH_CNP_WG_CTL, 0x9a }, + { WCD939X_HPH_CNP_WG_TIME, 0x14 }, + { WCD939X_HPH_OCP_CTL, 0x28 }, + { WCD939X_HPH_AUTO_CHOP, 0x56 }, + { WCD939X_HPH_CHOP_CTL, 0x83 }, + { WCD939X_HPH_PA_CTL1, 0x46 }, + { WCD939X_HPH_PA_CTL2, 0x50 }, + { WCD939X_HPH_L_EN, 0x80 }, + { WCD939X_HPH_L_TEST, 0xe0 }, + { WCD939X_HPH_L_ATEST, 0x50 }, + { WCD939X_HPH_R_EN, 0x80 }, + { WCD939X_HPH_R_TEST, 0xe0 }, + { WCD939X_HPH_R_ATEST, 0x50 }, + { WCD939X_HPH_RDAC_CLK_CTL1, 0x80 }, + { WCD939X_HPH_RDAC_CLK_CTL2, 0x0b }, + { WCD939X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD939X_HPH_REFBUFF_UHQA_CTL, 0x00 }, + { WCD939X_HPH_REFBUFF_LP_CTL, 0x8e }, + { WCD939X_HPH_L_DAC_CTL, 0x20 }, + { WCD939X_HPH_R_DAC_CTL, 0x20 }, + { WCD939X_HPH_SURGE_COMP_SEL, 0x55 }, + { WCD939X_HPH_SURGE_EN, 0x19 }, + { WCD939X_HPH_SURGE_MISC1, 0xa0 }, + { WCD939X_EAR_EN, 0x22 }, + { WCD939X_EAR_PA_CON, 0x44 }, + { WCD939X_EAR_SP_CON, 0xdb }, + { WCD939X_EAR_DAC_CON, 0x80 }, + { WCD939X_EAR_CNP_FSM_CON, 0xb2 }, + { WCD939X_EAR_TEST_CTL, 0x00 }, + { WCD939X_FLYBACK_NEW_CTRL_2, 0x00 }, + { WCD939X_FLYBACK_NEW_CTRL_3, 0x00 }, + { WCD939X_FLYBACK_NEW_CTRL_4, 0x44 }, + { WCD939X_ANA_NEW_PAGE, 0x00 }, + { WCD939X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD939X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD939X_SLEEP_CTL, 0x18 }, + { WCD939X_SLEEP_WATCHDOG_CTL, 0x00 }, + { WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 }, + { WCD939X_MBHC_NEW_CTL_1, 0x02 }, + { WCD939X_MBHC_NEW_CTL_2, 0x05 }, + { WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 }, + { WCD939X_MBHC_NEW_ZDET_ANA_CTL, 0x0f }, + { WCD939X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 }, + { WCD939X_TX_NEW_CH12_MUX, 0x11 }, + { WCD939X_TX_NEW_CH34_MUX, 0x23 }, + { WCD939X_DIE_CRACK_DET_EN, 0x00 }, + { WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L, 0x00 }, + { WCD939X_HPH_NEW_INT_RDAC_VREF_CTL, 0x08 }, + { WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_MISC1, 0x32 }, + { WCD939X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD939X_HPH_NEW_INT_TIMER1, 0xfe }, + { WCD939X_HPH_NEW_INT_TIMER2, 0x02 }, + { WCD939X_HPH_NEW_INT_TIMER3, 0x4e }, + { WCD939X_HPH_NEW_INT_TIMER4, 0x54 }, + { WCD939X_HPH_NEW_INT_PA_RDAC_MISC2, 0x0b }, + { WCD939X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, + { WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xa0 }, + { WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xa0 }, + { WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x64 }, + { WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 }, + { WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 }, + { WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57 }, + { WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01 }, + { WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00 }, + { WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW, 0x47 }, + { WCD939X_EAR_INT_NEW_CHOPPER_CON, 0xa8 }, + { WCD939X_EAR_INT_NEW_CNP_VCM_CON1, 0x42 }, + { WCD939X_EAR_INT_NEW_CNP_VCM_CON2, 0x22 }, + { WCD939X_EAR_INT_NEW_DYNAMIC_BIAS, 0x00 }, + { WCD939X_SLEEP_INT_WATCHDOG_CTL_1, 0x0a }, + { WCD939X_SLEEP_INT_WATCHDOG_CTL_2, 0x0a }, + { WCD939X_DIE_CRACK_INT_DET_INT1, 0x02 }, + { WCD939X_DIE_CRACK_INT_DET_INT2, 0x60 }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2, 0xff }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1, 0x7f }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0, 0x3f }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M, 0x1f }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M, 0x0f }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1, 0xd7 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0, 0xc8 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP, 0xc6 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1, 0x95 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0, 0x6a }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP, 0x05 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0, 0xa5 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, 0x13 }, + { WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1, 0x88 }, + { WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP, 0x42 }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_L2, 0xff }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_L1, 0x64 }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_L0, 0x64 }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_ULP, 0x77 }, + { WCD939X_DIGITAL_PAGE, 0x00 }, + { WCD939X_DIGITAL_SWR_TX_CLK_RATE, 0x00 }, + { WCD939X_DIGITAL_CDC_RST_CTL, 0x03 }, + { WCD939X_DIGITAL_TOP_CLK_CFG, 0x00 }, + { WCD939X_DIGITAL_CDC_ANA_CLK_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_DIG_CLK_CTL, 0xf0 }, + { WCD939X_DIGITAL_SWR_RST_EN, 0x00 }, + { WCD939X_DIGITAL_CDC_PATH_MODE, 0x55 }, + { WCD939X_DIGITAL_CDC_RX_RST, 0x00 }, + { WCD939X_DIGITAL_CDC_RX0_CTL, 0xfc }, + { WCD939X_DIGITAL_CDC_RX1_CTL, 0xfc }, + { WCD939X_DIGITAL_CDC_RX2_CTL, 0xfc }, + { WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, 0x00 }, + { WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, 0x00 }, + { WCD939X_DIGITAL_CDC_COMP_CTL_0, 0x00 }, + { WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL, 0x1e }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A3_0, 0xac }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1a }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A5_0, 0xbc }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A6_0, 0xc7 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A7_0, 0xf8 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_0, 0x47 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_1, 0x43 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_2, 0xb1 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_3, 0x17 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R1, 0x4d }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R2, 0x29 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R3, 0x34 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R4, 0x59 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R5, 0x66 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R6, 0x87 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R7, 0x64 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A1_0, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A1_1, 0x01 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A2_0, 0x96 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A2_1, 0x09 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A3_0, 0xab }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A3_1, 0x05 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A4_0, 0x1c }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A4_1, 0x02 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A5_0, 0x17 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A5_1, 0x02 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A6_0, 0xaa }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A7_0, 0xe3 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_0, 0x69 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_1, 0x54 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_2, 0x02 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_3, 0x15 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R1, 0xa4 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R2, 0xb5 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R3, 0x86 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R4, 0x85 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R5, 0xaa }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R6, 0xe2 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R7, 0x62 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xa9 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3d }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2e }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01 }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1, 0xfc }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2, 0x01 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_SWR_CLH, 0x00 }, + { WCD939X_DIGITAL_SWR_CLH_BYP, 0x00 }, + { WCD939X_DIGITAL_CDC_TX0_CTL, 0x68 }, + { WCD939X_DIGITAL_CDC_TX1_CTL, 0x68 }, + { WCD939X_DIGITAL_CDC_TX2_CTL, 0x68 }, + { WCD939X_DIGITAL_CDC_TX_RST, 0x00 }, + { WCD939X_DIGITAL_CDC_REQ_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_RST, 0x00 }, + { WCD939X_DIGITAL_CDC_AMIC_CTL, 0x0f }, + { WCD939X_DIGITAL_CDC_DMIC_CTL, 0x04 }, + { WCD939X_DIGITAL_CDC_DMIC1_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_DMIC2_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_DMIC3_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_DMIC4_CTL, 0x01 }, + { WCD939X_DIGITAL_EFUSE_PRG_CTL, 0x00 }, + { WCD939X_DIGITAL_EFUSE_CTL, 0x2b }, + { WCD939X_DIGITAL_CDC_DMIC_RATE_1_2, 0x11 }, + { WCD939X_DIGITAL_CDC_DMIC_RATE_3_4, 0x11 }, + { WCD939X_DIGITAL_PDM_WD_CTL0, 0x00 }, + { WCD939X_DIGITAL_PDM_WD_CTL1, 0x00 }, + { WCD939X_DIGITAL_PDM_WD_CTL2, 0x00 }, + { WCD939X_DIGITAL_INTR_MODE, 0x00 }, + { WCD939X_DIGITAL_INTR_MASK_0, 0xff }, + { WCD939X_DIGITAL_INTR_MASK_1, 0xe7 }, + { WCD939X_DIGITAL_INTR_MASK_2, 0x0e }, + { WCD939X_DIGITAL_INTR_CLEAR_0, 0x00 }, + { WCD939X_DIGITAL_INTR_CLEAR_1, 0x00 }, + { WCD939X_DIGITAL_INTR_CLEAR_2, 0x00 }, + { WCD939X_DIGITAL_INTR_LEVEL_0, 0x00 }, + { WCD939X_DIGITAL_INTR_LEVEL_1, 0x00 }, + { WCD939X_DIGITAL_INTR_LEVEL_2, 0x00 }, + { WCD939X_DIGITAL_INTR_SET_0, 0x00 }, + { WCD939X_DIGITAL_INTR_SET_1, 0x00 }, + { WCD939X_DIGITAL_INTR_SET_2, 0x00 }, + { WCD939X_DIGITAL_INTR_TEST_0, 0x00 }, + { WCD939X_DIGITAL_INTR_TEST_1, 0x00 }, + { WCD939X_DIGITAL_INTR_TEST_2, 0x00 }, + { WCD939X_DIGITAL_TX_MODE_DBG_EN, 0x00 }, + { WCD939X_DIGITAL_TX_MODE_DBG_0_1, 0x00 }, + { WCD939X_DIGITAL_TX_MODE_DBG_2_3, 0x00 }, + { WCD939X_DIGITAL_LB_IN_SEL_CTL, 0x00 }, + { WCD939X_DIGITAL_LOOP_BACK_MODE, 0x00 }, + { WCD939X_DIGITAL_SWR_DAC_TEST, 0x00 }, + { WCD939X_DIGITAL_SWR_HM_TEST_RX_0, 0x40 }, + { WCD939X_DIGITAL_SWR_HM_TEST_TX_0, 0x40 }, + { WCD939X_DIGITAL_SWR_HM_TEST_RX_1, 0x00 }, + { WCD939X_DIGITAL_SWR_HM_TEST_TX_1, 0x00 }, + { WCD939X_DIGITAL_SWR_HM_TEST_TX_2, 0x00 }, + { WCD939X_DIGITAL_PAD_CTL_SWR_0, 0x8f }, + { WCD939X_DIGITAL_PAD_CTL_SWR_1, 0x06 }, + { WCD939X_DIGITAL_I2C_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE, 0x00 }, + { WCD939X_DIGITAL_EFUSE_TEST_CTL_0, 0x00 }, + { WCD939X_DIGITAL_EFUSE_TEST_CTL_1, 0x00 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_RX0, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_RX1, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_TX0, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_TX1, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_TX2, 0xf1 }, + { WCD939X_DIGITAL_PAD_INP_DIS_0, 0x00 }, + { WCD939X_DIGITAL_PAD_INP_DIS_1, 0x00 }, + { WCD939X_DIGITAL_DRIVE_STRENGTH_0, 0x00 }, + { WCD939X_DIGITAL_DRIVE_STRENGTH_1, 0x00 }, + { WCD939X_DIGITAL_DRIVE_STRENGTH_2, 0x00 }, + { WCD939X_DIGITAL_RX_DATA_EDGE_CTL, 0x1f }, + { WCD939X_DIGITAL_TX_DATA_EDGE_CTL, 0x80 }, + { WCD939X_DIGITAL_GPIO_MODE, 0x00 }, + { WCD939X_DIGITAL_PIN_CTL_OE, 0x00 }, + { WCD939X_DIGITAL_PIN_CTL_DATA_0, 0x00 }, + { WCD939X_DIGITAL_PIN_CTL_DATA_1, 0x00 }, + { WCD939X_DIGITAL_DIG_DEBUG_CTL, 0x00 }, + { WCD939X_DIGITAL_DIG_DEBUG_EN, 0x00 }, + { WCD939X_DIGITAL_ANA_CSR_DBG_ADD, 0x00 }, + { WCD939X_DIGITAL_ANA_CSR_DBG_CTL, 0x48 }, + { WCD939X_DIGITAL_SSP_DBG, 0x00 }, + { WCD939X_DIGITAL_SPARE_0, 0x00 }, + { WCD939X_DIGITAL_SPARE_1, 0x00 }, + { WCD939X_DIGITAL_SPARE_2, 0x00 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_0, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_1, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_2, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_3, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_4, 0x88 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA0, 0x55 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA1, 0x55 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA2, 0x55 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA3, 0x01 }, + { WCD939X_DIGITAL_DEM_SECOND_ORDER, 0x03 }, + { WCD939X_DIGITAL_DSM_CTRL, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_0, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_1, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_2, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_3, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_0, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_1, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_2, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_3, 0x00 }, + { WCD939X_RX_TOP_PAGE, 0x00 }, + { WCD939X_RX_TOP_TOP_CFG0, 0x00 }, + { WCD939X_RX_TOP_HPHL_COMP_WR_LSB, 0x00 }, + { WCD939X_RX_TOP_HPHL_COMP_WR_MSB, 0x00 }, + { WCD939X_RX_TOP_HPHL_COMP_LUT, 0x00 }, + { WCD939X_RX_TOP_HPHR_COMP_WR_LSB, 0x00 }, + { WCD939X_RX_TOP_HPHR_COMP_WR_MSB, 0x00 }, + { WCD939X_RX_TOP_HPHR_COMP_LUT, 0x00 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG1, 0x05 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG2, 0x08 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG3, 0x00 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG4, 0x00 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG1, 0x03 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG2, 0x08 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG3, 0x00 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG4, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_CFG0, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_CFG1, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_CFG0, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_CFG1, 0x00 }, + { WCD939X_RX_TOP_PATH_CFG2, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC0, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC1, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC2, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC3, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC0, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC1, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC2, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC3, 0x00 }, + { WCD939X_RX_TOP_PATH_SEC4, 0x00 }, + { WCD939X_RX_TOP_PATH_SEC5, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL0, 0x60 }, + { WCD939X_COMPANDER_HPHL_CTL1, 0xdb }, + { WCD939X_COMPANDER_HPHL_CTL2, 0xff }, + { WCD939X_COMPANDER_HPHL_CTL3, 0x35 }, + { WCD939X_COMPANDER_HPHL_CTL4, 0xff }, + { WCD939X_COMPANDER_HPHL_CTL5, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL7, 0x08 }, + { WCD939X_COMPANDER_HPHL_CTL8, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL9, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL10, 0x06 }, + { WCD939X_COMPANDER_HPHL_CTL11, 0x12 }, + { WCD939X_COMPANDER_HPHL_CTL12, 0x1e }, + { WCD939X_COMPANDER_HPHL_CTL13, 0x2a }, + { WCD939X_COMPANDER_HPHL_CTL14, 0x36 }, + { WCD939X_COMPANDER_HPHL_CTL15, 0x3c }, + { WCD939X_COMPANDER_HPHL_CTL16, 0xc4 }, + { WCD939X_COMPANDER_HPHL_CTL17, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL18, 0x0c }, + { WCD939X_COMPANDER_HPHL_CTL19, 0x16 }, + { WCD939X_R_CTL0, 0x60 }, + { WCD939X_R_CTL1, 0xdb }, + { WCD939X_R_CTL2, 0xff }, + { WCD939X_R_CTL3, 0x35 }, + { WCD939X_R_CTL4, 0xff }, + { WCD939X_R_CTL5, 0x00 }, + { WCD939X_R_CTL7, 0x08 }, + { WCD939X_R_CTL8, 0x00 }, + { WCD939X_R_CTL9, 0x00 }, + { WCD939X_R_CTL10, 0x06 }, + { WCD939X_R_CTL11, 0x12 }, + { WCD939X_R_CTL12, 0x1e }, + { WCD939X_R_CTL13, 0x2a }, + { WCD939X_R_CTL14, 0x36 }, + { WCD939X_R_CTL15, 0x3c }, + { WCD939X_R_CTL16, 0xc4 }, + { WCD939X_R_CTL17, 0x00 }, + { WCD939X_R_CTL18, 0x0c }, + { WCD939X_R_CTL19, 0x16 }, + { WCD939X_E_PATH_CTL, 0x00 }, + { WCD939X_E_CFG0, 0x07 }, + { WCD939X_E_CFG1, 0x3c }, + { WCD939X_E_CFG2, 0x00 }, + { WCD939X_E_CFG3, 0x00 }, + { WCD939X_DSD_HPHL_PATH_CTL, 0x00 }, + { WCD939X_DSD_HPHL_CFG0, 0x00 }, + { WCD939X_DSD_HPHL_CFG1, 0x00 }, + { WCD939X_DSD_HPHL_CFG2, 0x22 }, + { WCD939X_DSD_HPHL_CFG3, 0x00 }, + { WCD939X_DSD_HPHL_CFG4, 0x1a }, + { WCD939X_DSD_HPHL_CFG5, 0x00 }, + { WCD939X_DSD_HPHR_PATH_CTL, 0x00 }, + { WCD939X_DSD_HPHR_CFG0, 0x00 }, + { WCD939X_DSD_HPHR_CFG1, 0x00 }, + { WCD939X_DSD_HPHR_CFG2, 0x22 }, + { WCD939X_DSD_HPHR_CFG3, 0x00 }, + { WCD939X_DSD_HPHR_CFG4, 0x1a }, + { WCD939X_DSD_HPHR_CFG5, 0x00 }, +}; + +static bool wcd939x_rdwr_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD939X_ANA_PAGE: + case WCD939X_ANA_BIAS: + case WCD939X_ANA_RX_SUPPLIES: + case WCD939X_ANA_HPH: + case WCD939X_ANA_EAR: + case WCD939X_ANA_EAR_COMPANDER_CTL: + case WCD939X_ANA_TX_CH1: + case WCD939X_ANA_TX_CH2: + case WCD939X_ANA_TX_CH3: + case WCD939X_ANA_TX_CH4: + case WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC: + case WCD939X_ANA_MICB3_DSP_EN_LOGIC: + case WCD939X_ANA_MBHC_MECH: + case WCD939X_ANA_MBHC_ELECT: + case WCD939X_ANA_MBHC_ZDET: + case WCD939X_ANA_MBHC_BTN0: + case WCD939X_ANA_MBHC_BTN1: + case WCD939X_ANA_MBHC_BTN2: + case WCD939X_ANA_MBHC_BTN3: + case WCD939X_ANA_MBHC_BTN4: + case WCD939X_ANA_MBHC_BTN5: + case WCD939X_ANA_MBHC_BTN6: + case WCD939X_ANA_MBHC_BTN7: + case WCD939X_ANA_MICB1: + case WCD939X_ANA_MICB2: + case WCD939X_ANA_MICB2_RAMP: + case WCD939X_ANA_MICB3: + case WCD939X_ANA_MICB4: + case WCD939X_BIAS_CTL: + case WCD939X_BIAS_VBG_FINE_ADJ: + case WCD939X_LDOL_VDDCX_ADJUST: + case WCD939X_LDOL_DISABLE_LDOL: + case WCD939X_MBHC_CTL_CLK: + case WCD939X_MBHC_CTL_ANA: + case WCD939X_MBHC_ZDET_VNEG_CTL: + case WCD939X_MBHC_ZDET_BIAS_CTL: + case WCD939X_MBHC_CTL_BCS: + case WCD939X_MBHC_TEST_CTL: + case WCD939X_LDOH_MODE: + case WCD939X_LDOH_BIAS: + case WCD939X_LDOH_STB_LOADS: + case WCD939X_LDOH_SLOWRAMP: + case WCD939X_MICB1_TEST_CTL_1: + case WCD939X_MICB1_TEST_CTL_2: + case WCD939X_MICB1_TEST_CTL_3: + case WCD939X_MICB2_TEST_CTL_1: + case WCD939X_MICB2_TEST_CTL_2: + case WCD939X_MICB2_TEST_CTL_3: + case WCD939X_MICB3_TEST_CTL_1: + case WCD939X_MICB3_TEST_CTL_2: + case WCD939X_MICB3_TEST_CTL_3: + case WCD939X_MICB4_TEST_CTL_1: + case WCD939X_MICB4_TEST_CTL_2: + case WCD939X_MICB4_TEST_CTL_3: + case WCD939X_TX_COM_ADC_VCM: + case WCD939X_TX_COM_BIAS_ATEST: + case WCD939X_TX_COM_SPARE1: + case WCD939X_TX_COM_SPARE2: + case WCD939X_TX_COM_TXFE_DIV_CTL: + case WCD939X_TX_COM_TXFE_DIV_START: + case WCD939X_TX_COM_SPARE3: + case WCD939X_TX_COM_SPARE4: + case WCD939X_TX_1_2_TEST_EN: + case WCD939X_TX_1_2_ADC_IB: + case WCD939X_TX_1_2_ATEST_REFCTL: + case WCD939X_TX_1_2_TEST_CTL: + case WCD939X_TX_1_2_TEST_BLK_EN1: + case WCD939X_TX_1_2_TXFE1_CLKDIV: + case WCD939X_TX_3_4_TEST_EN: + case WCD939X_TX_3_4_ADC_IB: + case WCD939X_TX_3_4_ATEST_REFCTL: + case WCD939X_TX_3_4_TEST_CTL: + case WCD939X_TX_3_4_TEST_BLK_EN3: + case WCD939X_TX_3_4_TXFE3_CLKDIV: + case WCD939X_TX_3_4_TEST_BLK_EN2: + case WCD939X_TX_3_4_TXFE2_CLKDIV: + case WCD939X_TX_3_4_SPARE1: + case WCD939X_TX_3_4_TEST_BLK_EN4: + case WCD939X_TX_3_4_TXFE4_CLKDIV: + case WCD939X_TX_3_4_SPARE2: + case WCD939X_CLASSH_MODE_1: + case WCD939X_CLASSH_MODE_2: + case WCD939X_CLASSH_MODE_3: + case WCD939X_CLASSH_CTRL_VCL_1: + case WCD939X_CLASSH_CTRL_VCL_2: + case WCD939X_CLASSH_CTRL_CCL_1: + case WCD939X_CLASSH_CTRL_CCL_2: + case WCD939X_CLASSH_CTRL_CCL_3: + case WCD939X_CLASSH_CTRL_CCL_4: + case WCD939X_CLASSH_CTRL_CCL_5: + case WCD939X_CLASSH_BUCK_TMUX_A_D: + case WCD939X_CLASSH_BUCK_SW_DRV_CNTL: + case WCD939X_CLASSH_SPARE: + case WCD939X_FLYBACK_EN: + case WCD939X_FLYBACK_VNEG_CTRL_1: + case WCD939X_FLYBACK_VNEG_CTRL_2: + case WCD939X_FLYBACK_VNEG_CTRL_3: + case WCD939X_FLYBACK_VNEG_CTRL_4: + case WCD939X_FLYBACK_VNEG_CTRL_5: + case WCD939X_FLYBACK_VNEG_CTRL_6: + case WCD939X_FLYBACK_VNEG_CTRL_7: + case WCD939X_FLYBACK_VNEG_CTRL_8: + case WCD939X_FLYBACK_VNEG_CTRL_9: + case WCD939X_FLYBACK_VNEGDAC_CTRL_1: + case WCD939X_FLYBACK_VNEGDAC_CTRL_2: + case WCD939X_FLYBACK_VNEGDAC_CTRL_3: + case WCD939X_FLYBACK_CTRL_1: + case WCD939X_FLYBACK_TEST_CTL: + case WCD939X_RX_AUX_SW_CTL: + case WCD939X_RX_PA_AUX_IN_CONN: + case WCD939X_RX_TIMER_DIV: + case WCD939X_RX_OCP_CTL: + case WCD939X_RX_OCP_COUNT: + case WCD939X_RX_BIAS_EAR_DAC: + case WCD939X_RX_BIAS_EAR_AMP: + case WCD939X_RX_BIAS_HPH_LDO: + case WCD939X_RX_BIAS_HPH_PA: + case WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2: + case WCD939X_RX_BIAS_HPH_RDAC_LDO: + case WCD939X_RX_BIAS_HPH_CNP1: + case WCD939X_RX_BIAS_HPH_LOWPOWER: + case WCD939X_RX_BIAS_AUX_DAC: + case WCD939X_RX_BIAS_AUX_AMP: + case WCD939X_RX_BIAS_VNEGDAC_BLEEDER: + case WCD939X_RX_BIAS_MISC: + case WCD939X_RX_BIAS_BUCK_RST: + case WCD939X_RX_BIAS_BUCK_VREF_ERRAMP: + case WCD939X_RX_BIAS_FLYB_ERRAMP: + case WCD939X_RX_BIAS_FLYB_BUFF: + case WCD939X_RX_BIAS_FLYB_MID_RST: + case WCD939X_HPH_CNP_EN: + case WCD939X_HPH_CNP_WG_CTL: + case WCD939X_HPH_CNP_WG_TIME: + case WCD939X_HPH_OCP_CTL: + case WCD939X_HPH_AUTO_CHOP: + case WCD939X_HPH_CHOP_CTL: + case WCD939X_HPH_PA_CTL1: + case WCD939X_HPH_PA_CTL2: + case WCD939X_HPH_L_EN: + case WCD939X_HPH_L_TEST: + case WCD939X_HPH_L_ATEST: + case WCD939X_HPH_R_EN: + case WCD939X_HPH_R_TEST: + case WCD939X_HPH_R_ATEST: + case WCD939X_HPH_RDAC_CLK_CTL1: + case WCD939X_HPH_RDAC_CLK_CTL2: + case WCD939X_HPH_RDAC_LDO_CTL: + case WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL: + case WCD939X_HPH_REFBUFF_UHQA_CTL: + case WCD939X_HPH_REFBUFF_LP_CTL: + case WCD939X_HPH_L_DAC_CTL: + case WCD939X_HPH_R_DAC_CTL: + case WCD939X_HPH_SURGE_COMP_SEL: + case WCD939X_HPH_SURGE_EN: + case WCD939X_HPH_SURGE_MISC1: + case WCD939X_EAR_EN: + case WCD939X_EAR_PA_CON: + case WCD939X_EAR_SP_CON: + case WCD939X_EAR_DAC_CON: + case WCD939X_EAR_CNP_FSM_CON: + case WCD939X_EAR_TEST_CTL: + case WCD939X_FLYBACK_NEW_CTRL_2: + case WCD939X_FLYBACK_NEW_CTRL_3: + case WCD939X_FLYBACK_NEW_CTRL_4: + case WCD939X_ANA_NEW_PAGE: + case WCD939X_HPH_NEW_ANA_HPH2: + case WCD939X_HPH_NEW_ANA_HPH3: + case WCD939X_SLEEP_CTL: + case WCD939X_SLEEP_WATCHDOG_CTL: + case WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL: + case WCD939X_MBHC_NEW_CTL_1: + case WCD939X_MBHC_NEW_CTL_2: + case WCD939X_MBHC_NEW_PLUG_DETECT_CTL: + case WCD939X_MBHC_NEW_ZDET_ANA_CTL: + case WCD939X_MBHC_NEW_ZDET_RAMP_CTL: + case WCD939X_TX_NEW_CH12_MUX: + case WCD939X_TX_NEW_CH34_MUX: + case WCD939X_DIE_CRACK_DET_EN: + case WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL: + case WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L: + case WCD939X_HPH_NEW_INT_RDAC_VREF_CTL: + case WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL: + case WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R: + case WCD939X_HPH_NEW_INT_PA_MISC1: + case WCD939X_HPH_NEW_INT_PA_MISC2: + case WCD939X_HPH_NEW_INT_PA_RDAC_MISC: + case WCD939X_HPH_NEW_INT_TIMER1: + case WCD939X_HPH_NEW_INT_TIMER2: + case WCD939X_HPH_NEW_INT_TIMER3: + case WCD939X_HPH_NEW_INT_TIMER4: + case WCD939X_HPH_NEW_INT_PA_RDAC_MISC2: + case WCD939X_HPH_NEW_INT_PA_RDAC_MISC3: + case WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L: + case WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R: + case WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI: + case WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP: + case WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP: + case WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL: + case WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL: + case WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT: + case WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW: + case WCD939X_EAR_INT_NEW_CHOPPER_CON: + case WCD939X_EAR_INT_NEW_CNP_VCM_CON1: + case WCD939X_EAR_INT_NEW_CNP_VCM_CON2: + case WCD939X_EAR_INT_NEW_DYNAMIC_BIAS: + case WCD939X_SLEEP_INT_WATCHDOG_CTL_1: + case WCD939X_SLEEP_INT_WATCHDOG_CTL_2: + case WCD939X_DIE_CRACK_INT_DET_INT1: + case WCD939X_DIE_CRACK_INT_DET_INT2: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP: + case WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1: + case WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP: + case WCD939X_TX_COM_NEW_INT_ADC_INT_L2: + case WCD939X_TX_COM_NEW_INT_ADC_INT_L1: + case WCD939X_TX_COM_NEW_INT_ADC_INT_L0: + case WCD939X_TX_COM_NEW_INT_ADC_INT_ULP: + case WCD939X_DIGITAL_PAGE: + case WCD939X_DIGITAL_SWR_TX_CLK_RATE: + case WCD939X_DIGITAL_CDC_RST_CTL: + case WCD939X_DIGITAL_TOP_CLK_CFG: + case WCD939X_DIGITAL_CDC_ANA_CLK_CTL: + case WCD939X_DIGITAL_CDC_DIG_CLK_CTL: + case WCD939X_DIGITAL_SWR_RST_EN: + case WCD939X_DIGITAL_CDC_PATH_MODE: + case WCD939X_DIGITAL_CDC_RX_RST: + case WCD939X_DIGITAL_CDC_RX0_CTL: + case WCD939X_DIGITAL_CDC_RX1_CTL: + case WCD939X_DIGITAL_CDC_RX2_CTL: + case WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1: + case WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3: + case WCD939X_DIGITAL_CDC_COMP_CTL_0: + case WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL: + case WCD939X_DIGITAL_CDC_HPH_DSM_A1_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A1_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A2_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A2_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A3_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A3_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A4_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A4_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A5_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A5_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A6_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A7_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_2: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_3: + case WCD939X_DIGITAL_CDC_HPH_DSM_R1: + case WCD939X_DIGITAL_CDC_HPH_DSM_R2: + case WCD939X_DIGITAL_CDC_HPH_DSM_R3: + case WCD939X_DIGITAL_CDC_HPH_DSM_R4: + case WCD939X_DIGITAL_CDC_HPH_DSM_R5: + case WCD939X_DIGITAL_CDC_HPH_DSM_R6: + case WCD939X_DIGITAL_CDC_HPH_DSM_R7: + case WCD939X_DIGITAL_CDC_EAR_DSM_A1_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A1_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A2_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A2_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A3_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A3_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A4_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A4_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A5_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A5_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A6_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A7_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_2: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_3: + case WCD939X_DIGITAL_CDC_EAR_DSM_R1: + case WCD939X_DIGITAL_CDC_EAR_DSM_R2: + case WCD939X_DIGITAL_CDC_EAR_DSM_R3: + case WCD939X_DIGITAL_CDC_EAR_DSM_R4: + case WCD939X_DIGITAL_CDC_EAR_DSM_R5: + case WCD939X_DIGITAL_CDC_EAR_DSM_R6: + case WCD939X_DIGITAL_CDC_EAR_DSM_R7: + case WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0: + case WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1: + case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0: + case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1: + case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2: + case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0: + case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1: + case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2: + case WCD939X_DIGITAL_CDC_HPH_GAIN_CTL: + case WCD939X_DIGITAL_CDC_EAR_GAIN_CTL: + case WCD939X_DIGITAL_CDC_EAR_PATH_CTL: + case WCD939X_DIGITAL_CDC_SWR_CLH: + case WCD939X_DIGITAL_SWR_CLH_BYP: + case WCD939X_DIGITAL_CDC_TX0_CTL: + case WCD939X_DIGITAL_CDC_TX1_CTL: + case WCD939X_DIGITAL_CDC_TX2_CTL: + case WCD939X_DIGITAL_CDC_TX_RST: + case WCD939X_DIGITAL_CDC_REQ_CTL: + case WCD939X_DIGITAL_CDC_RST: + case WCD939X_DIGITAL_CDC_AMIC_CTL: + case WCD939X_DIGITAL_CDC_DMIC_CTL: + case WCD939X_DIGITAL_CDC_DMIC1_CTL: + case WCD939X_DIGITAL_CDC_DMIC2_CTL: + case WCD939X_DIGITAL_CDC_DMIC3_CTL: + case WCD939X_DIGITAL_CDC_DMIC4_CTL: + case WCD939X_DIGITAL_EFUSE_PRG_CTL: + case WCD939X_DIGITAL_EFUSE_CTL: + case WCD939X_DIGITAL_CDC_DMIC_RATE_1_2: + case WCD939X_DIGITAL_CDC_DMIC_RATE_3_4: + case WCD939X_DIGITAL_PDM_WD_CTL0: + case WCD939X_DIGITAL_PDM_WD_CTL1: + case WCD939X_DIGITAL_PDM_WD_CTL2: + case WCD939X_DIGITAL_INTR_MODE: + case WCD939X_DIGITAL_INTR_MASK_0: + case WCD939X_DIGITAL_INTR_MASK_1: + case WCD939X_DIGITAL_INTR_MASK_2: + case WCD939X_DIGITAL_INTR_CLEAR_0: + case WCD939X_DIGITAL_INTR_CLEAR_1: + case WCD939X_DIGITAL_INTR_CLEAR_2: + case WCD939X_DIGITAL_INTR_LEVEL_0: + case WCD939X_DIGITAL_INTR_LEVEL_1: + case WCD939X_DIGITAL_INTR_LEVEL_2: + case WCD939X_DIGITAL_INTR_SET_0: + case WCD939X_DIGITAL_INTR_SET_1: + case WCD939X_DIGITAL_INTR_SET_2: + case WCD939X_DIGITAL_INTR_TEST_0: + case WCD939X_DIGITAL_INTR_TEST_1: + case WCD939X_DIGITAL_INTR_TEST_2: + case WCD939X_DIGITAL_TX_MODE_DBG_EN: + case WCD939X_DIGITAL_TX_MODE_DBG_0_1: + case WCD939X_DIGITAL_TX_MODE_DBG_2_3: + case WCD939X_DIGITAL_LB_IN_SEL_CTL: + case WCD939X_DIGITAL_LOOP_BACK_MODE: + case WCD939X_DIGITAL_SWR_DAC_TEST: + case WCD939X_DIGITAL_SWR_HM_TEST_RX_0: + case WCD939X_DIGITAL_SWR_HM_TEST_TX_0: + case WCD939X_DIGITAL_SWR_HM_TEST_RX_1: + case WCD939X_DIGITAL_SWR_HM_TEST_TX_1: + case WCD939X_DIGITAL_SWR_HM_TEST_TX_2: + case WCD939X_DIGITAL_PAD_CTL_SWR_0: + case WCD939X_DIGITAL_PAD_CTL_SWR_1: + case WCD939X_DIGITAL_I2C_CTL: + case WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE: + case WCD939X_DIGITAL_EFUSE_TEST_CTL_0: + case WCD939X_DIGITAL_EFUSE_TEST_CTL_1: + case WCD939X_DIGITAL_PAD_CTL_PDM_RX0: + case WCD939X_DIGITAL_PAD_CTL_PDM_RX1: + case WCD939X_DIGITAL_PAD_CTL_PDM_TX0: + case WCD939X_DIGITAL_PAD_CTL_PDM_TX1: + case WCD939X_DIGITAL_PAD_CTL_PDM_TX2: + case WCD939X_DIGITAL_PAD_INP_DIS_0: + case WCD939X_DIGITAL_PAD_INP_DIS_1: + case WCD939X_DIGITAL_DRIVE_STRENGTH_0: + case WCD939X_DIGITAL_DRIVE_STRENGTH_1: + case WCD939X_DIGITAL_DRIVE_STRENGTH_2: + case WCD939X_DIGITAL_RX_DATA_EDGE_CTL: + case WCD939X_DIGITAL_TX_DATA_EDGE_CTL: + case WCD939X_DIGITAL_GPIO_MODE: + case WCD939X_DIGITAL_PIN_CTL_OE: + case WCD939X_DIGITAL_PIN_CTL_DATA_0: + case WCD939X_DIGITAL_PIN_CTL_DATA_1: + case WCD939X_DIGITAL_DIG_DEBUG_CTL: + case WCD939X_DIGITAL_DIG_DEBUG_EN: + case WCD939X_DIGITAL_ANA_CSR_DBG_ADD: + case WCD939X_DIGITAL_ANA_CSR_DBG_CTL: + case WCD939X_DIGITAL_SSP_DBG: + case WCD939X_DIGITAL_SPARE_0: + case WCD939X_DIGITAL_SPARE_1: + case WCD939X_DIGITAL_SPARE_2: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_0: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_1: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_2: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_3: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_4: + case WCD939X_DIGITAL_DEM_BYPASS_DATA0: + case WCD939X_DIGITAL_DEM_BYPASS_DATA1: + case WCD939X_DIGITAL_DEM_BYPASS_DATA2: + case WCD939X_DIGITAL_DEM_BYPASS_DATA3: + case WCD939X_DIGITAL_DEM_SECOND_ORDER: + case WCD939X_DIGITAL_DSM_CTRL: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_0: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_1: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_2: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_3: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_0: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_1: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_2: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_3: + case WCD939X_RX_TOP_PAGE: + case WCD939X_RX_TOP_TOP_CFG0: + case WCD939X_RX_TOP_HPHL_COMP_WR_LSB: + case WCD939X_RX_TOP_HPHL_COMP_WR_MSB: + case WCD939X_RX_TOP_HPHL_COMP_LUT: + case WCD939X_RX_TOP_HPHR_COMP_WR_LSB: + case WCD939X_RX_TOP_HPHR_COMP_WR_MSB: + case WCD939X_RX_TOP_HPHR_COMP_LUT: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG1: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG2: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG3: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG4: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG1: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG2: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG3: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG4: + case WCD939X_RX_TOP_HPHL_PATH_CFG0: + case WCD939X_RX_TOP_HPHL_PATH_CFG1: + case WCD939X_RX_TOP_HPHR_PATH_CFG0: + case WCD939X_RX_TOP_HPHR_PATH_CFG1: + case WCD939X_RX_TOP_PATH_CFG2: + case WCD939X_RX_TOP_HPHL_PATH_SEC0: + case WCD939X_RX_TOP_HPHL_PATH_SEC1: + case WCD939X_RX_TOP_HPHL_PATH_SEC2: + case WCD939X_RX_TOP_HPHL_PATH_SEC3: + case WCD939X_RX_TOP_HPHR_PATH_SEC0: + case WCD939X_RX_TOP_HPHR_PATH_SEC1: + case WCD939X_RX_TOP_HPHR_PATH_SEC2: + case WCD939X_RX_TOP_HPHR_PATH_SEC3: + case WCD939X_RX_TOP_PATH_SEC4: + case WCD939X_RX_TOP_PATH_SEC5: + case WCD939X_COMPANDER_HPHL_CTL0: + case WCD939X_COMPANDER_HPHL_CTL1: + case WCD939X_COMPANDER_HPHL_CTL2: + case WCD939X_COMPANDER_HPHL_CTL3: + case WCD939X_COMPANDER_HPHL_CTL4: + case WCD939X_COMPANDER_HPHL_CTL5: + case WCD939X_COMPANDER_HPHL_CTL7: + case WCD939X_COMPANDER_HPHL_CTL8: + case WCD939X_COMPANDER_HPHL_CTL9: + case WCD939X_COMPANDER_HPHL_CTL10: + case WCD939X_COMPANDER_HPHL_CTL11: + case WCD939X_COMPANDER_HPHL_CTL12: + case WCD939X_COMPANDER_HPHL_CTL13: + case WCD939X_COMPANDER_HPHL_CTL14: + case WCD939X_COMPANDER_HPHL_CTL15: + case WCD939X_COMPANDER_HPHL_CTL16: + case WCD939X_COMPANDER_HPHL_CTL17: + case WCD939X_COMPANDER_HPHL_CTL18: + case WCD939X_COMPANDER_HPHL_CTL19: + case WCD939X_R_CTL0: + case WCD939X_R_CTL1: + case WCD939X_R_CTL2: + case WCD939X_R_CTL3: + case WCD939X_R_CTL4: + case WCD939X_R_CTL5: + case WCD939X_R_CTL7: + case WCD939X_R_CTL8: + case WCD939X_R_CTL9: + case WCD939X_R_CTL10: + case WCD939X_R_CTL11: + case WCD939X_R_CTL12: + case WCD939X_R_CTL13: + case WCD939X_R_CTL14: + case WCD939X_R_CTL15: + case WCD939X_R_CTL16: + case WCD939X_R_CTL17: + case WCD939X_R_CTL18: + case WCD939X_R_CTL19: + case WCD939X_E_PATH_CTL: + case WCD939X_E_CFG0: + case WCD939X_E_CFG1: + case WCD939X_E_CFG2: + case WCD939X_E_CFG3: + case WCD939X_DSD_HPHL_PATH_CTL: + case WCD939X_DSD_HPHL_CFG0: + case WCD939X_DSD_HPHL_CFG1: + case WCD939X_DSD_HPHL_CFG2: + case WCD939X_DSD_HPHL_CFG3: + case WCD939X_DSD_HPHL_CFG4: + case WCD939X_DSD_HPHL_CFG5: + case WCD939X_DSD_HPHR_PATH_CTL: + case WCD939X_DSD_HPHR_CFG0: + case WCD939X_DSD_HPHR_CFG1: + case WCD939X_DSD_HPHR_CFG2: + case WCD939X_DSD_HPHR_CFG3: + case WCD939X_DSD_HPHR_CFG4: + case WCD939X_DSD_HPHR_CFG5: + return true; + } + + return false; +} + +static bool wcd939x_readable_register(struct device *dev, unsigned int reg) +{ + /* Read-Only Registers */ + switch (reg) { + case WCD939X_ANA_MBHC_RESULT_1: + case WCD939X_ANA_MBHC_RESULT_2: + case WCD939X_ANA_MBHC_RESULT_3: + case WCD939X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD939X_TX_1_2_SAR2_ERR: + case WCD939X_TX_1_2_SAR1_ERR: + case WCD939X_TX_3_4_SAR4_ERR: + case WCD939X_TX_3_4_SAR3_ERR: + case WCD939X_HPH_L_STATUS: + case WCD939X_HPH_R_STATUS: + case WCD939X_HPH_SURGE_STATUS: + case WCD939X_EAR_STATUS_REG_1: + case WCD939X_EAR_STATUS_REG_2: + case WCD939X_MBHC_NEW_FSM_STATUS: + case WCD939X_MBHC_NEW_ADC_RESULT: + case WCD939X_DIE_CRACK_DET_OUT: + case WCD939X_DIGITAL_CHIP_ID0: + case WCD939X_DIGITAL_CHIP_ID1: + case WCD939X_DIGITAL_CHIP_ID2: + case WCD939X_DIGITAL_CHIP_ID3: + case WCD939X_DIGITAL_INTR_STATUS_0: + case WCD939X_DIGITAL_INTR_STATUS_1: + case WCD939X_DIGITAL_INTR_STATUS_2: + case WCD939X_DIGITAL_SWR_HM_TEST_0: + case WCD939X_DIGITAL_SWR_HM_TEST_1: + case WCD939X_DIGITAL_EFUSE_T_DATA_0: + case WCD939X_DIGITAL_EFUSE_T_DATA_1: + case WCD939X_DIGITAL_PIN_STATUS_0: + case WCD939X_DIGITAL_PIN_STATUS_1: + case WCD939X_DIGITAL_MODE_STATUS_0: + case WCD939X_DIGITAL_MODE_STATUS_1: + case WCD939X_DIGITAL_EFUSE_REG_0: + case WCD939X_DIGITAL_EFUSE_REG_1: + case WCD939X_DIGITAL_EFUSE_REG_2: + case WCD939X_DIGITAL_EFUSE_REG_3: + case WCD939X_DIGITAL_EFUSE_REG_4: + case WCD939X_DIGITAL_EFUSE_REG_5: + case WCD939X_DIGITAL_EFUSE_REG_6: + case WCD939X_DIGITAL_EFUSE_REG_7: + case WCD939X_DIGITAL_EFUSE_REG_8: + case WCD939X_DIGITAL_EFUSE_REG_9: + case WCD939X_DIGITAL_EFUSE_REG_10: + case WCD939X_DIGITAL_EFUSE_REG_11: + case WCD939X_DIGITAL_EFUSE_REG_12: + case WCD939X_DIGITAL_EFUSE_REG_13: + case WCD939X_DIGITAL_EFUSE_REG_14: + case WCD939X_DIGITAL_EFUSE_REG_15: + case WCD939X_DIGITAL_EFUSE_REG_16: + case WCD939X_DIGITAL_EFUSE_REG_17: + case WCD939X_DIGITAL_EFUSE_REG_18: + case WCD939X_DIGITAL_EFUSE_REG_19: + case WCD939X_DIGITAL_EFUSE_REG_20: + case WCD939X_DIGITAL_EFUSE_REG_21: + case WCD939X_DIGITAL_EFUSE_REG_22: + case WCD939X_DIGITAL_EFUSE_REG_23: + case WCD939X_DIGITAL_EFUSE_REG_24: + case WCD939X_DIGITAL_EFUSE_REG_25: + case WCD939X_DIGITAL_EFUSE_REG_26: + case WCD939X_DIGITAL_EFUSE_REG_27: + case WCD939X_DIGITAL_EFUSE_REG_28: + case WCD939X_DIGITAL_EFUSE_REG_29: + case WCD939X_DIGITAL_EFUSE_REG_30: + case WCD939X_DIGITAL_EFUSE_REG_31: + case WCD939X_RX_TOP_HPHL_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHL_COMP_RD_MSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_MSB: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG6: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG6: + case WCD939X_COMPANDER_HPHL_CTL6: + case WCD939X_R_CTL6: + return true; + } + + return wcd939x_rdwr_register(dev, reg); +} + +static bool wcd939x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD939X_ANA_MBHC_RESULT_1: + case WCD939X_ANA_MBHC_RESULT_2: + case WCD939X_ANA_MBHC_RESULT_3: + case WCD939X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD939X_TX_1_2_SAR2_ERR: + case WCD939X_TX_1_2_SAR1_ERR: + case WCD939X_TX_3_4_SAR4_ERR: + case WCD939X_TX_3_4_SAR3_ERR: + case WCD939X_HPH_L_STATUS: + case WCD939X_HPH_R_STATUS: + case WCD939X_HPH_SURGE_STATUS: + case WCD939X_EAR_STATUS_REG_1: + case WCD939X_EAR_STATUS_REG_2: + case WCD939X_MBHC_NEW_FSM_STATUS: + case WCD939X_MBHC_NEW_ADC_RESULT: + case WCD939X_DIE_CRACK_DET_OUT: + case WCD939X_DIGITAL_INTR_STATUS_0: + case WCD939X_DIGITAL_INTR_STATUS_1: + case WCD939X_DIGITAL_INTR_STATUS_2: + case WCD939X_DIGITAL_SWR_HM_TEST_0: + case WCD939X_DIGITAL_SWR_HM_TEST_1: + case WCD939X_DIGITAL_PIN_STATUS_0: + case WCD939X_DIGITAL_PIN_STATUS_1: + case WCD939X_DIGITAL_MODE_STATUS_0: + case WCD939X_DIGITAL_MODE_STATUS_1: + case WCD939X_RX_TOP_HPHL_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHL_COMP_RD_MSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_MSB: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG6: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG6: + case WCD939X_COMPANDER_HPHL_CTL6: + case WCD939X_R_CTL6: + return true; + } + return false; +} + +static bool wcd939x_writeable_register(struct device *dev, unsigned int reg) +{ + return wcd939x_rdwr_register(dev, reg); +} + +static const struct regmap_config wcd939x_regmap_config = { + .name = "wcd939x_csr", + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wcd939x_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd939x_defaults), + .max_register = WCD939X_MAX_REGISTER, + .readable_reg = wcd939x_readable_register, + .writeable_reg = wcd939x_writeable_register, + .volatile_reg = wcd939x_volatile_register, +}; + +static const struct sdw_slave_ops wcd9390_slave_ops = { + .update_status = wcd9390_update_status, + .interrupt_callback = wcd9390_interrupt_callback, + .bus_config = wcd9390_bus_config, +}; + +static int wcd939x_sdw_component_bind(struct device *dev, struct device *master, + void *data) +{ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} + +static void wcd939x_sdw_component_unbind(struct device *dev, + struct device *master, void *data) +{ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); +} + +static const struct component_ops wcd939x_sdw_component_ops = { + .bind = wcd939x_sdw_component_bind, + .unbind = wcd939x_sdw_component_unbind, +}; + +static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) +{ + struct device *dev = &pdev->dev; + struct wcd939x_sdw_priv *wcd; + int ret; + + wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + /* + * Port map index starts with 0, however the data port for this codec + * are from index 1 + */ + if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) { + wcd->is_tx = true; + ret = of_property_read_u32_array(dev->of_node, + "qcom,tx-port-mapping", + &pdev->m_port_map[1], + WCD939X_MAX_TX_SWR_PORTS); + } else { + ret = of_property_read_u32_array(dev->of_node, + "qcom,rx-port-mapping", + &pdev->m_port_map[1], + WCD939X_MAX_RX_SWR_PORTS); + } + + if (ret < 0) + dev_info(dev, "Static Port mapping not specified\n"); + + wcd->sdev = pdev; + dev_set_drvdata(dev, wcd); + + pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | + SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + pdev->prop.lane_control_support = true; + pdev->prop.simple_clk_stop_capable = true; + if (wcd->is_tx) { + pdev->prop.source_ports = GENMASK(WCD939X_MAX_TX_SWR_PORTS, 0); + pdev->prop.src_dpn_prop = wcd939x_tx_dpn_prop; + wcd->ch_info = &wcd939x_sdw_tx_ch_info[0]; + pdev->prop.wake_capable = true; + } else { + pdev->prop.sink_ports = GENMASK(WCD939X_MAX_RX_SWR_PORTS, 0); + pdev->prop.sink_dpn_prop = wcd939x_rx_dpn_prop; + wcd->ch_info = &wcd939x_sdw_rx_ch_info[0]; + } + + if (wcd->is_tx) { + /* + * Do not use devres here since devres_release_group() could + * be called by component_unbind() id the aggregate device + * fails to bind. + */ + wcd->regmap = regmap_init_sdw(pdev, &wcd939x_regmap_config); + if (IS_ERR(wcd->regmap)) + return dev_err_probe(dev, PTR_ERR(wcd->regmap), + "Regmap init failed\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(wcd->regmap, true); + } + + ret = component_add(dev, &wcd939x_sdw_component_ops); + if (ret) + return ret; + + /* Set suspended until aggregate device is bind */ + pm_runtime_set_suspended(dev); + + return 0; +} + +static int wcd9390_remove(struct sdw_slave *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + + component_del(dev, &wcd939x_sdw_component_ops); + + if (wcd->regmap) + regmap_exit(wcd->regmap); + + return 0; +} + +static const struct sdw_device_id wcd9390_slave_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x10e, 0), /* WCD9390 & WCD9390 RX/TX Device ID */ + {}, +}; +MODULE_DEVICE_TABLE(sdw, wcd9390_slave_id); + +static int __maybe_unused wcd939x_sdw_runtime_suspend(struct device *dev) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + } + + return 0; +} + +static int __maybe_unused wcd939x_sdw_runtime_resume(struct device *dev) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); + regcache_sync(wcd->regmap); + } + + return 0; +} + +static const struct dev_pm_ops wcd939x_sdw_pm_ops = { + SET_RUNTIME_PM_OPS(wcd939x_sdw_runtime_suspend, wcd939x_sdw_runtime_resume, NULL) +}; + +static struct sdw_driver wcd9390_codec_driver = { + .probe = wcd9390_probe, + .remove = wcd9390_remove, + .ops = &wcd9390_slave_ops, + .id_table = wcd9390_slave_id, + .driver = { + .name = "wcd9390-codec", + .pm = &wcd939x_sdw_pm_ops, + } +}; +module_sdw_driver(wcd9390_codec_driver); + +MODULE_DESCRIPTION("WCD939X SDW codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c new file mode 100644 index 000000000000..c49894aad8a5 --- /dev/null +++ b/sound/soc/codecs/wcd939x.c @@ -0,0 +1,3686 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#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> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <linux/regulator/consumer.h> +#include <linux/usb/typec_mux.h> +#include <linux/usb/typec_altmode.h> + +#include "wcd-clsh-v2.h" +#include "wcd-mbhc-v2.h" +#include "wcd939x.h" + +#define WCD939X_MAX_MICBIAS (4) +#define WCD939X_MAX_SUPPLY (4) +#define WCD939X_MBHC_MAX_BUTTONS (8) +#define TX_ADC_MAX (4) +#define WCD_MBHC_HS_V_MAX 1600 + +enum { + WCD939X_VERSION_1_0 = 0, + WCD939X_VERSION_1_1, + WCD939X_VERSION_2_0, +}; + +#define WCD939X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WCD939X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) +#define WCD939X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + (v) * 50) +#define SWR_CLK_RATE_0P6MHZ (600000) +#define SWR_CLK_RATE_1P2MHZ (1200000) +#define SWR_CLK_RATE_2P4MHZ (2400000) +#define SWR_CLK_RATE_4P8MHZ (4800000) +#define SWR_CLK_RATE_9P6MHZ (9600000) +#define SWR_CLK_RATE_11P2896MHZ (1128960) + +#define ADC_MODE_VAL_HIFI 0x01 +#define ADC_MODE_VAL_LO_HIF 0x02 +#define ADC_MODE_VAL_NORMAL 0x03 +#define ADC_MODE_VAL_LP 0x05 +#define ADC_MODE_VAL_ULP1 0x09 +#define ADC_MODE_VAL_ULP2 0x0B + +/* Z value defined in milliohm */ +#define WCD939X_ZDET_VAL_32 (32000) +#define WCD939X_ZDET_VAL_400 (400000) +#define WCD939X_ZDET_VAL_1200 (1200000) +#define WCD939X_ZDET_VAL_100K (100000000) + +/* Z floating defined in ohms */ +#define WCD939X_ZDET_FLOATING_IMPEDANCE (0x0FFFFFFE) +#define WCD939X_ZDET_NUM_MEASUREMENTS (900) +#define WCD939X_MBHC_GET_C1(c) (((c) & 0xC000) >> 14) +#define WCD939X_MBHC_GET_X1(x) ((x) & 0x3FFF) + +/* Z value compared in milliOhm */ +#define WCD939X_MBHC_IS_SECOND_RAMP_REQUIRED(z) false +#define WCD939X_ANA_MBHC_ZDET_CONST (1018 * 1024) + +enum { + WCD9390 = 0, + WCD9395 = 5, +}; + +enum { + /* INTR_CTRL_INT_MASK_0 */ + WCD939X_IRQ_MBHC_BUTTON_PRESS_DET = 0, + WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD939X_IRQ_MBHC_ELECT_INS_REM_DET, + WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD939X_IRQ_MBHC_SW_DET, + WCD939X_IRQ_HPHR_OCP_INT, + WCD939X_IRQ_HPHR_CNP_INT, + WCD939X_IRQ_HPHL_OCP_INT, + + /* INTR_CTRL_INT_MASK_1 */ + WCD939X_IRQ_HPHL_CNP_INT, + WCD939X_IRQ_EAR_CNP_INT, + WCD939X_IRQ_EAR_SCD_INT, + WCD939X_IRQ_HPHL_PDM_WD_INT, + WCD939X_IRQ_HPHR_PDM_WD_INT, + WCD939X_IRQ_EAR_PDM_WD_INT, + + /* INTR_CTRL_INT_MASK_2 */ + WCD939X_IRQ_MBHC_MOISTURE_INT, + WCD939X_IRQ_HPHL_SURGE_DET_INT, + WCD939X_IRQ_HPHR_SURGE_DET_INT, + WCD939X_NUM_IRQS, +}; + +enum { + MICB_BIAS_DISABLE = 0, + MICB_BIAS_ENABLE, + MICB_BIAS_PULL_UP, + MICB_BIAS_PULL_DOWN, +}; + +enum { + WCD_ADC1 = 0, + WCD_ADC2, + WCD_ADC3, + WCD_ADC4, + HPH_PA_DELAY, +}; + +enum { + ADC_MODE_INVALID = 0, + ADC_MODE_HIFI, + ADC_MODE_LO_HIF, + ADC_MODE_NORMAL, + ADC_MODE_LP, + ADC_MODE_ULP1, + ADC_MODE_ULP2, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + NUM_CODEC_DAIS, +}; + +static u8 tx_mode_bit[] = { + [ADC_MODE_INVALID] = 0x00, + [ADC_MODE_HIFI] = 0x01, + [ADC_MODE_LO_HIF] = 0x02, + [ADC_MODE_NORMAL] = 0x04, + [ADC_MODE_LP] = 0x08, + [ADC_MODE_ULP1] = 0x10, + [ADC_MODE_ULP2] = 0x20, +}; + +struct zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +struct wcd939x_priv { + struct sdw_slave *tx_sdw_dev; + struct wcd939x_sdw_priv *sdw_priv[NUM_CODEC_DAIS]; + struct device *txdev; + struct device *rxdev; + struct device_node *rxnode, *txnode; + struct regmap *regmap; + struct snd_soc_component *component; + /* micb setup lock */ + struct mutex micb_lock; + /* typec handling */ + bool typec_analog_mux; +#if IS_ENABLED(CONFIG_TYPEC) + struct typec_mux_dev *typec_mux; + struct typec_switch_dev *typec_sw; + enum typec_orientation typec_orientation; + unsigned long typec_mode; + struct typec_switch *typec_switch; +#endif /* CONFIG_TYPEC */ + /* mbhc module */ + struct wcd_mbhc *wcd_mbhc; + struct wcd_mbhc_config mbhc_cfg; + struct wcd_mbhc_intr intr_ids; + struct wcd_clsh_ctrl *clsh_info; + struct irq_domain *virq; + struct regmap_irq_chip *wcd_regmap_irq_chip; + struct regmap_irq_chip_data *irq_chip; + struct regulator_bulk_data supplies[WCD939X_MAX_SUPPLY]; + struct snd_soc_jack *jack; + unsigned long status_mask; + s32 micb_ref[WCD939X_MAX_MICBIAS]; + s32 pullup_ref[WCD939X_MAX_MICBIAS]; + u32 hph_mode; + u32 tx_mode[TX_ADC_MAX]; + int variant; + int reset_gpio; + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + u32 micb4_mv; + int hphr_pdm_wd_int; + int hphl_pdm_wd_int; + int ear_pdm_wd_int; + bool comp1_enable; + bool comp2_enable; + bool ldoh; +}; + +static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); + +static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD939X_ANA_MBHC_MECH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD939X_ANA_MBHC_MECH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD939X_ANA_MBHC_MECH, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x30), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD939X_ANA_MBHC_ELECT, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD939X_ANA_MBHC_MECH, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD939X_ANA_MBHC_MECH, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD939X_ANA_MBHC_ELECT, 0x06), + WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD939X_ANA_MBHC_ELECT, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F), + WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD939X_MBHC_NEW_CTL_1, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD939X_MBHC_NEW_CTL_2, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD939X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD939X_HPH_OCP_CTL, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x07), + WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD939X_ANA_MBHC_ELECT, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD939X_ANA_MICB2, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD939X_HPH_CNP_WG_TIME, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD939X_ANA_HPH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD939X_ANA_HPH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD939X_ANA_HPH, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD939X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD939X_MBHC_CTL_BCS, 0x02), + WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD939X_MBHC_NEW_CTL_2, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD939X_HPH_PA_CTL2, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD939X_HPH_PA_CTL2, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD939X_HPH_L_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD939X_HPH_R_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD939X_MBHC_NEW_CTL_1, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD939X_MBHC_NEW_FSM_STATUS, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD939X_MBHC_NEW_FSM_STATUS, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD939X_MBHC_NEW_ADC_RESULT, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD939X_ANA_MICB2, 0x3F), + WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD939X_MBHC_NEW_CTL_1, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD939X_MBHC_NEW_CTL_1, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD939X_ANA_MBHC_ZDET, 0x02), +}; + +static const struct regmap_irq wcd939x_irqs[WCD939X_NUM_IRQS] = { + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_SW_DET, 0, 0x10), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_OCP_INT, 0, 0x20), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_CNP_INT, 0, 0x40), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_OCP_INT, 0, 0x80), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_CNP_INT, 1, 0x01), + REGMAP_IRQ_REG(WCD939X_IRQ_EAR_CNP_INT, 1, 0x02), + REGMAP_IRQ_REG(WCD939X_IRQ_EAR_SCD_INT, 1, 0x04), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_PDM_WD_INT, 1, 0x20), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_PDM_WD_INT, 1, 0x40), + REGMAP_IRQ_REG(WCD939X_IRQ_EAR_PDM_WD_INT, 1, 0x80), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_MOISTURE_INT, 2, 0x02), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_SURGE_DET_INT, 2, 0x04), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), +}; + +static struct regmap_irq_chip wcd939x_regmap_irq_chip = { + .name = "wcd939x", + .irqs = wcd939x_irqs, + .num_irqs = ARRAY_SIZE(wcd939x_irqs), + .num_regs = 3, + .status_base = WCD939X_DIGITAL_INTR_STATUS_0, + .mask_base = WCD939X_DIGITAL_INTR_MASK_0, + .ack_base = WCD939X_DIGITAL_INTR_CLEAR_0, + .use_ack = 1, + .runtime_pm = true, + .irq_drv_data = NULL, +}; + +static int wcd939x_get_clk_rate(int mode) +{ + int rate; + + switch (mode) { + case ADC_MODE_ULP2: + rate = SWR_CLK_RATE_0P6MHZ; + break; + case ADC_MODE_ULP1: + rate = SWR_CLK_RATE_1P2MHZ; + break; + case ADC_MODE_LP: + rate = SWR_CLK_RATE_4P8MHZ; + break; + case ADC_MODE_NORMAL: + case ADC_MODE_LO_HIF: + case ADC_MODE_HIFI: + case ADC_MODE_INVALID: + default: + rate = SWR_CLK_RATE_9P6MHZ; + break; + } + + return rate; +} + +static int wcd939x_set_swr_clk_rate(struct snd_soc_component *component, int rate, int bank) +{ + u8 mask = (bank ? 0xF0 : 0x0F); + u8 val = 0; + + switch (rate) { + case SWR_CLK_RATE_0P6MHZ: + val = 6; + break; + case SWR_CLK_RATE_1P2MHZ: + val = 5; + break; + case SWR_CLK_RATE_2P4MHZ: + val = 3; + break; + case SWR_CLK_RATE_4P8MHZ: + val = 1; + break; + case SWR_CLK_RATE_9P6MHZ: + default: + val = 0; + break; + } + + snd_soc_component_write_field(component, WCD939X_DIGITAL_SWR_TX_CLK_RATE, mask, val); + + return 0; +} + +static int wcd939x_io_init(struct snd_soc_component *component) +{ + snd_soc_component_write_field(component, WCD939X_ANA_BIAS, + WCD939X_BIAS_ANALOG_BIAS_EN, true); + snd_soc_component_write_field(component, WCD939X_ANA_BIAS, + WCD939X_BIAS_PRECHRG_EN, true); + + /* 10 msec delay as per HW requirement */ + usleep_range(10000, 10010); + snd_soc_component_write_field(component, WCD939X_ANA_BIAS, + WCD939X_BIAS_PRECHRG_EN, false); + + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, + WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x15); + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x15); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL, + WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true); + + snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, + WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M, 1); + snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, + WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE, 4); + + snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP, + WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE, 8); + + snd_soc_component_write_field(component, WCD939X_MICB1_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_MICB2_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_MICB3_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_MICB4_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_TX_3_4_TEST_BLK_EN2, + WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN, false); + + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false); + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false); + + snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL, + WCD939X_OCP_CTL_OCP_FSM_EN, true); + snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL, + WCD939X_OCP_CTL_SCD_OP_EN, true); + + snd_soc_component_write(component, WCD939X_E_CFG0, + WCD939X_CFG0_IDLE_STEREO | + WCD939X_CFG0_AUTO_DISABLE_ANC); + + return 0; +} + +static int wcd939x_sdw_connect_port(struct wcd939x_sdw_ch_info *ch_info, + struct sdw_port_config *port_config, + u8 enable) +{ + u8 ch_mask, port_num; + + port_num = ch_info->port_num; + ch_mask = ch_info->ch_mask; + + port_config->num = port_num; + + if (enable) + port_config->ch_mask |= ch_mask; + else + port_config->ch_mask &= ~ch_mask; + + return 0; +} + +static int wcd939x_connect_port(struct wcd939x_sdw_priv *wcd, u8 port_num, u8 ch_id, u8 enable) +{ + return wcd939x_sdw_connect_port(&wcd->ch_info[ch_id], + &wcd->port_config[port_num - 1], + enable); +} + +static int wcd939x_codec_enable_rxclk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, true); + + /* Analog path clock controls */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN, + true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN, + true); + + /* Digital path clock controls */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, true); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_VNEG_EN, false); + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_VPOS_EN, false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN, + false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN, + false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, false); + + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, false); + + break; + } + + return 0; +} + +static int wcd939x_codec_hphl_dac_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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1, + WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN, + false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, true); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, + WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x1d); + if (wcd939x->comp1_enable) { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN, + true); + /* 5msec compander delay as per HW requirement */ + if (!wcd939x->comp2_enable || + snd_soc_component_read_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN)) + usleep_range(5000, 5010); + + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, + false); + } else { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN, + false); + snd_soc_component_write_field(component, WCD939X_HPH_L_EN, + WCD939X_L_EN_GAIN_SOURCE_SEL, true); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, + WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 1); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_hphr_dac_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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1, + WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN, + false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, true); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x1d); + if (wcd939x->comp2_enable) { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN, + true); + /* 5msec compander delay as per HW requirement */ + if (!wcd939x->comp1_enable || + snd_soc_component_read_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN)) + usleep_range(5000, 5010); + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, + false); + } else { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN, + false); + snd_soc_component_write_field(component, WCD939X_HPH_R_EN, + WCD939X_R_EN_GAIN_SOURCE_SEL, true); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 1); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_ear_dac_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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_EAR_GAIN_CTL, + WCD939X_CDC_EAR_GAIN_CTL_EAR_EN, true); + + snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON, + WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, false); + + /* 5 msec delay as per HW requirement */ + usleep_range(5000, 5010); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, CLS_AB_HIFI); + + snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4, + WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON, + WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, true); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_hphr_pa(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd939x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, true); + + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, hph_mode); + wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, true); + if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_PWR_LEVEL, 0); + + snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4, + WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd); + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_REF_ENABLE, true); + + if (snd_soc_component_read_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_REF_ENABLE)) + usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */ + + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1, + WCD939X_PDM_WD_CTL1_PDM_WD_EN, 3); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, + false); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI || + hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_REGULATOR_MODE, + true); + + enable_irq(wcd939x->hphr_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd939x->hphr_pdm_wd_int); + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (!wcd939x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_ENABLE, false); + + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_PRE_HPHR_PA_OFF); + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_HPHR_PA_OFF); + + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_REF_ENABLE, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1, + WCD939X_PDM_WD_CTL1_PDM_WD_EN, 0); + + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, hph_mode); + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_hphl_pa(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd939x->hph_mode; + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, true); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, hph_mode); + wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, + true); + if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_PWR_LEVEL, 0); + + snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4, + WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd); + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_REF_ENABLE, true); + + if (snd_soc_component_read_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_REF_ENABLE)) + usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */ + + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, + false); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI || + hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_REGULATOR_MODE, + true); + enable_irq(wcd939x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd939x->hphl_pdm_wd_int); + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (!wcd939x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_ENABLE, false); + + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF); + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp1_enable) + usleep_range(21000, 21100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_HPHL_PA_OFF); + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_REF_ENABLE, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, hph_mode); + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_ear_pa(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable watchdog interrupt for HPHL */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3); + /* For EAR, use CLASS_AB regulator mode */ + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_REGULATOR_MODE, true); + snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL, + WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 6 msec delay as per HW requirement */ + usleep_range(6000, 6010); + enable_irq(wcd939x->ear_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd939x->ear_pdm_wd_int); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL, + WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG, + false); + /* 7 msec delay as per HW requirement */ + usleep_range(7000, 7010); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, CLS_AB_HIFI); + break; + } + + return 0; +} + +/* TX Controls */ + +static int wcd939x_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 dmic_clk_reg, dmic_clk_en_reg; + u8 dmic_clk_en_mask; + u8 dmic_ctl_mask; + u8 dmic_clk_mask; + + switch (w->shift) { + case 0: + case 1: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC1_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL; + break; + case 2: + case 3: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC2_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL; + break; + case 4: + case 5: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC3_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL; + break; + case 6: + case 7: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC4_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL; + break; + default: + dev_err(component->dev, "%s: Invalid DMIC Selection\n", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL, + dmic_ctl_mask, false); + /* 250us sleep as per HW requirement */ + usleep_range(250, 260); + if (w->shift == 2) + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DMIC2_CTL, + WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN, + true); + /* Setting DMIC clock rate to 2.4MHz */ + snd_soc_component_write_field(component, dmic_clk_reg, + dmic_clk_mask, 3); + snd_soc_component_write_field(component, dmic_clk_en_reg, + dmic_clk_en_mask, true); + /* enable clock scaling */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL, + WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL, + WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN, true); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL, + dmic_ctl_mask, 1); + if (w->shift == 2) + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DMIC2_CTL, + WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN, + false); + snd_soc_component_write_field(component, dmic_clk_en_reg, + dmic_clk_en_mask, 0); + break; + } + return 0; +} + +static int wcd939x_tx_swr_ctrl(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int bank; + int rate; + + bank = wcd939x_swr_get_current_bank(wcd939x->sdw_priv[AIF1_CAP]->sdev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strnstr(w->name, "ADC", sizeof("ADC"))) { + int mode = 0; + + if (test_bit(WCD_ADC1, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC1]]; + if (test_bit(WCD_ADC2, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC2]]; + if (test_bit(WCD_ADC3, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC3]]; + if (test_bit(WCD_ADC4, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC4]]; + + if (mode) + rate = wcd939x_get_clk_rate(ffs(mode) - 1); + else + rate = wcd939x_get_clk_rate(ADC_MODE_INVALID); + wcd939x_set_swr_clk_rate(component, rate, bank); + wcd939x_set_swr_clk_rate(component, rate, !bank); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (strnstr(w->name, "ADC", sizeof("ADC"))) { + rate = wcd939x_get_clk_rate(ADC_MODE_INVALID); + wcd939x_set_swr_clk_rate(component, rate, !bank); + wcd939x_set_swr_clk_rate(component, rate, bank); + } + break; + } + + return 0; +} + +static int wcd939x_get_adc_mode(int val) +{ + int ret = 0; + + switch (val) { + case ADC_MODE_INVALID: + ret = ADC_MODE_VAL_NORMAL; + break; + case ADC_MODE_HIFI: + ret = ADC_MODE_VAL_HIFI; + break; + case ADC_MODE_LO_HIF: + ret = ADC_MODE_VAL_LO_HIF; + break; + case ADC_MODE_NORMAL: + ret = ADC_MODE_VAL_NORMAL; + break; + case ADC_MODE_LP: + ret = ADC_MODE_VAL_LP; + break; + case ADC_MODE_ULP1: + ret = ADC_MODE_VAL_ULP1; + break; + case ADC_MODE_ULP2: + ret = ADC_MODE_VAL_ULP2; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int wcd939x_codec_enable_adc(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN, + true); + set_bit(w->shift, &wcd939x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN, + false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN, + false); + clear_bit(w->shift, &wcd939x->status_mask); + break; + } + + return 0; +} + +static void wcd939x_tx_channel_config(struct snd_soc_component *component, + int channel, bool init) +{ + int reg, mask; + + switch (channel) { + case 0: + reg = WCD939X_ANA_TX_CH2; + mask = WCD939X_TX_CH2_HPF1_INIT; + break; + case 1: + reg = WCD939X_ANA_TX_CH2; + mask = WCD939X_TX_CH2_HPF2_INIT; + break; + case 2: + reg = WCD939X_ANA_TX_CH4; + mask = WCD939X_TX_CH4_HPF3_INIT; + break; + case 3: + reg = WCD939X_ANA_TX_CH4; + mask = WCD939X_TX_CH4_HPF4_INIT; + break; + default: + return; + } + + snd_soc_component_write_field(component, reg, mask, init); +} + +static int wcd939x_adc_enable_req(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL, + WCD939X_CDC_REQ_CTL_FS_RATE_4P8, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL, + WCD939X_CDC_REQ_CTL_NO_NOTCH, false); + + wcd939x_tx_channel_config(component, w->shift, true); + mode = wcd939x_get_adc_mode(wcd939x->tx_mode[w->shift]); + if (mode < 0) { + dev_info(component->dev, "Invalid ADC mode\n"); + return -EINVAL; + } + + switch (w->shift) { + case 0: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, + true); + break; + case 1: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, + true); + break; + case 2: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, + true); + break; + case 3: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, + true); + break; + default: + break; + } + + wcd939x_tx_channel_config(component, w->shift, false); + break; + case SND_SOC_DAPM_POST_PMD: + switch (w->shift) { + case 0: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, + false); + break; + case 1: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, + false); + break; + case 2: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, + false); + break; + case 3: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, + false); + break; + default: + break; + } + break; + } + + return 0; +} + +static int wcd939x_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int micb_index = micb_num - 1; + u16 micb_field; + u16 micb_reg; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD939X_ANA_MICB1; + micb_field = WCD939X_MICB1_ENABLE; + break; + case MIC_BIAS_2: + micb_reg = WCD939X_ANA_MICB2; + micb_field = WCD939X_MICB2_ENABLE; + break; + case MIC_BIAS_3: + micb_reg = WCD939X_ANA_MICB3; + micb_field = WCD939X_MICB3_ENABLE; + break; + case MIC_BIAS_4: + micb_reg = WCD939X_ANA_MICB4; + micb_field = WCD939X_MICB4_ENABLE; + break; + default: + dev_err(component->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + + switch (req) { + case MICB_PULLUP_ENABLE: + wcd939x->pullup_ref[micb_index]++; + if (wcd939x->pullup_ref[micb_index] == 1 && + wcd939x->micb_ref[micb_index] == 0) + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_PULL_UP); + break; + case MICB_PULLUP_DISABLE: + if (wcd939x->pullup_ref[micb_index] > 0) + wcd939x->pullup_ref[micb_index]--; + if (wcd939x->pullup_ref[micb_index] == 0 && + wcd939x->micb_ref[micb_index] == 0) + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_DISABLE); + break; + case MICB_ENABLE: + wcd939x->micb_ref[micb_index]++; + if (wcd939x->micb_ref[micb_index] == 1) { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN, + true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL, + WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN, + true); + snd_soc_component_write_field(component, + WCD939X_MICB1_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, + WCD939X_MICB2_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, + WCD939X_MICB3_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, + WCD939X_MICB4_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, micb_reg, micb_field, + MICB_BIAS_ENABLE); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_ON); + } + if (micb_num == MIC_BIAS_2 && is_dapm) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + break; + case MICB_DISABLE: + if (wcd939x->micb_ref[micb_index] > 0) + wcd939x->micb_ref[micb_index]--; + + if (wcd939x->micb_ref[micb_index] == 0 && + wcd939x->pullup_ref[micb_index] > 0) + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_PULL_UP); + else if (wcd939x->micb_ref[micb_index] == 0 && + wcd939x->pullup_ref[micb_index] == 0) { + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_PRE_MICBIAS_2_OFF); + + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_DISABLE); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_OFF); + } + if (is_dapm && micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd939x_micbias_control(component, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd939x_micbias_control(component, micb_num, MICB_DISABLE, true); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd939x_micbias_control(component, micb_num, + MICB_PULLUP_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd939x_micbias_control(component, micb_num, + MICB_PULLUP_DISABLE, true); + break; + } + + return 0; +} + +static int wcd939x_tx_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + ucontrol->value.enumerated.item[0] = wcd939x->tx_mode[path]; + + return 0; +} + +static int wcd939x_tx_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + if (wcd939x->tx_mode[path] == ucontrol->value.enumerated.item[0]) + return 0; + + wcd939x->tx_mode[path] = ucontrol->value.enumerated.item[0]; + + return 1; +} + +/* RX Controls */ + +static int wcd939x_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd939x->hph_mode; + + return 0; +} + +static int wcd939x_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + if (mode_val == wcd939x->hph_mode) + return 0; + + if (wcd939x->variant == WCD9390) { + switch (mode_val) { + case CLS_H_NORMAL: + case CLS_H_LP: + case CLS_AB: + case CLS_H_LOHIFI: + case CLS_H_ULP: + case CLS_AB_LP: + case CLS_AB_LOHIFI: + wcd939x->hph_mode = mode_val; + return 1; + } + } else { + switch (mode_val) { + case CLS_H_NORMAL: + case CLS_H_HIFI: + case CLS_H_LP: + case CLS_AB: + case CLS_H_LOHIFI: + case CLS_H_ULP: + case CLS_AB_HIFI: + case CLS_AB_LP: + case CLS_AB_LOHIFI: + wcd939x->hph_mode = mode_val; + return 1; + } + } + + dev_dbg(component->dev, "%s: Invalid HPH Mode\n", __func__); + return -EINVAL; +} + +static int wcd939x_get_compander(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (mc->shift) + ucontrol->value.integer.value[0] = wcd939x->comp2_enable ? 1 : 0; + else + ucontrol->value.integer.value[0] = wcd939x->comp1_enable ? 1 : 0; + + return 0; +} + +static int wcd939x_set_compander(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[AIF1_PB]; + bool value = !!ucontrol->value.integer.value[0]; + int portidx = wcd->ch_info[mc->reg].port_num; + + if (mc->shift) + wcd939x->comp2_enable = value; + else + wcd939x->comp1_enable = value; + + if (value) + wcd939x_connect_port(wcd, portidx, mc->reg, true); + else + wcd939x_connect_port(wcd, portidx, mc->reg, false); + + return 1; +} + +static int wcd939x_ldoh_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd939x->ldoh ? 1 : 0; + + return 0; +} + +static int wcd939x_ldoh_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (wcd939x->ldoh == !!ucontrol->value.integer.value[0]) + return 0; + + wcd939x->ldoh = !!ucontrol->value.integer.value[0]; + + return 1; +} + +static const char * const tx_mode_mux_text_wcd9390[] = { + "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", +}; + +static const struct soc_enum tx0_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const struct soc_enum tx1_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const struct soc_enum tx2_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const struct soc_enum tx3_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const char * const tx_mode_mux_text[] = { + "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", + "ADC_ULP1", "ADC_ULP2", +}; + +static const struct soc_enum tx0_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx1_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx2_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx3_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const char * const rx_hph_mode_mux_text_wcd9390[] = { + "CLS_H_NORMAL", "CLS_H_INVALID_1", "CLS_H_LP", "CLS_AB", + "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_H_INVALID_2", "CLS_AB_LP", + "CLS_AB_LOHIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text_wcd9390), + rx_hph_mode_mux_text_wcd9390); + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_NORMAL", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", + "CLS_H_ULP", "CLS_AB_HIFI", "CLS_AB_LP", "CLS_AB_LOHIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const struct snd_kcontrol_new wcd9390_snd_controls[] = { + SOC_SINGLE_TLV("EAR_PA Volume", WCD939X_ANA_EAR_COMPANDER_CTL, + 2, 0x10, 0, ear_pa_gain), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum_wcd9390, + wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put), + + SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), +}; + +static const struct snd_kcontrol_new wcd9395_snd_controls[] = { + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put), + + SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), +}; + +static const struct snd_kcontrol_new adc1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc4_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic4_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic5_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic6_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic7_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic8_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new ear_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphl_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphr_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const char * const adc1_mux_text[] = { + "CH1_AMIC_DISABLE", "CH1_AMIC1", "CH1_AMIC2", "CH1_AMIC3", "CH1_AMIC4", "CH1_AMIC5" +}; + +static const struct soc_enum adc1_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 0, + ARRAY_SIZE(adc1_mux_text), adc1_mux_text); + +static const struct snd_kcontrol_new tx_adc1_mux = + SOC_DAPM_ENUM("ADC1 MUX Mux", adc1_enum); + +static const char * const adc2_mux_text[] = { + "CH2_AMIC_DISABLE", "CH2_AMIC1", "CH2_AMIC2", "CH2_AMIC3", "CH2_AMIC4", "CH2_AMIC5" +}; + +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 3, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +static const struct snd_kcontrol_new tx_adc2_mux = + SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + +static const char * const adc3_mux_text[] = { + "CH3_AMIC_DISABLE", "CH3_AMIC1", "CH3_AMIC3", "CH3_AMIC4", "CH3_AMIC5" +}; + +static const struct soc_enum adc3_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 0, + ARRAY_SIZE(adc3_mux_text), adc3_mux_text); + +static const struct snd_kcontrol_new tx_adc3_mux = + SOC_DAPM_ENUM("ADC3 MUX Mux", adc3_enum); + +static const char * const adc4_mux_text[] = { + "CH4_AMIC_DISABLE", "CH4_AMIC1", "CH4_AMIC3", "CH4_AMIC4", "CH4_AMIC5" +}; + +static const struct soc_enum adc4_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 3, + ARRAY_SIZE(adc4_mux_text), adc4_mux_text); + +static const struct snd_kcontrol_new tx_adc4_mux = + SOC_DAPM_ENUM("ADC4 MUX Mux", adc4_enum); + +static const char * const rdac3_mux_text[] = { + "RX3", "RX1" +}; + +static const struct soc_enum rdac3_enum = + SOC_ENUM_SINGLE(WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0, + ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text); + +static const struct snd_kcontrol_new rx_rdac3_mux = + SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum); + +static int wcd939x_get_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift]; + unsigned int portidx = wcd->ch_info[mixer->reg].port_num; + + ucontrol->value.integer.value[0] = wcd->port_enable[portidx] ? 1 : 0; + + return 0; +} + +static const char *version_to_str(u32 version) +{ + switch (version) { + case WCD939X_VERSION_1_0: + return __stringify(WCD939X_1_0); + case WCD939X_VERSION_1_1: + return __stringify(WCD939X_1_1); + case WCD939X_VERSION_2_0: + return __stringify(WCD939X_2_0); + } + return NULL; +} + +static int wcd939x_set_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift]; + unsigned int portidx = wcd->ch_info[mixer->reg].port_num; + + wcd->port_enable[portidx] = !!ucontrol->value.integer.value[0]; + + wcd939x_connect_port(wcd, portidx, mixer->reg, wcd->port_enable[portidx]); + + return 1; +} + +/* MBHC Related */ + +static void wcd939x_mbhc_clk_setup(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_1, + WCD939X_CTL_1_RCO_EN, enable); +} + +static void wcd939x_mbhc_mbhc_bias_control(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_BIAS_EN, enable); +} + +static void wcd939x_mbhc_program_btn_thr(struct snd_soc_component *component, + int *btn_low, int *btn_high, + int num_btn, bool is_micbias) +{ + int i, vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(component->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + + for (i = 0; i < num_btn; i++) { + vth = (btn_high[i] * 2) / 25; + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_BTN0 + i, + WCD939X_MBHC_BTN0_VTH, vth); + dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool wcd939x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num) +{ + + if (micb_num == MIC_BIAS_2) { + u8 val; + + val = FIELD_GET(WCD939X_MICB2_ENABLE, + snd_soc_component_read(component, WCD939X_ANA_MICB2)); + if (val == MICB_BIAS_ENABLE) + return true; + } + + return false; +} + +static void wcd939x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component, + int pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur > HS_PULLUP_I_OFF || + pull_up_cur < HS_PULLUP_I_3P0_UA || + pull_up_cur == HS_PULLUP_I_DEFAULT) + pull_up_cur = HS_PULLUP_I_2P0_UA; + + dev_dbg(component->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, + WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL, pull_up_cur); +} + +static int wcd939x_mbhc_request_micbias(struct snd_soc_component *component, + int micb_num, int req) +{ + return wcd939x_micbias_control(component, micb_num, req, false); +} + +static void wcd939x_mbhc_micb_ramp_control(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_SHIFT_CTL, 3); + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_RAMP_ENABLE, true); + } else { + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_RAMP_ENABLE, false); + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_SHIFT_CTL, 0); + } +} + +static int wcd939x_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} + +static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + unsigned int micb_en_field, micb_vout_ctl_field; + unsigned int micb_reg, cur_vout_ctl, micb_en; + int req_vout_ctl; + int ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD939X_ANA_MICB1; + micb_en_field = WCD939X_MICB1_ENABLE; + micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL; + break; + case MIC_BIAS_2: + micb_reg = WCD939X_ANA_MICB2; + micb_en_field = WCD939X_MICB2_ENABLE; + micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL; + break; + case MIC_BIAS_3: + micb_reg = WCD939X_ANA_MICB3; + micb_en_field = WCD939X_MICB3_ENABLE; + micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL; + break; + case MIC_BIAS_4: + micb_reg = WCD939X_ANA_MICB4; + micb_en_field = WCD939X_MICB4_ENABLE; + micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL; + break; + default: + return -EINVAL; + } + mutex_lock(&wcd939x->micb_lock); + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_en = snd_soc_component_read_field(component, micb_reg, + micb_en_field); + cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, + micb_vout_ctl_field); + + req_vout_ctl = wcd939x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = req_vout_ctl; + goto exit; + } + + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == MICB_BIAS_ENABLE) + snd_soc_component_write_field(component, micb_reg, + micb_en_field, MICB_BIAS_PULL_DOWN); + + snd_soc_component_write_field(component, micb_reg, + micb_vout_ctl_field, req_vout_ctl); + + if (micb_en == MICB_BIAS_ENABLE) { + snd_soc_component_write_field(component, micb_reg, + micb_en_field, MICB_BIAS_ENABLE); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } + +exit: + mutex_unlock(&wcd939x->micb_lock); + return ret; +} + +static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, + int micb_num, bool req_en) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (wcd939x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd939x->micb2_mv; + + return wcd939x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); +} + +/* Selected by WCD939X_MBHC_GET_C1() */ +static const s16 wcd939x_wcd_mbhc_d1_a[4] = { + 0, 30, 30, 6 +}; + +/* Selected by zdet_param.noff */ +static const int wcd939x_mbhc_mincode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 +}; + +static const struct zdet_param wcd939x_mbhc_zdet_param = { + .ldo_ctl = 4, + .noff = 0, + .nshift = 6, + .btn5 = 0x18, + .btn6 = 0x60, + .btn7 = 0x78, +}; + +static void wcd939x_mbhc_get_result_params(struct snd_soc_component *component, + int32_t *zdet) +{ + const struct zdet_param *zdet_param = &wcd939x_mbhc_zdet_param; + s32 x1, d1, denom; + int val; + s16 c1; + int i; + + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_CHG_EN, true); + for (i = 0; i < WCD939X_ZDET_NUM_MEASUREMENTS; i++) { + val = snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_2, + WCD939X_MBHC_RESULT_2_Z_RESULT_MSB); + if (val & BIT(7)) + break; + } + val = val << 8; + val |= snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_1, + WCD939X_MBHC_RESULT_1_Z_RESULT_LSB); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_CHG_EN, false); + x1 = WCD939X_MBHC_GET_X1(val); + c1 = WCD939X_MBHC_GET_C1(val); + + /* If ramp is not complete, give additional 5ms */ + if (c1 < 2 && x1) + mdelay(5); + + if (!c1 || !x1) { + dev_dbg(component->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + + d1 = wcd939x_wcd_mbhc_d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - zdet_param->noff)); + if (denom > 0) + *zdet = (WCD939X_ANA_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < wcd939x_mbhc_mincode_param[zdet_param->noff]) + *zdet = WCD939X_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(component->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + val = snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_1, + WCD939X_MBHC_RESULT_1_Z_RESULT_LSB) << 8; + val |= snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_2, + WCD939X_MBHC_RESULT_2_Z_RESULT_MSB); + x1 = WCD939X_MBHC_GET_X1(val); + i++; + if (i == WCD939X_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void wcd939x_mbhc_zdet_ramp(struct snd_soc_component *component, + s32 *zl, int32_t *zr) +{ + const struct zdet_param *zdet_param = &wcd939x_mbhc_zdet_param; + s32 zdet = 0; + + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, + WCD939X_ZDET_ANA_CTL_MAXV_CTL, zdet_param->ldo_ctl); + snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN5, WCD939X_MBHC_BTN5_VTH, + zdet_param->btn5); + snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN6, WCD939X_MBHC_BTN6_VTH, + zdet_param->btn6); + snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN7, WCD939X_MBHC_BTN7_VTH, + zdet_param->btn7); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, + WCD939X_ZDET_ANA_CTL_RANGE_CTL, zdet_param->noff); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_RAMP_CTL, + WCD939X_ZDET_RAMP_CTL_TIME_CTL, zdet_param->nshift); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_RAMP_CTL, + WCD939X_ZDET_RAMP_CTL_ACC1_MIN_CTL, 6); /*acc1_min_63 */ + + if (!zl) + goto z_right; + + /* Start impedance measurement for HPH_L */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN, true); + dev_dbg(component->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + wcd939x_mbhc_get_result_params(component, &zdet); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN, false); + + *zl = zdet; + +z_right: + if (!zr) + return; + + /* Start impedance measurement for HPH_R */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN, true); + dev_dbg(component->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + wcd939x_mbhc_get_result_params(component, &zdet); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN, false); + + *zr = zdet; +} + +static void wcd939x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component, + s32 *z_val, int flag_l_r) +{ + int q1_cal; + s16 q1; + + q1 = snd_soc_component_read(component, WCD939X_DIGITAL_EFUSE_REG_21 + flag_l_r); + if (q1 & BIT(7)) + q1_cal = (10000 - ((q1 & GENMASK(6, 0)) * 10)); + else + q1_cal = (10000 + (q1 * 10)); + + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void wcd939x_wcd_mbhc_calc_impedance(struct snd_soc_component *component, + u32 *zl, uint32_t *zr) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(component->dev); + unsigned int reg0, reg1, reg2, reg3, reg4; + int z_mono, z_diff1, z_diff2; + bool is_fsm_disable = false; + s32 z1l, z1r, z1ls; + + reg0 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN5); + reg1 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN6); + reg2 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN7); + reg3 = snd_soc_component_read(component, WCD939X_MBHC_CTL_CLK); + reg4 = snd_soc_component_read(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_component_read_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_FSM_EN)) { + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_FSM_EN, false); + is_fsm_disable = true; + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (wcd939x->mbhc_cfg.hphl_swh) + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_L_DET_EN, false); + + /* Turn off 100k pull down on HPHL */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND, + false); + + /* + * Disable surge protection before impedance detection. + * This is done to give correct value for high impedance. + */ + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false); + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false); + + /* 1ms delay needed after disable surge protection */ + usleep_range(1000, 1010); + + /* First get impedance on Left */ + wcd939x_mbhc_zdet_ramp(component, &z1l, NULL); + if (z1l == WCD939X_ZDET_FLOATING_IMPEDANCE || z1l > WCD939X_ZDET_VAL_100K) { + *zl = WCD939X_ZDET_FLOATING_IMPEDANCE; + } else { + *zl = z1l / 1000; + wcd939x_wcd_mbhc_qfuse_cal(component, zl, 0); + } + dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + wcd939x_mbhc_zdet_ramp(component, NULL, &z1r); + if (z1r == WCD939X_ZDET_FLOATING_IMPEDANCE || z1r > WCD939X_ZDET_VAL_100K) { + *zr = WCD939X_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1r / 1000; + wcd939x_wcd_mbhc_qfuse_cal(component, zr, 1); + } + dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if (*zl == WCD939X_ZDET_FLOATING_IMPEDANCE && + *zr == WCD939X_ZDET_FLOATING_IMPEDANCE) { + dev_dbg(component->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + + if (*zl == WCD939X_ZDET_FLOATING_IMPEDANCE || + *zr == WCD939X_ZDET_FLOATING_IMPEDANCE || + (*zl < WCD_MONO_HS_MIN_THR && *zr > WCD_MONO_HS_MIN_THR) || + (*zl > WCD_MONO_HS_MIN_THR && *zr < WCD_MONO_HS_MIN_THR)) { + dev_dbg(component->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_MONO); + goto zdet_complete; + } + + snd_soc_component_write_field(component, WCD939X_HPH_R_ATEST, + WCD939X_R_ATEST_HPH_GND_OVR, true); + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_R, true); + wcd939x_mbhc_zdet_ramp(component, &z1ls, NULL); + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_R, false); + snd_soc_component_write_field(component, WCD939X_HPH_R_ATEST, + WCD939X_R_ATEST_HPH_GND_OVR, false); + + z1ls /= 1000; + wcd939x_wcd_mbhc_qfuse_cal(component, &z1ls, 0); + + /* Parallel of left Z and 9 ohm pull down resistor */ + z_mono = (*zl * 9) / (*zl + 9); + z_diff1 = z1ls > z_mono ? z1ls - z_mono : z_mono - z1ls; + z_diff2 = *zl > z1ls ? *zl - z1ls : z1ls - *zl; + if ((z_diff1 * (*zl + z1ls)) > (z_diff2 * (z1ls + z_mono))) { + dev_dbg(component->dev, "%s: stereo plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_STEREO); + } else { + dev_dbg(component->dev, "%s: MONO plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_MONO); + } + + /* Enable surge protection again after impedance detection */ + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHR, true); + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHL, true); + +zdet_complete: + snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN5, reg0); + snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN6, reg1); + snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN7, reg2); + + /* Turn on 100k pull down on HPHL */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND, true); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (wcd939x->mbhc_cfg.hphl_swh) + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_L_DET_EN, true); + + snd_soc_component_write(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_component_write(component, WCD939X_MBHC_CTL_CLK, reg3); + + if (is_fsm_disable) + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_FSM_EN, true); +} + +static void wcd939x_mbhc_gnd_det_ctrl(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN, + true); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_GND_DET_EN, true); + } else { + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_GND_DET_EN, false); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN, + false); + } +} + +static void wcd939x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_R, enable); + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_L, enable); +} + +static void wcd939x_mbhc_moisture_config(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (wcd939x->mbhc_cfg.moist_rref == R_OFF || wcd939x->typec_analog_mux) { + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + return; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd939x->mbhc_cfg.hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + return; + } + + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, wcd939x->mbhc_cfg.moist_rref); +} + +static void wcd939x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (enable) + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, + wcd939x->mbhc_cfg.moist_rref); + else + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); +} + +static bool wcd939x_mbhc_get_moisture_status(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + bool ret = false; + + if (wcd939x->mbhc_cfg.moist_rref == R_OFF || wcd939x->typec_analog_mux) { + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + goto done; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd939x->mbhc_cfg.hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + goto done; + } + + /* + * If moisture_en is already enabled, then skip to plug type + * detection. + */ + if (snd_soc_component_read_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL)) + goto done; + + wcd939x_mbhc_moisture_detect_en(component, true); + + /* Read moisture comparator status, invert of status bit */ + ret = !snd_soc_component_read_field(component, WCD939X_MBHC_NEW_FSM_STATUS, + WCD939X_FSM_STATUS_HS_M_COMP_STATUS); +done: + return ret; +} + +static void wcd939x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, + WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, + WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_EN_POLLING, + enable); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .clk_setup = wcd939x_mbhc_clk_setup, + .mbhc_bias = wcd939x_mbhc_mbhc_bias_control, + .set_btn_thr = wcd939x_mbhc_program_btn_thr, + .micbias_enable_status = wcd939x_mbhc_micb_en_status, + .hph_pull_up_control_v2 = wcd939x_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = wcd939x_mbhc_request_micbias, + .mbhc_micb_ramp_control = wcd939x_mbhc_micb_ramp_control, + .mbhc_micb_ctrl_thr_mic = wcd939x_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = wcd939x_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = wcd939x_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = wcd939x_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = wcd939x_mbhc_moisture_config, + .mbhc_get_moisture_status = wcd939x_mbhc_get_moisture_status, + .mbhc_moisture_polling_ctrl = wcd939x_mbhc_moisture_polling_ctrl, + .mbhc_moisture_detect_en = wcd939x_mbhc_moisture_detect_en, +}; + +static int wcd939x_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd939x->wcd_mbhc); + + return 0; +} + +static int wcd939x_hph_impedance_get(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + bool hphr = mc->shift; + u32 zl, zr; + + wcd_mbhc_get_impedance(wcd939x->wcd_mbhc, &zl, &zr); + dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + wcd939x_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + wcd939x_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + wcd939x_hph_impedance_get, NULL), +}; + +static int wcd939x_mbhc_init(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct wcd_mbhc_intr *intr_ids = &wcd939x->intr_ids; + + intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_SW_DET); + intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_BUTTON_PRESS_DET); + intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET); + intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET); + intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_ELECT_INS_REM_DET); + intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHL_OCP_INT); + intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHR_OCP_INT); + + wcd939x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true); + if (IS_ERR(wcd939x->wcd_mbhc)) + return PTR_ERR(wcd939x->wcd_mbhc); + + snd_soc_add_component_controls(component, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_component_controls(component, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + return 0; +} + +static void wcd939x_mbhc_deinit(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + wcd_mbhc_deinit(wcd939x->wcd_mbhc); +} + +/* END MBHC */ + +static const struct snd_kcontrol_new wcd939x_snd_controls[] = { + /* RX Path */ + SOC_SINGLE_EXT("HPHL_COMP Switch", WCD939X_COMP_L, 0, 1, 0, + wcd939x_get_compander, wcd939x_set_compander), + SOC_SINGLE_EXT("HPHR_COMP Switch", WCD939X_COMP_R, 1, 1, 0, + wcd939x_get_compander, wcd939x_set_compander), + SOC_SINGLE_EXT("HPHL Switch", WCD939X_HPH_L, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("HPHR Switch", WCD939X_HPH_R, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("CLSH Switch", WCD939X_CLSH, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("LO Switch", WCD939X_LO, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DSD_L Switch", WCD939X_DSD_L, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DSD_R Switch", WCD939X_DSD_R, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_TLV("HPHL Volume", WCD939X_HPH_L_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD939X_HPH_R_EN, 0, 20, 1, line_gain), + SOC_SINGLE_EXT("LDOH Enable Switch", SND_SOC_NOPM, 0, 1, 0, + wcd939x_ldoh_get, wcd939x_ldoh_put), + + /* TX Path */ + SOC_SINGLE_EXT("ADC1 Switch", WCD939X_ADC1, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("ADC2 Switch", WCD939X_ADC2, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("ADC3 Switch", WCD939X_ADC3, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("ADC4 Switch", WCD939X_ADC4, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC0 Switch", WCD939X_DMIC0, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC1 Switch", WCD939X_DMIC1, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("MBHC Switch", WCD939X_MBHC, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC2 Switch", WCD939X_DMIC2, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC3 Switch", WCD939X_DMIC3, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC4 Switch", WCD939X_DMIC4, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC5 Switch", WCD939X_DMIC5, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC6 Switch", WCD939X_DMIC6, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC7 Switch", WCD939X_DMIC7, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_TLV("ADC1 Volume", WCD939X_ANA_TX_CH1, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD939X_ANA_TX_CH2, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD939X_ANA_TX_CH3, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD939X_ANA_TX_CH4, 0, 20, 0, + analog_gain), +}; + +static const struct snd_soc_dapm_widget wcd939x_dapm_widgets[] = { + /*input widgets*/ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + + SND_SOC_DAPM_MIC("Analog Mic1", NULL), + SND_SOC_DAPM_MIC("Analog Mic2", NULL), + SND_SOC_DAPM_MIC("Analog Mic3", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + SND_SOC_DAPM_MIC("Analog Mic5", NULL), + + /* TX widgets */ + SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC4", NULL, SND_SOC_NOPM, 3, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC7", NULL, SND_SOC_NOPM, 6, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC8", NULL, SND_SOC_NOPM, 7, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 1, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 2, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC4 REQ", SND_SOC_NOPM, 3, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("ADC1 MUX", SND_SOC_NOPM, 0, 0, &tx_adc1_mux), + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + SND_SOC_DAPM_MUX("ADC3 MUX", SND_SOC_NOPM, 0, 0, &tx_adc3_mux), + SND_SOC_DAPM_MUX("ADC4 MUX", SND_SOC_NOPM, 0, 0, &tx_adc4_mux), + + /* tx mixers */ + SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0, + adc1_switch, ARRAY_SIZE(adc1_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 0, 0, + adc2_switch, ARRAY_SIZE(adc2_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 0, 0, + adc3_switch, ARRAY_SIZE(adc3_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC4_MIXER", SND_SOC_NOPM, 0, 0, + adc4_switch, ARRAY_SIZE(adc4_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0, 0, + dmic1_switch, ARRAY_SIZE(dmic1_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 0, 0, + dmic2_switch, ARRAY_SIZE(dmic2_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 0, 0, + dmic3_switch, ARRAY_SIZE(dmic3_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 0, 0, + dmic4_switch, ARRAY_SIZE(dmic4_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 0, 0, + dmic5_switch, ARRAY_SIZE(dmic5_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 0, 0, + dmic6_switch, ARRAY_SIZE(dmic6_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC7_MIXER", SND_SOC_NOPM, 0, 0, + dmic7_switch, ARRAY_SIZE(dmic7_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC8_MIXER", SND_SOC_NOPM, 0, 0, + dmic8_switch, ARRAY_SIZE(dmic8_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* micbias widgets */ + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* micbias pull up widgets */ + SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* output widgets tx */ + SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC4_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC7_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC8_OUTPUT"), + + SND_SOC_DAPM_INPUT("IN1_HPHL"), + SND_SOC_DAPM_INPUT("IN2_HPHR"), + SND_SOC_DAPM_INPUT("IN3_EAR"), + + /* rx widgets */ + SND_SOC_DAPM_PGA_E("EAR PGA", WCD939X_ANA_EAR, 7, 0, NULL, 0, + wcd939x_codec_enable_ear_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PGA", WCD939X_ANA_HPH, 7, 0, NULL, 0, + wcd939x_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PGA", WCD939X_ANA_HPH, 6, 0, NULL, 0, + wcd939x_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux), + + SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0, + wcd939x_codec_enable_rxclk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + + /* rx mixer widgets */ + SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0, + ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, + hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, + hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)), + + /* output widgets rx */ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), +}; + +static const struct snd_soc_dapm_route wcd939x_audio_map[] = { + /* TX Path */ + {"ADC1_OUTPUT", NULL, "ADC1_MIXER"}, + {"ADC1_MIXER", "Switch", "ADC1 REQ"}, + {"ADC1 REQ", NULL, "ADC1"}, + {"ADC1", NULL, "ADC1 MUX"}, + {"ADC1 MUX", "CH1_AMIC1", "AMIC1"}, + {"ADC1 MUX", "CH1_AMIC2", "AMIC2"}, + {"ADC1 MUX", "CH1_AMIC3", "AMIC3"}, + {"ADC1 MUX", "CH1_AMIC4", "AMIC4"}, + {"ADC1 MUX", "CH1_AMIC5", "AMIC5"}, + + {"ADC2_OUTPUT", NULL, "ADC2_MIXER"}, + {"ADC2_MIXER", "Switch", "ADC2 REQ"}, + {"ADC2 REQ", NULL, "ADC2"}, + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "CH2_AMIC1", "AMIC1"}, + {"ADC2 MUX", "CH2_AMIC2", "AMIC2"}, + {"ADC2 MUX", "CH2_AMIC3", "AMIC3"}, + {"ADC2 MUX", "CH2_AMIC4", "AMIC4"}, + {"ADC2 MUX", "CH2_AMIC5", "AMIC5"}, + + {"ADC3_OUTPUT", NULL, "ADC3_MIXER"}, + {"ADC3_MIXER", "Switch", "ADC3 REQ"}, + {"ADC3 REQ", NULL, "ADC3"}, + {"ADC3", NULL, "ADC3 MUX"}, + {"ADC3 MUX", "CH3_AMIC1", "AMIC1"}, + {"ADC3 MUX", "CH3_AMIC3", "AMIC3"}, + {"ADC3 MUX", "CH3_AMIC4", "AMIC4"}, + {"ADC3 MUX", "CH3_AMIC5", "AMIC5"}, + + {"ADC4_OUTPUT", NULL, "ADC4_MIXER"}, + {"ADC4_MIXER", "Switch", "ADC4 REQ"}, + {"ADC4 REQ", NULL, "ADC4"}, + {"ADC4", NULL, "ADC4 MUX"}, + {"ADC4 MUX", "CH4_AMIC1", "AMIC1"}, + {"ADC4 MUX", "CH4_AMIC3", "AMIC3"}, + {"ADC4 MUX", "CH4_AMIC4", "AMIC4"}, + {"ADC4 MUX", "CH4_AMIC5", "AMIC5"}, + + {"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"}, + {"DMIC1_MIXER", "Switch", "DMIC1"}, + + {"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"}, + {"DMIC2_MIXER", "Switch", "DMIC2"}, + + {"DMIC3_OUTPUT", NULL, "DMIC3_MIXER"}, + {"DMIC3_MIXER", "Switch", "DMIC3"}, + + {"DMIC4_OUTPUT", NULL, "DMIC4_MIXER"}, + {"DMIC4_MIXER", "Switch", "DMIC4"}, + + {"DMIC5_OUTPUT", NULL, "DMIC5_MIXER"}, + {"DMIC5_MIXER", "Switch", "DMIC5"}, + + {"DMIC6_OUTPUT", NULL, "DMIC6_MIXER"}, + {"DMIC6_MIXER", "Switch", "DMIC6"}, + + {"DMIC7_OUTPUT", NULL, "DMIC7_MIXER"}, + {"DMIC7_MIXER", "Switch", "DMIC7"}, + + {"DMIC8_OUTPUT", NULL, "DMIC8_MIXER"}, + {"DMIC8_MIXER", "Switch", "DMIC8"}, + + /* RX Path */ + {"IN1_HPHL", NULL, "VDD_BUCK"}, + {"IN1_HPHL", NULL, "CLS_H_PORT"}, + + {"RX1", NULL, "IN1_HPHL"}, + {"RX1", NULL, "RXCLK"}, + {"RDAC1", NULL, "RX1"}, + {"HPHL_RDAC", "Switch", "RDAC1"}, + {"HPHL PGA", NULL, "HPHL_RDAC"}, + {"HPHL", NULL, "HPHL PGA"}, + + {"IN2_HPHR", NULL, "VDD_BUCK"}, + {"IN2_HPHR", NULL, "CLS_H_PORT"}, + {"RX2", NULL, "IN2_HPHR"}, + {"RDAC2", NULL, "RX2"}, + {"RX2", NULL, "RXCLK"}, + {"HPHR_RDAC", "Switch", "RDAC2"}, + {"HPHR PGA", NULL, "HPHR_RDAC"}, + {"HPHR", NULL, "HPHR PGA"}, + + {"IN3_EAR", NULL, "VDD_BUCK"}, + {"RX3", NULL, "IN3_EAR"}, + {"RX3", NULL, "RXCLK"}, + + {"RDAC3_MUX", "RX3", "RX3"}, + {"RDAC3_MUX", "RX1", "RX1"}, + {"RDAC3", NULL, "RDAC3_MUX"}, + {"EAR_RDAC", "Switch", "RDAC3"}, + {"EAR PGA", NULL, "EAR_RDAC"}, + {"EAR", NULL, "EAR PGA"}, +}; + +static int wcd939x_set_micbias_data(struct wcd939x_priv *wcd939x) +{ + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + + /* set micbias voltage */ + vout_ctl_1 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb1_mv); + vout_ctl_2 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb2_mv); + vout_ctl_3 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb3_mv); + vout_ctl_4 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb4_mv); + if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || vout_ctl_3 < 0 || vout_ctl_4 < 0) + return -EINVAL; + + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB1, + WCD939X_MICB1_VOUT_CTL, vout_ctl_1); + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB2, + WCD939X_MICB2_VOUT_CTL, vout_ctl_2); + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB3, + WCD939X_MICB3_VOUT_CTL, vout_ctl_3); + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB4, + WCD939X_MICB4_VOUT_CTL, vout_ctl_4); + + return 0; +} + +static irqreturn_t wcd939x_wd_handle_irq(int irq, void *data) +{ + /* + * HPHR/HPHL/EAR Watchdog interrupt threaded handler + * + * Watchdog interrupts are expected to be enabled when switching + * on the HPHL/R and EAR RX PGA in order to make sure the interrupts + * are acked by the regmap_irq handler to allow PDM sync. + * We could leave those interrupts masked but we would not have + * any valid way to enable/disable them without violating irq layers. + * + * The HPHR/HPHL/EAR Watchdog interrupts are handled + * by regmap_irq, so requesting a threaded handler is the + * safest way to be able to ack those interrupts without + * colliding with the regmap_irq setup. + */ + + return IRQ_HANDLED; +} + +/* + * Setup a virtual interrupt domain to hook regmap_irq + * The root domain will have a single interrupt which mapping + * will trigger the regmap_irq handler. + * + * root: + * wcd_irq_chip + * [0] wcd939x_regmap_irq_chip + * [0] MBHC_BUTTON_PRESS_DET + * [1] MBHC_BUTTON_RELEASE_DET + * ... + * [16] HPHR_SURGE_DET_INT + * + * Interrupt trigger: + * soundwire_interrupt_callback() + * \-handle_nested_irq(0) + * \- regmap_irq_thread() + * \- handle_nested_irq(i) + */ +static struct irq_chip wcd_irq_chip = { + .name = "WCD939x", +}; + +static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops wcd_domain_ops = { + .map = wcd_irq_chip_map, +}; + +static int wcd939x_irq_init(struct wcd939x_priv *wcd, struct device *dev) +{ + wcd->virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL); + if (!(wcd->virq)) { + dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); + return -EINVAL; + } + + return devm_regmap_add_irq_chip(dev, wcd->regmap, + irq_create_mapping(wcd->virq, 0), + IRQF_ONESHOT, 0, &wcd939x_regmap_irq_chip, + &wcd->irq_chip); +} + +static int wcd939x_soc_codec_probe(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct sdw_slave *tx_sdw_dev = wcd939x->tx_sdw_dev; + struct device *dev = component->dev; + unsigned long time_left; + int ret, i; + + time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, + msecs_to_jiffies(2000)); + if (!time_left) { + dev_err(dev, "soundwire device init timeout\n"); + return -ETIMEDOUT; + } + + snd_soc_component_init_regmap(component, wcd939x->regmap); + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + wcd939x->variant = snd_soc_component_read_field(component, + WCD939X_DIGITAL_EFUSE_REG_0, + WCD939X_EFUSE_REG_0_WCD939X_ID); + + wcd939x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD939X); + if (IS_ERR(wcd939x->clsh_info)) { + pm_runtime_put(dev); + return PTR_ERR(wcd939x->clsh_info); + } + + wcd939x_io_init(component); + + /* Set all interrupts as edge triggered */ + for (i = 0; i < wcd939x_regmap_irq_chip.num_regs; i++) + regmap_write(wcd939x->regmap, + (WCD939X_DIGITAL_INTR_LEVEL_0 + i), 0); + + pm_runtime_put(dev); + + /* Request for watchdog interrupt */ + wcd939x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHR_PDM_WD_INT); + wcd939x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHL_PDM_WD_INT); + wcd939x->ear_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_EAR_PDM_WD_INT); + + ret = request_threaded_irq(wcd939x->hphr_pdm_wd_int, NULL, wcd939x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHR PDM WD INT", wcd939x); + if (ret) { + dev_err(dev, "Failed to request HPHR WD interrupt (%d)\n", ret); + goto err_free_clsh_ctrl; + } + + ret = request_threaded_irq(wcd939x->hphl_pdm_wd_int, NULL, wcd939x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHL PDM WD INT", wcd939x); + if (ret) { + dev_err(dev, "Failed to request HPHL WD interrupt (%d)\n", ret); + goto err_free_hphr_pdm_wd_int; + } + + ret = request_threaded_irq(wcd939x->ear_pdm_wd_int, NULL, wcd939x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "AUX PDM WD INT", wcd939x); + if (ret) { + dev_err(dev, "Failed to request Aux WD interrupt (%d)\n", ret); + goto err_free_hphl_pdm_wd_int; + } + + /* Disable watchdog interrupt for HPH and AUX */ + disable_irq_nosync(wcd939x->hphr_pdm_wd_int); + disable_irq_nosync(wcd939x->hphl_pdm_wd_int); + disable_irq_nosync(wcd939x->ear_pdm_wd_int); + + switch (wcd939x->variant) { + case WCD9390: + ret = snd_soc_add_component_controls(component, wcd9390_snd_controls, + ARRAY_SIZE(wcd9390_snd_controls)); + if (ret < 0) { + dev_err(component->dev, + "%s: Failed to add snd ctrls for variant: %d\n", + __func__, wcd939x->variant); + goto err_free_ear_pdm_wd_int; + } + break; + case WCD9395: + ret = snd_soc_add_component_controls(component, wcd9395_snd_controls, + ARRAY_SIZE(wcd9395_snd_controls)); + if (ret < 0) { + dev_err(component->dev, + "%s: Failed to add snd ctrls for variant: %d\n", + __func__, wcd939x->variant); + goto err_free_ear_pdm_wd_int; + } + break; + default: + break; + } + + ret = wcd939x_mbhc_init(component); + if (ret) { + dev_err(component->dev, "mbhc initialization failed\n"); + goto err_free_ear_pdm_wd_int; + } + + return 0; + +err_free_ear_pdm_wd_int: + free_irq(wcd939x->ear_pdm_wd_int, wcd939x); +err_free_hphl_pdm_wd_int: + free_irq(wcd939x->hphl_pdm_wd_int, wcd939x); +err_free_hphr_pdm_wd_int: + free_irq(wcd939x->hphr_pdm_wd_int, wcd939x); +err_free_clsh_ctrl: + wcd_clsh_ctrl_free(wcd939x->clsh_info); + + return ret; +} + +static void wcd939x_soc_codec_remove(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + wcd939x_mbhc_deinit(component); + + free_irq(wcd939x->ear_pdm_wd_int, wcd939x); + free_irq(wcd939x->hphl_pdm_wd_int, wcd939x); + free_irq(wcd939x->hphr_pdm_wd_int, wcd939x); + + wcd_clsh_ctrl_free(wcd939x->clsh_info); +} + +static int wcd939x_codec_set_jack(struct snd_soc_component *comp, + struct snd_soc_jack *jack, void *data) +{ + struct wcd939x_priv *wcd = dev_get_drvdata(comp->dev); + + if (jack) + return wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack); + + wcd_mbhc_stop(wcd->wcd_mbhc); + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_wcd939x = { + .name = "wcd939x_codec", + .probe = wcd939x_soc_codec_probe, + .remove = wcd939x_soc_codec_remove, + .controls = wcd939x_snd_controls, + .num_controls = ARRAY_SIZE(wcd939x_snd_controls), + .dapm_widgets = wcd939x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd939x_dapm_widgets), + .dapm_routes = wcd939x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd939x_audio_map), + .set_jack = wcd939x_codec_set_jack, + .endianness = 1, +}; + +#if IS_ENABLED(CONFIG_TYPEC) +/* Get USB-C plug orientation to provide swap event for MBHC */ +static int wcd939x_typec_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct wcd939x_priv *wcd939x = typec_switch_get_drvdata(sw); + + wcd939x->typec_orientation = orientation; + + return 0; +} + +static int wcd939x_typec_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct wcd939x_priv *wcd939x = typec_mux_get_drvdata(mux); + unsigned int previous_mode = wcd939x->typec_mode; + + if (!wcd939x->wcd_mbhc) + return -EINVAL; + + if (wcd939x->typec_mode != state->mode) { + wcd939x->typec_mode = state->mode; + + if (wcd939x->typec_mode == TYPEC_MODE_AUDIO) + return wcd_mbhc_typec_report_plug(wcd939x->wcd_mbhc); + else if (previous_mode == TYPEC_MODE_AUDIO) + return wcd_mbhc_typec_report_unplug(wcd939x->wcd_mbhc); + } + + return 0; +} +#endif /* CONFIG_TYPEC */ + +static void wcd939x_dt_parse_micbias_info(struct device *dev, struct wcd939x_priv *wcd) +{ + struct device_node *np = dev->of_node; + u32 prop_val = 0; + int rc = 0; + + rc = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val); + if (!rc) + wcd->micb1_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias1 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val); + if (!rc) + wcd->micb2_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias2 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val); + if (!rc) + wcd->micb3_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias3 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias4-microvolt", &prop_val); + if (!rc) + wcd->micb4_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias4 DT property not found\n", __func__); +} + +#if IS_ENABLED(CONFIG_TYPEC) +static bool wcd939x_swap_gnd_mic(struct snd_soc_component *component, bool active) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (!wcd939x->typec_analog_mux || !wcd939x->typec_switch) + return false; + + /* Report inversion via Type Switch of USBSS */ + typec_switch_set(wcd939x->typec_switch, + wcd939x->typec_orientation == TYPEC_ORIENTATION_REVERSE ? + TYPEC_ORIENTATION_NORMAL : TYPEC_ORIENTATION_REVERSE); + + return true; +} +#endif /* CONFIG_TYPEC */ + +static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device *dev) +{ + struct wcd_mbhc_config *cfg = &wcd939x->mbhc_cfg; +#if IS_ENABLED(CONFIG_TYPEC) + struct device_node *np; +#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->supplies[0].supply = "vdd-rxtx"; + wcd939x->supplies[1].supply = "vdd-io"; + wcd939x->supplies[2].supply = "vdd-buck"; + wcd939x->supplies[3].supply = "vdd-mic-bias"; + + ret = regulator_bulk_get(dev, WCD939X_MAX_SUPPLY, wcd939x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get supplies\n"); + + ret = regulator_bulk_enable(WCD939X_MAX_SUPPLY, wcd939x->supplies); + if (ret) { + regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); + return dev_err_probe(dev, ret, "Failed to enable supplies\n"); + } + + wcd939x_dt_parse_micbias_info(dev, wcd939x); + + cfg->mbhc_micbias = MIC_BIAS_2; + cfg->anc_micbias = MIC_BIAS_2; + cfg->v_hs_max = WCD_MBHC_HS_V_MAX; + cfg->num_btn = WCD939X_MBHC_MAX_BUTTONS; + cfg->micb_mv = wcd939x->micb2_mv; + cfg->linein_th = 5000; + cfg->hs_thr = 1700; + cfg->hph_thr = 50; + + wcd_dt_parse_mbhc_data(dev, cfg); + +#if IS_ENABLED(CONFIG_TYPEC) + /* + * Is node has a port and a valid remote endpoint + * consider HP lines are connected to the USBSS part + */ + np = of_graph_get_remote_node(dev->of_node, 0, 0); + if (np) { + wcd939x->typec_analog_mux = true; + cfg->typec_analog_mux = true; + cfg->swap_gnd_mic = wcd939x_swap_gnd_mic; + } +#endif /* CONFIG_TYPEC */ + + return 0; +} + +static int wcd939x_reset(struct wcd939x_priv *wcd939x) +{ + gpio_direction_output(wcd939x->reset_gpio, 0); + /* 20us sleep required after pulling the reset gpio to LOW */ + usleep_range(20, 30); + gpio_set_value(wcd939x->reset_gpio, 1); + /* 20us sleep required after pulling the reset gpio to HIGH */ + usleep_range(20, 30); + + return 0; +} + +static int wcd939x_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id]; + + return wcd939x_sdw_hw_params(wcd, substream, params, dai); +} + +static int wcd939x_codec_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id]; + + return wcd939x_sdw_free(wcd, substream, dai); +} + +static int wcd939x_codec_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id]; + + return wcd939x_sdw_set_sdw_stream(wcd, dai, stream, direction); +} + +static const struct snd_soc_dai_ops wcd939x_sdw_dai_ops = { + .hw_params = wcd939x_codec_hw_params, + .hw_free = wcd939x_codec_free, + .set_stream = wcd939x_codec_set_sdw_stream, +}; + +static struct snd_soc_dai_driver wcd939x_dais[] = { + [0] = { + .name = "wcd939x-sdw-rx", + .playback = { + .stream_name = "WCD AIF1 Playback", + .rates = WCD939X_RATES_MASK | WCD939X_FRAC_RATES_MASK, + .formats = WCD939X_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd939x_sdw_dai_ops, + }, + [1] = { + .name = "wcd939x-sdw-tx", + .capture = { + .stream_name = "WCD AIF1 Capture", + .rates = WCD939X_RATES_MASK | WCD939X_FRAC_RATES_MASK, + .formats = WCD939X_FORMATS, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd939x_sdw_dai_ops, + }, +}; + +static int wcd939x_bind(struct device *dev) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dev); + unsigned int version, id1, status1; + int ret; + +#if IS_ENABLED(CONFIG_TYPEC) + /* + * Get USBSS type-c switch to send gnd/mic swap events + * typec_switch is fetched now to avoid a probe deadlock since + * the USBSS depends on the typec_mux register in wcd939x_probe() + */ + if (wcd939x->typec_analog_mux) { + wcd939x->typec_switch = fwnode_typec_switch_get(dev->fwnode); + if (IS_ERR(wcd939x->typec_switch)) + return dev_err_probe(dev, PTR_ERR(wcd939x->typec_switch), + "failed to acquire orientation-switch\n"); + } +#endif /* CONFIG_TYPEC */ + + ret = component_bind_all(dev, wcd939x); + if (ret) { + dev_err(dev, "%s: Slave bind failed, ret = %d\n", + __func__, ret); + goto err_put_typec_switch; + } + + wcd939x->rxdev = wcd939x_sdw_device_get(wcd939x->rxnode); + if (!wcd939x->rxdev) { + dev_err(dev, "could not find slave with matching of node\n"); + ret = -EINVAL; + goto err_unbind; + } + wcd939x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd939x->rxdev); + wcd939x->sdw_priv[AIF1_PB]->wcd939x = wcd939x; + + wcd939x->txdev = wcd939x_sdw_device_get(wcd939x->txnode); + if (!wcd939x->txdev) { + dev_err(dev, "could not find txslave with matching of node\n"); + ret = -EINVAL; + goto err_put_rxdev; + } + wcd939x->sdw_priv[AIF1_CAP] = dev_get_drvdata(wcd939x->txdev); + wcd939x->sdw_priv[AIF1_CAP]->wcd939x = wcd939x; + wcd939x->tx_sdw_dev = dev_to_sdw_dev(wcd939x->txdev); + + /* + * As TX is main CSR reg interface, which should not be suspended first. + * explicitly add the dependency link + */ + if (!device_link_add(wcd939x->rxdev, wcd939x->txdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink tx and rx\n"); + ret = -EINVAL; + goto err_put_txdev; + } + + if (!device_link_add(dev, wcd939x->txdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink wcd and tx\n"); + ret = -EINVAL; + goto err_remove_rxtx_link; + } + + if (!device_link_add(dev, wcd939x->rxdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink wcd and rx\n"); + ret = -EINVAL; + goto err_remove_tx_link; + } + + /* Get regmap from TX SoundWire device */ + wcd939x->regmap = wcd939x_swr_get_regmap(wcd939x->sdw_priv[AIF1_CAP]); + if (IS_ERR(wcd939x->regmap)) { + dev_err(dev, "could not get TX device regmap\n"); + ret = PTR_ERR(wcd939x->regmap); + goto err_remove_rx_link; + } + + ret = wcd939x_irq_init(wcd939x, dev); + if (ret) { + dev_err(dev, "%s: IRQ init failed: %d\n", __func__, ret); + goto err_remove_rx_link; + } + + wcd939x->sdw_priv[AIF1_PB]->slave_irq = wcd939x->virq; + wcd939x->sdw_priv[AIF1_CAP]->slave_irq = wcd939x->virq; + + ret = wcd939x_set_micbias_data(wcd939x); + if (ret < 0) { + dev_err(dev, "%s: bad micbias pdata\n", __func__); + goto err_remove_rx_link; + } + + /* Check WCD9395 version */ + regmap_read(wcd939x->regmap, WCD939X_DIGITAL_CHIP_ID1, &id1); + regmap_read(wcd939x->regmap, WCD939X_EAR_STATUS_REG_1, &status1); + + if (id1 == 0) + version = ((status1 & 0x3) ? WCD939X_VERSION_1_1 : WCD939X_VERSION_1_0); + else + version = WCD939X_VERSION_2_0; + + dev_dbg(dev, "wcd939x version: %s\n", version_to_str(version)); + + ret = snd_soc_register_component(dev, &soc_codec_dev_wcd939x, + wcd939x_dais, ARRAY_SIZE(wcd939x_dais)); + if (ret) { + dev_err(dev, "%s: Codec registration failed\n", + __func__); + goto err_remove_rx_link; + } + + return 0; + +err_remove_rx_link: + device_link_remove(dev, wcd939x->rxdev); +err_remove_tx_link: + device_link_remove(dev, wcd939x->txdev); +err_remove_rxtx_link: + device_link_remove(wcd939x->rxdev, wcd939x->txdev); +err_put_txdev: + put_device(wcd939x->txdev); +err_put_rxdev: + put_device(wcd939x->rxdev); +err_unbind: + component_unbind_all(dev, wcd939x); +err_put_typec_switch: +#if IS_ENABLED(CONFIG_TYPEC) + if (wcd939x->typec_analog_mux) + typec_switch_put(wcd939x->typec_switch); +#endif /* CONFIG_TYPEC */ + + return ret; +} + +static void wcd939x_unbind(struct device *dev) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dev); + + snd_soc_unregister_component(dev); + device_link_remove(dev, wcd939x->txdev); + device_link_remove(dev, wcd939x->rxdev); + device_link_remove(wcd939x->rxdev, wcd939x->txdev); + put_device(wcd939x->txdev); + put_device(wcd939x->rxdev); + component_unbind_all(dev, wcd939x); +} + +static const struct component_master_ops wcd939x_comp_ops = { + .bind = wcd939x_bind, + .unbind = wcd939x_unbind, +}; + +static int wcd939x_add_slave_components(struct wcd939x_priv *wcd939x, + struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np = dev->of_node; + + wcd939x->rxnode = of_parse_phandle(np, "qcom,rx-device", 0); + if (!wcd939x->rxnode) { + dev_err(dev, "%s: Rx-device node not defined\n", __func__); + return -ENODEV; + } + + of_node_get(wcd939x->rxnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd939x->rxnode); + + wcd939x->txnode = of_parse_phandle(np, "qcom,tx-device", 0); + if (!wcd939x->txnode) { + dev_err(dev, "%s: Tx-device node not defined\n", __func__); + return -ENODEV; + } + of_node_get(wcd939x->txnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd939x->txnode); + return 0; +} + +static int wcd939x_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + struct wcd939x_priv *wcd939x = NULL; + struct device *dev = &pdev->dev; + int ret; + + wcd939x = devm_kzalloc(dev, sizeof(struct wcd939x_priv), + GFP_KERNEL); + if (!wcd939x) + return -ENOMEM; + + dev_set_drvdata(dev, wcd939x); + mutex_init(&wcd939x->micb_lock); + + ret = wcd939x_populate_dt_data(wcd939x, dev); + if (ret) { + dev_err(dev, "%s: Fail to obtain platform data\n", __func__); + return -EINVAL; + } + +#if IS_ENABLED(CONFIG_TYPEC) + /* + * Is USBSS is used to mux analog lines, + * register a typec mux/switch to get typec events + */ + if (wcd939x->typec_analog_mux) { + struct typec_mux_desc mux_desc = { + .drvdata = wcd939x, + .fwnode = dev_fwnode(dev), + .set = wcd939x_typec_mux_set, + }; + struct typec_switch_desc sw_desc = { + .drvdata = wcd939x, + .fwnode = dev_fwnode(dev), + .set = wcd939x_typec_switch_set, + }; + + wcd939x->typec_mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(wcd939x->typec_mux)) { + ret = dev_err_probe(dev, PTR_ERR(wcd939x->typec_mux), + "failed to register typec mux\n"); + goto err_disable_regulators; + } + + wcd939x->typec_sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(wcd939x->typec_sw)) { + ret = dev_err_probe(dev, PTR_ERR(wcd939x->typec_sw), + "failed to register typec switch\n"); + goto err_unregister_typec_mux; + } + } +#endif /* CONFIG_TYPEC */ + + ret = wcd939x_add_slave_components(wcd939x, dev, &match); + if (ret) + goto err_unregister_typec_switch; + + wcd939x_reset(wcd939x); + + ret = component_master_add_with_match(dev, &wcd939x_comp_ops, match); + if (ret) + goto err_disable_regulators; + + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +#if IS_ENABLED(CONFIG_TYPEC) +err_unregister_typec_mux: + if (wcd939x->typec_analog_mux) + typec_mux_unregister(wcd939x->typec_mux); +#endif /* CONFIG_TYPEC */ + +err_unregister_typec_switch: +#if IS_ENABLED(CONFIG_TYPEC) + if (wcd939x->typec_analog_mux) + typec_switch_unregister(wcd939x->typec_sw); +#endif /* CONFIG_TYPEC */ + +err_disable_regulators: + regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies); + regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); + + return ret; +} + +static void wcd939x_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd939x_priv *wcd939x = dev_get_drvdata(dev); + + component_master_del(dev, &wcd939x_comp_ops); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); + + regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies); + regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); +} + +#if defined(CONFIG_OF) +static const struct of_device_id wcd939x_dt_match[] = { + { .compatible = "qcom,wcd9390-codec" }, + { .compatible = "qcom,wcd9395-codec" }, + {} +}; +MODULE_DEVICE_TABLE(of, wcd939x_dt_match); +#endif + +static struct platform_driver wcd939x_codec_driver = { + .probe = wcd939x_probe, + .remove_new = wcd939x_remove, + .driver = { + .name = "wcd939x_codec", + .of_match_table = of_match_ptr(wcd939x_dt_match), + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(wcd939x_codec_driver); +MODULE_DESCRIPTION("WCD939X Codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h new file mode 100644 index 000000000000..807cf3113d20 --- /dev/null +++ b/sound/soc/codecs/wcd939x.h @@ -0,0 +1,989 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __WCD939X_H__ +#define __WCD939X_H__ +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> + +#define WCD939X_BASE (0x3000) +#define WCD939X_ANA_PAGE (0x3000) +#define WCD939X_ANA_BIAS (0x3001) +#define WCD939X_BIAS_ANALOG_BIAS_EN BIT(7) +#define WCD939X_BIAS_PRECHRG_EN BIT(6) +#define WCD939X_BIAS_PRECHRG_CTL_MODE BIT(5) +#define WCD939X_ANA_RX_SUPPLIES (0x3008) +#define WCD939X_RX_SUPPLIES_VPOS_EN BIT(7) +#define WCD939X_RX_SUPPLIES_VNEG_EN BIT(6) +#define WCD939X_RX_SUPPLIES_VPOS_PWR_LVL BIT(3) +#define WCD939X_RX_SUPPLIES_VNEG_PWR_LVL BIT(2) +#define WCD939X_RX_SUPPLIES_REGULATOR_MODE BIT(1) +#define WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE BIT(0) +#define WCD939X_ANA_HPH (0x3009) +#define WCD939X_HPH_HPHL_ENABLE BIT(7) +#define WCD939X_HPH_HPHR_ENABLE BIT(6) +#define WCD939X_HPH_HPHL_REF_ENABLE BIT(5) +#define WCD939X_HPH_HPHR_REF_ENABLE BIT(4) +#define WCD939X_HPH_PWR_LEVEL GENMASK(3, 2) +#define WCD939X_ANA_EAR (0x300a) +#define WCD939X_ANA_EAR_COMPANDER_CTL (0x300b) +#define WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG BIT(7) +#define WCD939X_EAR_COMPANDER_CTL_EAR_GAIN GENMASK(6, 2) +#define WCD939X_EAR_COMPANDER_CTL_COMP_DFF_BYP BIT(1) +#define WCD939X_EAR_COMPANDER_CTL_COMP_DFF_CLK_EDGE BIT(0) +#define WCD939X_ANA_TX_CH1 (0x300e) +#define WCD939X_ANA_TX_CH2 (0x300f) +#define WCD939X_TX_CH2_ENABLE BIT(7) +#define WCD939X_TX_CH2_HPF1_INIT BIT(6) +#define WCD939X_TX_CH2_HPF2_INIT BIT(5) +#define WCD939X_TX_CH2_GAIN GENMASK(4, 0) +#define WCD939X_ANA_TX_CH3 (0x3010) +#define WCD939X_ANA_TX_CH4 (0x3011) +#define WCD939X_TX_CH4_ENABLE BIT(7) +#define WCD939X_TX_CH4_HPF3_INIT BIT(6) +#define WCD939X_TX_CH4_HPF4_INIT BIT(5) +#define WCD939X_TX_CH4_GAIN GENMASK(4, 0) +#define WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC (0x3012) +#define WCD939X_ANA_MICB3_DSP_EN_LOGIC (0x3013) +#define WCD939X_ANA_MBHC_MECH (0x3014) +#define WCD939X_MBHC_MECH_L_DET_EN BIT(7) +#define WCD939X_MBHC_MECH_GND_DET_EN BIT(6) +#define WCD939X_MBHC_MECH_MECH_DETECT_TYPE BIT(5) +#define WCD939X_MBHC_MECH_HPHL_PLUG_TYPE BIT(4) +#define WCD939X_MBHC_MECH_GND_PLUG_TYPE BIT(3) +#define WCD939X_MBHC_MECH_MECH_HS_L_PULLUP_COMP_EN BIT(2) +#define WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN BIT(1) +#define WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND BIT(0) +#define WCD939X_ANA_MBHC_ELECT (0x3015) +#define WCD939X_MBHC_ELECT_FSM_EN BIT(7) +#define WCD939X_MBHC_ELECT_BTNDET_ISRC_CTL GENMASK(6, 4) +#define WCD939X_MBHC_ELECT_ELECT_DET_TYPE BIT(3) +#define WCD939X_MBHC_ELECT_ELECT_SCHMT_ISRC_CTL GENMASK(2, 1) +#define WCD939X_MBHC_ELECT_BIAS_EN BIT(0) +#define WCD939X_ANA_MBHC_ZDET (0x3016) +#define WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN BIT(7) +#define WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN BIT(6) +#define WCD939X_MBHC_ZDET_ZDET_CHG_EN BIT(5) +#define WCD939X_MBHC_ZDET_ZDET_ILEAK_COMP_EN BIT(4) +#define WCD939X_MBHC_ZDET_ELECT_ISRC_EN BIT(1) +#define WCD939X_ANA_MBHC_RESULT_1 (0x3017) +#define WCD939X_MBHC_RESULT_1_Z_RESULT_LSB GENMASK(7, 0) +#define WCD939X_ANA_MBHC_RESULT_2 (0x3018) +#define WCD939X_MBHC_RESULT_2_Z_RESULT_MSB GENMASK(7, 0) +#define WCD939X_ANA_MBHC_RESULT_3 (0x3019) +#define WCD939X_ANA_MBHC_BTN0 (0x301a) +#define WCD939X_MBHC_BTN0_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN1 (0x301b) +#define WCD939X_MBHC_BTN1_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN2 (0x301c) +#define WCD939X_MBHC_BTN2_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN3 (0x301d) +#define WCD939X_MBHC_BTN3_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN4 (0x301e) +#define WCD939X_MBHC_BTN4_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN5 (0x301f) +#define WCD939X_MBHC_BTN5_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN6 (0x3020) +#define WCD939X_MBHC_BTN6_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN7 (0x3021) +#define WCD939X_MBHC_BTN7_VTH GENMASK(7, 2) +#define WCD939X_ANA_MICB1 (0x3022) +#define WCD939X_MICB1_ENABLE GENMASK(7, 6) +#define WCD939X_MICB1_VOUT_CTL GENMASK(5, 0) +#define WCD939X_ANA_MICB2 (0x3023) +#define WCD939X_MICB2_ENABLE GENMASK(7, 6) +#define WCD939X_MICB2_VOUT_CTL GENMASK(5, 0) +#define WCD939X_ANA_MICB2_RAMP (0x3024) +#define WCD939X_MICB2_RAMP_RAMP_ENABLE BIT(7) +#define WCD939X_MICB2_RAMP_MB2_IN2P_SHORT_ENABLE BIT(6) +#define WCD939X_MICB2_RAMP_ALLSW_OVRD_ENABLE BIT(5) +#define WCD939X_MICB2_RAMP_SHIFT_CTL GENMASK(4, 2) +#define WCD939X_MICB2_RAMP_USB_MGDET_MICB2_RAMP GENMASK(1, 0) +#define WCD939X_ANA_MICB3 (0x3025) +#define WCD939X_MICB3_ENABLE GENMASK(7, 6) +#define WCD939X_MICB3_VOUT_CTL GENMASK(5, 0) +#define WCD939X_ANA_MICB4 (0x3026) +#define WCD939X_MICB4_ENABLE GENMASK(7, 6) +#define WCD939X_MICB4_VOUT_CTL GENMASK(5, 0) +#define WCD939X_BIAS_CTL (0x3028) +#define WCD939X_BIAS_VBG_FINE_ADJ (0x3029) +#define WCD939X_LDOL_VDDCX_ADJUST (0x3040) +#define WCD939X_LDOL_DISABLE_LDOL (0x3041) +#define WCD939X_MBHC_CTL_CLK (0x3056) +#define WCD939X_MBHC_CTL_ANA (0x3057) +#define WCD939X_MBHC_ZDET_VNEG_CTL (0x3058) +#define WCD939X_MBHC_ZDET_BIAS_CTL (0x3059) +#define WCD939X_MBHC_CTL_BCS (0x305a) +#define WCD939X_MBHC_MOISTURE_DET_FSM_STATUS (0x305b) +#define WCD939X_MBHC_TEST_CTL (0x305c) +#define WCD939X_LDOH_MODE (0x3067) +#define WCD939X_MODE_LDOH_EN BIT(7) +#define WCD939X_MODE_PWRDN_STATE BIT(6) +#define WCD939X_MODE_SLOWRAMP_EN BIT(5) +#define WCD939X_MODE_VOUT_ADJUST GENMASK(4, 3) +#define WCD939X_MODE_VOUT_COARSE_ADJ GENMASK(2, 0) +#define WCD939X_LDOH_BIAS (0x3068) +#define WCD939X_LDOH_STB_LOADS (0x3069) +#define WCD939X_LDOH_SLOWRAMP (0x306a) +#define WCD939X_MICB1_TEST_CTL_1 (0x306b) +#define WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL GENMASK(7, 5) +#define WCD939X_TEST_CTL_1_EN_VREFGEN BIT(4) +#define WCD939X_TEST_CTL_1_EN_LDO BIT(3) +#define WCD939X_TEST_CTL_1_LDO_BLEEDER_I_CTRL GENMASK(2, 0) +#define WCD939X_MICB1_TEST_CTL_2 (0x306c) +#define WCD939X_TEST_CTL_2_IBIAS_VREFGEN GENMASK(7, 6) +#define WCD939X_TEST_CTL_2_INRUSH_CURRENT_FIX_DIS BIT(5) +#define WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER GENMASK(2, 0) +#define WCD939X_MICB1_TEST_CTL_3 (0x306d) +#define WCD939X_TEST_CTL_3_CFILT_REF_EN BIT(7) +#define WCD939X_TEST_CTL_3_RZ_LDO_VAL GENMASK(6, 4) +#define WCD939X_TEST_CTL_3_IBIAS_LDO_STG3 GENMASK(3, 2) +#define WCD939X_TEST_CTL_3_ATEST_CTRL GENMASK(1, 0) +#define WCD939X_MICB2_TEST_CTL_1 (0x306e) +#define WCD939X_MICB2_TEST_CTL_2 (0x306f) +#define WCD939X_MICB2_TEST_CTL_3 (0x3070) +#define WCD939X_MICB3_TEST_CTL_1 (0x3071) +#define WCD939X_MICB3_TEST_CTL_2 (0x3072) +#define WCD939X_MICB3_TEST_CTL_3 (0x3073) +#define WCD939X_MICB4_TEST_CTL_1 (0x3074) +#define WCD939X_MICB4_TEST_CTL_2 (0x3075) +#define WCD939X_MICB4_TEST_CTL_3 (0x3076) +#define WCD939X_TX_COM_ADC_VCM (0x3077) +#define WCD939X_TX_COM_BIAS_ATEST (0x3078) +#define WCD939X_TX_COM_SPARE1 (0x3079) +#define WCD939X_TX_COM_SPARE2 (0x307a) +#define WCD939X_TX_COM_TXFE_DIV_CTL (0x307b) +#define WCD939X_TX_COM_TXFE_DIV_START (0x307c) +#define WCD939X_TX_COM_SPARE3 (0x307d) +#define WCD939X_TX_COM_SPARE4 (0x307e) +#define WCD939X_TX_1_2_TEST_EN (0x307f) +#define WCD939X_TX_1_2_ADC_IB (0x3080) +#define WCD939X_TX_1_2_ATEST_REFCTL (0x3081) +#define WCD939X_TX_1_2_TEST_CTL (0x3082) +#define WCD939X_TX_1_2_TEST_BLK_EN1 (0x3083) +#define WCD939X_TX_1_2_TXFE1_CLKDIV (0x3084) +#define WCD939X_TX_1_2_SAR2_ERR (0x3085) +#define WCD939X_TX_1_2_SAR1_ERR (0x3086) +#define WCD939X_TX_3_4_TEST_EN (0x3087) +#define WCD939X_TX_3_4_ADC_IB (0x3088) +#define WCD939X_TX_3_4_ATEST_REFCTL (0x3089) +#define WCD939X_TX_3_4_TEST_CTL (0x308a) +#define WCD939X_TX_3_4_TEST_BLK_EN3 (0x308b) +#define WCD939X_TX_3_4_TXFE3_CLKDIV (0x308c) +#define WCD939X_TX_3_4_SAR4_ERR (0x308d) +#define WCD939X_TX_3_4_SAR3_ERR (0x308e) +#define WCD939X_TX_3_4_TEST_BLK_EN2 (0x308f) +#define WCD939X_TEST_BLK_EN2_ADC2_INT1_EN BIT(7) +#define WCD939X_TEST_BLK_EN2_ADC2_INT2_EN BIT(6) +#define WCD939X_TEST_BLK_EN2_ADC2_SAR_EN BIT(5) +#define WCD939X_TEST_BLK_EN2_ADC2_CMGEN_EN BIT(4) +#define WCD939X_TEST_BLK_EN2_ADC2_CLKGEN_EN BIT(3) +#define WCD939X_TEST_BLK_EN2_ADC12_VREF_NONL2 GENMASK(2, 1) +#define WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN BIT(0) +#define WCD939X_TX_3_4_TXFE2_CLKDIV (0x3090) +#define WCD939X_TX_3_4_SPARE1 (0x3091) +#define WCD939X_TX_3_4_TEST_BLK_EN4 (0x3092) +#define WCD939X_TX_3_4_TXFE4_CLKDIV (0x3093) +#define WCD939X_TX_3_4_SPARE2 (0x3094) +#define WCD939X_CLASSH_MODE_1 (0x3097) +#define WCD939X_CLASSH_MODE_2 (0x3098) +#define WCD939X_CLASSH_MODE_3 (0x3099) +#define WCD939X_CLASSH_CTRL_VCL_1 (0x309a) +#define WCD939X_CLASSH_CTRL_VCL_2 (0x309b) +#define WCD939X_CLASSH_CTRL_CCL_1 (0x309c) +#define WCD939X_CLASSH_CTRL_CCL_2 (0x309d) +#define WCD939X_CLASSH_CTRL_CCL_3 (0x309e) +#define WCD939X_CLASSH_CTRL_CCL_4 (0x309f) +#define WCD939X_CLASSH_CTRL_CCL_5 (0x30a0) +#define WCD939X_CLASSH_BUCK_TMUX_A_D (0x30a1) +#define WCD939X_CLASSH_BUCK_SW_DRV_CNTL (0x30a2) +#define WCD939X_CLASSH_SPARE (0x30a3) +#define WCD939X_FLYBACK_EN (0x30a4) +#define WCD939X_FLYBACK_VNEG_CTRL_1 (0x30a5) +#define WCD939X_FLYBACK_VNEG_CTRL_2 (0x30a6) +#define WCD939X_FLYBACK_VNEG_CTRL_3 (0x30a7) +#define WCD939X_FLYBACK_VNEG_CTRL_4 (0x30a8) +#define WCD939X_VNEG_CTRL_4_ILIM_SEL GENMASK(7, 4) +#define WCD939X_VNEG_CTRL_4_PW_BUF_POS GENMASK(3, 2) +#define WCD939X_VNEG_CTRL_4_PW_BUF_NEG GENMASK(1, 0) +#define WCD939X_FLYBACK_VNEG_CTRL_5 (0x30a9) +#define WCD939X_FLYBACK_VNEG_CTRL_6 (0x30aa) +#define WCD939X_FLYBACK_VNEG_CTRL_7 (0x30ab) +#define WCD939X_FLYBACK_VNEG_CTRL_8 (0x30ac) +#define WCD939X_FLYBACK_VNEG_CTRL_9 (0x30ad) +#define WCD939X_FLYBACK_VNEGDAC_CTRL_1 (0x30ae) +#define WCD939X_FLYBACK_VNEGDAC_CTRL_2 (0x30af) +#define WCD939X_FLYBACK_VNEGDAC_CTRL_3 (0x30b0) +#define WCD939X_FLYBACK_CTRL_1 (0x30b1) +#define WCD939X_FLYBACK_TEST_CTL (0x30b2) +#define WCD939X_RX_AUX_SW_CTL (0x30b3) +#define WCD939X_RX_PA_AUX_IN_CONN (0x30b4) +#define WCD939X_RX_TIMER_DIV (0x30b5) +#define WCD939X_RX_OCP_CTL (0x30b6) +#define WCD939X_RX_OCP_COUNT (0x30b7) +#define WCD939X_RX_BIAS_EAR_DAC (0x30b8) +#define WCD939X_RX_BIAS_EAR_AMP (0x30b9) +#define WCD939X_RX_BIAS_HPH_LDO (0x30ba) +#define WCD939X_RX_BIAS_HPH_PA (0x30bb) +#define WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2 (0x30bc) +#define WCD939X_RX_BIAS_HPH_RDAC_LDO (0x30bd) +#define WCD939X_RX_BIAS_HPH_CNP1 (0x30be) +#define WCD939X_RX_BIAS_HPH_LOWPOWER (0x30bf) +#define WCD939X_RX_BIAS_AUX_DAC (0x30c0) +#define WCD939X_RX_BIAS_AUX_AMP (0x30c1) +#define WCD939X_RX_BIAS_VNEGDAC_BLEEDER (0x30c2) +#define WCD939X_RX_BIAS_MISC (0x30c3) +#define WCD939X_RX_BIAS_BUCK_RST (0x30c4) +#define WCD939X_RX_BIAS_BUCK_VREF_ERRAMP (0x30c5) +#define WCD939X_RX_BIAS_FLYB_ERRAMP (0x30c6) +#define WCD939X_RX_BIAS_FLYB_BUFF (0x30c7) +#define WCD939X_RX_BIAS_FLYB_MID_RST (0x30c8) +#define WCD939X_HPH_L_STATUS (0x30c9) +#define WCD939X_HPH_R_STATUS (0x30ca) +#define WCD939X_HPH_CNP_EN (0x30cb) +#define WCD939X_HPH_CNP_WG_CTL (0x30cc) +#define WCD939X_HPH_CNP_WG_TIME (0x30cd) +#define WCD939X_HPH_OCP_CTL (0x30ce) +#define WCD939X_OCP_CTL_OCP_CURR_LIMIT GENMASK(7, 5) +#define WCD939X_OCP_CTL_OCP_FSM_EN BIT(4) +#define WCD939X_OCP_CTL_SPARE_BITS BIT(3) +#define WCD939X_OCP_CTL_SCD_OP_EN BIT(1) +#define WCD939X_HPH_AUTO_CHOP (0x30cf) +#define WCD939X_HPH_CHOP_CTL (0x30d0) +#define WCD939X_HPH_PA_CTL1 (0x30d1) +#define WCD939X_HPH_PA_CTL2 (0x30d2) +#define WCD939X_PA_CTL2_HPHPA_GND_R BIT(6) +#define WCD939X_PA_CTL2_HPHPA_GND_L BIT(4) +#define WCD939X_PA_CTL2_GM3_CASCODE_CTL_NORMAL GENMASK(1, 0) +#define WCD939X_HPH_L_EN (0x30d3) +#define WCD939X_L_EN_CONST_SEL_L GENMASK(7, 6) +#define WCD939X_L_EN_GAIN_SOURCE_SEL BIT(5) +#define WCD939X_L_EN_SPARE_BITS GENMASK(4, 0) +#define WCD939X_HPH_L_TEST (0x30d4) +#define WCD939X_HPH_L_ATEST (0x30d5) +#define WCD939X_HPH_R_EN (0x30d6) +#define WCD939X_R_EN_CONST_SEL_R GENMASK(7, 6) +#define WCD939X_R_EN_GAIN_SOURCE_SEL BIT(5) +#define WCD939X_R_EN_SPARE_BITS GENMASK(4, 0) +#define WCD939X_HPH_R_TEST (0x30d7) +#define WCD939X_HPH_R_ATEST (0x30d8) +#define WCD939X_R_ATEST_DACR_REF_ATEST1_CONN BIT(7) +#define WCD939X_R_ATEST_LDO1_R_ATEST2_CONN BIT(6) +#define WCD939X_R_ATEST_LDO_R_ATEST2_CAL BIT(5) +#define WCD939X_R_ATEST_LDO2_R_ATEST2_CONN BIT(4) +#define WCD939X_R_ATEST_LDO_1P65V_ATEST1_CONN BIT(3) +#define WCD939X_R_ATEST_HPH_GND_OVR BIT(1) +#define WCD939X_HPH_RDAC_CLK_CTL1 (0x30d9) +#define WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN BIT(7) +#define WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_DIV_CTRL GENMASK(6, 4) +#define WCD939X_RDAC_CLK_CTL1_SPARE_BITS GENMASK(3, 0) +#define WCD939X_HPH_RDAC_CLK_CTL2 (0x30da) +#define WCD939X_HPH_RDAC_LDO_CTL (0x30db) +#define WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL (0x30dc) +#define WCD939X_HPH_REFBUFF_UHQA_CTL (0x30dd) +#define WCD939X_REFBUFF_UHQA_CTL_SPARE_BITS GENMASK(7, 6) +#define WCD939X_REFBUFF_UHQA_CTL_HPH_VNEGREG2_COMP_CTL_OV BIT(5) +#define WCD939X_REFBUFF_UHQA_CTL_REFBUFN_RBIAS_ADJUST BIT(4) +#define WCD939X_REFBUFF_UHQA_CTL_REFBUFP_IOUT_CTL GENMASK(3, 2) +#define WCD939X_REFBUFF_UHQA_CTL_REFBUFN_IOUT_CTL GENMASK(1, 0) +#define WCD939X_HPH_REFBUFF_LP_CTL (0x30de) +#define WCD939X_REFBUFF_LP_CTL_HPH_VNEGREG2_CURR_COMP GENMASK(7, 6) +#define WCD939X_REFBUFF_LP_CTL_SPARE_BITS GENMASK(5, 4) +#define WCD939X_REFBUFF_LP_CTL_EN_PREREF_FILT_STARTUP_CLKDIV BIT(3) +#define WCD939X_REFBUFF_LP_CTL_PREREF_FILT_STARTUP_CLKDIV_CTL GENMASK(2, 1) +#define WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS BIT(0) +#define WCD939X_HPH_L_DAC_CTL (0x30df) +#define WCD939X_HPH_R_DAC_CTL (0x30e0) +#define WCD939X_HPH_SURGE_COMP_SEL (0x30e1) +#define WCD939X_HPH_SURGE_EN (0x30e2) +#define WCD939X_EN_EN_SURGE_PROTECTION_HPHL BIT(7) +#define WCD939X_EN_EN_SURGE_PROTECTION_HPHR BIT(6) +#define WCD939X_EN_SEL_SURGE_COMP_IQ GENMASK(5, 4) +#define WCD939X_EN_SURGE_VOLT_MODE_SHUTOFF_EN BIT(3) +#define WCD939X_EN_LATCH_INTR_OP_STG_HIZ_EN BIT(2) +#define WCD939X_EN_SURGE_LATCH_REG_RESET BIT(1) +#define WCD939X_EN_SWTICH_VN_VNDAC_NSURGE_EN BIT(0) +#define WCD939X_HPH_SURGE_MISC1 (0x30e3) +#define WCD939X_HPH_SURGE_STATUS (0x30e4) +#define WCD939X_EAR_EN (0x30e9) +#define WCD939X_EAR_PA_CON (0x30ea) +#define WCD939X_EAR_SP_CON (0x30eb) +#define WCD939X_EAR_DAC_CON (0x30ec) +#define WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL BIT(7) +#define WCD939X_DAC_CON_REF_DBG_EN BIT(6) +#define WCD939X_DAC_CON_REF_DBG_GAIN GENMASK(5, 3) +#define WCD939X_DAC_CON_GAIN_DAC GENMASK(2, 1) +#define WCD939X_DAC_CON_INV_DATA BIT(0) +#define WCD939X_EAR_CNP_FSM_CON (0x30ed) +#define WCD939X_EAR_TEST_CTL (0x30ee) +#define WCD939X_EAR_STATUS_REG_1 (0x30ef) +#define WCD939X_EAR_STATUS_REG_2 (0x30f0) +#define WCD939X_FLYBACK_NEW_CTRL_2 (0x30f6) +#define WCD939X_FLYBACK_NEW_CTRL_3 (0x30f7) +#define WCD939X_FLYBACK_NEW_CTRL_4 (0x30f8) +#define WCD939X_ANA_NEW_PAGE (0x3100) +#define WCD939X_HPH_NEW_ANA_HPH2 (0x3101) +#define WCD939X_HPH_NEW_ANA_HPH3 (0x3102) +#define WCD939X_SLEEP_CTL (0x3103) +#define WCD939X_SLEEP_WATCHDOG_CTL (0x3104) +#define WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL (0x311f) +#define WCD939X_MBHC_NEW_CTL_1 (0x3120) +#define WCD939X_CTL_1_RCO_EN BIT(7) +#define WCD939X_CTL_1_ADC_MODE BIT(4) +#define WCD939X_CTL_1_ADC_ENABLE BIT(3) +#define WCD939X_CTL_1_DETECTION_DONE BIT(2) +#define WCD939X_CTL_1_BTN_DBNC_CTL GENMASK(1, 0) +#define WCD939X_MBHC_NEW_CTL_2 (0x3121) +#define WCD939X_CTL_2_MUX_CTL GENMASK(6, 4) +#define WCD939X_CTL_2_M_RTH_CTL GENMASK(3, 2) +#define WCD939X_CTL_2_HS_VREF_CTL GENMASK(1, 0) +#define WCD939X_MBHC_NEW_PLUG_DETECT_CTL (0x3122) +#define WCD939X_MBHC_NEW_ZDET_ANA_CTL (0x3123) +#define WCD939X_ZDET_ANA_CTL_AVERAGING_EN BIT(7) +#define WCD939X_ZDET_ANA_CTL_MAXV_CTL GENMASK(6, 4) +#define WCD939X_ZDET_ANA_CTL_RANGE_CTL GENMASK(3, 0) +#define WCD939X_MBHC_NEW_ZDET_RAMP_CTL (0x3124) +#define WCD939X_ZDET_RAMP_CTL_ACC1_MIN_CTL GENMASK(6, 4) +#define WCD939X_ZDET_RAMP_CTL_TIME_CTL GENMASK(3, 0) +#define WCD939X_MBHC_NEW_FSM_STATUS (0x3125) +#define WCD939X_FSM_STATUS_ADC_TIMEOUT BIT(7) +#define WCD939X_FSM_STATUS_ADC_COMPLETE BIT(6) +#define WCD939X_FSM_STATUS_HS_M_COMP_STATUS BIT(5) +#define WCD939X_FSM_STATUS_FAST_PRESS_FLAG_STATUS BIT(4) +#define WCD939X_FSM_STATUS_FAST_REMOVAL_FLAG_STATUS BIT(3) +#define WCD939X_FSM_STATUS_REMOVAL_FLAG_STATUS BIT(2) +#define WCD939X_FSM_STATUS_ELECT_REM_RT_STATUS BIT(1) +#define WCD939X_FSM_STATUS_BTN_STATUS BIT(0) +#define WCD939X_MBHC_NEW_ADC_RESULT (0x3126) +#define WCD939X_ADC_RESULT_VALUE GENMASK(7, 0) +#define WCD939X_TX_NEW_CH12_MUX (0x3127) +#define WCD939X_TX_NEW_CH34_MUX (0x3128) +#define WCD939X_DIE_CRACK_DET_EN (0x312c) +#define WCD939X_DIE_CRACK_DET_OUT (0x312d) +#define WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL (0x3132) +#define WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L (0x3133) +#define WCD939X_PA_GAIN_CTL_L_EN_HPHPA_2VPK BIT(7) +#define WCD939X_PA_GAIN_CTL_L_RX_SUPPLY_LEVEL BIT(6) +#define WCD939X_PA_GAIN_CTL_L_DAC_DR_BOOST BIT(5) +#define WCD939X_PA_GAIN_CTL_L_VALUE GENMASK(4, 0) +#define WCD939X_HPH_NEW_INT_RDAC_VREF_CTL (0x3134) +#define WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL (0x3135) +#define WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R (0x3136) +#define WCD939X_PA_GAIN_CTL_R_D_RCO_CLK_EN BIT(7) +#define WCD939X_PA_GAIN_CTL_R_SPARE_BITS GENMASK(6, 5) +#define WCD939X_PA_GAIN_CTL_R_VALUE GENMASK(4, 0) +#define WCD939X_HPH_NEW_INT_PA_MISC1 (0x3137) +#define WCD939X_HPH_NEW_INT_PA_MISC2 (0x3138) +#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC (0x3139) +#define WCD939X_HPH_NEW_INT_TIMER1 (0x313a) +#define WCD939X_TIMER1_CURR_IDIV_CTL_CMPDR_OFF GENMASK(7, 5) +#define WCD939X_TIMER1_CURR_IDIV_CTL_AUTOCHOP GENMASK(4, 2) +#define WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN BIT(1) +#define WCD939X_HPH_NEW_INT_TIMER2 (0x313b) +#define WCD939X_HPH_NEW_INT_TIMER3 (0x313c) +#define WCD939X_HPH_NEW_INT_TIMER4 (0x313d) +#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC2 (0x313e) +#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC3 (0x313f) +#define WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L (0x3140) +#define WCD939X_RDAC_HD2_CTL_L_EN_HD2_RES_DIV_L BIT(7) +#define WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_PULLGND_L BIT(6) +#define WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L GENMASK(5, 0) +#define WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R (0x3141) +#define WCD939X_RDAC_HD2_CTL_R_EN_HD2_RES_DIV_R BIT(7) +#define WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_PULLGND_L BIT(6) +#define WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R GENMASK(5, 0) +#define WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI (0x3145) +#define WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP (0x3146) +#define WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP (0x3147) +#define WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL (0x31af) +#define WCD939X_MOISTURE_DET_DC_CTRL_ONCOUNT GENMASK(6, 5) +#define WCD939X_MOISTURE_DET_DC_CTRL_OFFCOUNT GENMASK(4, 0) +#define WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL (0x31b0) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_HPHL_PA_EN BIT(6) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_DTEST_EN GENMASK(5, 4) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_OVRD_POLLING BIT(3) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_EN_POLLING BIT(2) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_DBNC_TIME GENMASK(1, 0) +#define WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT (0x31b1) +#define WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL GENMASK(4, 0) +#define WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW (0x31b2) +#define WCD939X_EAR_INT_NEW_CHOPPER_CON (0x31b7) +#define WCD939X_EAR_INT_NEW_CNP_VCM_CON1 (0x31b8) +#define WCD939X_EAR_INT_NEW_CNP_VCM_CON2 (0x31b9) +#define WCD939X_EAR_INT_NEW_DYNAMIC_BIAS (0x31ba) +#define WCD939X_SLEEP_INT_WATCHDOG_CTL_1 (0x31d0) +#define WCD939X_SLEEP_INT_WATCHDOG_CTL_2 (0x31d1) +#define WCD939X_DIE_CRACK_INT_DET_INT1 (0x31d3) +#define WCD939X_DIE_CRACK_INT_DET_INT2 (0x31d4) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2 (0x31d5) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1 (0x31d6) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0 (0x31d7) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M (0x31d8) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M (0x31d9) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1 (0x31da) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0 (0x31db) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP (0x31dc) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1 (0x31dd) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0 (0x31de) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP (0x31df) +#define WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE GENMASK(4, 0) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0 (0x31e0) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP (0x31e1) +#define WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M GENMASK(7, 4) +#define WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE GENMASK(3, 0) +#define WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1 (0x31e2) +#define WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP (0x31e3) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_L2 (0x31e4) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_L1 (0x31e5) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_L0 (0x31e6) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_ULP (0x31e7) +#define WCD939X_DIGITAL_PAGE (0x3400) +#define WCD939X_DIGITAL_CHIP_ID0 (0x3401) +#define WCD939X_DIGITAL_CHIP_ID1 (0x3402) +#define WCD939X_DIGITAL_CHIP_ID2 (0x3403) +#define WCD939X_DIGITAL_CHIP_ID3 (0x3404) +#define WCD939X_DIGITAL_SWR_TX_CLK_RATE (0x3405) +#define WCD939X_DIGITAL_CDC_RST_CTL (0x3406) +#define WCD939X_DIGITAL_TOP_CLK_CFG (0x3407) +#define WCD939X_DIGITAL_CDC_ANA_CLK_CTL (0x3408) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV4_CLK_EN BIT(5) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN BIT(4) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN BIT(3) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN BIT(2) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN BIT(1) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN BIT(0) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN BIT(4) +#define WCD939X_DIGITAL_CDC_DIG_CLK_CTL (0x3409) +#define WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN BIT(7) +#define WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN BIT(6) +#define WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN BIT(5) +#define WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN BIT(4) +#define WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN BIT(2) +#define WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN BIT(1) +#define WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN BIT(0) +#define WCD939X_DIGITAL_SWR_RST_EN (0x340a) +#define WCD939X_DIGITAL_CDC_PATH_MODE (0x340b) +#define WCD939X_DIGITAL_CDC_RX_RST (0x340c) +#define WCD939X_DIGITAL_CDC_RX0_CTL (0x340d) +#define WCD939X_DIGITAL_CDC_RX1_CTL (0x340e) +#define WCD939X_DIGITAL_CDC_RX2_CTL (0x340f) +#define WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1 (0x3410) +#define WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE GENMASK(7, 4) +#define WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE GENMASK(3, 0) +#define WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3 (0x3411) +#define WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE GENMASK(7, 4) +#define WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE GENMASK(3, 0) +#define WCD939X_DIGITAL_CDC_COMP_CTL_0 (0x3414) +#define WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN BIT(1) +#define WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN BIT(0) +#define WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL (0x3417) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_MBHC_1P2M_CLK_EN BIT(5) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX3_ADC_CLK_EN BIT(4) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX2_ADC_CLK_EN BIT(3) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX1_ADC_CLK_EN BIT(2) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX0_ADC_CLK_EN BIT(1) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN BIT(0) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A1_0 (0x3418) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A1_1 (0x3419) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A2_0 (0x341a) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A2_1 (0x341b) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A3_0 (0x341c) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A3_1 (0x341d) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A4_0 (0x341e) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A4_1 (0x341f) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A5_0 (0x3420) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A5_1 (0x3421) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A6_0 (0x3422) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A7_0 (0x3423) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_0 (0x3424) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_1 (0x3425) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_2 (0x3426) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_3 (0x3427) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R1 (0x3428) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R2 (0x3429) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R3 (0x342a) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R4 (0x342b) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R5 (0x342c) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R6 (0x342d) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R7 (0x342e) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A1_0 (0x342f) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A1_1 (0x3430) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A2_0 (0x3431) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A2_1 (0x3432) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A3_0 (0x3433) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A3_1 (0x3434) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A4_0 (0x3435) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A4_1 (0x3436) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A5_0 (0x3437) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A5_1 (0x3438) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A6_0 (0x3439) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A7_0 (0x343a) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_0 (0x343b) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_1 (0x343c) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_2 (0x343d) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_3 (0x343e) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R1 (0x343f) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R2 (0x3440) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R3 (0x3441) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R4 (0x3442) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R5 (0x3443) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R6 (0x3444) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R7 (0x3445) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0 (0x3446) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1 (0x3447) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0 (0x3448) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1 (0x3449) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2 (0x344a) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0 (0x344b) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1 (0x344c) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2 (0x344d) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_CTL (0x344e) +#define WCD939X_CDC_HPH_GAIN_CTL_HPH_STEREO_EN BIT(4) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN BIT(3) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN BIT(2) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHR_DSD_EN BIT(1) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHL_DSD_EN BIT(0) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_CTL (0x344f) +#define WCD939X_CDC_EAR_GAIN_CTL_EAR_EN BIT(0) +#define WCD939X_DIGITAL_CDC_EAR_PATH_CTL (0x3450) +#define WCD939X_DIGITAL_CDC_SWR_CLH (0x3451) +#define WCD939X_CDC_SWR_CLH_CLH_CTL GENMASK(7, 0) +#define WCD939X_DIGITAL_SWR_CLH_BYP (0x3452) +#define WCD939X_DIGITAL_CDC_TX0_CTL (0x3453) +#define WCD939X_DIGITAL_CDC_TX1_CTL (0x3454) +#define WCD939X_DIGITAL_CDC_TX2_CTL (0x3455) +#define WCD939X_DIGITAL_CDC_TX_RST (0x3456) +#define WCD939X_DIGITAL_CDC_REQ_CTL (0x3457) +#define WCD939X_CDC_REQ_CTL_TX3_WIDE_BAND BIT(5) +#define WCD939X_CDC_REQ_CTL_TX2_WIDE_BAND BIT(4) +#define WCD939X_CDC_REQ_CTL_TX1_WIDE_BAND BIT(3) +#define WCD939X_CDC_REQ_CTL_TX0_WIDE_BAND BIT(2) +#define WCD939X_CDC_REQ_CTL_FS_RATE_4P8 BIT(1) +#define WCD939X_CDC_REQ_CTL_NO_NOTCH BIT(0) +#define WCD939X_DIGITAL_CDC_RST (0x3458) +#define WCD939X_DIGITAL_CDC_AMIC_CTL (0x345a) +#define WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL BIT(3) +#define WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL BIT(2) +#define WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL BIT(1) +#define WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL BIT(0) +#define WCD939X_DIGITAL_CDC_DMIC_CTL (0x345b) +#define WCD939X_CDC_DMIC_CTL_DMIC_LEGACY_SW_MODE BIT(3) +#define WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN BIT(2) +#define WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN BIT(1) +#define WCD939X_CDC_DMIC_CTL_SOFT_RESET BIT(0) +#define WCD939X_DIGITAL_CDC_DMIC1_CTL (0x345c) +#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_CDC_DMIC2_CTL (0x345d) +#define WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN BIT(7) +#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_CDC_DMIC3_CTL (0x345e) +#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_CDC_DMIC4_CTL (0x345f) +#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_EFUSE_PRG_CTL (0x3460) +#define WCD939X_DIGITAL_EFUSE_CTL (0x3461) +#define WCD939X_DIGITAL_CDC_DMIC_RATE_1_2 (0x3462) +#define WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE GENMASK(7, 4) +#define WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE GENMASK(3, 0) +#define WCD939X_DIGITAL_CDC_DMIC_RATE_3_4 (0x3463) +#define WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE GENMASK(7, 4) +#define WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE GENMASK(3, 0) +#define WCD939X_DIGITAL_PDM_WD_CTL0 (0x3465) +#define WCD939X_PDM_WD_CTL0_HOLD_OFF BIT(4) +#define WCD939X_PDM_WD_CTL0_TIME_OUT_SEL BIT(3) +#define WCD939X_PDM_WD_CTL0_PDM_WD_EN GENMASK(2, 0) +#define WCD939X_DIGITAL_PDM_WD_CTL1 (0x3466) +#define WCD939X_PDM_WD_CTL1_HOLD_OFF BIT(4) +#define WCD939X_PDM_WD_CTL1_TIME_OUT_SEL BIT(3) +#define WCD939X_PDM_WD_CTL1_PDM_WD_EN GENMASK(2, 0) +#define WCD939X_DIGITAL_PDM_WD_CTL2 (0x3467) +#define WCD939X_DIGITAL_INTR_MODE (0x346a) +#define WCD939X_DIGITAL_INTR_MASK_0 (0x346b) +#define WCD939X_DIGITAL_INTR_MASK_1 (0x346c) +#define WCD939X_DIGITAL_INTR_MASK_2 (0x346d) +#define WCD939X_DIGITAL_INTR_STATUS_0 (0x346e) +#define WCD939X_DIGITAL_INTR_STATUS_1 (0x346f) +#define WCD939X_DIGITAL_INTR_STATUS_2 (0x3470) +#define WCD939X_DIGITAL_INTR_CLEAR_0 (0x3471) +#define WCD939X_DIGITAL_INTR_CLEAR_1 (0x3472) +#define WCD939X_DIGITAL_INTR_CLEAR_2 (0x3473) +#define WCD939X_DIGITAL_INTR_LEVEL_0 (0x3474) +#define WCD939X_DIGITAL_INTR_LEVEL_1 (0x3475) +#define WCD939X_DIGITAL_INTR_LEVEL_2 (0x3476) +#define WCD939X_DIGITAL_INTR_SET_0 (0x3477) +#define WCD939X_DIGITAL_INTR_SET_1 (0x3478) +#define WCD939X_DIGITAL_INTR_SET_2 (0x3479) +#define WCD939X_DIGITAL_INTR_TEST_0 (0x347a) +#define WCD939X_DIGITAL_INTR_TEST_1 (0x347b) +#define WCD939X_DIGITAL_INTR_TEST_2 (0x347c) +#define WCD939X_DIGITAL_TX_MODE_DBG_EN (0x347f) +#define WCD939X_DIGITAL_TX_MODE_DBG_0_1 (0x3480) +#define WCD939X_DIGITAL_TX_MODE_DBG_2_3 (0x3481) +#define WCD939X_DIGITAL_LB_IN_SEL_CTL (0x3482) +#define WCD939X_DIGITAL_LOOP_BACK_MODE (0x3483) +#define WCD939X_DIGITAL_SWR_DAC_TEST (0x3484) +#define WCD939X_DIGITAL_SWR_HM_TEST_RX_0 (0x3485) +#define WCD939X_DIGITAL_SWR_HM_TEST_TX_0 (0x3486) +#define WCD939X_DIGITAL_SWR_HM_TEST_RX_1 (0x3487) +#define WCD939X_DIGITAL_SWR_HM_TEST_TX_1 (0x3488) +#define WCD939X_DIGITAL_SWR_HM_TEST_TX_2 (0x3489) +#define WCD939X_DIGITAL_SWR_HM_TEST_0 (0x348a) +#define WCD939X_DIGITAL_SWR_HM_TEST_1 (0x348b) +#define WCD939X_DIGITAL_PAD_CTL_SWR_0 (0x348c) +#define WCD939X_DIGITAL_PAD_CTL_SWR_1 (0x348d) +#define WCD939X_DIGITAL_I2C_CTL (0x348e) +#define WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE (0x348f) +#define WCD939X_DIGITAL_EFUSE_TEST_CTL_0 (0x3490) +#define WCD939X_DIGITAL_EFUSE_TEST_CTL_1 (0x3491) +#define WCD939X_DIGITAL_EFUSE_T_DATA_0 (0x3492) +#define WCD939X_DIGITAL_EFUSE_T_DATA_1 (0x3493) +#define WCD939X_DIGITAL_PAD_CTL_PDM_RX0 (0x3494) +#define WCD939X_DIGITAL_PAD_CTL_PDM_RX1 (0x3495) +#define WCD939X_DIGITAL_PAD_CTL_PDM_TX0 (0x3496) +#define WCD939X_DIGITAL_PAD_CTL_PDM_TX1 (0x3497) +#define WCD939X_DIGITAL_PAD_CTL_PDM_TX2 (0x3498) +#define WCD939X_DIGITAL_PAD_INP_DIS_0 (0x3499) +#define WCD939X_DIGITAL_PAD_INP_DIS_1 (0x349a) +#define WCD939X_DIGITAL_DRIVE_STRENGTH_0 (0x349b) +#define WCD939X_DIGITAL_DRIVE_STRENGTH_1 (0x349c) +#define WCD939X_DIGITAL_DRIVE_STRENGTH_2 (0x349d) +#define WCD939X_DIGITAL_RX_DATA_EDGE_CTL (0x349e) +#define WCD939X_DIGITAL_TX_DATA_EDGE_CTL (0x349f) +#define WCD939X_DIGITAL_GPIO_MODE (0x34a0) +#define WCD939X_DIGITAL_PIN_CTL_OE (0x34a1) +#define WCD939X_DIGITAL_PIN_CTL_DATA_0 (0x34a2) +#define WCD939X_DIGITAL_PIN_CTL_DATA_1 (0x34a3) +#define WCD939X_DIGITAL_PIN_STATUS_0 (0x34a4) +#define WCD939X_DIGITAL_PIN_STATUS_1 (0x34a5) +#define WCD939X_DIGITAL_DIG_DEBUG_CTL (0x34a6) +#define WCD939X_DIGITAL_DIG_DEBUG_EN (0x34a7) +#define WCD939X_DIGITAL_ANA_CSR_DBG_ADD (0x34a8) +#define WCD939X_DIGITAL_ANA_CSR_DBG_CTL (0x34a9) +#define WCD939X_DIGITAL_SSP_DBG (0x34aa) +#define WCD939X_DIGITAL_MODE_STATUS_0 (0x34ab) +#define WCD939X_DIGITAL_MODE_STATUS_1 (0x34ac) +#define WCD939X_DIGITAL_SPARE_0 (0x34ad) +#define WCD939X_DIGITAL_SPARE_1 (0x34ae) +#define WCD939X_DIGITAL_SPARE_2 (0x34af) +#define WCD939X_DIGITAL_EFUSE_REG_0 (0x34b0) +#define WCD939X_EFUSE_REG_0_WCD939X_ID GENMASK(4, 1) +#define WCD939X_EFUSE_REG_0_EFUSE_BLOWN BIT(0) +#define WCD939X_DIGITAL_EFUSE_REG_1 (0x34b1) +#define WCD939X_DIGITAL_EFUSE_REG_2 (0x34b2) +#define WCD939X_DIGITAL_EFUSE_REG_3 (0x34b3) +#define WCD939X_DIGITAL_EFUSE_REG_4 (0x34b4) +#define WCD939X_DIGITAL_EFUSE_REG_5 (0x34b5) +#define WCD939X_DIGITAL_EFUSE_REG_6 (0x34b6) +#define WCD939X_DIGITAL_EFUSE_REG_7 (0x34b7) +#define WCD939X_DIGITAL_EFUSE_REG_8 (0x34b8) +#define WCD939X_DIGITAL_EFUSE_REG_9 (0x34b9) +#define WCD939X_DIGITAL_EFUSE_REG_10 (0x34ba) +#define WCD939X_DIGITAL_EFUSE_REG_11 (0x34bb) +#define WCD939X_DIGITAL_EFUSE_REG_12 (0x34bc) +#define WCD939X_DIGITAL_EFUSE_REG_13 (0x34bd) +#define WCD939X_DIGITAL_EFUSE_REG_14 (0x34be) +#define WCD939X_DIGITAL_EFUSE_REG_15 (0x34bf) +#define WCD939X_DIGITAL_EFUSE_REG_16 (0x34c0) +#define WCD939X_DIGITAL_EFUSE_REG_17 (0x34c1) +#define WCD939X_DIGITAL_EFUSE_REG_18 (0x34c2) +#define WCD939X_DIGITAL_EFUSE_REG_19 (0x34c3) +#define WCD939X_DIGITAL_EFUSE_REG_20 (0x34c4) +#define WCD939X_DIGITAL_EFUSE_REG_21 (0x34c5) +#define WCD939X_DIGITAL_EFUSE_REG_22 (0x34c6) +#define WCD939X_DIGITAL_EFUSE_REG_23 (0x34c7) +#define WCD939X_DIGITAL_EFUSE_REG_24 (0x34c8) +#define WCD939X_DIGITAL_EFUSE_REG_25 (0x34c9) +#define WCD939X_DIGITAL_EFUSE_REG_26 (0x34ca) +#define WCD939X_DIGITAL_EFUSE_REG_27 (0x34cb) +#define WCD939X_DIGITAL_EFUSE_REG_28 (0x34cc) +#define WCD939X_DIGITAL_EFUSE_REG_29 (0x34cd) +#define WCD939X_DIGITAL_EFUSE_REG_30 (0x34ce) +#define WCD939X_DIGITAL_EFUSE_REG_31 (0x34cf) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_0 (0x34d0) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_1 (0x34d1) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_2 (0x34d2) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_3 (0x34d3) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_4 (0x34d4) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA0 (0x34d5) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA1 (0x34d6) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA2 (0x34d7) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA3 (0x34d8) +#define WCD939X_DIGITAL_DEM_SECOND_ORDER (0x34d9) +#define WCD939X_DIGITAL_DSM_CTRL (0x34da) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_0 (0x34db) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_1 (0x34dc) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_2 (0x34dd) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_3 (0x34de) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_0 (0x34df) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_1 (0x34e0) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_2 (0x34e1) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_3 (0x34e2) +#define WCD939X_RX_TOP_PAGE (0x3500) +#define WCD939X_RX_TOP_TOP_CFG0 (0x3501) +#define WCD939X_TOP_CFG0_HPH_DAC_RATE_SEL BIT(1) +#define WCD939X_TOP_CFG0_PGA_UPDATE BIT(0) +#define WCD939X_RX_TOP_HPHL_COMP_WR_LSB (0x3502) +#define WCD939X_RX_TOP_HPHL_COMP_WR_MSB (0x3503) +#define WCD939X_RX_TOP_HPHL_COMP_LUT (0x3504) +#define WCD939X_RX_TOP_HPHL_COMP_RD_LSB (0x3505) +#define WCD939X_RX_TOP_HPHL_COMP_RD_MSB (0x3506) +#define WCD939X_RX_TOP_HPHR_COMP_WR_LSB (0x3507) +#define WCD939X_RX_TOP_HPHR_COMP_WR_MSB (0x3508) +#define WCD939X_RX_TOP_HPHR_COMP_LUT (0x3509) +#define WCD939X_RX_TOP_HPHR_COMP_RD_LSB (0x350a) +#define WCD939X_RX_TOP_HPHR_COMP_RD_MSB (0x350b) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG1 (0x350c) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG2 (0x350d) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG3 (0x350e) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG4 (0x350f) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG5 (0x3510) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG6 (0x3511) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG1 (0x3512) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG2 (0x3513) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG3 (0x3514) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG4 (0x3515) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG5 (0x3516) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG6 (0x3517) +#define WCD939X_RX_TOP_HPHL_PATH_CFG0 (0x351c) +#define WCD939X_HPHL_PATH_CFG0_INT_EN BIT(1) +#define WCD939X_HPHL_PATH_CFG0_DLY_ZN_EN BIT(0) +#define WCD939X_RX_TOP_HPHL_PATH_CFG1 (0x351d) +#define WCD939X_HPHL_PATH_CFG1_DSM_SOFT_RST BIT(5) +#define WCD939X_HPHL_PATH_CFG1_INT_SOFT_RST BIT(4) +#define WCD939X_HPHL_PATH_CFG1_FMT_CONV BIT(3) +#define WCD939X_HPHL_PATH_CFG1_IDLE_OVRD_EN BIT(2) +#define WCD939X_HPHL_PATH_CFG1_RX_DC_DROOP_COEFF_SEL GENMASK(1, 0) +#define WCD939X_RX_TOP_HPHR_PATH_CFG0 (0x351e) +#define WCD939X_HPHR_PATH_CFG0_INT_EN BIT(2) +#define WCD939X_HPHR_PATH_CFG0_DLY_ZN_EN BIT(1) +#define WCD939X_RX_TOP_HPHR_PATH_CFG1 (0x351f) +#define WCD939X_HPHR_PATH_CFG1_DSM_SOFT_RST BIT(5) +#define WCD939X_HPHR_PATH_CFG1_INT_SOFT_RST BIT(4) +#define WCD939X_HPHR_PATH_CFG1_FMT_CONV BIT(3) +#define WCD939X_HPHR_PATH_CFG1_IDLE_OVRD_EN BIT(2) +#define WCD939X_HPHR_PATH_CFG1_RX_DC_DROOP_COEFF_SEL GENMASK(1, 0) +#define WCD939X_RX_TOP_PATH_CFG2 (0x3520) +#define WCD939X_RX_TOP_HPHL_PATH_SEC0 (0x3521) +#define WCD939X_RX_TOP_HPHL_PATH_SEC1 (0x3522) +#define WCD939X_RX_TOP_HPHL_PATH_SEC2 (0x3523) +#define WCD939X_RX_TOP_HPHL_PATH_SEC3 (0x3524) +#define WCD939X_RX_TOP_HPHR_PATH_SEC0 (0x3525) +#define WCD939X_RX_TOP_HPHR_PATH_SEC1 (0x3526) +#define WCD939X_RX_TOP_HPHR_PATH_SEC2 (0x3527) +#define WCD939X_RX_TOP_HPHR_PATH_SEC3 (0x3528) +#define WCD939X_RX_TOP_PATH_SEC4 (0x3529) +#define WCD939X_RX_TOP_PATH_SEC5 (0x352a) +#define WCD939X_COMPANDER_HPHL_CTL0 (0x3540) +#define WCD939X_COMPANDER_HPHL_CTL1 (0x3541) +#define WCD939X_COMPANDER_HPHL_CTL2 (0x3542) +#define WCD939X_COMPANDER_HPHL_CTL3 (0x3543) +#define WCD939X_COMPANDER_HPHL_CTL4 (0x3544) +#define WCD939X_COMPANDER_HPHL_CTL5 (0x3545) +#define WCD939X_COMPANDER_HPHL_CTL6 (0x3546) +#define WCD939X_COMPANDER_HPHL_CTL7 (0x3547) +#define WCD939X_COMPANDER_HPHL_CTL8 (0x3548) +#define WCD939X_COMPANDER_HPHL_CTL9 (0x3549) +#define WCD939X_COMPANDER_HPHL_CTL10 (0x354a) +#define WCD939X_COMPANDER_HPHL_CTL11 (0x354b) +#define WCD939X_COMPANDER_HPHL_CTL12 (0x354c) +#define WCD939X_COMPANDER_HPHL_CTL13 (0x354d) +#define WCD939X_COMPANDER_HPHL_CTL14 (0x354e) +#define WCD939X_COMPANDER_HPHL_CTL15 (0x354f) +#define WCD939X_COMPANDER_HPHL_CTL16 (0x3550) +#define WCD939X_COMPANDER_HPHL_CTL17 (0x3551) +#define WCD939X_COMPANDER_HPHL_CTL18 (0x3552) +#define WCD939X_COMPANDER_HPHL_CTL19 (0x3553) +#define WCD939X_R_CTL0 (0x3560) +#define WCD939X_R_CTL1 (0x3561) +#define WCD939X_R_CTL2 (0x3562) +#define WCD939X_R_CTL3 (0x3563) +#define WCD939X_R_CTL4 (0x3564) +#define WCD939X_R_CTL5 (0x3565) +#define WCD939X_R_CTL6 (0x3566) +#define WCD939X_R_CTL7 (0x3567) +#define WCD939X_R_CTL8 (0x3568) +#define WCD939X_R_CTL9 (0x3569) +#define WCD939X_R_CTL10 (0x356a) +#define WCD939X_R_CTL11 (0x356b) +#define WCD939X_R_CTL12 (0x356c) +#define WCD939X_R_CTL13 (0x356d) +#define WCD939X_R_CTL14 (0x356e) +#define WCD939X_R_CTL15 (0x356f) +#define WCD939X_R_CTL16 (0x3570) +#define WCD939X_R_CTL17 (0x3571) +#define WCD939X_R_CTL18 (0x3572) +#define WCD939X_R_CTL19 (0x3573) +#define WCD939X_E_PATH_CTL (0x3580) +#define WCD939X_E_CFG0 (0x3581) +#define WCD939X_CFG0_AUTO_DISABLE_ANC BIT(2) +#define WCD939X_CFG0_AUTO_DISABLE_DSD BIT(1) +#define WCD939X_CFG0_IDLE_STEREO BIT(0) +#define WCD939X_E_CFG1 (0x3582) +#define WCD939X_E_CFG2 (0x3583) +#define WCD939X_E_CFG3 (0x3584) +#define WCD939X_DSD_HPHL_PATH_CTL (0x3590) +#define WCD939X_DSD_HPHL_CFG0 (0x3591) +#define WCD939X_DSD_HPHL_CFG1 (0x3592) +#define WCD939X_DSD_HPHL_CFG2 (0x3593) +#define WCD939X_DSD_HPHL_CFG3 (0x3594) +#define WCD939X_DSD_HPHL_CFG4 (0x3595) +#define WCD939X_DSD_HPHL_CFG5 (0x3596) +#define WCD939X_DSD_HPHR_PATH_CTL (0x35a0) +#define WCD939X_DSD_HPHR_CFG0 (0x35a1) +#define WCD939X_DSD_HPHR_CFG1 (0x35a2) +#define WCD939X_DSD_HPHR_CFG2 (0x35a3) +#define WCD939X_DSD_HPHR_CFG3 (0x35a4) +#define WCD939X_DSD_HPHR_CFG4 (0x35a5) +#define WCD939X_DSD_HPHR_CFG5 (0x35a6) +#define WCD939X_MAX_REGISTER (WCD939X_DSD_HPHR_CFG5) + +#define WCD939X_MAX_SWR_PORTS (6) +#define WCD939X_MAX_RX_SWR_PORTS (6) +#define WCD939X_MAX_TX_SWR_PORTS (4) +#define WCD939X_MAX_SWR_CH_IDS (15) + +struct wcd939x_sdw_ch_info { + int port_num; + unsigned int ch_mask; +}; + +#define WCD_SDW_CH(id, pn, cmask) \ + [id] = { \ + .port_num = pn, \ + .ch_mask = cmask, \ + } + +enum wcd939x_tx_sdw_ports { + WCD939X_ADC_1_4_PORT = 1, + WCD939X_ADC_DMIC_1_2_PORT, + WCD939X_DMIC_0_3_MBHC_PORT, + WCD939X_DMIC_3_7_PORT, +}; + +enum wcd939x_tx_sdw_channels { + WCD939X_ADC1, + WCD939X_ADC2, + WCD939X_ADC3, + WCD939X_ADC4, + WCD939X_DMIC0, + WCD939X_DMIC1, + WCD939X_MBHC, + WCD939X_DMIC2, + WCD939X_DMIC3, + WCD939X_DMIC4, + WCD939X_DMIC5, + WCD939X_DMIC6, + WCD939X_DMIC7, +}; + +enum wcd939x_rx_sdw_ports { + WCD939X_HPH_PORT = 1, + WCD939X_CLSH_PORT, + WCD939X_COMP_PORT, + WCD939X_LO_PORT, + WCD939X_DSD_PORT, + WCD939X_HIFI_PCM_PORT, +}; + +enum wcd939x_rx_sdw_channels { + WCD939X_HPH_L, + WCD939X_HPH_R, + WCD939X_CLSH, + WCD939X_COMP_L, + WCD939X_COMP_R, + WCD939X_LO, + WCD939X_DSD_L, + WCD939X_DSD_R, + WCD939X_HIFI_PCM_L, + WCD939X_HIFI_PCM_R, +}; + +enum { + WCD939X_SDW_DIR_RX, + WCD939X_SDW_DIR_TX, +}; + +struct wcd939x_priv; +struct wcd939x_sdw_priv { + struct sdw_slave *sdev; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS]; + struct wcd939x_sdw_ch_info *ch_info; + bool port_enable[WCD939X_MAX_SWR_CH_IDS]; + int active_ports; + int num_ports; + bool is_tx; + struct wcd939x_priv *wcd939x; + struct irq_domain *slave_irq; + struct regmap *regmap; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD939X_SDW) +int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction); +int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); + +struct device *wcd939x_sdw_device_get(struct device_node *np); +unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev); + +struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd); +#else + +static inline int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} + +static inline int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction) +{ + return -EOPNOTSUPP; +} + +static inline int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} + +static inline struct device *wcd939x_sdw_device_get(struct device_node *np) +{ + return NULL; +} + +static inline unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev) +{ + return 0; +} + +struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd) +{ + return PTR_ERR(-EINVAL); +} +#endif /* CONFIG_SND_SOC_WCD939X_SDW */ + +#endif /* __WCD939X_H__ */ diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 36ea0dcdc7ab..e451c009f2d9 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1092,27 +1092,36 @@ static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp) dsp->fatal_error = false; } +int wm_adsp_run(struct wm_adsp *dsp) +{ + flush_work(&dsp->boot_work); + + return cs_dsp_run(&dsp->cs_dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp_run); + +void wm_adsp_stop(struct wm_adsp *dsp) +{ + cs_dsp_stop(&dsp->cs_dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp_stop); + int wm_adsp_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 wm_adsp *dsps = snd_soc_component_get_drvdata(component); struct wm_adsp *dsp = &dsps[w->shift]; - int ret = 0; switch (event) { case SND_SOC_DAPM_POST_PMU: - flush_work(&dsp->boot_work); - ret = cs_dsp_run(&dsp->cs_dsp); - break; + return wm_adsp_run(dsp); case SND_SOC_DAPM_PRE_PMD: - cs_dsp_stop(&dsp->cs_dsp); - break; + wm_adsp_stop(dsp); + return 0; default: - break; + return 0; } - - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_event); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 067d807a7ca8..e53dfcf1f78f 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -98,6 +98,8 @@ irqreturn_t wm_adsp2_bus_error(int irq, void *data); irqreturn_t wm_halo_bus_error(int irq, void *data); irqreturn_t wm_halo_wdt_expire(int irq, void *data); +int wm_adsp_run(struct wm_adsp *dsp); +void wm_adsp_stop(struct wm_adsp *dsp); int wm_adsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index f2653df84e4a..a9767ef0e39d 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -13,6 +13,7 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/slab.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_registers.h> @@ -699,6 +700,7 @@ struct wsa884x_priv { struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS]; struct gpio_desc *sd_n; + struct reset_control *sd_reset; bool port_prepared[WSA884X_MAX_SWR_PORTS]; bool port_enable[WSA884X_MAX_SWR_PORTS]; unsigned int variant; @@ -1799,9 +1801,22 @@ static struct snd_soc_dai_driver wsa884x_dais[] = { }, }; -static void wsa884x_gpio_powerdown(void *data) +static void wsa884x_reset_powerdown(void *data) { - gpiod_direction_output(data, 1); + struct wsa884x_priv *wsa884x = data; + + if (wsa884x->sd_reset) + reset_control_assert(wsa884x->sd_reset); + else + gpiod_direction_output(wsa884x->sd_n, 1); +} + +static void wsa884x_reset_deassert(struct wsa884x_priv *wsa884x) +{ + if (wsa884x->sd_reset) + reset_control_deassert(wsa884x->sd_reset); + else + gpiod_direction_output(wsa884x->sd_n, 0); } static void wsa884x_regulator_disable(void *data) @@ -1809,6 +1824,27 @@ static void wsa884x_regulator_disable(void *data) regulator_bulk_disable(WSA884X_SUPPLIES_NUM, data); } +static int wsa884x_get_reset(struct device *dev, struct wsa884x_priv *wsa884x) +{ + wsa884x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL); + if (IS_ERR(wsa884x->sd_reset)) + return dev_err_probe(dev, PTR_ERR(wsa884x->sd_reset), + "Failed to get reset\n"); + else if (wsa884x->sd_reset) + return 0; + /* + * else: NULL, so use the backwards compatible way for powerdown-gpios, + * which does not handle sharing GPIO properly. + */ + wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(wsa884x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n), + "Shutdown Control GPIO not found\n"); + + return 0; +} + static int wsa884x_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) { @@ -1838,11 +1874,9 @@ static int wsa884x_probe(struct sdw_slave *pdev, if (ret) return ret; - wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown", - GPIOD_OUT_HIGH); - if (IS_ERR(wsa884x->sd_n)) - return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n), - "Shutdown Control GPIO not found\n"); + ret = wsa884x_get_reset(dev, wsa884x); + if (ret) + return ret; dev_set_drvdata(dev, wsa884x); wsa884x->slave = pdev; @@ -1858,9 +1892,8 @@ static int wsa884x_probe(struct sdw_slave *pdev, pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; - /* Bring out of reset */ - gpiod_direction_output(wsa884x->sd_n, 0); - ret = devm_add_action_or_reset(dev, wsa884x_gpio_powerdown, wsa884x->sd_n); + wsa884x_reset_deassert(wsa884x); + ret = devm_add_action_or_reset(dev, wsa884x_reset_powerdown, wsa884x); if (ret) return ret; |