Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: cs35l56: Support for factory calibration through ALSA controls

Richard Fitzgerald <rf@opensource.cirrus.com> says:

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.

Patch #1 adds a new control definition macro to create a boolean control
with specified access permissions. (new in V2)

Patch #2 is the implementation in the cs35l56 driver.

+127
+1
include/sound/cs35l56.h
··· 435 435 ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base, 436 436 const char __user *from, size_t count, 437 437 loff_t *ppos); 438 + int cs35l56_factory_calibrate(struct cs35l56_base *cs35l56_base); 438 439 void cs35l56_create_cal_debugfs(struct cs35l56_base *cs35l56_base, 439 440 const struct cs35l56_cal_debugfs_fops *fops); 440 441 void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base);
+6
include/sound/soc.h
··· 311 311 .info = snd_soc_info_bool_ext, \ 312 312 .get = xhandler_get, .put = xhandler_put, \ 313 313 .private_value = xdata } 314 + #define SOC_SINGLE_BOOL_EXT_ACC(xname, xdata, xhandler_get, xhandler_put, xaccess) \ 315 + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 316 + .access = xaccess, \ 317 + .info = snd_soc_info_bool_ext, \ 318 + .get = xhandler_get, .put = xhandler_put, \ 319 + .private_value = xdata } 314 320 #define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ 315 321 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 316 322 .info = snd_soc_info_enum_double, \
+13
sound/soc/codecs/Kconfig
··· 921 921 922 922 If unsure select "N". 923 923 924 + config SND_SOC_CS35L56_CAL_PERFORM_CTRL 925 + bool "CS35L56 ALSA control to perform factory calibration" 926 + default N 927 + select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON 928 + help 929 + Allow performing factory calibration data through an ALSA 930 + control. It is recommended to use the debugfs method instead 931 + because debugfs has restricted access permissions. 932 + 933 + On most platforms this is not needed. 934 + 935 + If unsure select "N". 936 + 924 937 config SND_SOC_CS35L56_TEST 925 938 tristate "KUnit test for Cirrus Logic cs35l56 driver" if !KUNIT_ALL_TESTS 926 939 depends on SND_SOC_CS35L56 && KUNIT
+9
sound/soc/codecs/cs35l56-shared.c
··· 1185 1185 } 1186 1186 EXPORT_SYMBOL_NS_GPL(cs35l56_calibrate_debugfs_write, "SND_SOC_CS35L56_SHARED"); 1187 1187 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 + 1188 1197 ssize_t cs35l56_cal_ambient_debugfs_write(struct cs35l56_base *cs35l56_base, 1189 1198 const char __user *from, size_t count, 1190 1199 loff_t *ppos)
+96
sound/soc/codecs/cs35l56.c
··· 1109 1109 return 1; 1110 1110 } 1111 1111 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 + 1112 1194 static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = { 1113 1195 SND_SOC_BYTES_E("CAL_DATA", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32), 1114 1196 cs35l56_cal_data_ctl_get, cs35l56_cal_data_ctl_set), 1115 1197 SND_SOC_BYTES_E_ACC("CAL_DATA_RB", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32), 1116 1198 cs35l56_cal_data_rb_ctl_get, NULL, 1117 1199 SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE), 1200 + }; 1201 + 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), 1118 1208 }; 1119 1209 1120 1210 VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) ··· 1378 1288 ret = snd_soc_add_component_controls(component, 1379 1289 cs35l56_cal_data_restore_controls, 1380 1290 ARRAY_SIZE(cs35l56_cal_data_restore_controls)); 1291 + } 1292 + 1293 + if (!ret && IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_PERFORM_CTRL)) { 1294 + ret = snd_soc_add_component_controls(component, 1295 + cs35l56_cal_perform_controls, 1296 + ARRAY_SIZE(cs35l56_cal_perform_controls)); 1381 1297 } 1382 1298 1383 1299 if (ret)
+2
sound/soc/codecs/cs35l56.h
··· 54 54 bool sysclk_set; 55 55 u8 sdw_link_num; 56 56 u8 sdw_unique_id; 57 + 58 + u8 ambient_ctl_value; 57 59 }; 58 60 59 61 static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_base *cs35l56_base)