Unverified Commit 191a27fa authored by Richard Fitzgerald's avatar Richard Fitzgerald Committed by Mark Brown
Browse files

ASoC: cs35l56: Create debugfs files for factory calibration



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 patch 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.

Signed-off-by: default avatarRichard Fitzgerald <rf@opensource.cirrus.com>
Reviewed-by: default avatarTakashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20251021105022.1013685-5-rf@opensource.cirrus.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent f7097161
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -899,6 +899,21 @@ config SND_SOC_CS35L56_SDW
config SND_SOC_CS35L56_CAL_DEBUGFS_COMMON
	bool

menu "CS35L56 driver options"
	depends on SND_SOC_CS35L56

config SND_SOC_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_SOC_CS40L50
	tristate "Cirrus Logic CS40L50 CODEC"
	depends on MFD_CS40L50_CORE
+159 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
@@ -250,6 +251,8 @@ static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = {
	SND_SOC_DAPM_SIGGEN("VDDBMON ADC"),
	SND_SOC_DAPM_SIGGEN("VBSTMON ADC"),
	SND_SOC_DAPM_SIGGEN("TEMPMON ADC"),

	SND_SOC_DAPM_INPUT("Calibrate"),
};

#define CS35L56_SRC_ROUTE(name) \
@@ -286,6 +289,7 @@ static const struct snd_soc_dapm_route cs35l56_audio_map[] = {
	{ "DSP1", NULL, "ASP1RX1" },
	{ "DSP1", NULL, "ASP1RX2" },
	{ "DSP1", NULL, "SDW1 Playback" },
	{ "DSP1", NULL, "Calibrate" },
	{ "AMP", NULL, "DSP1" },
	{ "SPK", NULL, "AMP" },

@@ -874,6 +878,152 @@ static void cs35l56_dsp_work(struct work_struct *work)
	pm_runtime_put_autosuspend(cs35l56->base.dev);
}

static struct snd_soc_dapm_context *cs35l56_power_up_for_cal(struct cs35l56_private *cs35l56)
{
	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cs35l56->component);
	int ret;

	ret = snd_soc_component_enable_pin(cs35l56->component, "Calibrate");
	if (ret)
		return ERR_PTR(ret);

	snd_soc_dapm_sync(dapm);

	return dapm;
}

static void cs35l56_power_down_after_cal(struct cs35l56_private *cs35l56)
{
	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cs35l56->component);

	snd_soc_component_disable_pin(cs35l56->component, "Calibrate");
	snd_soc_dapm_sync(dapm);
}

static ssize_t cs35l56_debugfs_calibrate_write(struct file *file,
					       const char __user *from,
					       size_t count, loff_t *ppos)
{
	struct cs35l56_base *cs35l56_base = file->private_data;
	struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base);
	struct snd_soc_dapm_context *dapm;
	ssize_t ret;

	dapm = cs35l56_power_up_for_cal(cs35l56);
	if (IS_ERR(dapm))
		return PTR_ERR(dapm);

	snd_soc_dapm_mutex_lock(dapm);
	ret = cs35l56_calibrate_debugfs_write(&cs35l56->base, from, count, ppos);
	snd_soc_dapm_mutex_unlock(dapm);

	cs35l56_power_down_after_cal(cs35l56);

	return ret;
}

static ssize_t cs35l56_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;
	struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base);
	struct snd_soc_dapm_context *dapm;
	ssize_t ret;

	dapm = cs35l56_power_up_for_cal(cs35l56);
	if (IS_ERR(dapm))
		return PTR_ERR(dapm);

	ret = cs35l56_cal_ambient_debugfs_write(&cs35l56->base, from, count, ppos);
	cs35l56_power_down_after_cal(cs35l56);

	return ret;
}

static ssize_t cs35l56_debugfs_cal_data_read(struct file *file,
					     char __user *to,
					     size_t count, loff_t *ppos)
{
	struct cs35l56_base *cs35l56_base = file->private_data;
	struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base);
	struct snd_soc_dapm_context *dapm;
	ssize_t ret;

	dapm = cs35l56_power_up_for_cal(cs35l56);
	if (IS_ERR(dapm))
		return PTR_ERR(dapm);

	ret = cs35l56_cal_data_debugfs_read(&cs35l56->base, to, count, ppos);
	cs35l56_power_down_after_cal(cs35l56);

	return ret;
}

static int cs35l56_new_cal_data_apply(struct cs35l56_private *cs35l56)
{
	struct snd_soc_dapm_context *dapm;
	int ret;

	if (!cs35l56->base.cal_data_valid)
		return -ENXIO;

	if (cs35l56->base.secured)
		return -EACCES;

	dapm = cs35l56_power_up_for_cal(cs35l56);
	if (IS_ERR(dapm))
		return PTR_ERR(dapm);

	snd_soc_dapm_mutex_lock(dapm);
	ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp,
				      cs35l56->base.calibration_controls,
				      &cs35l56->base.cal_data);
	if (ret == 0)
		cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
	else
		ret = -EIO;

	snd_soc_dapm_mutex_unlock(dapm);
	cs35l56_power_down_after_cal(cs35l56);

	return ret;
}

static ssize_t cs35l56_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_private *cs35l56 = cs35l56_private_from_base(cs35l56_base);
	int ret;

	ret = cs35l56_cal_data_debugfs_write(&cs35l56->base, from, count, ppos);
	if (ret == -ENODATA)
		return count;	/* Ignore writes of empty cal blobs */
	else if (ret < 0)
		return -EIO;

	ret = cs35l56_new_cal_data_apply(cs35l56);
	if (ret)
		return ret;

	return count;
}

static const struct cs35l56_cal_debugfs_fops cs35l56_cal_debugfs_fops = {
	.calibrate = {
		.write = cs35l56_debugfs_calibrate_write,
	},
	.cal_temperature = {
		.write = cs35l56_debugfs_cal_temperature_write,
	},
	.cal_data = {
		.read = cs35l56_debugfs_cal_data_read,
		.write = cs35l56_debugfs_cal_data_write,
	},
};

static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
{
	if (cs35l56->dsp.fwf_suffix)
@@ -971,6 +1121,13 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
	if (ret)
		return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n");

	ret = snd_soc_component_disable_pin(component, "Calibrate");
	if (ret)
		return ret;

	if (IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_DEBUGFS))
		cs35l56_create_cal_debugfs(&cs35l56->base, &cs35l56_cal_debugfs_fops);

	queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);

	return 0;
@@ -982,6 +1139,8 @@ static void cs35l56_component_remove(struct snd_soc_component *component)

	cancel_work_sync(&cs35l56->dsp_work);

	cs35l56_remove_cal_debugfs(&cs35l56->base);

	if (cs35l56->dsp.cs_dsp.booted)
		wm_adsp_power_down(&cs35l56->dsp);

+6 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#define CS35L56_H

#include <linux/completion.h>
#include <linux/container_of.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/workqueue.h>
@@ -54,6 +55,11 @@ struct cs35l56_private {
	u8 sdw_unique_id;
};

static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_base *cs35l56_base)
{
	return container_of(cs35l56_base, struct cs35l56_private, base);
}

extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi;

int cs35l56_system_suspend(struct device *dev);