History

ASoC: cs35l56: Allow factory calibration through ALSA controls

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.

Richard Fitzgerald committed 7 months ago

Commit ee7d6551 parent 37c277f5 files · +121 −0Browse files

Unified · Split

5 files
  1. include/sound/cs35l56.h +1 −0
  2. sound/soc/codecs/Kconfig +13 −0
  3. sound/soc/codecs/cs35l56-shared.c +9 −0
  4. sound/soc/codecs/cs35l56.c +96 −0
  5. sound/soc/codecs/cs35l56.h +2 −0
include/sound/cs35l56.h+1−0
@@ -435,6 +435,7 @@ ssize_t cs35l56_cal_data_debugfs_read(struct cs35l56_base *cs35l56_base, · 434 unmodified lines
435435 ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base,
436436 const char __user *from, size_t count,
437437 loff_t *ppos);
438+int cs35l56_factory_calibrate(struct cs35l56_base *cs35l56_base);
438439 void cs35l56_create_cal_debugfs(struct cs35l56_base *cs35l56_base,
439440 const struct cs35l56_cal_debugfs_fops *fops);
440441 void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base);
sound/soc/codecs/Kconfig+13−0
@@ -928,6 +928,19 @@ config SND_SOC_CS35L56_CAL_SET_CTRL · 927 unmodified lines
928928
929929 If unsure select "N".
930930
931+config SND_SOC_CS35L56_CAL_PERFORM_CTRL
932+ bool "CS35L56 ALSA control to perform factory calibration"
933+ default N
934+ select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON
935+ help
936+ Allow performing factory calibration data through an ALSA
937+ control. It is recommended to use the debugfs method instead
938+ because debugfs has restricted access permissions.
939+
940+ On most platforms this is not needed.
941+
942+ If unsure select "N".
943+
931944 config SND_SOC_CS35L56_TEST
932945 tristate "KUnit test for Cirrus Logic cs35l56 driver" if !KUNIT_ALL_TESTS
933946 depends on SND_SOC_CS35L56 && KUNIT
sound/soc/codecs/cs35l56-shared.c+9−0
@@ -1185,6 +1185,15 @@ ssize_t cs35l56_calibrate_debugfs_write(struct cs35l56_base *cs35l56_base, · 1184 unmodified lines
11851185 }
11861186 EXPORT_SYMBOL_NS_GPL(cs35l56_calibrate_debugfs_write, "SND_SOC_CS35L56_SHARED");
11871187
1188+int cs35l56_factory_calibrate(struct cs35l56_base *cs35l56_base)
1189+{
1190+ if (!IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_PERFORM_CTRL))
1191+ return -ENXIO;
1192+
1193+ return cs35l56_perform_calibration(cs35l56_base);
1194+}
1195+EXPORT_SYMBOL_NS_GPL(cs35l56_factory_calibrate, "SND_SOC_CS35L56_SHARED");
1196+
11881197 ssize_t cs35l56_cal_ambient_debugfs_write(struct cs35l56_base *cs35l56_base,
11891198 const char __user *from, size_t count,
11901199 loff_t *ppos)
sound/soc/codecs/cs35l56.c+96−0
@@ -1109,6 +1109,88 @@ static int cs35l56_cal_data_ctl_set(struct snd_kcontrol *kcontrol, · 1108 unmodified lines
11091109 return 1;
11101110 }
11111111
1112+static int cs35l56_cal_ambient_ctl_get(struct snd_kcontrol *kcontrol,
1113+ struct snd_ctl_elem_value *ucontrol)
1114+{
1115+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
1116+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
1117+
1118+ ucontrol->value.integer.value[0] = cs35l56->ambient_ctl_value;
1119+
1120+ return 0;
1121+}
1122+
1123+static int cs35l56_cal_ambient_ctl_set(struct snd_kcontrol *kcontrol,
1124+ struct snd_ctl_elem_value *ucontrol)
1125+{
1126+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
1127+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
1128+ struct snd_soc_dapm_context *dapm;
1129+ int temperature = ucontrol->value.integer.value[0];
1130+ int ret;
1131+
1132+ if (temperature == cs35l56->ambient_ctl_value)
1133+ return 0;
1134+
1135+ if ((temperature < 0) || (temperature > 40))
1136+ return -EINVAL;
1137+
1138+ dapm = cs35l56_power_up_for_cal(cs35l56);
1139+ if (IS_ERR(dapm))
1140+ return PTR_ERR(dapm);
1141+
1142+ ret = cs_amp_write_ambient_temp(&cs35l56->dsp.cs_dsp,
1143+ cs35l56->base.calibration_controls,
1144+ temperature);
1145+ cs35l56_power_down_after_cal(cs35l56);
1146+
1147+ if (ret)
1148+ return ret;
1149+
1150+ cs35l56->ambient_ctl_value = temperature;
1151+
1152+ return 1;
1153+}
1154+
1155+static int cs35l56_calibrate_ctl_get(struct snd_kcontrol *kcontrol,
1156+ struct snd_ctl_elem_value *ucontrol)
1157+{
1158+ /*
1159+ * Allow reading because of user-side libraries that assume all
1160+ * controls are readable. But always return false to prevent dumb
1161+ * save-restore tools like alsactl accidentically triggering a
1162+ * factory calibration when they restore.
1163+ */
1164+ ucontrol->value.integer.value[0] = 0;
1165+
1166+ return 0;
1167+}
1168+
1169+static int cs35l56_calibrate_ctl_set(struct snd_kcontrol *kcontrol,
1170+ struct snd_ctl_elem_value *ucontrol)
1171+{
1172+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
1173+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
1174+ struct snd_soc_dapm_context *dapm;
1175+ int ret;
1176+
1177+ if (ucontrol->value.integer.value[0] == 0)
1178+ return 0;
1179+
1180+ dapm = cs35l56_power_up_for_cal(cs35l56);
1181+ if (IS_ERR(dapm))
1182+ return PTR_ERR(dapm);
1183+
1184+ snd_soc_dapm_mutex_lock(dapm);
1185+ ret = cs35l56_factory_calibrate(&cs35l56->base);
1186+ snd_soc_dapm_mutex_unlock(dapm);
1187+ cs35l56_power_down_after_cal(cs35l56);
1188+ if (ret < 0)
1189+ return ret;
1190+
1191+ return 1;
1192+}
1193+
11121194 static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = {
11131195 SND_SOC_BYTES_E("CAL_DATA", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32),
11141196 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[] = { · 2 unmodified lines
11171199 SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
11181200 };
11191201
1202+static const struct snd_kcontrol_new cs35l56_cal_perform_controls[] = {
1203+ SOC_SINGLE_EXT("CAL_AMBIENT", SND_SOC_NOPM, 0, 40, 0,
1204+ cs35l56_cal_ambient_ctl_get, cs35l56_cal_ambient_ctl_set),
1205+ SOC_SINGLE_BOOL_EXT_ACC("Calibrate Switch", 0,
1206+ cs35l56_calibrate_ctl_get, cs35l56_calibrate_ctl_set,
1207+ SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
1208+};
1209+
11201210 VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
11211211 {
11221212 unsigned short vendor, device;
@@ -1290,6 +1380,12 @@ static int cs35l56_component_probe(struct snd_soc_component *component) · 167 unmodified lines
12901380 ARRAY_SIZE(cs35l56_cal_data_restore_controls));
12911381 }
12921382
1383+ if (!ret && IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_PERFORM_CTRL)) {
1384+ ret = snd_soc_add_component_controls(component,
1385+ cs35l56_cal_perform_controls,
1386+ ARRAY_SIZE(cs35l56_cal_perform_controls));
1387+ }
1388+
12931389 if (ret)
12941390 return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n");
12951391
sound/soc/codecs/cs35l56.h+2−0
@@ -53,6 +53,8 @@ struct cs35l56_private { · 52 unmodified lines
5353 bool sysclk_set;
5454 u8 sdw_link_num;
5555 u8 sdw_unique_id;
56+
57+ u8 ambient_ctl_value;
5658 };
5759
5860 static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_base *cs35l56_base)