Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/hda/codecs/side-codecs/tas2781_hda.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0
2
//
3
// TAS2781 HDA Shared Lib for I2C&SPI driver
4
//
5
// Copyright 2025 Texas Instruments, Inc.
6
//
7
// Author: Shenghao Ding <[email protected]>
8
9
#include <linux/component.h>
10
#include <linux/crc8.h>
11
#include <linux/crc32.h>
12
#include <linux/efi.h>
13
#include <linux/firmware.h>
14
#include <linux/i2c.h>
15
#include <linux/pm_runtime.h>
16
#include <sound/soc.h>
17
#include <sound/tas2781.h>
18
19
#include "tas2781_hda.h"
20
21
#define CALIBRATION_DATA_AREA_NUM 2
22
23
const efi_guid_t tasdev_fct_efi_guid[] = {
24
/* DELL */
25
EFI_GUID(0xcc92382d, 0x6337, 0x41cb, 0xa8, 0x8b, 0x8e, 0xce, 0x74,
26
0x91, 0xea, 0x9f),
27
/* HP */
28
EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a,
29
0xa3, 0x5d, 0xb3),
30
/* LENOVO & OTHERS */
31
EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, 0x09, 0x43, 0xa3, 0xf4,
32
0x31, 0x0a, 0x92),
33
};
34
EXPORT_SYMBOL_NS_GPL(tasdev_fct_efi_guid, "SND_HDA_SCODEC_TAS2781");
35
36
static void tas2781_apply_calib(struct tasdevice_priv *p)
37
{
38
struct calidata *cali_data = &p->cali_data;
39
struct cali_reg *r = &cali_data->cali_reg_array;
40
unsigned char *data = cali_data->data;
41
unsigned int *tmp_val = (unsigned int *)data;
42
unsigned int cali_reg[TASDEV_CALIB_N] = {
43
TASDEVICE_REG(0, 0x17, 0x74),
44
TASDEVICE_REG(0, 0x18, 0x0c),
45
TASDEVICE_REG(0, 0x18, 0x14),
46
TASDEVICE_REG(0, 0x13, 0x70),
47
TASDEVICE_REG(0, 0x18, 0x7c),
48
};
49
unsigned int crc, oft, node_num;
50
unsigned char *buf;
51
int i, j, k, l;
52
53
if (tmp_val[0] == 2781) {
54
/*
55
* New features were added in calibrated Data V3:
56
* 1. Added calibration registers address define in
57
* a node, marked as Device id == 0x80.
58
* New features were added in calibrated Data V2:
59
* 1. Added some the fields to store the link_id and
60
* uniqie_id for multi-link solutions
61
* 2. Support flexible number of devices instead of
62
* fixed one in V1.
63
* Layout of calibrated data V2 in UEFI(total 256 bytes):
64
* ChipID (2781, 4 bytes)
65
* Data-Group-Sum (4 bytes)
66
* TimeStamp of Calibration (4 bytes)
67
* for (i = 0; i < Data-Group-Sum; i++) {
68
* if (Data type != 0x80) (4 bytes)
69
* Calibrated Data of Device #i (20 bytes)
70
* else
71
* Calibration registers address (5*4 = 20 bytes)
72
* # V2: No reg addr in data grp section.
73
* # V3: Normally the last grp is the reg addr.
74
* }
75
* CRC (4 bytes)
76
* Reserved (the rest)
77
*/
78
crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0;
79
80
if (crc != tmp_val[3 + tmp_val[1] * 6]) {
81
cali_data->total_sz = 0;
82
dev_err(p->dev, "%s: CRC error\n", __func__);
83
return;
84
}
85
node_num = tmp_val[1];
86
87
for (j = 0, k = 0; j < node_num; j++) {
88
oft = j * 6 + 3;
89
if (tmp_val[oft] == TASDEV_UEFI_CALI_REG_ADDR_FLG) {
90
for (i = 0; i < TASDEV_CALIB_N; i++) {
91
buf = &data[(oft + i + 1) * 4];
92
cali_reg[i] = TASDEVICE_REG(buf[1],
93
buf[2], buf[3]);
94
}
95
} else {
96
l = j * (cali_data->cali_dat_sz_per_dev + 1);
97
if (k >= p->ndev || l > oft * 4) {
98
dev_err(p->dev, "%s: dev sum error\n",
99
__func__);
100
cali_data->total_sz = 0;
101
return;
102
}
103
104
data[l] = k;
105
oft++;
106
for (i = 0; i < TASDEV_CALIB_N * 4; i++)
107
data[l + i + 1] = data[4 * oft + i];
108
k++;
109
}
110
}
111
} else {
112
/*
113
* Calibration data is in V1 format.
114
* struct cali_data {
115
* char cali_data[20];
116
* }
117
*
118
* struct {
119
* struct cali_data cali_data[4];
120
* int TimeStamp of Calibration (4 bytes)
121
* int CRC (4 bytes)
122
* } ueft;
123
*/
124
crc = crc32(~0, data, 84) ^ ~0;
125
if (crc != tmp_val[21]) {
126
cali_data->total_sz = 0;
127
dev_err(p->dev, "%s: V1 CRC error\n", __func__);
128
return;
129
}
130
131
for (j = p->ndev - 1; j >= 0; j--) {
132
l = j * (cali_data->cali_dat_sz_per_dev + 1);
133
for (i = TASDEV_CALIB_N * 4; i > 0 ; i--)
134
data[l + i] = data[p->index * 5 + i];
135
data[l+i] = j;
136
}
137
}
138
139
if (p->dspbin_typ == TASDEV_BASIC) {
140
r->r0_reg = cali_reg[0];
141
r->invr0_reg = cali_reg[1];
142
r->r0_low_reg = cali_reg[2];
143
r->pow_reg = cali_reg[3];
144
r->tlimit_reg = cali_reg[4];
145
}
146
147
p->is_user_space_calidata = true;
148
cali_data->total_sz = p->ndev * (cali_data->cali_dat_sz_per_dev + 1);
149
}
150
151
/*
152
* Update the calibration data, including speaker impedance, f0, etc,
153
* into algo. Calibrate data is done by manufacturer in the factory.
154
* The data is used by Algo for calculating the speaker temperature,
155
* speaker membrane excursion and f0 in real time during playback.
156
* Calibration data format in EFI is V2, since 2024.
157
*/
158
int tas2781_save_calibration(struct tas2781_hda *hda)
159
{
160
/*
161
* GUID was used for data access in BIOS, it was provided by board
162
* manufactory.
163
*/
164
efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO];
165
/*
166
* Some devices save the calibrated data into L"CALI_DATA",
167
* and others into L"SmartAmpCalibrationData".
168
*/
169
static efi_char16_t *efi_name[CALIBRATION_DATA_AREA_NUM] = {
170
L"CALI_DATA",
171
L"SmartAmpCalibrationData",
172
};
173
struct tasdevice_priv *p = hda->priv;
174
struct calidata *cali_data = &p->cali_data;
175
unsigned long total_sz = 0;
176
unsigned int attr, size;
177
unsigned char *data;
178
efi_status_t status;
179
int i;
180
181
if (hda->catlog_id < LENOVO)
182
efi_guid = tasdev_fct_efi_guid[hda->catlog_id];
183
184
cali_data->cali_dat_sz_per_dev = 20;
185
size = p->ndev * (cali_data->cali_dat_sz_per_dev + 1);
186
for (i = 0; i < CALIBRATION_DATA_AREA_NUM; i++) {
187
/* Get real size of UEFI variable */
188
status = efi.get_variable(efi_name[i], &efi_guid, &attr,
189
&total_sz, NULL);
190
cali_data->total_sz = total_sz > size ? total_sz : size;
191
if (status == EFI_BUFFER_TOO_SMALL) {
192
/* Allocate data buffer of data_size bytes */
193
data = cali_data->data = devm_kzalloc(p->dev,
194
cali_data->total_sz, GFP_KERNEL);
195
if (!data) {
196
status = -ENOMEM;
197
continue;
198
}
199
/* Get variable contents into buffer */
200
status = efi.get_variable(efi_name[i], &efi_guid,
201
&attr, &cali_data->total_sz, data);
202
}
203
/* Check whether get the calibrated data */
204
if (status == EFI_SUCCESS)
205
break;
206
}
207
208
if (status != EFI_SUCCESS) {
209
cali_data->total_sz = 0;
210
return status;
211
}
212
213
tas2781_apply_calib(p);
214
215
return 0;
216
}
217
EXPORT_SYMBOL_NS_GPL(tas2781_save_calibration, "SND_HDA_SCODEC_TAS2781");
218
219
void tas2781_hda_remove(struct device *dev,
220
const struct component_ops *ops)
221
{
222
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
223
224
component_del(tas_hda->dev, ops);
225
226
pm_runtime_get_sync(tas_hda->dev);
227
pm_runtime_disable(tas_hda->dev);
228
229
pm_runtime_put_noidle(tas_hda->dev);
230
231
tasdevice_remove(tas_hda->priv);
232
}
233
EXPORT_SYMBOL_NS_GPL(tas2781_hda_remove, "SND_HDA_SCODEC_TAS2781");
234
235
int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
236
struct snd_ctl_elem_info *uinfo)
237
{
238
struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
239
240
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
241
uinfo->count = 1;
242
uinfo->value.integer.min = 0;
243
uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1;
244
245
return 0;
246
}
247
EXPORT_SYMBOL_NS_GPL(tasdevice_info_profile, "SND_HDA_SCODEC_TAS2781");
248
249
int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
250
struct snd_ctl_elem_info *uinfo)
251
{
252
struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
253
254
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
255
uinfo->count = 1;
256
uinfo->value.integer.min = 0;
257
uinfo->value.integer.max = tas_priv->fmw->nr_programs - 1;
258
259
return 0;
260
}
261
EXPORT_SYMBOL_NS_GPL(tasdevice_info_programs, "SND_HDA_SCODEC_TAS2781");
262
263
int tasdevice_info_config(struct snd_kcontrol *kcontrol,
264
struct snd_ctl_elem_info *uinfo)
265
{
266
struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
267
struct tasdevice_fw *tas_fw = tas_priv->fmw;
268
269
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
270
uinfo->count = 1;
271
uinfo->value.integer.min = 0;
272
uinfo->value.integer.max = tas_fw->nr_configurations - 1;
273
274
return 0;
275
}
276
EXPORT_SYMBOL_NS_GPL(tasdevice_info_config, "SND_HDA_SCODEC_TAS2781");
277
278
int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
279
struct snd_ctl_elem_value *ucontrol)
280
{
281
struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
282
283
ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id;
284
285
dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
286
kcontrol->id.name, tas_priv->rcabin.profile_cfg_id);
287
288
return 0;
289
}
290
EXPORT_SYMBOL_NS_GPL(tasdevice_get_profile_id, "SND_HDA_SCODEC_TAS2781");
291
292
int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
293
struct snd_ctl_elem_value *ucontrol)
294
{
295
struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
296
int profile_id = ucontrol->value.integer.value[0];
297
int max = tas_priv->rcabin.ncfgs - 1;
298
int val, ret = 0;
299
300
val = clamp(profile_id, 0, max);
301
302
guard(mutex)(&tas_priv->codec_lock);
303
304
dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
305
kcontrol->id.name, tas_priv->rcabin.profile_cfg_id, val);
306
307
if (tas_priv->rcabin.profile_cfg_id != val) {
308
tas_priv->rcabin.profile_cfg_id = val;
309
ret = 1;
310
}
311
312
return ret;
313
}
314
EXPORT_SYMBOL_NS_GPL(tasdevice_set_profile_id, "SND_HDA_SCODEC_TAS2781");
315
316
int tasdevice_program_get(struct snd_kcontrol *kcontrol,
317
struct snd_ctl_elem_value *ucontrol)
318
{
319
struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
320
321
ucontrol->value.integer.value[0] = tas_priv->cur_prog;
322
323
dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
324
kcontrol->id.name, tas_priv->cur_prog);
325
326
return 0;
327
}
328
EXPORT_SYMBOL_NS_GPL(tasdevice_program_get, "SND_HDA_SCODEC_TAS2781");
329
330
int tasdevice_program_put(struct snd_kcontrol *kcontrol,
331
struct snd_ctl_elem_value *ucontrol)
332
{
333
struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
334
struct tasdevice_fw *tas_fw = tas_priv->fmw;
335
int nr_program = ucontrol->value.integer.value[0];
336
int max = tas_fw->nr_programs - 1;
337
int val, ret = 0;
338
339
val = clamp(nr_program, 0, max);
340
341
guard(mutex)(&tas_priv->codec_lock);
342
343
dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
344
kcontrol->id.name, tas_priv->cur_prog, val);
345
346
if (tas_priv->cur_prog != val) {
347
tas_priv->cur_prog = val;
348
ret = 1;
349
}
350
351
return ret;
352
}
353
EXPORT_SYMBOL_NS_GPL(tasdevice_program_put, "SND_HDA_SCODEC_TAS2781");
354
355
int tasdevice_config_get(struct snd_kcontrol *kcontrol,
356
struct snd_ctl_elem_value *ucontrol)
357
{
358
struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
359
360
ucontrol->value.integer.value[0] = tas_priv->cur_conf;
361
362
dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
363
kcontrol->id.name, tas_priv->cur_conf);
364
365
return 0;
366
}
367
EXPORT_SYMBOL_NS_GPL(tasdevice_config_get, "SND_HDA_SCODEC_TAS2781");
368
369
int tasdevice_config_put(struct snd_kcontrol *kcontrol,
370
struct snd_ctl_elem_value *ucontrol)
371
{
372
struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
373
struct tasdevice_fw *tas_fw = tas_priv->fmw;
374
int nr_config = ucontrol->value.integer.value[0];
375
int max = tas_fw->nr_configurations - 1;
376
int val, ret = 0;
377
378
val = clamp(nr_config, 0, max);
379
380
guard(mutex)(&tas_priv->codec_lock);
381
382
dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
383
kcontrol->id.name, tas_priv->cur_conf, val);
384
385
if (tas_priv->cur_conf != val) {
386
tas_priv->cur_conf = val;
387
ret = 1;
388
}
389
390
return ret;
391
}
392
EXPORT_SYMBOL_NS_GPL(tasdevice_config_put, "SND_HDA_SCODEC_TAS2781");
393
394
MODULE_DESCRIPTION("TAS2781 HDA Driver");
395
MODULE_LICENSE("GPL");
396
MODULE_AUTHOR("Shenghao Ding, TI, <[email protected]>");
397
398