Linux Sound subsystem development
 help / color / mirror / Atom feed
From: Richard Fitzgerald <rf@opensource.cirrus.com>
To: broonie@kernel.org, tiwai@suse.com
Cc: linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
	patches@opensource.cirrus.com
Subject: [PATCH v2 05/11] ALSA: hda/cs35l56: Create debugfs files for factory calibration
Date: Tue, 21 Oct 2025 11:50:16 +0100	[thread overview]
Message-ID: <20251021105022.1013685-6-rf@opensource.cirrus.com> (raw)
In-Reply-To: <20251021105022.1013685-1-rf@opensource.cirrus.com>

Create debugfs files that can be used to perform factory calibration.

During manufacture, the production line must perform a factory calibration
of the amps. This commit adds this functionality via debugfs files.

As this is only needed during manufacture, there is no need for this to be
available in a normal system so a Kconfig item has been added to enable
this. The new Kconfig option is inside a sub-menu because items do not
group and indent if the parent is invisible or there are multiple parent
dependencies. Anyway the sub-menu reduces the clutter.

cs35l56_hda_apply_calibration() has been changed to return an error code
that can be reported back through the debugfs write. The original call to
this function doesn't check the return code because in normal use it
doesn't matter whether this fails - the firmware will default to a safe
calibration for the platform. But tooling using the debugfs files might
want to know if there was an error.

Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
---
Changes in V2:
- File callbacks rewritten to be debugfs callbacks instead of
  sysfs callbacks.
- Replace the sysfs file definition and creation with debugfs creation.

 sound/hda/codecs/side-codecs/Kconfig       |  15 +++
 sound/hda/codecs/side-codecs/cs35l56_hda.c | 114 ++++++++++++++++++++-
 sound/hda/codecs/side-codecs/cs35l56_hda.h |   6 ++
 3 files changed, 130 insertions(+), 5 deletions(-)

diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig
index cbf1847896bc..f674e9a9c7d7 100644
--- a/sound/hda/codecs/side-codecs/Kconfig
+++ b/sound/hda/codecs/side-codecs/Kconfig
@@ -88,6 +88,21 @@ config SND_HDA_SCODEC_CS35L56_SPI
 	  Say Y or M here to include CS35L56 amplifier support with
 	  SPI control.
 
+menu "CS35L56 driver options"
+	depends on SND_HDA_SCODEC_CS35L56
+
+config SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS
+	bool "CS35L56 create debugfs for factory calibration"
+	default N
+	depends on DEBUG_FS
+	select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON
+	help
+	  Create debugfs entries used during factory-line manufacture
+	  for factory calibration.
+
+	  If unsure select "N".
+endmenu
+
 config SND_HDA_SCODEC_TAS2781
 	tristate
 	select SND_HDA_GENERIC
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c
index 5bb1c4ebeaf3..03f565312459 100644
--- a/sound/hda/codecs/side-codecs/cs35l56_hda.c
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c
@@ -548,20 +548,24 @@ static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmw
 	kfree(coeff_filename);
 }
 
-static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
+static int cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
 {
 	int ret;
 
 	if (!cs35l56->base.cal_data_valid || cs35l56->base.secured)
-		return;
+		return -EACCES;
 
 	ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp,
 				      &cs35l56_calibration_controls,
 				      &cs35l56->base.cal_data);
-	if (ret < 0)
+	if (ret < 0) {
 		dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret);
-	else
-		dev_info(cs35l56->base.dev, "Calibration applied\n");
+		return ret;
+	}
+
+	dev_info(cs35l56->base.dev, "Calibration applied\n");
+
+	return 0;
 }
 
 static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
@@ -669,7 +673,9 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
 	if (ret)
 		dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
 
+	/* Don't need to check return code, it's not fatal if this fails */
 	cs35l56_hda_apply_calibration(cs35l56);
+
 	ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
 	if (ret)
 		cs_dsp_stop(&cs35l56->cs_dsp);
@@ -695,6 +701,100 @@ static void cs35l56_hda_dsp_work(struct work_struct *work)
 	cs35l56_hda_fw_load(cs35l56);
 }
 
