|
18 | 18 |
|
19 | 19 | #include "tas2781_hda.h"
|
20 | 20 |
|
| 21 | +const efi_guid_t tasdev_fct_efi_guid[] = { |
| 22 | + /* DELL */ |
| 23 | + EFI_GUID(0xcc92382d, 0x6337, 0x41cb, 0xa8, 0x8b, 0x8e, 0xce, 0x74, |
| 24 | + 0x91, 0xea, 0x9f), |
| 25 | + /* HP */ |
| 26 | + EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, |
| 27 | + 0xa3, 0x5d, 0xb3), |
| 28 | + /* LENOVO & OTHERS */ |
| 29 | + EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, 0x09, 0x43, 0xa3, 0xf4, |
| 30 | + 0x31, 0x0a, 0x92), |
| 31 | +}; |
| 32 | +EXPORT_SYMBOL_NS_GPL(tasdev_fct_efi_guid, "SND_HDA_SCODEC_TAS2781"); |
| 33 | + |
| 34 | +static void tas2781_apply_calib(struct tasdevice_priv *p) |
| 35 | +{ |
| 36 | + struct calidata *cali_data = &p->cali_data; |
| 37 | + struct cali_reg *r = &cali_data->cali_reg_array; |
| 38 | + unsigned char *data = cali_data->data; |
| 39 | + unsigned int *tmp_val = (unsigned int *)data; |
| 40 | + unsigned int cali_reg[TASDEV_CALIB_N] = { |
| 41 | + TASDEVICE_REG(0, 0x17, 0x74), |
| 42 | + TASDEVICE_REG(0, 0x18, 0x0c), |
| 43 | + TASDEVICE_REG(0, 0x18, 0x14), |
| 44 | + TASDEVICE_REG(0, 0x13, 0x70), |
| 45 | + TASDEVICE_REG(0, 0x18, 0x7c), |
| 46 | + }; |
| 47 | + unsigned int crc, oft; |
| 48 | + unsigned char *buf; |
| 49 | + int i, j, k, l; |
| 50 | + |
| 51 | + if (tmp_val[0] == 2781) { |
| 52 | + /* |
| 53 | + * New features were added in calibrated Data V3: |
| 54 | + * 1. Added calibration registers address define in |
| 55 | + * a node, marked as Device id == 0x80. |
| 56 | + * New features were added in calibrated Data V2: |
| 57 | + * 1. Added some the fields to store the link_id and |
| 58 | + * uniqie_id for multi-link solutions |
| 59 | + * 2. Support flexible number of devices instead of |
| 60 | + * fixed one in V1. |
| 61 | + * Layout of calibrated data V2 in UEFI(total 256 bytes): |
| 62 | + * ChipID (2781, 4 bytes) |
| 63 | + * Data-Group-Sum (4 bytes) |
| 64 | + * TimeStamp of Calibration (4 bytes) |
| 65 | + * for (i = 0; i < Data-Group-Sum; i++) { |
| 66 | + * if (Data type != 0x80) (4 bytes) |
| 67 | + * Calibrated Data of Device #i (20 bytes) |
| 68 | + * else |
| 69 | + * Calibration registers address (5*4 = 20 bytes) |
| 70 | + * # V2: No reg addr in data grp section. |
| 71 | + * # V3: Normally the last grp is the reg addr. |
| 72 | + * } |
| 73 | + * CRC (4 bytes) |
| 74 | + * Reserved (the rest) |
| 75 | + */ |
| 76 | + crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0; |
| 77 | + |
| 78 | + if (crc != tmp_val[3 + tmp_val[1] * 6]) { |
| 79 | + cali_data->total_sz = 0; |
| 80 | + dev_err(p->dev, "%s: CRC error\n", __func__); |
| 81 | + return; |
| 82 | + } |
| 83 | + |
| 84 | + for (j = 0, k = 0; j < tmp_val[1]; j++) { |
| 85 | + oft = j * 6 + 3; |
| 86 | + if (tmp_val[oft] == TASDEV_UEFI_CALI_REG_ADDR_FLG) { |
| 87 | + for (i = 0; i < TASDEV_CALIB_N; i++) { |
| 88 | + buf = &data[(oft + i + 1) * 4]; |
| 89 | + cali_reg[i] = TASDEVICE_REG(buf[1], |
| 90 | + buf[2], buf[3]); |
| 91 | + } |
| 92 | + } else { |
| 93 | + l = j * (cali_data->cali_dat_sz_per_dev + 1); |
| 94 | + if (k >= p->ndev || l > oft * 4) { |
| 95 | + dev_err(p->dev, "%s: dev sum error\n", |
| 96 | + __func__); |
| 97 | + cali_data->total_sz = 0; |
| 98 | + return; |
| 99 | + } |
| 100 | + |
| 101 | + data[l] = k; |
| 102 | + for (i = 0; i < TASDEV_CALIB_N * 4; i++) |
| 103 | + data[l + i] = data[4 * oft + i]; |
| 104 | + k++; |
| 105 | + } |
| 106 | + } |
| 107 | + } else { |
| 108 | + /* |
| 109 | + * Calibration data is in V1 format. |
| 110 | + * struct cali_data { |
| 111 | + * char cali_data[20]; |
| 112 | + * } |
| 113 | + * |
| 114 | + * struct { |
| 115 | + * struct cali_data cali_data[4]; |
| 116 | + * int TimeStamp of Calibration (4 bytes) |
| 117 | + * int CRC (4 bytes) |
| 118 | + * } ueft; |
| 119 | + */ |
| 120 | + crc = crc32(~0, data, 84) ^ ~0; |
| 121 | + if (crc != tmp_val[21]) { |
| 122 | + cali_data->total_sz = 0; |
| 123 | + dev_err(p->dev, "%s: V1 CRC error\n", __func__); |
| 124 | + return; |
| 125 | + } |
| 126 | + |
| 127 | + for (j = p->ndev - 1; j >= 0; j--) { |
| 128 | + l = j * (cali_data->cali_dat_sz_per_dev + 1); |
| 129 | + for (i = TASDEV_CALIB_N * 4; i > 0 ; i--) |
| 130 | + data[l + i] = data[p->index * 5 + i]; |
| 131 | + data[l+i] = j; |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + if (p->dspbin_typ == TASDEV_BASIC) { |
| 136 | + r->r0_reg = cali_reg[0]; |
| 137 | + r->invr0_reg = cali_reg[1]; |
| 138 | + r->r0_low_reg = cali_reg[2]; |
| 139 | + r->pow_reg = cali_reg[3]; |
| 140 | + r->tlimit_reg = cali_reg[4]; |
| 141 | + } |
| 142 | + |
| 143 | + p->is_user_space_calidata = true; |
| 144 | + cali_data->total_sz = p->ndev * (cali_data->cali_dat_sz_per_dev + 1); |
| 145 | +} |
| 146 | + |
| 147 | +/* |
| 148 | + * Update the calibration data, including speaker impedance, f0, etc, |
| 149 | + * into algo. Calibrate data is done by manufacturer in the factory. |
| 150 | + * The data is used by Algo for calculating the speaker temperature, |
| 151 | + * speaker membrane excursion and f0 in real time during playback. |
| 152 | + * Calibration data format in EFI is V2, since 2024. |
| 153 | + */ |
| 154 | +int tas2781_save_calibration(struct tas2781_hda *hda) |
| 155 | +{ |
| 156 | + /* |
| 157 | + * GUID was used for data access in BIOS, it was provided by board |
| 158 | + * manufactory. |
| 159 | + */ |
| 160 | + efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO]; |
| 161 | + static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME; |
| 162 | + struct tasdevice_priv *p = hda->priv; |
| 163 | + struct calidata *cali_data = &p->cali_data; |
| 164 | + unsigned long total_sz = 0; |
| 165 | + unsigned int attr, size; |
| 166 | + unsigned char *data; |
| 167 | + efi_status_t status; |
| 168 | + |
| 169 | + if (hda->catlog_id < LENOVO) |
| 170 | + efi_guid = tasdev_fct_efi_guid[hda->catlog_id]; |
| 171 | + |
| 172 | + cali_data->cali_dat_sz_per_dev = 20; |
| 173 | + size = p->ndev * (cali_data->cali_dat_sz_per_dev + 1); |
| 174 | + /* Get real size of UEFI variable */ |
| 175 | + status = efi.get_variable(efi_name, &efi_guid, &attr, &total_sz, NULL); |
| 176 | + cali_data->total_sz = total_sz > size ? total_sz : size; |
| 177 | + if (status == EFI_BUFFER_TOO_SMALL) { |
| 178 | + /* Allocate data buffer of data_size bytes */ |
| 179 | + data = p->cali_data.data = devm_kzalloc(p->dev, |
| 180 | + p->cali_data.total_sz, GFP_KERNEL); |
| 181 | + if (!data) { |
| 182 | + p->cali_data.total_sz = 0; |
| 183 | + return -ENOMEM; |
| 184 | + } |
| 185 | + /* Get variable contents into buffer */ |
| 186 | + status = efi.get_variable(efi_name, &efi_guid, &attr, |
| 187 | + &p->cali_data.total_sz, data); |
| 188 | + } |
| 189 | + if (status != EFI_SUCCESS) { |
| 190 | + p->cali_data.total_sz = 0; |
| 191 | + return status; |
| 192 | + } |
| 193 | + |
| 194 | + tas2781_apply_calib(p); |
| 195 | + |
| 196 | + return 0; |
| 197 | +} |
| 198 | +EXPORT_SYMBOL_NS_GPL(tas2781_save_calibration, "SND_HDA_SCODEC_TAS2781"); |
| 199 | + |
21 | 200 | void tas2781_hda_remove(struct device *dev,
|
22 | 201 | const struct component_ops *ops)
|
23 | 202 | {
|
|
0 commit comments