mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Mark Brown <broonie@kernel.org>
To: Richard Fitzgerald <rf@opensource.cirrus.com>
Cc: linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
	patches@opensource.cirrus.com
Subject: Re: [PATCH v2 2/2] ASoC: cs35l56: Allow factory calibration through ALSA controls
Date: Wed, 25 Mar 2026 17:53:59 +0000	[thread overview]
Message-ID: <6e2d8ded-00ce-4148-bd93-aded513b9f30@sirena.org.uk> (raw)
In-Reply-To: <20260325170841.1405368-3-rf@opensource.cirrus.com>

[-- Attachment #1: Type: text/plain, Size: 9106 bytes --]

On Wed, Mar 25, 2026 at 05:08:41PM +0000, Richard Fitzgerald wrote:
> Add support for using ALSA controls to trigger a factory calibration.
> This is protected by a new Kconfig option so that it is only available
> if explicitly enabled in the kernel. By default it is not enabled.

> Factory calibration is normally done through debugfs files.
> Google have requested that factory calibration can be performed by
> repair shops. These repair shops only have access to the standard
> "user" kernel, which does not include debugfs.

AFAICT this doesn't have an EFI write command like the debugfs code does
(when you write "store_uefi").  I don't mind, just wanted to double
check that this is desirable?

> 
> Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
> ---
> Changes in V2:
> - Rename "Calibrate" control to "Calibrate Switch" to conform to naming
>   convention for boolean controls.
> - Mark "Calibrate Switch" control volatile.
> - Cache the value written to the "CAL_AMBIENT" control so that a read will
>   return the last set value.
> - Return 1 from writing "CAL_AMBIENT" if the value was changed.
> - Return 1 from writing the "Calibrate Switch" control to true because
>   that always causes some activity in the amp.
> - Replace use of the confusing in_range() with normal comparisons against
>   limits and make the limits the same as the control definition.
> - Simplify the code in cs35l56_calibrate_ctl_set(). It's a boolean so the
>   value written to it is either false or true.
> 
>  include/sound/cs35l56.h           |  1 +
>  sound/soc/codecs/Kconfig          | 13 +++++
>  sound/soc/codecs/cs35l56-shared.c |  9 +++
>  sound/soc/codecs/cs35l56.c        | 96 +++++++++++++++++++++++++++++++
>  sound/soc/codecs/cs35l56.h        |  2 +
>  5 files changed, 121 insertions(+)
> 
> diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h
> index 7ca25487030a..c3b10587cb4c 100644
> --- a/include/sound/cs35l56.h
> +++ b/include/sound/cs35l56.h
> @@ -435,6 +435,7 @@ ssize_t cs35l56_cal_data_debugfs_read(struct cs35l56_base *cs35l56_base,
>  ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base,
>  				       const char __user *from, size_t count,
>  				       loff_t *ppos);
> +int cs35l56_factory_calibrate(struct cs35l56_base *cs35l56_base);
>  void cs35l56_create_cal_debugfs(struct cs35l56_base *cs35l56_base,
>  				const struct cs35l56_cal_debugfs_fops *fops);
>  void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base);
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index d6104796db4f..ca3e47db126e 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -921,6 +921,19 @@ config SND_SOC_CS35L56_CAL_SET_CTRL
>  
>  	  If unsure select "N".
>  
> +config SND_SOC_CS35L56_CAL_PERFORM_CTRL
> +	bool "CS35L56 ALSA control to perform factory calibration"
> +	default N
> +	select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON
> +	help
> +	  Allow performing factory calibration data through an ALSA
> +	  control. It is recommended to use the debugfs method instead
> +	  because debugfs has restricted access permissions.
> +
> +	  On most platforms this is not needed.
> +
> +	  If unsure select "N".
> +
>  config SND_SOC_CS35L56_TEST
>  	tristate "KUnit test for Cirrus Logic cs35l56 driver" if !KUNIT_ALL_TESTS
>  	depends on SND_SOC_CS35L56 && KUNIT
> diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
> index af87ebae98cb..e05d975ba794 100644
> --- a/sound/soc/codecs/cs35l56-shared.c
> +++ b/sound/soc/codecs/cs35l56-shared.c
> @@ -1185,6 +1185,15 @@ ssize_t cs35l56_calibrate_debugfs_write(struct cs35l56_base *cs35l56_base,
>  }
>  EXPORT_SYMBOL_NS_GPL(cs35l56_calibrate_debugfs_write, "SND_SOC_CS35L56_SHARED");
>  
> +int cs35l56_factory_calibrate(struct cs35l56_base *cs35l56_base)
> +{
> +	if (!IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_PERFORM_CTRL))
> +		return -ENXIO;
> +
> +	return cs35l56_perform_calibration(cs35l56_base);
> +}
> +EXPORT_SYMBOL_NS_GPL(cs35l56_factory_calibrate, "SND_SOC_CS35L56_SHARED");
> +
>  ssize_t cs35l56_cal_ambient_debugfs_write(struct cs35l56_base *cs35l56_base,
>  					  const char __user *from, size_t count,
>  					  loff_t *ppos)
> diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
> index 9d35797e000a..378017fcea10 100644
> --- a/sound/soc/codecs/cs35l56.c
> +++ b/sound/soc/codecs/cs35l56.c
> @@ -1109,6 +1109,88 @@ static int cs35l56_cal_data_ctl_set(struct snd_kcontrol *kcontrol,
>  	return 1;
>  }
>  
> +static int cs35l56_cal_ambient_ctl_get(struct snd_kcontrol *kcontrol,
> +				       struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
> +	struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
> +
> +	ucontrol->value.integer.value[0] = cs35l56->ambient_ctl_value;
> +
> +	return 0;
> +}
> +
> +static int cs35l56_cal_ambient_ctl_set(struct snd_kcontrol *kcontrol,
> +				       struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
> +	struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
> +	struct snd_soc_dapm_context *dapm;
> +	int temperature = ucontrol->value.integer.value[0];
> +	int ret;
> +
> +	if (temperature == cs35l56->ambient_ctl_value)
> +		return 0;
> +
> +	if ((temperature < 0) || (temperature > 40))
> +		return -EINVAL;
> +
> +	dapm = cs35l56_power_up_for_cal(cs35l56);
> +	if (IS_ERR(dapm))
> +		return PTR_ERR(dapm);
> +
> +	ret = cs_amp_write_ambient_temp(&cs35l56->dsp.cs_dsp,
> +					cs35l56->base.calibration_controls,
> +					temperature);
> +	cs35l56_power_down_after_cal(cs35l56);
> +
> +	if (ret)
> +		return ret;
> +
> +	cs35l56->ambient_ctl_value = temperature;
> +
> +	return 1;
> +}
> +
> +static int cs35l56_calibrate_ctl_get(struct snd_kcontrol *kcontrol,
> +				     struct snd_ctl_elem_value *ucontrol)
> +{
> +	/*
> +	 * Allow reading because of user-side libraries that assume all
> +	 * controls are readable. But always return false to prevent dumb
> +	 * save-restore tools like alsactl accidentically triggering a
> +	 * factory calibration when they restore.
> +	 */
> +	ucontrol->value.integer.value[0] = 0;
> +
> +	return 0;
> +}
> +
> +static int cs35l56_calibrate_ctl_set(struct snd_kcontrol *kcontrol,
> +				     struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
> +	struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
> +	struct snd_soc_dapm_context *dapm;
> +	int ret;
> +
> +	if (ucontrol->value.integer.value[0] == 0)
> +		return 0;
> +
> +	dapm = cs35l56_power_up_for_cal(cs35l56);
> +	if (IS_ERR(dapm))
> +		return PTR_ERR(dapm);
> +
> +	snd_soc_dapm_mutex_lock(dapm);
> +	ret = cs35l56_factory_calibrate(&cs35l56->base);
> +	snd_soc_dapm_mutex_unlock(dapm);
> +	cs35l56_power_down_after_cal(cs35l56);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 1;
> +}
> +
>  static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = {
>  	SND_SOC_BYTES_E("CAL_DATA", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32),
>  			cs35l56_cal_data_ctl_get, cs35l56_cal_data_ctl_set),
> @@ -1117,6 +1199,14 @@ static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = {
>  			SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
>  };
>  
> +static const struct snd_kcontrol_new cs35l56_cal_perform_controls[] = {
> +	SOC_SINGLE_EXT("CAL_AMBIENT", SND_SOC_NOPM, 0, 40, 0,
> +		       cs35l56_cal_ambient_ctl_get, cs35l56_cal_ambient_ctl_set),
> +	SOC_SINGLE_BOOL_EXT_ACC("Calibrate Switch", 0,
> +				cs35l56_calibrate_ctl_get, cs35l56_calibrate_ctl_set,
> +				SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
> +};
> +
>  VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
>  {
>  	unsigned short vendor, device;
> @@ -1290,6 +1380,12 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
>  						     ARRAY_SIZE(cs35l56_cal_data_restore_controls));
>  	}
>  
> +	if (!ret && IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_PERFORM_CTRL)) {
> +		ret = snd_soc_add_component_controls(component,
> +						     cs35l56_cal_perform_controls,
> +						     ARRAY_SIZE(cs35l56_cal_perform_controls));
> +	}
> +
>  	if (ret)
>  		return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n");
>  
> diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
> index 36d239d571cd..cd71b23b2a3a 100644
> --- a/sound/soc/codecs/cs35l56.h
> +++ b/sound/soc/codecs/cs35l56.h
> @@ -54,6 +54,8 @@ struct cs35l56_private {
>  	bool sysclk_set;
>  	u8 sdw_link_num;
>  	u8 sdw_unique_id;
> +
> +	u8 ambient_ctl_value;
>  };
>  
>  static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_base *cs35l56_base)
> -- 
> 2.47.3
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

  reply	other threads:[~2026-03-25 17:54 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-25 17:08 [PATCH v2 0/2] ASoC: cs35l56: Support for " Richard Fitzgerald
2026-03-25 17:08 ` [PATCH v2 1/2] ASoC: soc.h: Add SOC_SINGLE_BOOL_EXT_ACC() to allow setting access flags Richard Fitzgerald
2026-03-25 17:08 ` [PATCH v2 2/2] ASoC: cs35l56: Allow factory calibration through ALSA controls Richard Fitzgerald
2026-03-25 17:53   ` Mark Brown [this message]
2026-03-26 10:16     ` Richard Fitzgerald
2026-03-26 10:20 ` [PATCH v2 0/2] ASoC: cs35l56: Support for " 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=6e2d8ded-00ce-4148-bd93-aded513b9f30@sirena.org.uk \
    --to=broonie@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=patches@opensource.cirrus.com \
    --cc=rf@opensource.cirrus.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

all inboxes | Powered by JetHome®