+static ssize_t cs35l56_hda_debugfs_calibrate_write(struct file *file,
+						   const char __user *from,
+						   size_t count, loff_t *ppos)
+{
+	struct cs35l56_base *cs35l56_base = file->private_data;
+	ssize_t ret;
+
+	ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+	if (ret)
+		return ret;
+
+	ret = cs35l56_calibrate_debugfs_write(cs35l56_base, from, count, ppos);
+	pm_runtime_autosuspend(cs35l56_base->dev);
+
+	return ret;
+}
+
+static ssize_t cs35l56_hda_debugfs_cal_temperature_write(struct file *file,
+							 const char __user *from,
+							 size_t count, loff_t *ppos)
+{
+	struct cs35l56_base *cs35l56_base = file->private_data;
+	ssize_t ret;
+
+	ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+	if (ret)
+		return ret;
+
+	ret = cs35l56_cal_ambient_debugfs_write(cs35l56_base, from, count, ppos);
+	pm_runtime_autosuspend(cs35l56_base->dev);
+
+	return ret;
+}
+
+static ssize_t cs35l56_hda_debugfs_cal_data_read(struct file *file,
+						 char __user *to,
+						 size_t count, loff_t *ppos)
+{
+	struct cs35l56_base *cs35l56_base = file->private_data;
+	ssize_t ret;
+
+	ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+	if (ret)
+		return ret;
+
+	ret = cs35l56_cal_data_debugfs_read(cs35l56_base, to, count, ppos);
+	pm_runtime_autosuspend(cs35l56_base->dev);
+
+	return ret;
+}
+
+static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file,
+						  const char __user *from,
+						  size_t count, loff_t *ppos)
+{
+	struct cs35l56_base *cs35l56_base = file->private_data;
+	struct cs35l56_hda *cs35l56 = cs35l56_hda_from_base(cs35l56_base);
+	ssize_t ret;
+
+	ret = cs35l56_cal_data_debugfs_write(cs35l56_base, from, count, ppos);
+	if (ret == -ENODATA)
+		return count;	/* Ignore writes of empty cal blobs */
+
+	if (ret < 0)
+		return ret;
+
+	ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+	if (ret)
+		return ret;
+
+	ret = cs35l56_hda_apply_calibration(cs35l56);
+	if (ret == 0)
+		cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+	else
+		count = -EIO;
+
+	pm_runtime_autosuspend(cs35l56_base->dev);
+
+	return count;
+}
+
+static const struct cs35l56_cal_debugfs_fops cs35l56_hda_cal_debugfs_fops = {
+	.calibrate = {
+		.write = cs35l56_hda_debugfs_calibrate_write,
+	},
+	.cal_temperature = {
+		.write = cs35l56_hda_debugfs_cal_temperature_write,
+	},
+	.cal_data = {
+		.read = cs35l56_hda_debugfs_cal_data_read,
+		.write = cs35l56_hda_debugfs_cal_data_write,
+	},
+};
+
 static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data)
 {
 	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
@@ -722,6 +822,9 @@ static int cs35l56_hda_bind(struct device *dev, struct device *master, void *mas
 	cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root);
 #endif
 
+	if (IS_ENABLED(CONFIG_SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS))
+		cs35l56_create_cal_debugfs(&cs35l56->base, &cs35l56_hda_cal_debugfs_fops);
+
 	dev_dbg(cs35l56->base.dev, "Bound\n");
 
 	return 0;
@@ -735,6 +838,7 @@ static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *
 
 	cancel_work_sync(&cs35l56->dsp_work);
 
+	cs35l56_remove_cal_debugfs(&cs35l56->base);
 	cs35l56_hda_remove_controls(cs35l56);
 
 #if IS_ENABLED(CONFIG_SND_DEBUG)
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.h b/sound/hda/codecs/side-codecs/cs35l56_hda.h
index 38d94fb213a5..cb4b5e7356a3 100644
--- a/sound/hda/codecs/side-codecs/cs35l56_hda.h
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda.h
@@ -9,6 +9,7 @@
 #ifndef __CS35L56_HDA_H__
 #define __CS35L56_HDA_H__
 
+#include <linux/container_of.h>
 #include <linux/device.h>
 #include <linux/gpio/consumer.h>
 #include <linux/firmware/cirrus/cs_dsp.h>
@@ -42,6 +43,11 @@ struct cs35l56_hda {
 #endif
 };
 
+static inline struct cs35l56_hda *cs35l56_hda_from_base(struct cs35l56_base *cs35l56_base)
+{
+	return container_of(cs35l56_base, struct cs35l56_hda, base);
+}
+
 extern const struct dev_pm_ops cs35l56_hda_pm_ops;
 
 int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id);
-- 
2.47.3


  parent reply	other threads:[~2025-10-21 10:50 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-21 10:50 [PATCH v2 00/11] ALSA: cs35l56: Add support for factory calibration Richard Fitzgerald
2025-10-21 10:50 ` [PATCH v2 01/11] ASoC: cs35l56: Read silicon ID during initialization and save it Richard Fitzgerald
2025-10-21 10:50 ` [PATCH v2 02/11] ASoC: cs-amp-lib: Add helpers for factory calibration Richard Fitzgerald
2025-10-21 10:50 ` [PATCH v2 03/11] ASoC: cs35l56: Add common code " Richard Fitzgerald
2025-10-21 10:50 ` [PATCH v2 04/11] ASoC: cs35l56: Create debugfs files " Richard Fitzgerald
2025-10-21 10:50 ` Richard Fitzgerald [this message]
2025-10-21 10:50 ` [PATCH v2 06/11] ASoC: cs-amp-lib-test: Add cases for factory calibration helpers Richard Fitzgerald
2025-10-21 10:50 ` [PATCH v2 07/11] ASoC: cs-amp-lib: Return attributes from cs_amp_get_efi_variable() Richard Fitzgerald
2025-10-21 10:50 ` [PATCH v2 08/11] ASoC: cs-amp-lib: Add function to write calibration to UEFI Richard Fitzgerald
2025-10-21 10:50 ` [PATCH v2 09/11] ASoC: cs35l56: Add calibration command to store into UEFI Richard Fitzgerald
2025-10-21 10:50 ` [PATCH v2 10/11] ALSA: hda/cs35l56: Set cal_index to the amp index Richard Fitzgerald
2025-10-21 10:50 ` [PATCH v2 11/11] ASoC: cs-amp-lib-test: Add test cases for cs_amp_set_efi_calibration_data() Richard Fitzgerald
2025-10-22 19:14 ` [PATCH v2 00/11] ALSA: cs35l56: Add support for factory calibration Mark Brown
2025-10-27  9:02   ` Takashi Iwai
2025-10-28 16:09 ` Mark Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251021105022.1013685-6-rf@opensource.cirrus.com \
    --to=rf@opensource.cirrus.com \
    --cc=broonie@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=patches@opensource.cirrus.com \
    --cc=tiwai@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox