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 restoring calibration on

Merge series from Richard Fitzgerald <rf@opensource.cirrus.com>:

These two patches add ALSA controls to support restoring factory calibration
during OS boot on ChromeOS.

ChromeOS applies calibration during boot using a process that has restricted
access permissions. This process needs ALSA controls for all settings that
it must restore.

+159 -2
+11
include/sound/cs35l56.h
··· 16 16 #include <linux/spi/spi.h> 17 17 #include <sound/cs-amp-lib.h> 18 18 19 + struct snd_ctl_elem_value; 20 + 19 21 #define CS35L56_DEVID 0x0000000 20 22 #define CS35L56_REVID 0x0000004 21 23 #define CS35L56_RELID 0x000000C ··· 270 268 #define CS35L56_CAL_STATUS_SUCCESS 1 271 269 #define CS35L56_CAL_STATUS_OUT_OF_RANGE 3 272 270 271 + #define CS35L56_CAL_SET_STATUS_UNKNOWN 0 272 + #define CS35L56_CAL_SET_STATUS_DEFAULT 1 273 + #define CS35L56_CAL_SET_STATUS_SET 2 274 + 273 275 #define CS35L56_CONTROL_PORT_READY_US 2200 274 276 #define CS35L56_HALO_STATE_POLL_US 1000 275 277 #define CS35L56_HALO_STATE_TIMEOUT_US 250000 ··· 369 363 extern const struct regmap_config cs35l63_regmap_sdw; 370 364 371 365 extern const struct cirrus_amp_cal_controls cs35l56_calibration_controls; 366 + extern const char * const cs35l56_cal_set_status_text[3]; 372 367 373 368 extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC]; 374 369 extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC]; ··· 388 381 int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire); 389 382 void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp); 390 383 int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base); 384 + int cs35l56_stash_calibration(struct cs35l56_base *cs35l56_base, 385 + const struct cirrus_amp_cal_data *data); 391 386 ssize_t cs35l56_calibrate_debugfs_write(struct cs35l56_base *cs35l56_base, 392 387 const char __user *from, size_t count, 393 388 loff_t *ppos); ··· 405 396 void cs35l56_create_cal_debugfs(struct cs35l56_base *cs35l56_base, 406 397 const struct cs35l56_cal_debugfs_fops *fops); 407 398 void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base); 399 + int cs35l56_cal_set_status_get(struct cs35l56_base *cs35l56_base, 400 + struct snd_ctl_elem_value *uvalue); 408 401 int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, 409 402 bool *fw_missing, unsigned int *fw_version); 410 403 void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
+14
sound/soc/codecs/Kconfig
··· 913 913 for factory calibration. 914 914 915 915 If unsure select "N". 916 + 917 + config SND_SOC_CS35L56_CAL_SET_CTRL 918 + bool "CS35L56 ALSA control to restore factory calibration" 919 + default N 920 + select SND_SOC_CS35L56_CAL_SYSFS_COMMON 921 + help 922 + Allow restoring factory calibration data through an ALSA 923 + control. This is only needed on platforms without UEFI or 924 + some other method of non-volatile storage that the driver 925 + can access directly. 926 + 927 + On most platforms this is not needed. 928 + 929 + If unsure select "N". 916 930 endmenu 917 931 918 932 config SND_SOC_CS40L50
+51 -2
sound/soc/codecs/cs35l56-shared.c
··· 962 962 } 963 963 EXPORT_SYMBOL_NS_GPL(cs35l56_get_calibration, "SND_SOC_CS35L56_SHARED"); 964 964 965 - static int cs35l56_stash_calibration(struct cs35l56_base *cs35l56_base, 966 - const struct cirrus_amp_cal_data *data) 965 + int cs35l56_stash_calibration(struct cs35l56_base *cs35l56_base, 966 + const struct cirrus_amp_cal_data *data) 967 967 { 968 968 969 969 /* Ignore if it is empty */ ··· 980 980 981 981 return 0; 982 982 } 983 + EXPORT_SYMBOL_NS_GPL(cs35l56_stash_calibration, "SND_SOC_CS35L56_SHARED"); 983 984 984 985 static int cs35l56_perform_calibration(struct cs35l56_base *cs35l56_base) 985 986 { ··· 1262 1261 debugfs_remove_recursive(cs35l56_base->debugfs); 1263 1262 } 1264 1263 EXPORT_SYMBOL_NS_GPL(cs35l56_remove_cal_debugfs, "SND_SOC_CS35L56_SHARED"); 1264 + 1265 + const char * const cs35l56_cal_set_status_text[] = { 1266 + "Unknown", "Default", "Set", 1267 + }; 1268 + EXPORT_SYMBOL_NS_GPL(cs35l56_cal_set_status_text, "SND_SOC_CS35L56_SHARED"); 1269 + 1270 + int cs35l56_cal_set_status_get(struct cs35l56_base *cs35l56_base, 1271 + struct snd_ctl_elem_value *uvalue) 1272 + { 1273 + struct cs_dsp *dsp = cs35l56_base->dsp; 1274 + __be32 cal_set_status_be; 1275 + int alg_id; 1276 + int ret; 1277 + 1278 + switch (cs35l56_base->type) { 1279 + case 0x54: 1280 + case 0x56: 1281 + case 0x57: 1282 + alg_id = 0x9f210; 1283 + break; 1284 + default: 1285 + alg_id = 0xbf210; 1286 + break; 1287 + } 1288 + 1289 + scoped_guard(mutex, &dsp->pwr_lock) { 1290 + ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(dsp, 1291 + "CAL_SET_STATUS", 1292 + WMFW_ADSP2_YM, alg_id), 1293 + 0, &cal_set_status_be, 1294 + sizeof(cal_set_status_be)); 1295 + } 1296 + if (ret) { 1297 + uvalue->value.enumerated.item[0] = CS35L56_CAL_SET_STATUS_UNKNOWN; 1298 + return 0; 1299 + } 1300 + 1301 + switch (be32_to_cpu(cal_set_status_be)) { 1302 + case CS35L56_CAL_SET_STATUS_DEFAULT: 1303 + case CS35L56_CAL_SET_STATUS_SET: 1304 + uvalue->value.enumerated.item[0] = be32_to_cpu(cal_set_status_be); 1305 + return 0; 1306 + default: 1307 + uvalue->value.enumerated.item[0] = CS35L56_CAL_SET_STATUS_UNKNOWN; 1308 + return 0; 1309 + } 1310 + } 1311 + EXPORT_SYMBOL_NS_GPL(cs35l56_cal_set_status_get, "SND_SOC_CS35L56_SHARED"); 1265 1312 1266 1313 int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, 1267 1314 bool *fw_missing, unsigned int *fw_version)
+83
sound/soc/codecs/cs35l56.c
··· 66 66 67 67 static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0); 68 68 69 + static SOC_ENUM_SINGLE_DECL(cs35l56_cal_set_status_enum, SND_SOC_NOPM, 0, 70 + cs35l56_cal_set_status_text); 71 + 72 + static int cs35l56_cal_set_status_ctl_get(struct snd_kcontrol *kcontrol, 73 + struct snd_ctl_elem_value *ucontrol) 74 + { 75 + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 76 + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); 77 + 78 + return cs35l56_cal_set_status_get(&cs35l56->base, ucontrol); 79 + } 80 + 69 81 static const struct snd_kcontrol_new cs35l56_controls[] = { 70 82 SOC_SINGLE_EXT("Speaker Switch", 71 83 CS35L56_MAIN_RENDER_USER_MUTE, 0, 1, 1, ··· 95 83 SOC_SINGLE_EXT("Posture Number", CS35L56_MAIN_POSTURE_NUMBER, 96 84 0, 255, 0, 97 85 cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), 86 + SOC_ENUM_EXT("CAL_SET_STATUS", cs35l56_cal_set_status_enum, 87 + cs35l56_cal_set_status_ctl_get, NULL), 98 88 }; 99 89 100 90 static const struct snd_kcontrol_new cs35l63_controls[] = { ··· 116 102 SOC_SINGLE_EXT("Posture Number", CS35L63_MAIN_POSTURE_NUMBER, 117 103 0, 255, 0, 118 104 cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), 105 + SOC_ENUM_EXT("CAL_SET_STATUS", cs35l56_cal_set_status_enum, 106 + cs35l56_cal_set_status_ctl_get, NULL), 119 107 }; 120 108 121 109 static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum, ··· 1040 1024 }, 1041 1025 }; 1042 1026 1027 + static int cs35l56_cal_data_rb_ctl_get(struct snd_kcontrol *kcontrol, 1028 + struct snd_ctl_elem_value *ucontrol) 1029 + { 1030 + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 1031 + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); 1032 + 1033 + if (!cs35l56->base.cal_data_valid) 1034 + return -ENODATA; 1035 + 1036 + memcpy(ucontrol->value.bytes.data, &cs35l56->base.cal_data, 1037 + sizeof(cs35l56->base.cal_data)); 1038 + 1039 + return 0; 1040 + } 1041 + 1042 + static int cs35l56_cal_data_ctl_get(struct snd_kcontrol *kcontrol, 1043 + struct snd_ctl_elem_value *ucontrol) 1044 + { 1045 + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 1046 + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); 1047 + 1048 + /* 1049 + * This control is write-only but mixer libraries often try to read 1050 + * a control before writing it. So we have to implement read. 1051 + * Return zeros so a write of valid data will always be a change 1052 + * from its "current value". 1053 + */ 1054 + memset(ucontrol->value.bytes.data, 0, sizeof(cs35l56->base.cal_data)); 1055 + 1056 + return 0; 1057 + } 1058 + 1059 + static int cs35l56_cal_data_ctl_set(struct snd_kcontrol *kcontrol, 1060 + struct snd_ctl_elem_value *ucontrol) 1061 + { 1062 + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 1063 + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); 1064 + const struct cirrus_amp_cal_data *cal_data = (const void *)ucontrol->value.bytes.data; 1065 + int ret; 1066 + 1067 + if (cs35l56->base.cal_data_valid) 1068 + return -EACCES; 1069 + 1070 + ret = cs35l56_stash_calibration(&cs35l56->base, cal_data); 1071 + if (ret) 1072 + return ret; 1073 + 1074 + ret = cs35l56_new_cal_data_apply(cs35l56); 1075 + if (ret < 0) 1076 + return ret; 1077 + 1078 + return 1; 1079 + } 1080 + 1081 + static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = { 1082 + SND_SOC_BYTES_E("CAL_DATA", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32), 1083 + cs35l56_cal_data_ctl_get, cs35l56_cal_data_ctl_set), 1084 + SND_SOC_BYTES_E("CAL_DATA_RB", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32), 1085 + cs35l56_cal_data_rb_ctl_get, NULL), 1086 + }; 1087 + 1043 1088 static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) 1044 1089 { 1045 1090 if (cs35l56->dsp.fwf_suffix) ··· 1193 1116 default: 1194 1117 ret = -ENODEV; 1195 1118 break; 1119 + } 1120 + 1121 + if (!ret && IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_SET_CTRL)) { 1122 + ret = snd_soc_add_component_controls(component, 1123 + cs35l56_cal_data_restore_controls, 1124 + ARRAY_SIZE(cs35l56_cal_data_restore_controls)); 1196 1125 } 1197 1126 1198 1127 if (ret)