1 // SPDX-License-Identifier: GPL-2.0-only 1 2 // 3 // Common code for Cirrus Logic Smart Amplifie 4 // 5 // Copyright (C) 2024 Cirrus Logic, Inc. and 6 // Cirrus Logic International Se 7 8 #include <asm/byteorder.h> 9 #include <kunit/static_stub.h> 10 #include <linux/dev_printk.h> 11 #include <linux/efi.h> 12 #include <linux/firmware/cirrus/cs_dsp.h> 13 #include <linux/module.h> 14 #include <linux/slab.h> 15 #include <linux/types.h> 16 #include <sound/cs-amp-lib.h> 17 18 #define CS_AMP_CAL_GUID \ 19 EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0 20 21 #define CS_AMP_CAL_NAME L"CirrusSmartAmpCalibr 22 23 static int cs_amp_write_cal_coeff(struct cs_ds 24 const struct 25 const char * 26 { 27 struct cs_dsp_coeff_ctl *cs_ctl; 28 __be32 beval = cpu_to_be32(val); 29 int ret; 30 31 KUNIT_STATIC_STUB_REDIRECT(cs_amp_writ 32 33 if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { 34 mutex_lock(&dsp->pwr_lock); 35 cs_ctl = cs_dsp_get_ctl(dsp, c 36 ret = cs_dsp_coeff_write_ctrl( 37 mutex_unlock(&dsp->pwr_lock); 38 39 if (ret < 0) { 40 dev_err(dsp->dev, "Fai 41 return ret; 42 } 43 44 return 0; 45 } 46 47 return -ENODEV; 48 } 49 50 static int _cs_amp_write_cal_coeffs(struct cs_ 51 const stru 52 const stru 53 { 54 int ret; 55 56 dev_dbg(dsp->dev, "Calibration: Ambien 57 data->calAmbient, data->calSta 58 59 if (list_empty(&dsp->ctl_list)) { 60 dev_info(dsp->dev, "Calibratio 61 return -ENOENT; 62 } 63 64 ret = cs_amp_write_cal_coeff(dsp, cont 65 if (ret) 66 return ret; 67 68 ret = cs_amp_write_cal_coeff(dsp, cont 69 if (ret) 70 return ret; 71 72 ret = cs_amp_write_cal_coeff(dsp, cont 73 if (ret) 74 return ret; 75 76 ret = cs_amp_write_cal_coeff(dsp, cont 77 if (ret) 78 return ret; 79 80 return 0; 81 } 82 83 /** 84 * cs_amp_write_cal_coeffs - Write calibration 85 * @dsp: Pointer to struct cs_dsp. 86 * @controls: Pointer to definition of firmw 87 * @data: Pointer to calibration data. 88 * 89 * Returns: 0 on success, else negative error 90 */ 91 int cs_amp_write_cal_coeffs(struct cs_dsp *dsp 92 const struct cirru 93 const struct cirru 94 { 95 if (IS_REACHABLE(CONFIG_FW_CS_DSP) || 96 return _cs_amp_write_cal_coeff 97 else 98 return -ENODEV; 99 } 100 EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, 101 102 static efi_status_t cs_amp_get_efi_variable(ef 103 ef 104 un 105 vo 106 { 107 u32 attr; 108 109 KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_ 110 111 if (efi_rt_services_supported(EFI_RT_S 112 return efi.get_variable(name, 113 114 return EFI_NOT_FOUND; 115 } 116 117 static struct cirrus_amp_efi_data *cs_amp_get_ 118 { 119 struct cirrus_amp_efi_data *efi_data; 120 unsigned long data_size = 0; 121 u8 *data; 122 efi_status_t status; 123 int ret; 124 125 /* Get real size of UEFI variable */ 126 status = cs_amp_get_efi_variable(CS_AM 127 if (status != EFI_BUFFER_TOO_SMALL) 128 return ERR_PTR(-ENOENT); 129 130 if (data_size < sizeof(*efi_data)) { 131 dev_err(dev, "EFI cal variable 132 return ERR_PTR(-EOVERFLOW); 133 } 134 135 /* Get variable contents into buffer * 136 data = kmalloc(data_size, GFP_KERNEL); 137 if (!data) 138 return ERR_PTR(-ENOMEM); 139 140 status = cs_amp_get_efi_variable(CS_AM 141 if (status != EFI_SUCCESS) { 142 ret = -EINVAL; 143 goto err; 144 } 145 146 efi_data = (struct cirrus_amp_efi_data 147 dev_dbg(dev, "Calibration: Size=%d, Am 148 149 if ((efi_data->count > 128) || 150 offsetof(struct cirrus_amp_efi_dat 151 dev_err(dev, "EFI cal variable 152 ret = -EOVERFLOW; 153 goto err; 154 } 155 156 return efi_data; 157 158 err: 159 kfree(data); 160 dev_err(dev, "Failed to read calibrati 161 162 return ERR_PTR(ret); 163 } 164 165 static u64 cs_amp_cal_target_u64(const struct 166 { 167 return ((u64)data->calTarget[1] << 32) 168 } 169 170 static int _cs_amp_get_efi_calibration_data(st 171 st 172 { 173 struct cirrus_amp_efi_data *efi_data; 174 struct cirrus_amp_cal_data *cal = NULL 175 int i, ret; 176 177 efi_data = cs_amp_get_cal_efi_buffer(d 178 if (IS_ERR(efi_data)) 179 return PTR_ERR(efi_data); 180 181 if (target_uid) { 182 for (i = 0; i < efi_data->coun 183 u64 cal_target = cs_am 184 185 /* Skip empty entries 186 if (!efi_data->data[i] 187 continue; 188 189 /* Skip entries with u 190 if (cal_target == 0) 191 continue; 192 193 if (cal_target == targ 194 cal = &efi_dat 195 break; 196 } 197 } 198 } 199 200 if (!cal && (amp_index >= 0) && (amp_i 201 (efi_data->data[amp_index].calTime 202 u64 cal_target = cs_amp_cal_ta 203 204 /* 205 * Treat unpopulated cal_targe 206 * If target_uid != 0 we can o 207 * or it didn't match any cal_ 208 * If target_uid == 0 it is a 209 */ 210 if ((cal_target == 0) || (targ 211 cal = &efi_data->data[ 212 else 213 dev_warn(dev, "Calibra 214 } 215 216 if (cal) { 217 memcpy(out_data, cal, sizeof(* 218 ret = 0; 219 } else { 220 dev_warn(dev, "No calibration 221 ret = -ENOENT; 222 } 223 224 kfree(efi_data); 225 226 return ret; 227 } 228 229 /** 230 * cs_amp_get_efi_calibration_data - get an en 231 * @dev: struct device of the caller. 232 * @target_uid: UID to match, or zero to ignor 233 * @amp_index: Entry index to use, or -1 to p 234 * @out_data: struct cirrus_amp_cal_data whe 235 * 236 * This function can perform 3 types of lookup 237 * 238 * (target_uid > 0, amp_index >= 0) 239 * UID search with fallback to using the 240 * Search the calibration data for a non- 241 * target_uid, and if found return that e 242 * [amp_index] has calTarget == 0, return 243 * 244 * (target_uid > 0, amp_index < 0) 245 * UID search only. 246 * Search the calibration data for a non- 247 * target_uid, and if found return that e 248 * 249 * (target_uid == 0, amp_index >= 0) 250 * Array index fetch only. 251 * Return the entry at [amp_index]. 252 * 253 * An array lookup will be skipped if amp_inde 254 * entries in the calibration array, and in th 255 * be -ENOENT. An out-of-range amp_index does 256 * target_uid - it has the same effect as pass 257 * 258 * If the EFI data is too short to be a valid 259 * in the EFI data overflows the actual length 260 * returns -EOVERFLOW. 261 * 262 * Return: 0 if the entry was found, -ENOENT i 263 * -EOVERFLOW if the EFI file is corru 264 */ 265 int cs_amp_get_efi_calibration_data(struct dev 266 struct cir 267 { 268 if (IS_ENABLED(CONFIG_EFI) || IS_ENABL 269 return _cs_amp_get_efi_calibra 270 else 271 return -ENOENT; 272 } 273 EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibratio 274 275 static const struct cs_amp_test_hooks cs_amp_t 276 .get_efi_variable = cs_amp_get_efi_var 277 .write_cal_coeff = cs_amp_write_cal_co 278 }; 279 280 const struct cs_amp_test_hooks * const cs_amp_ 281 PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AM 282 EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, SND_SO 283 284 MODULE_DESCRIPTION("Cirrus Logic amplifier lib 285 MODULE_AUTHOR("Richard Fitzgerald <rf@opensour 286 MODULE_LICENSE("GPL"); 287 MODULE_IMPORT_NS(FW_CS_DSP); 288
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.