summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sound/cs35l56.h1
-rw-r--r--sound/soc/codecs/Kconfig13
-rw-r--r--sound/soc/codecs/cs35l56-shared.c9
-rw-r--r--sound/soc/codecs/cs35l56.c96
-rw-r--r--sound/soc/codecs/cs35l56.h2
5 files changed, 121 insertions, 0 deletions
diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h
index 28f9f5940ab6..4c1969cd84d8 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 adb3fb923be3..8bcffb812828 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -928,6 +928,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 37909a319f88..f03a2b47dc6c 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 691f857d0bd8..9aaff2140bbb 100644
--- a/sound/soc/codecs/cs35l56.h
+++ b/sound/soc/codecs/cs35l56.h
@@ -53,6 +53,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)