Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
March 2024
- 109 participants
- 225 discussions
[PATCH v3] ASoC: soc-core.c: Prefer to return dai->driver->name in snd_soc_dai_name_get()
by Chancel Liu 05 Mar '24
by Chancel Liu 05 Mar '24
05 Mar '24
ASoC machine driver can use snd_soc_{of_}get_dlc() (A) to get DAI name
for dlc (snd_soc_dai_link_component). In this function call
dlc->dai_name is parsed via snd_soc_dai_name_get() (B).
(A) int snd_soc_get_dlc(...)
{
...
(B) dlc->dai_name = snd_soc_dai_name_get(dai);
...
}
(B) has a priority to return dai->name as dlc->dai_name. In most cases
card can probe successfully. However it has an issue that ASoC tries to
rebind card. Here is a simplified flow for example:
| a) Card probes successfully at first
| b) One of the component bound to this card is removed for some
| reason the component->dev is released
| c) That component is re-registered
v d) ASoC calls snd_soc_try_rebind_card()
a) points dlc->dai_name to dai->name. b) releases all resource of the
old DAI. c) creates new DAI structure. In result d) can not use
dlc->dai_name to add new created DAI.
So it's reasonable that prefer to return dai->driver->name in
snd_soc_dai_name_get() because dai->driver is a pre-defined global
variable. Also update snd_soc_is_matching_dai() for alignment.
Signed-off-by: Chancel Liu <chancel.liu(a)nxp.com>
---
changes in v2:
- update snd_soc_is_matching_dai() for alignment
changes in v3:
- resolve conflicts when applying patch, no others changed
sound/soc/soc-core.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 507cd3015ff4..1e94edba12eb 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -283,13 +283,13 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc,
/* see snd_soc_dai_name_get() */
- if (strcmp(dlc->dai_name, dai->name) == 0)
- return 1;
-
if (dai->driver->name &&
strcmp(dlc->dai_name, dai->driver->name) == 0)
return 1;
+ if (strcmp(dlc->dai_name, dai->name) == 0)
+ return 1;
+
if (dai->component->name &&
strcmp(dlc->dai_name, dai->component->name) == 0)
return 1;
@@ -300,12 +300,12 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc,
const char *snd_soc_dai_name_get(struct snd_soc_dai *dai)
{
/* see snd_soc_is_matching_dai() */
- if (dai->name)
- return dai->name;
-
if (dai->driver->name)
return dai->driver->name;
+ if (dai->name)
+ return dai->name;
+
if (dai->component->name)
return dai->component->name;
--
2.43.0
2
1
[PATCH v3] ASoC: cs-amp-lib: Add KUnit test for calibration helpers
by Richard Fitzgerald 05 Mar '24
by Richard Fitzgerald 05 Mar '24
05 Mar '24
Add a KUnit test for the cs-amp-lib library. This has test cases
for cs_amp_get_efi_calibration_data() and cs_amp_write_cal_coeffs().
A KUNIT_STATIC_STUB_REDIRECT() has been added to
cs_amp_get_efi_variable() and cs_amp_write_cal_coeff() so that the
KUnit test can redirect these to test harness functions.
Much of the testing involves invoking the same function with different
parameters, i.e. the number of amps and the amp index within the array.
This uses parameterization rather than looping. The idea is to avoid
looping over configurations within one test case as that has a higher
chance of having a bug that doesn't actually test all the expected cases.
Having the test run exactly one configuration, and then tear-down, is less
prone to accidentally skipped configurations.
Signed-off-by: Richard Fitzgerald <rf(a)opensource.cirrus.com>
---
include/sound/cs-amp-lib.h | 14 +
sound/soc/codecs/Kconfig | 13 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/cs-amp-lib-test.c | 709 +++++++++++++++++++++++++++++
sound/soc/codecs/cs-amp-lib.c | 18 +-
5 files changed, 754 insertions(+), 2 deletions(-)
create mode 100644 sound/soc/codecs/cs-amp-lib-test.c
diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h
index 077fe36885b5..f481148735e1 100644
--- a/include/sound/cs-amp-lib.h
+++ b/include/sound/cs-amp-lib.h
@@ -49,4 +49,18 @@ int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
const struct cirrus_amp_cal_data *data);
int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
struct cirrus_amp_cal_data *out_data);
+
+struct cs_amp_test_hooks {
+ efi_status_t (*get_efi_variable)(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf);
+
+ int (*write_cal_coeff)(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const char *ctl_name, u32 val);
+};
+
+extern const struct cs_amp_test_hooks * const cs_amp_test_hooks;
+
#endif /* CS_AMP_LIB_H */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 15f287784d8b..f78ea2f86fa6 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -732,6 +732,19 @@ config SND_SOC_CROS_EC_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
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0fc40640e5d0..7c075539dc47 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -60,6 +60,7 @@ 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
@@ -454,6 +455,7 @@ 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
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(a)opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
index 4e2e5157a73f..01ef4db5407d 100644
--- a/sound/soc/codecs/cs-amp-lib.c
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -6,6 +6,7 @@
// 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>
@@ -27,6 +28,8 @@ static int cs_amp_write_cal_coeff(struct cs_dsp *dsp,
__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);
@@ -84,7 +87,7 @@ 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))
+ 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;
@@ -98,6 +101,8 @@ static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,
{
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);
@@ -250,13 +255,22 @@ static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid,
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))
+ 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(a)opensource.cirrus.com>");
MODULE_LICENSE("GPL");
--
2.39.2
2
1
The ASoC component provides the majority of the functionality of the
device, all the audio functions.
Signed-off-by: Shenghao Ding <shenghao-ding(a)ti.com>
---
Change in v10:
- simplify tasdevice_set_sdw_stream
- fixed some Linux coding style
- fixed the spelling mistakes
- Select left/right channel based on unique id
- a longer description has been added
- remove unused crc8 in KCONFIG
- Invert of RE here means reciprocal of RE
- correct indentation
- correct the wrong pow calibrated register
- correct comment on RESET register
- fixed usually lines don't end with an open parenthesis
- fixed tasdevice_sdw_shutdown
- use defines for function, entity, etc. for readable
- remove unnecessary codec_lock in component_probe()
- remove struct sdw_stream_data
- remove unused macro definitions
- use BIT(2) and BIT(1) instead of 0x2 and 0x1
- implement pm_runtime
- reg_start is u8, reg_start + i only adds i byte, it should be
reg_start + i * 4, because the calibration values
- fixed total_sz in tas2783_calibration
- add a define for TASDEVICE_REG(0, 0, 0x0a)
- rework macro style in header file
- Add select CRC32
- rewrite the mute and the return value handling
- Add comments on bin file
- Add link_id into the fw name to distinguish the same sdw id on different
sdw id.
- remove unnecessary mutex_lock
- use set_active() in tasdevice_io_init and move all other parts in the
probe.
- move pm_runtime_get_noresume to the probe
- fix the return value of putvol function
- add put_noidle in tasdevice_sdw_remove
- remove unused header file(of.h)
- correct the style of the multi-line comment
- Set the readable registers in tas2783_readable_register for debugfs
- Use new PM macros
- Use default put and get inline SOC_SINGLE_RANGE_TLV and
SOC_SINGLE_SX_TLV
- Use respective %pt to log the calibrated time
- Remove unnecessary struct tm
- Add trailing comma tasdevice_dapm_widgets and tasdevice_audio_map
- Rewrite the return value handling in tasdevice_sdw_probe
- use dev_num instead of id.unique_id
- rework the log
- Add code to check the size of calibrated data in UEFI
- Add description of Layout of calibrated Data in UEFI
- switch back link_id and unique_id to identify the devcie
- unique_id offset as the device index of the calibrated data
- update calibrated data layout stored in UEFI, add link_id and unique_id
into the calibrated data in case of tas2783 sitting in different SDQW
buses.
---
sound/soc/codecs/Kconfig | 16 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tas2783-sdw.c | 932 +++++++++++++++++++++++++++++++++
sound/soc/codecs/tas2783.h | 106 ++++
4 files changed, 1056 insertions(+)
create mode 100644 sound/soc/codecs/tas2783-sdw.c
create mode 100644 sound/soc/codecs/tas2783.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 59f9742e9ff4..6ba3fd0c0811 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -247,6 +247,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TAS2781_COMLIB
imply SND_SOC_TAS2781_FMWLIB
imply SND_SOC_TAS2781_I2C
+ imply SND_SOC_TAS2783
imply SND_SOC_TAS5086
imply SND_SOC_TAS571X
imply SND_SOC_TAS5720
@@ -1847,6 +1848,21 @@ config SND_SOC_TAS2781_I2C
algo coefficient setting, for one, two or even multiple TAS2781
chips.
+config SND_SOC_TAS2783
+ tristate "Texas Instruments TAS2783 speaker amplifier (sdw)"
+ depends on SOUNDWIRE
+ depends on EFI
+ select REGMAP
+ select REGMAP_SOUNDWIRE
+ select CRC32
+ select CRC32_SARWATE
+ help
+ Enable support for Texas Instruments TAS2783 Smart Amplifier
+ Digital input mono Class-D and DSP-inside audio power amplifiers.
+ TAS2783 supports the MIPI SoundWire v1.2 compatible interface
+ for audio and control data. At the end of calibrated data appends
+ the crc data with crc32_sarwate for error detection.
+
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f53baa2b9565..6afceb126ae3 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -284,6 +284,7 @@ snd-soc-tas2770-objs := tas2770.o
snd-soc-tas2781-comlib-objs := tas2781-comlib.o
snd-soc-tas2781-fmwlib-objs := tas2781-fmwlib.o
snd-soc-tas2781-i2c-objs := tas2781-i2c.o
+snd-soc-tas2783-objs := tas2783-sdw.o
snd-soc-tfa9879-objs := tfa9879.o
snd-soc-tfa989x-objs := tfa989x.o
snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o
@@ -664,6 +665,7 @@ obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o
obj-$(CONFIG_SND_SOC_TAS2781_COMLIB) += snd-soc-tas2781-comlib.o
obj-$(CONFIG_SND_SOC_TAS2781_FMWLIB) += snd-soc-tas2781-fmwlib.o
obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.o
+obj-$(CONFIG_SND_SOC_TAS2783) += snd-soc-tas2783.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
new file mode 100644
index 000000000000..7bd151942a83
--- /dev/null
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -0,0 +1,932 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAS2783 Audio Smart Amplifier
+//
+// Copyright (C) 2023 - 2024 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// Author: Baojun Xu <baojun.xu(a)ti.com>
+// Kevin Lu <kevin-lu(a)ti.com>
+// Shenghao Ding <shenghao-ding(a)ti.com>
+//
+
+#include <asm/unaligned.h>
+#include <linux/acpi.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <sound/tas2781-tlv.h>
+#include "tas2783.h"
+
+static const unsigned int tas2783_cali_reg[] = {
+ TAS2783_CALIBRATION_RE, /* Resistance */
+ TAS2783_CALIBRATION_RE_LOW, /* Low limitation of RE */
+ TAS2783_CALIBRATION_INV_RE, /* Reciprocal of RE */
+ TAS2783_CALIBRATION_POW, /* RMS Power */
+ TAS2783_CALIBRATION_TLIMIT /* Temperature limitation */
+};
+
+static const struct reg_default tas2783_reg_defaults[] = {
+ /* Default values for ROM mode. Activated. */
+ { 0x8002, 0x1a }, /* AMP inactive. */
+ { 0x8097, 0xc8 },
+ { 0x80b5, 0x74 },
+ { 0x8099, 0x20 },
+ { 0xfe8d, 0x0d },
+ { 0xfebe, 0x4a },
+ { 0x8230, 0x00 },
+ { 0x8231, 0x00 },
+ { 0x8232, 0x00 },
+ { 0x8233, 0x01 },
+ { 0x8418, 0x00 }, /* Set volume to 0 dB. */
+ { 0x8419, 0x00 },
+ { 0x841a, 0x00 },
+ { 0x841b, 0x00 },
+ { 0x8428, 0x40 }, /* Unmute channel */
+ { 0x8429, 0x00 },
+ { 0x842a, 0x00 },
+ { 0x842b, 0x00 },
+ { 0x8548, 0x00 }, /* Set volume to 0 dB. */
+ { 0x8549, 0x00 },
+ { 0x854a, 0x00 },
+ { 0x854b, 0x00 },
+ { 0x8558, 0x40 }, /* Unmute channel */
+ { 0x8559, 0x00 },
+ { 0x855a, 0x00 },
+ { 0x855b, 0x00 },
+ { 0x800a, 0x3a }, /* Enable both channel */
+ { 0x800e, 0x44 },
+ { 0x800f, 0x40 },
+ { 0x805c, 0x99 },
+ { SDW_SDCA_CTL(TAS2783_FUNC_TYPE_SMART_AMP, TAS2783_SDCA_ENT_FU21,
+ TAS2783_SDCA_CTL_FU_MUTE, 0), 0 },
+ { SDW_SDCA_CTL(TAS2783_FUNC_TYPE_SMART_AMP, TAS2783_SDCA_ENT_FU21,
+ TAS2783_SDCA_CTL_FU_VOLUME, 0), 0 },
+ { SDW_SDCA_CTL(TAS2783_FUNC_TYPE_SMART_AMP, TAS2783_SDCA_ENT_FU23,
+ TAS2783_SDCA_CTL_FU_MUTE, 0), 0 },
+};
+
+static bool tas2783_readable_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case 0x8000 ... 0x807F: /* Page 0 */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tas2783_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ /*
+ * Only software reset register was volatiled.
+ * Bit is self clearing.
+ * 0b = Don't reset
+ * 1b = reset
+ */
+ case 0x8001:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = tas2783_readable_register,
+ .volatile_reg = tas2783_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = tas2783_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas2783_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct snd_kcontrol_new tas2783_snd_controls[] = {
+ SOC_SINGLE_RANGE_TLV("Digital Volume", TAS2783_DVC_LVL,
+ 0, 0, 200, 1, dvc_tlv),
+ SOC_SINGLE_SX_TLV("Amp Gain Volume", TAS2783_AMP_LEVEL,
+ 1, 0, 20, amp_vol_tlv),
+};
+
+static void tas2783_apply_calibv1(struct tasdevice_priv *tas_dev,
+ unsigned int *cali_data)
+{
+ struct regmap *map = tas_dev->regmap;
+ u8 *cali_start;
+ u16 offset;
+ int ret;
+
+ if (!tas_dev->sdw_peripheral) {
+ dev_err(tas_dev->dev, "%s: peripheral doesn't exist.\n",
+ __func__);
+ return;
+ }
+
+ offset = tas_dev->sdw_peripheral->id.unique_id -
+ TAS2783_UNIQUE_ID_BASE;
+ if (offset >= TAS2783_MAX_DEV_NUM) {
+ dev_err(tas_dev->dev, "%s: offset(%u) is out of range.\n",
+ __func__, offset);
+ return;
+ }
+
+ /*
+ * The area saving tas2783 calibrated data is specified by its
+ * unique_id offset. cali_start is the first address of current
+ * tas2783's calibrated data.
+ */
+ cali_start = (u8 *)(cali_data + offset * sizeof(tas2783_cali_reg));
+ for (int i = 0; i < ARRAY_SIZE(tas2783_cali_reg); i++) {
+ ret = regmap_bulk_write(map, tas2783_cali_reg[i],
+ &cali_start[4 * i], 4);
+ if (ret) {
+ dev_err(tas_dev->dev, "Cali failed %x:%d\n",
+ tas2783_cali_reg[i], ret);
+ break;
+ }
+ }
+}
+
+static void tas2783_apply_calibv2(struct tasdevice_priv *tas_dev,
+ unsigned int *cali_data)
+{
+ const unsigned int arr_size = ARRAY_SIZE(tas2783_cali_reg);
+ struct regmap *map = tas_dev->regmap;
+ unsigned int dev_sum = cali_data[1], i, j, k;
+ u8 *cali_start;
+ u16 dev_info;
+ int ret;
+
+ if (!tas_dev->sdw_peripheral) {
+ dev_err(tas_dev->dev, "%s: peripheral doesn't exist.\n",
+ __func__);
+ return;
+ }
+
+ dev_info = tas_dev->sdw_peripheral->bus->link_id |
+ tas_dev->sdw_peripheral->id.unique_id << 16;
+
+ /*
+ * The area saving tas2783 calibrated data is specified by its
+ * unique_id offset. cali_start is the first address of current
+ * tas2783's calibrated data.
+ */
+ cali_start = (u8 *)&cali_data[3];
+ for (i = 0; i < dev_sum; i++) {
+ k = i * (arr_size + 1) + 3;
+ if (dev_info != cali_data[k]) {
+ for (j = 0; j < arr_size; j++) {
+ k = 4 * (k + 1 + j);
+ ret = regmap_bulk_write(map,
+ tas2783_cali_reg[j],
+ &cali_start[k], 4);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "Cali failed %x:%d\n",
+ tas2783_cali_reg[j], ret);
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Load the calibration data, including speaker impedance, f0, etc.
+ * Calibration is done by the manufacturer in the factory. The calibration
+ * data are used by the algorithm for calculating the speaker temperature,
+ * speaker membrane excursion and f0 in real time during playback.
+ * The DSP will work with default data values if calibrated data are
+ * missing or are invalid.
+ * Layout of calibrated Data in UEFI(total 256 bytes):
+ * V2:
+ * ChipID (2783, 4 bytes)
+ * Device-Sum (4 bytes)
+ * TimeStamp of Calibration (4 bytes)
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i info () {
+ * SDW link id (2bytes)
+ * SDW unique_id (2bytes)
+ * }
+ * Calibrated Data of Device #i (20 bytes)
+ * }
+ * CRC (4 bytes)
+ * Reserved (the rest)
+ * Or
+ * V1:
+ * Calibrated Data of Dev 0(unique_id offset 0) (20 bytes)
+ * Calibrated Data of Dev 1(unique_id offset 1) (20 bytes)
+ * Calibrated Data of Dev 2(unique_id offset 2) (20 bytes)
+ * Calibrated Data of Dev 3(unique_id offset 3) (20 bytes)
+ * Calibrated Data of Dev 4(unique_id offset 4) (20 bytes)
+ * Calibrated Data of Dev 5(unique_id offset 5) (20 bytes)
+ * Calibrated Data of Dev 6(unique_id offset 6) (20 bytes)
+ * Calibrated Data of Dev 7(unique_id offset 7) (20 bytes)
+ * TimeStamp of Calibration (4 bytes)
+ * CRC (4 bytes)
+ * Reserved (88 bytes)
+ */
+static void tas2783_calibration(struct tasdevice_priv *tas_dev)
+{
+ efi_guid_t efi_guid = EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc,
+ 0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92);
+ static efi_char16_t efi_name[] = L"CALI_DATA";
+ struct calibration_data cali_data;
+ unsigned int *tmp_val;
+ unsigned int crc;
+ efi_status_t status;
+
+ cali_data.total_sz = 0;
+
+ status = efi.get_variable(efi_name, &efi_guid, NULL,
+ &cali_data.total_sz, NULL);
+ if (status == EFI_BUFFER_TOO_SMALL
+ && cali_data.total_sz < TAS2783_MAX_CALIDATA_SIZE) {
+ status = efi.get_variable(efi_name, &efi_guid, NULL,
+ &cali_data.total_sz,
+ cali_data.data);
+ dev_dbg(tas_dev->dev, "%s: cali get %lx bytes result:%ld\n",
+ __func__, cali_data.total_sz, status);
+ }
+ if (status != 0) {
+ /* Failed got calibration data from EFI. */
+ dev_dbg(tas_dev->dev, "No calibration data in UEFI.");
+ return;
+ }
+
+ tmp_val = (unsigned int *)cali_data.data;
+
+ if (tmp_val[0] == 2783) {
+ /* Calibrated Data V2 */
+ unsigned int dev_sum = tmp_val[1];
+
+ if (dev_sum > TAS2783_MAX_DEV_NUM ||
+ dev_sum == 0) {
+ dev_dbg(tas_dev->dev, "No dev in calibrated data V2.");
+ return;
+ }
+ crc = crc32(~0, cali_data.data, 12 + dev_sum * 24) ^ ~0;
+ if (crc == tmp_val[3 + dev_sum * 6]) {
+ tas2783_apply_calibv2(tas_dev, tmp_val);
+ dev_dbg(tas_dev->dev, "V2: %ptTs", &tmp_val[40]);
+ } else {
+ dev_dbg(tas_dev->dev,
+ "V2: CRC 0x%08x not match 0x%08x\n",
+ crc, tmp_val[41]);
+ }
+ } else {
+ /* Calibrated Data V1 */
+ /* 8 devs * 20 bytes calibrated data/dev + 4 bytes Timestamp */
+ crc = crc32(~0, cali_data.data, 164) ^ ~0;
+ if (crc == tmp_val[41]) {
+ /* Date and time of when calibration was done. */
+ tas2783_apply_calibv1(tas_dev, tmp_val);
+ dev_dbg(tas_dev->dev, "V1: %ptTs", &tmp_val[40]);
+ } else {
+ dev_dbg(tas_dev->dev,
+ "V1: CRC 0x%08x not match 0x%08x\n",
+ crc, tmp_val[41]);
+ }
+ }
+}
+
+static void tasdevice_dspfw_ready(const struct firmware *fmw,
+ void *context)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) context;
+ struct tas2783_firmware_node *p;
+ struct regmap *map = tas_dev->regmap;
+ unsigned char *buf = NULL;
+ int offset = 0, img_sz;
+ int ret;
+
+ if (!fmw || !fmw->data) {
+ dev_warn(tas_dev->dev,
+ "%s: failed to read %s: work in bypass-dsp mode.\n",
+ __func__, tas_dev->dspfw_binaryname);
+ return;
+ }
+ buf = (unsigned char *)fmw->data;
+
+ img_sz = get_unaligned_le32(&buf[offset]);
+ offset += sizeof(img_sz);
+ if (img_sz != fmw->size) {
+ dev_warn(tas_dev->dev, "%s: size not matching, %d %u.",
+ __func__, (int)fmw->size, img_sz);
+ return;
+ }
+
+ while (offset < img_sz) {
+ p = (struct tas2783_firmware_node *)(buf + offset);
+ if (p->length > 1) {
+ ret = regmap_bulk_write(map, p->download_addr,
+ buf + offset + sizeof(unsigned int) * 5, p->length);
+ } else {
+ ret = regmap_write(map, p->download_addr,
+ *(buf + offset + sizeof(unsigned int) * 5));
+ }
+
+ if (ret != 0) {
+ dev_dbg(tas_dev->dev,
+ "%s: load FW fail: %d, work in bypass.\n",
+ __func__, ret);
+ return;
+ }
+ offset += sizeof(unsigned int) * 5 + p->length;
+ }
+
+ tas2783_calibration(tas_dev);
+}
+
+static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+};
+
+static const struct snd_soc_dapm_route tasdevice_audio_map[] = {
+ {"OUT", NULL, "ASI"},
+ {"ASI OUT", NULL, "DMIC"},
+};
+
+static int tasdevice_set_sdw_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void tasdevice_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int tasdevice_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tasdevice_priv *tas_priv =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream;
+ int ret;
+
+ dev_dbg(dai->dev, "%s: dai_name %s", __func__, dai->name);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!tas_priv->sdw_peripheral)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params,
+ &stream_config, &port_config);
+
+ /* port 1 for playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = 1;
+ else
+ port_config.num = 2;
+
+ ret = sdw_stream_add_slave(tas_priv->sdw_peripheral,
+ &stream_config, &port_config, 1, sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return ret;
+ }
+
+ dev_dbg(dai->dev, "%s: format: %i rate: %i\n", __func__,
+ params_format(params), params_rate(params));
+
+ return 0;
+}
+
+static int tasdevice_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tasdevice_priv *tas_priv =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!tas_priv->sdw_peripheral)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(tas_priv->sdw_peripheral, sdw_stream);
+
+ return 0;
+}
+
+static int tasdevice_mute(struct snd_soc_dai *dai, int mute,
+ int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(component);
+ struct regmap *map = tas_dev->regmap;
+ int ret;
+
+ dev_dbg(tas_dev->dev, "%s: %d.\n", __func__, mute);
+
+ if (mute) {
+ if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = regmap_update_bits(map, TAS2873_REG_PWR_CTRL,
+ TAS2783_REG_AEF_MASK,
+ TAS2783_REG_AEF_INACTIVE);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s: Disable AEF failed.\n", __func__);
+ } else {
+ /* FU23 mute (0x40400108) */
+ ret = regmap_write(map,
+ SDW_SDCA_CTL(TAS2783_FUNC_TYPE_SMART_AMP,
+ TAS2783_SDCA_ENT_FU23,
+ TAS2783_SDCA_CTL_FU_MUTE, 0), 1);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: FU23 mute failed.\n", __func__);
+ goto out;
+ }
+ /*
+ * Both playback and echo data will be shutdown in
+ * playback stream.
+ */
+ ret = regmap_update_bits(map, TAS2873_REG_PWR_CTRL,
+ TAS2783_REG_PWR_MODE_MASK |
+ TAS2783_REG_AEF_MASK,
+ TAS2783_REG_PWR_MODE_ACTIVE |
+ TAS2783_REG_PWR_MODE_SW_PWD);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: PWR&AEF shutdown failed.\n",
+ __func__);
+ goto out;
+ }
+ tas_dev->pstream = false;
+ }
+ } else {
+ /* FU23 Unmute, 0x40400108. */
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = regmap_write(map,
+ SDW_SDCA_CTL(TAS2783_FUNC_TYPE_SMART_AMP,
+ TAS2783_SDCA_ENT_FU23,
+ TAS2783_SDCA_CTL_FU_MUTE, 0), 0);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: FU23 Unmute failed.\n", __func__);
+ goto out;
+ }
+ ret = regmap_update_bits(map, TAS2873_REG_PWR_CTRL,
+ TAS2783_REG_PWR_MODE_MASK,
+ TAS2783_REG_PWR_MODE_ACTIVE);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: PWR Unmute failed.\n", __func__);
+ goto out;
+ }
+ tas_dev->pstream = true;
+ } else {
+ /* Capture stream is the echo ref data for voice.
+ * Without playback, it can't be active.
+ */
+ if (tas_dev->pstream == true) {
+ ret = regmap_update_bits(map,
+ TAS2873_REG_PWR_CTRL,
+ TAS2783_REG_AEF_MASK,
+ TAS2783_REG_AEF_ACTIVE);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: AEF enable failed.\n",
+ __func__);
+ goto out;
+ }
+ } else {
+ dev_err(tas_dev->dev,
+ "%s: No playback, no AEF!", __func__);
+ ret = -EINVAL;
+ }
+ }
+ }
+out:
+ if (ret)
+ dev_err(tas_dev->dev, "Mute or unmute %d failed %d.\n",
+ mute, ret);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops tasdevice_dai_ops = {
+ .mute_stream = tasdevice_mute,
+ .hw_params = tasdevice_sdw_hw_params,
+ .hw_free = tasdevice_sdw_pcm_hw_free,
+ .set_stream = tasdevice_set_sdw_stream,
+ .shutdown = tasdevice_sdw_shutdown,
+};
+
+static struct snd_soc_dai_driver tasdevice_dai_driver[] = {
+ {
+ .name = "tas2783-codec",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAS2783_DEVICE_RATES,
+ .formats = TAS2783_DEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAS2783_DEVICE_RATES,
+ .formats = TAS2783_DEVICE_FORMATS,
+ },
+ .ops = &tasdevice_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static int tasdevice_comp_probe(struct snd_soc_component *comp)
+{
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(comp);
+ acpi_handle handle = ACPI_HANDLE(tas_dev->dev);
+ const struct firmware *fw_entry = NULL;
+ const char *sub = NULL;
+ int ret;
+
+ if (handle) {
+ sub = acpi_get_subsystem_id(handle);
+ if (IS_ERR(sub))
+ sub = NULL;
+ }
+
+ tas_dev->component = comp;
+
+ /*
+ * Each tas2783 in the system has its own dspfw.
+ */
+ if (comp->name_prefix) {
+ /*
+ * name_prefix.bin stores the dsp firmware including speaker
+ * protection algorithm, audio acoustic algorithm, speaker
+ * characters and algorithm params, it must be copied into
+ * firmware folder.
+ */
+ scnprintf(tas_dev->dspfw_binaryname,
+ TAS2783_DSPFW_FILENAME_LEN, "%s-tas2783.bin",
+ comp->name_prefix);
+ } else {
+ /* Compatible with the previous naming rule */
+ if (sub) {
+ /*
+ * subsys_id-link_id[0,1,...,N]-unique_id[1,...,4].bin
+ * stores the dsp firmware including speaker protection
+ * algorithm, audio acoustic algorithm, speaker
+ * characters and algorithm params, it must be copied
+ * into firmware folder.
+ */
+ scnprintf(tas_dev->dspfw_binaryname,
+ TAS2783_DSPFW_FILENAME_LEN,
+ "%s-%d-%d.bin", sub,
+ tas_dev->sdw_peripheral->bus->link_id,
+ tas_dev->sdw_peripheral->id.unique_id);
+ } else {
+ /*
+ * tas2783-link_id[0,1,...,N]-unique_id[1,...,4].bin
+ * stores the dsp firmware including speaker protection
+ * algorithm, audio acoustic algorithm, speaker
+ * characters and algorithm params, it must be copied
+ * into firmware folder.
+ */
+ scnprintf(tas_dev->dspfw_binaryname,
+ TAS2783_DSPFW_FILENAME_LEN,
+ "tas2783-%d-%d.bin",
+ tas_dev->sdw_peripheral->bus->link_id,
+ tas_dev->sdw_peripheral->id.unique_id);
+ }
+ }
+
+ ret = request_firmware(&fw_entry, tas_dev->dspfw_binaryname,
+ tas_dev->dev);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s: request_firmware %x open status: %d.\n", __func__,
+ tas_dev->sdw_peripheral->id.unique_id, ret);
+ goto out;
+ }
+
+ tasdevice_dspfw_ready(fw_entry, tas_dev);
+
+out:
+ if (fw_entry)
+ release_firmware(fw_entry);
+ return 0;
+}
+
+static const struct snd_soc_component_driver
+ soc_codec_driver_tasdevice = {
+ .probe = tasdevice_comp_probe,
+ .controls = tas2783_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2783_snd_controls),
+ .dapm_widgets = tasdevice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets),
+ .dapm_routes = tasdevice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static int tasdevice_init(struct tasdevice_priv *tas_dev)
+{
+ int ret;
+
+ dev_set_drvdata(tas_dev->dev, tas_dev);
+
+ ret = devm_snd_soc_register_component(tas_dev->dev,
+ &soc_codec_driver_tasdevice,
+ tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver));
+ if (ret) {
+ dev_err(tas_dev->dev, "%s: codec register error:%d.\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(tas_dev->dev, 3000);
+ pm_runtime_use_autosuspend(tas_dev->dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(tas_dev->dev);
+ pm_runtime_enable(tas_dev->dev);
+
+ return 0;
+}
+
+static int tasdevice_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ struct sdw_dpn_prop *dpn;
+ unsigned long addr;
+ int nval, i, j;
+ u32 bit;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(2); /* BITMAP: 00000100 */
+ prop->sink_ports = BIT(1); /* BITMAP: 00000010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 20;
+
+ return 0;
+}
+
+static int tasdevice_io_init(struct device *dev,
+ struct sdw_slave *slave)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+ int ret;
+
+ regcache_cache_only(tas_priv->regmap, false);
+
+ if (tas_priv->first_hw_init) {
+ regcache_cache_bypass(tas_priv->regmap, true);
+ } else {
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ * Update count of parent 'active' children.
+ */
+ pm_runtime_set_active(&slave->dev);
+ }
+
+ /* sw reset */
+ ret = regmap_write(tas_priv->regmap, TAS2873_REG_SWRESET,
+ TAS2873_REG_SWRESET_RESET);
+ if (ret) {
+ dev_err(tas_priv->dev, "Reset failed.\n");
+ goto out;
+ }
+
+ if (tas_priv->first_hw_init) {
+ regcache_cache_bypass(tas_priv->regmap, false);
+ regcache_mark_dirty(tas_priv->regmap);
+ }
+
+ tas_priv->first_hw_init = true;
+ tas_priv->hw_init = true;
+
+out:
+ return ret;
+}
+
+static int tasdevice_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(&slave->dev);
+
+ /* Update the status */
+ tas_priv->status = status;
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ tas_priv->hw_init = false;
+ /*
+ * Perform initialization only if slave status
+ * is present and hw_init flag is false
+ */
+ if (tas_priv->hw_init || tas_priv->status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return tasdevice_io_init(&slave->dev, slave);
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static const struct sdw_slave_ops tasdevice_sdw_ops = {
+ .read_prop = tasdevice_read_prop,
+ .update_status = tasdevice_update_status,
+};
+
+static int tasdevice_sdw_probe(struct sdw_slave *peripheral,
+ const struct sdw_device_id *id)
+{
+ struct device *dev = &peripheral->dev;
+ struct tasdevice_priv *tas_dev;
+
+ tas_dev = devm_kzalloc(dev, sizeof(*tas_dev), GFP_KERNEL);
+ if (!tas_dev)
+ return -ENOMEM;
+
+ tas_dev->dev = dev;
+ tas_dev->chip_id = id->driver_data;
+ tas_dev->sdw_peripheral = peripheral;
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ tas_dev->hw_init = false;
+ tas_dev->first_hw_init = false;
+
+ dev_set_drvdata(dev, tas_dev);
+
+ tas_dev->regmap = devm_regmap_init_sdw(peripheral, &tasdevice_regmap);
+ if (IS_ERR(tas_dev->regmap))
+ return dev_err_probe(dev, PTR_ERR(tas_dev->regmap),
+ "Failed devm_regmap_init_sdw.");
+
+ return tasdevice_init(tas_dev);
+}
+
+static int tasdevice_sdw_remove(struct sdw_slave *peripheral)
+{
+ struct tasdevice_priv *tas_dev = dev_get_drvdata(&peripheral->dev);
+
+ if (tas_dev->first_hw_init)
+ pm_runtime_disable(tas_dev->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id tasdevice_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x0102, 0x0000, 0),
+ {}
+};
+MODULE_DEVICE_TABLE(sdw, tasdevice_sdw_id);
+
+static int __maybe_unused tas2783_sdca_dev_suspend(struct device *dev)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+
+ if (!tas_priv->hw_init)
+ return 0;
+
+ regcache_cache_only(tas_priv->regmap, true);
+
+ return 0;
+}
+
+#define TAS2783_PROBE_TIMEOUT_MS 5000
+
+static int __maybe_unused tas2783_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!tas_priv->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(TAS2783_PROBE_TIMEOUT_MS));
+ if (!time) {
+ dev_err(&slave->dev, "Init not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(tas_priv->regmap, false);
+ regcache_sync(tas_priv->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tas2783_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(tas2783_sdca_dev_suspend,
+ tas2783_sdca_dev_resume)
+ RUNTIME_PM_OPS(tas2783_sdca_dev_suspend,
+ tas2783_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver tasdevice_sdw_driver = {
+ .driver = {
+ .name = "slave-tas2783",
+ .pm = &tas2783_sdca_pm,
+ },
+ .probe = tasdevice_sdw_probe,
+ .remove = tasdevice_sdw_remove,
+ .ops = &tasdevice_sdw_ops,
+ .id_table = tasdevice_sdw_id,
+};
+
+module_sdw_driver(tasdevice_sdw_driver);
+
+MODULE_AUTHOR("Baojun Xu <baojun.xu(a)ti.com>");
+MODULE_AUTHOR("Shenghao Ding <shenghao-ding(a)ti.com>");
+MODULE_DESCRIPTION("ASoC TAS2783 SoundWire Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2783.h b/sound/soc/codecs/tas2783.h
new file mode 100644
index 000000000000..631776dcec4c
--- /dev/null
+++ b/sound/soc/codecs/tas2783.h
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC Texas Instruments TAS2783 Audio Smart Amplifier
+ *
+ * Copyright (C) 2023 - 2024 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * Author: Baojun Xu <baojun.xu(a)ti.com>
+ * Shenghao Ding <shenghao-ding(a)ti.com>
+ */
+
+#ifndef __TAS2783_H__
+#define __TAS2783_H__
+
+/* Unique id start */
+#define TAS2783_UNIQUE_ID_BASE 0x08
+#define TAS2783_MAX_DEV_NUM 8
+
+#define TAS2783_DEVICE_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_88200)
+
+#define TAS2783_DEVICE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/* BOOK, PAGE Control Register */
+#define TASDEVICE_REG(book, page, reg) ((book * 256 * 256) + 0x8000 + \
+ (page * 128) + reg)
+
+/*Software Reset */
+#define TAS2873_REG_SWRESET TASDEVICE_REG(0x0, 0x00, 0x01)
+#define TAS2873_REG_SWRESET_MASK BIT(0)
+#define TAS2873_REG_SWRESET_RESET BIT(0)
+
+#define TAS2873_REG_PWR_CTRL TASDEVICE_REG(0x0, 0x00, 0x02)
+#define TAS2783_REG_AEF_MASK GENMASK(4, 3)
+#define TAS2783_REG_AEF_ACTIVE 0
+#define TAS2783_REG_AEF_INACTIVE GENMASK(4, 3)
+#define TAS2783_REG_PWR_MODE_MASK GENMASK(2, 0)
+#define TAS2783_REG_PWR_MODE_ACTIVE 0
+#define TAS2783_REG_PWR_MODE_SW_PWD BIT(1)
+
+#define TAS2783_REG_TDM_RX_CFG TASDEVICE_REG(0x0, 0x00, 0x0a)
+
+/* Volume control */
+#define TAS2783_DVC_LVL TASDEVICE_REG(0x0, 0x00, 0x1a)
+#define TAS2783_AMP_LEVEL TASDEVICE_REG(0x0, 0x00, 0x03)
+#define TAS2783_AMP_LEVEL_MASK GENMASK(5, 1)
+
+/* Calibration data */
+#define TAS2783_CALIBRATION_RE TASDEVICE_REG(0x0, 0x17, 0x74)
+#define TAS2783_CALIBRATION_RE_LOW TASDEVICE_REG(0x0, 0x18, 0x14)
+#define TAS2783_CALIBRATION_INV_RE TASDEVICE_REG(0x0, 0x18, 0x0c)
+#define TAS2783_CALIBRATION_POW TASDEVICE_REG(0x0, 0x13, 0x70)
+#define TAS2783_CALIBRATION_TLIMIT TASDEVICE_REG(0x0, 0x18, 0x7c)
+
+/* TAS2783 SDCA Control - function number */
+#define TAS2783_FUNC_TYPE_SMART_AMP 0x01
+
+/* TAS2783 SDCA entity */
+#define TAS2783_SDCA_ENT_FU21 0x01
+#define TAS2783_SDCA_ENT_FU23 0x02
+
+/* TAS2783 SDCA control */
+#define TAS2783_SDCA_CTL_REQ_POWER_STATE 0x01
+#define TAS2783_SDCA_CTL_FU_MUTE 0x01
+#define TAS2783_SDCA_CTL_FU_VOLUME 0x02
+#define TAS2783_SDCA_CTL_UDMPU_CLUSTER 0x10
+
+#define TAS2783_DEVICE_CHANNEL_LEFT 1
+#define TAS2783_DEVICE_CHANNEL_RIGHT 2
+#define TAS2783_DSPFW_FILENAME_LEN 64
+
+#define TAS2783_MAX_CALIDATA_SIZE 256
+
+struct tas2783_firmware_node {
+ unsigned int vendor_id;
+ unsigned int file_id;
+ unsigned int version_id;
+ unsigned int length;
+ unsigned int download_addr;
+};
+
+struct calibration_data {
+ unsigned long total_sz;
+ unsigned char data[TAS2783_MAX_CALIDATA_SIZE];
+};
+
+struct tasdevice_priv {
+ struct snd_soc_component *component;
+ struct sdw_slave *sdw_peripheral;
+ enum sdw_slave_status status;
+ struct sdw_bus_params params;
+ struct regmap *regmap;
+ struct device *dev;
+ unsigned char dspfw_binaryname[TAS2783_DSPFW_FILENAME_LEN];
+ unsigned char dev_name[32];
+ unsigned int chip_id;
+ bool pstream;
+ bool hw_init;
+ bool first_hw_init;
+};
+
+#endif /*__TAS2783_H__ */
--
2.34.1
2
1
[PATCH] mfd: cs42l43: Fix wrong GPIO_FN_SEL and SPI_CLK_CONFIG1 defaults
by Maciej Strozek 05 Mar '24
by Maciej Strozek 05 Mar '24
05 Mar '24
Two regs have wrong values in existing fields, change them to match
the datasheet.
Fixes: ace6d1448138 ("mfd: cs42l43: Add support for cs42l43 core driver")
Signed-off-by: Maciej Strozek <mstrozek(a)opensource.cirrus.com>
---
drivers/mfd/cs42l43.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c
index 73c88ee6a866..1cea3f8f467d 100644
--- a/drivers/mfd/cs42l43.c
+++ b/drivers/mfd/cs42l43.c
@@ -84,7 +84,7 @@ const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS] = {
{ CS42L43_DRV_CTRL_5, 0x136C00C0 },
{ CS42L43_GPIO_CTRL1, 0x00000707 },
{ CS42L43_GPIO_CTRL2, 0x00000000 },
- { CS42L43_GPIO_FN_SEL, 0x00000000 },
+ { CS42L43_GPIO_FN_SEL, 0x00000004 },
{ CS42L43_MCLK_SRC_SEL, 0x00000000 },
{ CS42L43_SAMPLE_RATE1, 0x00000003 },
{ CS42L43_SAMPLE_RATE2, 0x00000003 },
@@ -217,7 +217,7 @@ const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS] = {
{ CS42L43_CTRL_REG, 0x00000006 },
{ CS42L43_FDIV_FRAC, 0x40000000 },
{ CS42L43_CAL_RATIO, 0x00000080 },
- { CS42L43_SPI_CLK_CONFIG1, 0x00000000 },
+ { CS42L43_SPI_CLK_CONFIG1, 0x00000001 },
{ CS42L43_SPI_CONFIG1, 0x00000000 },
{ CS42L43_SPI_CONFIG2, 0x00000000 },
{ CS42L43_SPI_CONFIG3, 0x00000001 },
--
2.30.2
3
2
Hello,
I want to report a bug with snd_fireface.
issue
audio suddenly freezes, repeating the last buffer to infinity
other symptoms
Most of the time, the GUI also freezes to a point that no interaction
is possible.
Not even a user console can be reached (ctrl + alt + F#).
The only way to get the system back in this scenario is to use REISUB
or power it down, I can not reach any console.
Luckily, sometimes the kernel messages at the bottom are written to
the logfile.
occurrence
This issue is triggered by more "stressful" circumstances, specifically
while using the Windows VST Kontakt in linux-native reaper through
yabridge (wine).
The timeframe is 5-20 minutes.
Reaper connects to the interface using ALSA, no other application is
playing audio.
My default sample rate is 96k, increasing buffer size to 512 samples
decreases CPU load but does not prevent this crash.
variables I tried to exclude
OS
Manjaro or fresh Artix install makes no difference
host application
the crash happens just as well with linux-native bitwig under similar
circumstances
wine variants
wine, wine-staging, wine-tkg w esync and fsync patches
The reason why I attribute both these issues to snd_firewire is that I
get no freezes / instabilities at all under these circumstances:
* in Linux, using an usb audio interface or the onboard audio instead.
To me, this suggests Wine, Reaper and Kontakt are not likely the
culprit.
* in Windows, using RME's dirver.
To me, this suggests FW643 and my platform have no issue working
together and the FireFace works as it should.
* in Linux, passing the FW643 card through to a windows VM with
vfio-pci.
Here I am just booting the same physical drive in QEMU,
so using the same RME driver.
To me, this suggests there is no strange interaction between linux
and FW643.
system
Interface: RME FireFace 800
FireWire controller: LSI FW643 rev 8
Platform: MSI ms7d25 Z690
Kernel: 6.8.0-rc5-1-mainline #8 SMP PREEMPT_DYNAMIC
OS: Artix Xfce
video driver: mesa
An related issue?
During normal use, as of recent kernel versions, just playing back audio
from any pulse audio application, I get a similar freeze but less severe:
no other applications are blocked and it can be removed by just stoping
playback, then after 5 seconds the audible freeze stops and I can start
playback again.
This happens less often, 5-60 minutes between freezes.
I managed to capture this unicorn event yesterday in a relatively short
file using 'sudo trace-cmd record -e snd_firewire_lib:amdtp_packet',
maybe it can give you a hint as to what may be going on:
https://pixeldrain.com/u/tgoSA1mk
I tried to do the same for my real issue (the one with the full system
freeze but I can not stop the recording and the output after rebooting
are files split by cpu cores.
I hope this is the right place to turn to for help.
It has prevented me from using my interface in linux for audio production
for a long time and I want to help fix it.
If I can provide any form of further assistance or somehow collect
any more data, please let me know.
Kind regards,
Edmund Raile.
watchdog: Watchdog detected hard LOCKUP on cpu 13
Modules linked in: ntfs3 fuse 8021q garp mrp stp llc bnep bluetooth ecdh_generic rfkill vfat fat snd_sof_pci_intel_tgl snd_sof_intel_hda_common soundwire_intel snd_sof_intel_hda_mlink snd_hda_codec_hdmi soundwire_cadence snd_sof_intel_hda snd_sof_pci snd_sof_xtensa_dsp snd_sof intel_rapl_msr snd_sof_utils intel_rapl_common snd_soc_hdac_hda snd_hda_ext_core snd_soc_acpi_intel_match intel_uncore_frequency intel_uncore_frequency_common snd_soc_acpi intel_tcc_cooling soundwire_generic_allocation snd_hda_codec_realtek x86_pkg_temp_thermal soundwire_bus intel_powerclamp snd_hda_codec_generic snd_soc_core coretemp snd_compress kvm_intel ac97_bus snd_pcm_dmaengine snd_hda_intel snd_usb_audio snd_intel_dspcfg snd_fireface snd_intel_sdw_acpi kvm snd_usbmidi_lib snd_dice snd_hda_codec snd_firewire_lib snd_ump snd_hda_core snd_rawmidi irqbypass mc snd_hwdep snd_seq_device rapl iTCO_wdt snd_pcm intel_pmc_bxt intel_cstate mei_pxp mei_hdcp iTCO_vendor_support ee1004 snd_timer intel_uncore psmouse pcspkr crypto_user
spi_nor snd i2c_i801 mei_me mousedev wacom joydev mtd igc soundcore i2c_smbus intel_pmc_core intel_vsec mei pmt_telemetry pmt_class mac_hid coreboot_table intel_scu_pltdrv ext4 crc32c_generic crc16 mbcache jbd2 hid_logitech ff_memless hid_logitech_hidpp hid_logitech_dj dm_crypt cbc uas usb_storage encrypted_keys trusted asn1_encoder tee dm_mod usbhid crct10dif_pclmul crc32_pclmul crc32c_intel polyval_clmulni polyval_generic gf128mul ghash_clmulni_intel sha512_ssse3 sha256_ssse3 sha1_ssse3 serio_raw aesni_intel atkbd nvme libps2 spi_intel_pci vivaldi_fmap firewire_ohci crypto_simd nvme_core firewire_core cryptd xhci_pci spi_intel nvme_auth xhci_pci_renesas i8042 crc_itu_t serio i915 i2c_algo_bit drm_buddy video wmi ttm intel_gtt drm_display_helper cec
CPU: 13 PID: 1993 Comm: reaper/audio Not tainted 6.8.0-rc5-1-mainline #8 c2061649224b2c533bd915b824b700586979f2da
Hardware name: Micro-Star International Co., Ltd. MS-7D25/PRO Z690-A DDR4(MS-7D25), BIOS Dasharo (coreboot+UEFI) v1.1.3 01/17/2024
RIP: 0010:tasklet_unlock_spin_wait+0xd/0x20
Code: c0 c3 cc cc cc cc 0f 1f 80 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 0f 1f 44 00 00 eb 02 f3 90 <48> 8b 47 08 a8 02 75 f6 c3 cc cc cc cc 66 0f 1f 44 00 00 90 90 90
RSP: 0018:ffffb828c1cdbd08 EFLAGS: 00000002
RAX: 0000000000000002 RBX: ffff951841aba028 RCX: 0000000000000000
RDX: 0000000000000001 RSI: ffff951841a2aa70 RDI: ffff951841aba0a8
RBP: ffffb828c1cdbea0 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
R13: 0000000000075800 R14: ffff951846a9f600 R15: 0000000000000000
FS: 00007f7f394006c0(0000) GS:ffff952b9fb40000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fc65c2a0000 CR3: 000000018516a000 CR4: 0000000000f50ef0
PKRU: 55555554
Call Trace:
<NMI>
? watchdog_hardlockup_check+0xfc/0x1d0
? __perf_event_overflow+0xe5/0x2a0
? handle_pmi_common+0x16f/0x380
? intel_pmu_handle_irq+0x104/0x480
? perf_event_nmi_handler+0x2a/0x50
? nmi_handle+0x5e/0x150
? default_do_nmi+0x40/0x100
? exc_nmi+0x139/0x1c0
? end_repeat_nmi+0xf/0x60
? tasklet_unlock_spin_wait+0xd/0x20
? tasklet_unlock_spin_wait+0xd/0x20
? tasklet_unlock_spin_wait+0xd/0x20
</NMI>
<TASK>
ohci_flush_iso_completions+0x1d/0x100 [firewire_ohci 83160820eab58a73fec0d1e78730dd51dd0ac98c]
amdtp_domain_stream_pcm_pointer+0x41/0x50 [snd_firewire_lib 95578abd4cf50f7c2e87b0d8233b959be2154fc3]
snd_pcm_update_hw_ptr0+0x6c/0x430 [snd_pcm df3bd3d8840cfa728bc0f17b93055a5a1595ec6d]
? sched_clock_cpu+0xf/0x190
? __smp_call_single_queue+0xad/0x120
snd_pcm_delay+0x33/0x140 [snd_pcm df3bd3d8840cfa728bc0f17b93055a5a1595ec6d]
snd_pcm_common_ioctl+0x922/0x12b0 [snd_pcm df3bd3d8840cfa728bc0f17b93055a5a1595ec6d]
? wake_up_q+0x4e/0x90
? futex_wake+0x159/0x190
snd_pcm_ioctl+0x2e/0x50 [snd_pcm df3bd3d8840cfa728bc0f17b93055a5a1595ec6d]
__x64_sys_ioctl+0x94/0xd0
do_syscall_64+0x86/0x170
? do_syscall_64+0x96/0x170
? do_syscall_64+0x96/0x170
? do_syscall_64+0x96/0x170
? do_syscall_64+0x96/0x170
? __irq_exit_rcu+0x4b/0xc0
entry_SYSCALL_64_after_hwframe+0x6e/0x76
RIP: 0033:0x7f7f47cfd4ff
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 18 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007f7f393ffd50 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000003cfc060 RCX: 00007f7f47cfd4ff
RDX: 00007f7f393ffdf0 RSI: 0000000080084121 RDI: 000000000000000a
RBP: 0000000000000001 R08: 0000000000000001 R09: 0000000000000004
R10: 00007f7f393ffb20 R11: 0000000000000246 R12: 0000000000005000
R13: 0000000003272a80 R14: 0000000000000050 R15: 00000000032706c0
</TASK>
watchdog: Watchdog detected hard LOCKUP on cpu 6
CPU: 6 PID: 0 Comm: swapper/6 Not tainted 6.8.0-rc5-1-mainline #8 c2061649224b2c533bd915b824b700586979f2da
Hardware name: Micro-Star International Co., Ltd. MS-7D25/PRO Z690-A DDR4(MS-7D25), BIOS Dasharo (coreboot+UEFI) v1.1.3 01/17/2024
RIP: 0010:native_queued_spin_lock_slowpath+0x6e/0x2e0
Code: 77 7f f0 0f ba 2b 08 0f 92 c2 8b 03 0f b6 d2 c1 e2 08 30 e4 09 d0 3d ff 00 00 00 77 5b 85 c0 74 10 0f b6 03 84 c0 74 09 f3 90 <0f> b6 03 84 c0 75 f7 b8 01 00 00 00 66 89 03 65 48 ff 05 83 a6 23
RSP: 0018:ffffb828c01ece08 EFLAGS: 00000002
RAX: 0000000000000001 RBX: ffff951846a9f6f8 RCX: 0000000000000300
RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff951846a9f6f8
RBP: ffff95184684c438 R08: ffff951879f3f800 R09: ffff951841a94028
R10: ffff951879f3f800 R11: 0000000000000000 R12: 0000000000000031
R13: 0000000000000000 R14: ffff9518a562e000 R15: ffff9518c49e5800
FS: 0000000000000000(0000) GS:ffff952b9f980000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000001010fdac8 CR3: 000000018516a000 CR4: 0000000000f50ef0
PKRU: 55555554
Call Trace:
<NMI>
? watchdog_hardlockup_check+0xfc/0x1d0
? __perf_event_overflow+0xe5/0x2a0
? handle_pmi_common+0x16f/0x380
? intel_pmu_handle_irq+0x104/0x480
? perf_event_nmi_handler+0x2a/0x50
? nmi_handle+0x5e/0x150
? default_do_nmi+0x40/0x100
? exc_nmi+0x139/0x1c0
? end_repeat_nmi+0xf/0x60
? native_queued_spin_lock_slowpath+0x6e/0x2e0
? native_queued_spin_lock_slowpath+0x6e/0x2e0
? native_queued_spin_lock_slowpath+0x6e/0x2e0
</NMI>
<IRQ>
_raw_spin_lock_irqsave+0x3d/0x50
snd_pcm_period_elapsed+0x18/0x40 [snd_pcm df3bd3d8840cfa728bc0f17b93055a5a1595ec6d]
process_rx_packets+0x245/0x5a0 [snd_firewire_lib 95578abd4cf50f7c2e87b0d8233b959be2154fc3]
? irqtime_account_irq+0x40/0xc0
irq_target_callback+0x16/0x20 [snd_firewire_lib 95578abd4cf50f7c2e87b0d8233b959be2154fc3]
handle_it_packet+0x135/0x150 [firewire_ohci 83160820eab58a73fec0d1e78730dd51dd0ac98c]
context_tasklet+0x140/0x160 [firewire_ohci 83160820eab58a73fec0d1e78730dd51dd0ac98c]
tasklet_action_common.isra.0+0x146/0x240
__do_softirq+0xc9/0x2c8
__irq_exit_rcu+0xa3/0xc0
common_interrupt+0x86/0xa0
</IRQ>
<TASK>
asm_common_interrupt+0x26/0x40
RIP: 0010:cpuidle_enter_state+0xcc/0x440
Code: 2a 39 36 ff e8 05 f1 ff ff 8b 53 04 49 89 c5 0f 1f 44 00 00 31 ff e8 83 38 35 ff 45 84 ff 0f 85 56 02 00 00 fb 0f 1f 44 00 00 <45> 85 f6 0f 88 85 01 00 00 49 63 d6 48 8d 04 52 48 8d 04 82 49 8d
RSP: 0018:ffffb828c00ffe90 EFLAGS: 00000246
RAX: ffff952b9f9b4800 RBX: ffff952b9f9be208 RCX: 000000000000001f
RDX: 0000000000000006 RSI: 0000000022b8e38e RDI: 0000000000000000
RBP: 0000000000000001 R08: 0000000000000000 R09: 00000000000001c8
R10: 0000000000000018 R11: ffff952b9f9b31e4 R12: ffffffff83f49780
R13: 0000006b6114102b R14: 0000000000000001 R15: 0000000000000000
cpuidle_enter+0x2d/0x40
do_idle+0x1d8/0x230
cpu_startup_entry+0x2a/0x30
start_secondary+0x11e/0x140
secondary_startup_64_no_verify+0x184/0x18b
</TASK>
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: \x096-...0: (1 GPs behind) idle=d7ec/1/0x4000000000000002 softirq=52862/52863 fqs=5201
rcu: \x0913-...0: (1 GPs behind) idle=c9d4/1/0x4000000000000000 softirq=53654/53655 fqs=5201
rcu: \x09(detected by 2, t=18002 jiffies, g=76629, q=5124 ncpus=16)
Sending NMI from CPU 2 to CPUs 6:
NMI backtrace for cpu 6
CPU: 6 PID: 0 Comm: swapper/6 Not tainted 6.8.0-rc5-1-mainline #8 c2061649224b2c533bd915b824b700586979f2da
Hardware name: Micro-Star International Co., Ltd. MS-7D25/PRO Z690-A DDR4(MS-7D25), BIOS Dasharo (coreboot+UEFI) v1.1.3 01/17/2024
RIP: 0010:native_queued_spin_lock_slowpath+0x6e/0x2e0
Code: 77 7f f0 0f ba 2b 08 0f 92 c2 8b 03 0f b6 d2 c1 e2 08 30 e4 09 d0 3d ff 00 00 00 77 5b 85 c0 74 10 0f b6 03 84 c0 74 09 f3 90 <0f> b6 03 84 c0 75 f7 b8 01 00 00 00 66 89 03 65 48 ff 05 83 a6 23
RSP: 0018:ffffb828c01ece08 EFLAGS: 00000002
RAX: 0000000000000001 RBX: ffff951846a9f6f8 RCX: 0000000000000300
RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff951846a9f6f8
RBP: ffff95184684c438 R08: ffff951879f3f800 R09: ffff951841a94028
R10: ffff951879f3f800 R11: 0000000000000000 R12: 0000000000000031
R13: 0000000000000000 R14: ffff9518a562e000 R15: ffff9518c49e5800
FS: 0000000000000000(0000) GS:ffff952b9f980000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000001010fdac8 CR3: 000000018516a000 CR4: 0000000000f50ef0
PKRU: 55555554
Call Trace:
<NMI>
? nmi_cpu_backtrace+0x99/0x110
? nmi_cpu_backtrace_handler+0x11/0x20
? nmi_handle+0x5e/0x150
? default_do_nmi+0x40/0x100
? exc_nmi+0x139/0x1c0
? end_repeat_nmi+0xf/0x60
? native_queued_spin_lock_slowpath+0x6e/0x2e0
? native_queued_spin_lock_slowpath+0x6e/0x2e0
? native_queued_spin_lock_slowpath+0x6e/0x2e0
</NMI>
<IRQ>
_raw_spin_lock_irqsave+0x3d/0x50
snd_pcm_period_elapsed+0x18/0x40 [snd_pcm df3bd3d8840cfa728bc0f17b93055a5a1595ec6d]
process_rx_packets+0x245/0x5a0 [snd_firewire_lib 95578abd4cf50f7c2e87b0d8233b959be2154fc3]
? irqtime_account_irq+0x40/0xc0
irq_target_callback+0x16/0x20 [snd_firewire_lib 95578abd4cf50f7c2e87b0d8233b959be2154fc3]
handle_it_packet+0x135/0x150 [firewire_ohci 83160820eab58a73fec0d1e78730dd51dd0ac98c]
context_tasklet+0x140/0x160 [firewire_ohci 83160820eab58a73fec0d1e78730dd51dd0ac98c]
tasklet_action_common.isra.0+0x146/0x240
__do_softirq+0xc9/0x2c8
__irq_exit_rcu+0xa3/0xc0
common_interrupt+0x86/0xa0
</IRQ>
<TASK>
asm_common_interrupt+0x26/0x40
RIP: 0010:cpuidle_enter_state+0xcc/0x440
Code: 2a 39 36 ff e8 05 f1 ff ff 8b 53 04 49 89 c5 0f 1f 44 00 00 31 ff e8 83 38 35 ff 45 84 ff 0f 85 56 02 00 00 fb 0f 1f 44 00 00 <45> 85 f6 0f 88 85 01 00 00 49 63 d6 48 8d 04 52 48 8d 04 82 49 8d
RSP: 0018:ffffb828c00ffe90 EFLAGS: 00000246
RAX: ffff952b9f9b4800 RBX: ffff952b9f9be208 RCX: 000000000000001f
RDX: 0000000000000006 RSI: 0000000022b8e38e RDI: 0000000000000000
RBP: 0000000000000001 R08: 0000000000000000 R09: 00000000000001c8
R10: 0000000000000018 R11: ffff952b9f9b31e4 R12: ffffffff83f49780
R13: 0000006b6114102b R14: 0000000000000001 R15: 0000000000000000
cpuidle_enter+0x2d/0x40
do_idle+0x1d8/0x230
cpu_startup_entry+0x2a/0x30
start_secondary+0x11e/0x140
secondary_startup_64_no_verify+0x184/0x18b
</TASK>
Sending NMI from CPU 2 to CPUs 13:
NMI backtrace for cpu 13
CPU: 13 PID: 1993 Comm: reaper/audio Not tainted 6.8.0-rc5-1-mainline #8 c2061649224b2c533bd915b824b700586979f2da
Hardware name: Micro-Star International Co., Ltd. MS-7D25/PRO Z690-A DDR4(MS-7D25), BIOS Dasharo (coreboot+UEFI) v1.1.3 01/17/2024
RIP: 0010:tasklet_unlock_spin_wait+0xd/0x20
Code: c0 c3 cc cc cc cc 0f 1f 80 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 0f 1f 44 00 00 eb 02 f3 90 <48> 8b 47 08 a8 02 75 f6 c3 cc cc cc cc 66 0f 1f 44 00 00 90 90 90
RSP: 0018:ffffb828c1cdbd08 EFLAGS: 00000002
RAX: 0000000000000002 RBX: ffff951841aba028 RCX: 0000000000000000
RDX: 0000000000000001 RSI: ffff951841a2aa70 RDI: ffff951841aba0a8
RBP: ffffb828c1cdbea0 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
R13: 0000000000075800 R14: ffff951846a9f600 R15: 0000000000000000
FS: 00007f7f394006c0(0000) GS:ffff952b9fb40000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fc65c2a0000 CR3: 000000018516a000 CR4: 0000000000f50ef0
PKRU: 55555554
Call Trace:
<NMI>
? nmi_cpu_backtrace+0x99/0x110
? nmi_cpu_backtrace_handler+0x11/0x20
? nmi_handle+0x5e/0x150
? default_do_nmi+0x40/0x100
? exc_nmi+0x139/0x1c0
? end_repeat_nmi+0xf/0x60
? tasklet_unlock_spin_wait+0xd/0x20
? tasklet_unlock_spin_wait+0xd/0x20
? tasklet_unlock_spin_wait+0xd/0x20
</NMI>
<TASK>
ohci_flush_iso_completions+0x1d/0x100 [firewire_ohci 83160820eab58a73fec0d1e78730dd51dd0ac98c]
amdtp_domain_stream_pcm_pointer+0x41/0x50 [snd_firewire_lib 95578abd4cf50f7c2e87b0d8233b959be2154fc3]
snd_pcm_update_hw_ptr0+0x6c/0x430 [snd_pcm df3bd3d8840cfa728bc0f17b93055a5a1595ec6d]
? sched_clock_cpu+0xf/0x190
? __smp_call_single_queue+0xad/0x120
snd_pcm_delay+0x33/0x140 [snd_pcm df3bd3d8840cfa728bc0f17b93055a5a1595ec6d]
snd_pcm_common_ioctl+0x922/0x12b0 [snd_pcm df3bd3d8840cfa728bc0f17b93055a5a1595ec6d]
? wake_up_q+0x4e/0x90
? futex_wake+0x159/0x190
snd_pcm_ioctl+0x2e/0x50 [snd_pcm df3bd3d8840cfa728bc0f17b93055a5a1595ec6d]
__x64_sys_ioctl+0x94/0xd0
do_syscall_64+0x86/0x170
? do_syscall_64+0x96/0x170
? do_syscall_64+0x96/0x170
? do_syscall_64+0x96/0x170
? do_syscall_64+0x96/0x170
? __irq_exit_rcu+0x4b/0xc0
entry_SYSCALL_64_after_hwframe+0x6e/0x76
RIP: 0033:0x7f7f47cfd4ff
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 18 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007f7f393ffd50 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000003cfc060 RCX: 00007f7f47cfd4ff
RDX: 00007f7f393ffdf0 RSI: 0000000080084121 RDI: 000000000000000a
RBP: 0000000000000001 R08: 0000000000000001 R09: 0000000000000004
R10: 00007f7f393ffb20 R11: 0000000000000246 R12: 0000000000005000
R13: 0000000003272a80 R14: 0000000000000050 R15: 00000000032706c0
</TASK>
INFO: task khugepaged:124 blocked for more than 122 seconds.
Not tainted 6.8.0-rc5-1-mainline #8
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:khugepaged state:D stack:0 pid:124 tgid:124 ppid:2 flags:0x00004000
Call Trace:
<TASK>
__schedule+0x3e6/0x1520
? place_entity+0x1b/0xf0
schedule+0x32/0xd0
schedule_timeout+0x151/0x160
wait_for_completion+0x8a/0x160
__flush_work.isra.0+0x173/0x280
? __pfx_wq_barrier_func+0x10/0x10
__lru_add_drain_all+0x14c/0x200
khugepaged+0x72/0x950
? __pfx_autoremove_wake_function+0x10/0x10
? __pfx_khugepaged+0x10/0x10
kthread+0xe5/0x120
? __pfx_kthread+0x10/0x10
ret_from_fork+0x31/0x50
? __pfx_kthread+0x10/0x10
ret_from_fork_asm+0x1b/0x30
</TASK>
1
0
Hi,
I am hitting a clock issue with rockchip_i2s_tdm.c + simple-audio-card
(+ RK3308).
At boot the mclk clk_i2s0_8ch_tx is (somehow) initialized to some
(unimportant?) value 50176000 Hz. Note that this frequency is not
multiple of either 48kHz or 44.1kHz.
Method asoc_simple_parse_clk() reads this value and sets it to
simple_dai->sysclk.
Subsequently at asoc_simple_dai_init this "random" initial value is
stored to i2s_tdm->mclk_tx_freq:
17.839330] rockchip_i2s_tdm_set_sysclk+0x50/0xbc
[snd_soc_rockchip_i2s_tdm]
[ 17.839367] snd_soc_dai_set_sysclk+0x38/0xb8 [snd_soc_core]
[ 17.839596] asoc_simple_init_dai+0x94/0xc0 [snd_soc_simple_card_utils]
[ 17.839640] asoc_simple_dai_init+0x130/0x230 [snd_soc_simple_card_utils]
[ 17.839672] snd_soc_link_init+0x28/0x90 [snd_soc_core]
[ 17.839843] snd_soc_bind_card+0x60c/0xbb4 [snd_soc_core]
When starting playback, called by rockchip_i2s_tdm_hw_params(),
rockchip_i2s_tdm_calibrate_mclk() correctly switches parent of
mclk_parent to correct root pll clock mclk_root0/1 for the given
samplerate and correctly configures mclk_parent frequency.
https://github.com/torvalds/linux/blob/master/sound/soc/rockchip/rockchip_i…
But right after that, the next line of rockchip_i2s_tdm_hw_params()
calls rockchip_i2s_tdm_set_mclk()
https://github.com/torvalds/linux/blob/master/sound/soc/rockchip/rockchip_i…
This method calls clk_set_rate(i2s_tdm->mclk_tx, i2s_tdm->mclk_tx_freq),
which resets the clock and its parental chain to the original incorrect
value stored in i2s_tdm->mclk_tx_freq from the dai initialization.
https://github.com/torvalds/linux/blob/master/sound/soc/rockchip/rockchip_i…
As a result, no matter what sample rate is being played, the i2s mclk
clock always ends up configured incorrectly.
DTS I2S sets all clocks, therefore the clk calibration in
rockchip_i2s_tdm.c should be (and is) used:
i2s_8ch_0: i2s@ff300000 {
compatible = "rockchip,rk3308-i2s-tdm";
reg = <0x0 0xff300000 0x0 0x1000>;
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru SCLK_I2S0_8CH_TX>, <&cru SCLK_I2S0_8CH_RX>, <&cru
HCLK_I2S0_8CH>,
<&cru SCLK_I2S0_8CH_TX_SRC>,
<&cru SCLK_I2S0_8CH_RX_SRC>,
<&cru PLL_VPLL0>,
<&cru PLL_VPLL1>;
clock-names = "mclk_tx", "mclk_rx", "hclk",
"mclk_tx_src", "mclk_rx_src",
"mclk_root0", "mclk_root1";
.........
It seems to me that the calibration code should also rewrite the
initially incorrect i2s_tdm->mclk_tx_freq and i2s_tdm->mclk_rx_freq with
correct values corresponding to the momentary hw_params rate, or maybe
rockchip_i2s_tdm_set_mclk() should not be called if
rockchip_i2s_tdm_calibrate_mclk() is called a line above (i.e. putting
the call to rockchip_i2s_tdm_set_mclk() into "else" branch).
Thank you very much for help.
With regards,
Pavel.
2
1
[PATCH 5.10/5.15/6.1 0/1] soundwire: stream: use consistent pattern for freeing buffers
by Daniil Dulov 04 Mar '24
by Daniil Dulov 04 Mar '24
04 Mar '24
Svacer reports NULL-pointer dereference and double free issues in
do_bank_switch() in case sdw_ml_sync_bank_switch() returns an error
not on the first iteration of the list_for_each_entry() loop. These
problems are present in 5.10, 5.15 and 6.1 stable releases. These problems
have been fixed by the following upstream patch that can be cleanly
applied to 5.10, 5.15 and 6.1 branches.
1
0
[PATCH] [alsa-lib][PATCH] conf: USB-Audio: Add Corsair HS60 Pro to the IEC958 blacklist
by David Senoner 04 Mar '24
by David Senoner 04 Mar '24
04 Mar '24
This device is just an external USB soundcard with a female 3.5mm jack
for a headset, no S/PDIF | IEC958 connector. Add it to the blacklist to
prevent it being opened.
Signed-off-by: David Senoner <seda18(a)rolmail.net>
---
src/conf/cards/USB-Audio.conf | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/conf/cards/USB-Audio.conf b/src/conf/cards/USB-Audio.conf
index 80631b2e..08346a1b 100644
--- a/src/conf/cards/USB-Audio.conf
+++ b/src/conf/cards/USB-Audio.conf
@@ -53,6 +53,7 @@ USB-Audio.pcm.iec958_device {
"Blue Snowball" 999
"C-Media USB Headphone Set" 999
"Cmedia Audio" 999
+ "Corsair HS60 PRO Surround USB S" 999
"DELL PROFESSIONAL SOUND BAR AE5" 999
"HP Digital Stereo Headset" 999
"GN 9330" 999
--
2.43.2
3
2
[PATCH RESEND] conf: USB-Audio: Add more Scarlett devices to the IEC958 blacklist
by Geoffrey D. Bennett 04 Mar '24
by Geoffrey D. Bennett 04 Mar '24
04 Mar '24
The Scarlett Solo and 2i2 don't have S/PDIF outputs.
Signed-off-by: Geoffrey D. Bennett <g(a)b4.vu>
---
src/conf/cards/USB-Audio.conf | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/conf/cards/USB-Audio.conf b/src/conf/cards/USB-Audio.conf
index 80631b2e..05384a32 100644
--- a/src/conf/cards/USB-Audio.conf
+++ b/src/conf/cards/USB-Audio.conf
@@ -66,8 +66,11 @@ USB-Audio.pcm.iec958_device {
"Plantronics USB Headset" 999
"Plantronics Wireless Audio" 999
"SB WoW Headset" 999
+ "Scarlett 2i2 4th Gen" 999
"Scarlett 2i2 USB" 999
"Scarlett 2i4 USB" 999
+ "Scarlett Solo 4th Gen" 999
+ "Scarlett Solo USB" 999
"Sennheiser USB headset" 999
"SWTOR Gaming Headset by Razer" 999
"ThinkStation P620 Main" 999
--
2.43.0
2
1
04 Mar '24
A small set of changes to improve initialization of the audio stack on
HDAudio devices and pair of cleanups.
As the first change is the most important one here, following is the
technical background for it:
Commit 78f613ba1efb ("drm/i915: finish removal of CNL") and its friends
removed support for i915 for all CNL-based platforms. HDAudio library,
however, still treats such platforms as valid candidates for i915
binding. Update query mechanism to reflect changes made in drm tree.
At the same time, i915 support for LKF-based platforms has not been
provided so remove them from valid binding candidates.
The snd_soc_hda change is a follow up for the above and the cleanup
patches do not bring any functional changes.
Changes in v3:
- snd_soc_hda_codec now returns -ENODEV on attach() if i915 is not
present
- denylist now const
- added new patch for the avs-driver to address -ENODEV during
probe_codec()
- note: retained reviewed-by for patch 1/4 as changes are minimal
Changes in v2:
- list of problematic VGA devices is now declared locally, no more
touching drm stuff
Cezary Rojewski (5):
ALSA: hda: Skip i915 initialization on CNL/LKF-based platforms
ASoC: codecs: hda: Skip HDMI/DP registration if i915 is missing
ASoC: Intel: avs: Ignore codecs with no suppoting driver
ASoC: codecs: hda: Cleanup error messages
ALSA: hda: Reuse for_each_pcm_streams()
sound/hda/hdac_i915.c | 32 +++++++++++++++++++++++++++++---
sound/pci/hda/hda_codec.c | 2 +-
sound/soc/codecs/hda.c | 15 ++++++++++-----
sound/soc/intel/avs/core.c | 9 +++++----
4 files changed, 45 insertions(+), 13 deletions(-)
--
2.25.1
3
9