128a09d9eSShenghao Ding // SPDX-License-Identifier: GPL-2.0
228a09d9eSShenghao Ding //
328a09d9eSShenghao Ding // TAS2781 HDA Shared Lib for I2C&SPI driver
428a09d9eSShenghao Ding //
528a09d9eSShenghao Ding // Copyright 2025 Texas Instruments, Inc.
628a09d9eSShenghao Ding //
728a09d9eSShenghao Ding // Author: Shenghao Ding <shenghao-ding@ti.com>
828a09d9eSShenghao Ding
928a09d9eSShenghao Ding #include <linux/component.h>
1028a09d9eSShenghao Ding #include <linux/crc8.h>
1128a09d9eSShenghao Ding #include <linux/crc32.h>
1228a09d9eSShenghao Ding #include <linux/efi.h>
1328a09d9eSShenghao Ding #include <linux/firmware.h>
1428a09d9eSShenghao Ding #include <linux/i2c.h>
1528a09d9eSShenghao Ding #include <linux/pm_runtime.h>
1628a09d9eSShenghao Ding #include <sound/soc.h>
1728a09d9eSShenghao Ding #include <sound/tas2781.h>
1828a09d9eSShenghao Ding
1928a09d9eSShenghao Ding #include "tas2781_hda.h"
2028a09d9eSShenghao Ding
21*4fe23851SShenghao Ding const efi_guid_t tasdev_fct_efi_guid[] = {
22*4fe23851SShenghao Ding /* DELL */
23*4fe23851SShenghao Ding EFI_GUID(0xcc92382d, 0x6337, 0x41cb, 0xa8, 0x8b, 0x8e, 0xce, 0x74,
24*4fe23851SShenghao Ding 0x91, 0xea, 0x9f),
25*4fe23851SShenghao Ding /* HP */
26*4fe23851SShenghao Ding EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a,
27*4fe23851SShenghao Ding 0xa3, 0x5d, 0xb3),
28*4fe23851SShenghao Ding /* LENOVO & OTHERS */
29*4fe23851SShenghao Ding EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, 0x09, 0x43, 0xa3, 0xf4,
30*4fe23851SShenghao Ding 0x31, 0x0a, 0x92),
31*4fe23851SShenghao Ding };
32*4fe23851SShenghao Ding EXPORT_SYMBOL_NS_GPL(tasdev_fct_efi_guid, "SND_HDA_SCODEC_TAS2781");
33*4fe23851SShenghao Ding
tas2781_apply_calib(struct tasdevice_priv * p)34*4fe23851SShenghao Ding static void tas2781_apply_calib(struct tasdevice_priv *p)
35*4fe23851SShenghao Ding {
36*4fe23851SShenghao Ding struct calidata *cali_data = &p->cali_data;
37*4fe23851SShenghao Ding struct cali_reg *r = &cali_data->cali_reg_array;
38*4fe23851SShenghao Ding unsigned char *data = cali_data->data;
39*4fe23851SShenghao Ding unsigned int *tmp_val = (unsigned int *)data;
40*4fe23851SShenghao Ding unsigned int cali_reg[TASDEV_CALIB_N] = {
41*4fe23851SShenghao Ding TASDEVICE_REG(0, 0x17, 0x74),
42*4fe23851SShenghao Ding TASDEVICE_REG(0, 0x18, 0x0c),
43*4fe23851SShenghao Ding TASDEVICE_REG(0, 0x18, 0x14),
44*4fe23851SShenghao Ding TASDEVICE_REG(0, 0x13, 0x70),
45*4fe23851SShenghao Ding TASDEVICE_REG(0, 0x18, 0x7c),
46*4fe23851SShenghao Ding };
47*4fe23851SShenghao Ding unsigned int crc, oft;
48*4fe23851SShenghao Ding unsigned char *buf;
49*4fe23851SShenghao Ding int i, j, k, l;
50*4fe23851SShenghao Ding
51*4fe23851SShenghao Ding if (tmp_val[0] == 2781) {
52*4fe23851SShenghao Ding /*
53*4fe23851SShenghao Ding * New features were added in calibrated Data V3:
54*4fe23851SShenghao Ding * 1. Added calibration registers address define in
55*4fe23851SShenghao Ding * a node, marked as Device id == 0x80.
56*4fe23851SShenghao Ding * New features were added in calibrated Data V2:
57*4fe23851SShenghao Ding * 1. Added some the fields to store the link_id and
58*4fe23851SShenghao Ding * uniqie_id for multi-link solutions
59*4fe23851SShenghao Ding * 2. Support flexible number of devices instead of
60*4fe23851SShenghao Ding * fixed one in V1.
61*4fe23851SShenghao Ding * Layout of calibrated data V2 in UEFI(total 256 bytes):
62*4fe23851SShenghao Ding * ChipID (2781, 4 bytes)
63*4fe23851SShenghao Ding * Data-Group-Sum (4 bytes)
64*4fe23851SShenghao Ding * TimeStamp of Calibration (4 bytes)
65*4fe23851SShenghao Ding * for (i = 0; i < Data-Group-Sum; i++) {
66*4fe23851SShenghao Ding * if (Data type != 0x80) (4 bytes)
67*4fe23851SShenghao Ding * Calibrated Data of Device #i (20 bytes)
68*4fe23851SShenghao Ding * else
69*4fe23851SShenghao Ding * Calibration registers address (5*4 = 20 bytes)
70*4fe23851SShenghao Ding * # V2: No reg addr in data grp section.
71*4fe23851SShenghao Ding * # V3: Normally the last grp is the reg addr.
72*4fe23851SShenghao Ding * }
73*4fe23851SShenghao Ding * CRC (4 bytes)
74*4fe23851SShenghao Ding * Reserved (the rest)
75*4fe23851SShenghao Ding */
76*4fe23851SShenghao Ding crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0;
77*4fe23851SShenghao Ding
78*4fe23851SShenghao Ding if (crc != tmp_val[3 + tmp_val[1] * 6]) {
79*4fe23851SShenghao Ding cali_data->total_sz = 0;
80*4fe23851SShenghao Ding dev_err(p->dev, "%s: CRC error\n", __func__);
81*4fe23851SShenghao Ding return;
82*4fe23851SShenghao Ding }
83*4fe23851SShenghao Ding
84*4fe23851SShenghao Ding for (j = 0, k = 0; j < tmp_val[1]; j++) {
85*4fe23851SShenghao Ding oft = j * 6 + 3;
86*4fe23851SShenghao Ding if (tmp_val[oft] == TASDEV_UEFI_CALI_REG_ADDR_FLG) {
87*4fe23851SShenghao Ding for (i = 0; i < TASDEV_CALIB_N; i++) {
88*4fe23851SShenghao Ding buf = &data[(oft + i + 1) * 4];
89*4fe23851SShenghao Ding cali_reg[i] = TASDEVICE_REG(buf[1],
90*4fe23851SShenghao Ding buf[2], buf[3]);
91*4fe23851SShenghao Ding }
92*4fe23851SShenghao Ding } else {
93*4fe23851SShenghao Ding l = j * (cali_data->cali_dat_sz_per_dev + 1);
94*4fe23851SShenghao Ding if (k >= p->ndev || l > oft * 4) {
95*4fe23851SShenghao Ding dev_err(p->dev, "%s: dev sum error\n",
96*4fe23851SShenghao Ding __func__);
97*4fe23851SShenghao Ding cali_data->total_sz = 0;
98*4fe23851SShenghao Ding return;
99*4fe23851SShenghao Ding }
100*4fe23851SShenghao Ding
101*4fe23851SShenghao Ding data[l] = k;
102*4fe23851SShenghao Ding for (i = 0; i < TASDEV_CALIB_N * 4; i++)
103*4fe23851SShenghao Ding data[l + i] = data[4 * oft + i];
104*4fe23851SShenghao Ding k++;
105*4fe23851SShenghao Ding }
106*4fe23851SShenghao Ding }
107*4fe23851SShenghao Ding } else {
108*4fe23851SShenghao Ding /*
109*4fe23851SShenghao Ding * Calibration data is in V1 format.
110*4fe23851SShenghao Ding * struct cali_data {
111*4fe23851SShenghao Ding * char cali_data[20];
112*4fe23851SShenghao Ding * }
113*4fe23851SShenghao Ding *
114*4fe23851SShenghao Ding * struct {
115*4fe23851SShenghao Ding * struct cali_data cali_data[4];
116*4fe23851SShenghao Ding * int TimeStamp of Calibration (4 bytes)
117*4fe23851SShenghao Ding * int CRC (4 bytes)
118*4fe23851SShenghao Ding * } ueft;
119*4fe23851SShenghao Ding */
120*4fe23851SShenghao Ding crc = crc32(~0, data, 84) ^ ~0;
121*4fe23851SShenghao Ding if (crc != tmp_val[21]) {
122*4fe23851SShenghao Ding cali_data->total_sz = 0;
123*4fe23851SShenghao Ding dev_err(p->dev, "%s: V1 CRC error\n", __func__);
124*4fe23851SShenghao Ding return;
125*4fe23851SShenghao Ding }
126*4fe23851SShenghao Ding
127*4fe23851SShenghao Ding for (j = p->ndev - 1; j >= 0; j--) {
128*4fe23851SShenghao Ding l = j * (cali_data->cali_dat_sz_per_dev + 1);
129*4fe23851SShenghao Ding for (i = TASDEV_CALIB_N * 4; i > 0 ; i--)
130*4fe23851SShenghao Ding data[l + i] = data[p->index * 5 + i];
131*4fe23851SShenghao Ding data[l+i] = j;
132*4fe23851SShenghao Ding }
133*4fe23851SShenghao Ding }
134*4fe23851SShenghao Ding
135*4fe23851SShenghao Ding if (p->dspbin_typ == TASDEV_BASIC) {
136*4fe23851SShenghao Ding r->r0_reg = cali_reg[0];
137*4fe23851SShenghao Ding r->invr0_reg = cali_reg[1];
138*4fe23851SShenghao Ding r->r0_low_reg = cali_reg[2];
139*4fe23851SShenghao Ding r->pow_reg = cali_reg[3];
140*4fe23851SShenghao Ding r->tlimit_reg = cali_reg[4];
141*4fe23851SShenghao Ding }
142*4fe23851SShenghao Ding
143*4fe23851SShenghao Ding p->is_user_space_calidata = true;
144*4fe23851SShenghao Ding cali_data->total_sz = p->ndev * (cali_data->cali_dat_sz_per_dev + 1);
145*4fe23851SShenghao Ding }
146*4fe23851SShenghao Ding
147*4fe23851SShenghao Ding /*
148*4fe23851SShenghao Ding * Update the calibration data, including speaker impedance, f0, etc,
149*4fe23851SShenghao Ding * into algo. Calibrate data is done by manufacturer in the factory.
150*4fe23851SShenghao Ding * The data is used by Algo for calculating the speaker temperature,
151*4fe23851SShenghao Ding * speaker membrane excursion and f0 in real time during playback.
152*4fe23851SShenghao Ding * Calibration data format in EFI is V2, since 2024.
153*4fe23851SShenghao Ding */
tas2781_save_calibration(struct tas2781_hda * hda)154*4fe23851SShenghao Ding int tas2781_save_calibration(struct tas2781_hda *hda)
155*4fe23851SShenghao Ding {
156*4fe23851SShenghao Ding /*
157*4fe23851SShenghao Ding * GUID was used for data access in BIOS, it was provided by board
158*4fe23851SShenghao Ding * manufactory.
159*4fe23851SShenghao Ding */
160*4fe23851SShenghao Ding efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO];
161*4fe23851SShenghao Ding static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME;
162*4fe23851SShenghao Ding struct tasdevice_priv *p = hda->priv;
163*4fe23851SShenghao Ding struct calidata *cali_data = &p->cali_data;
164*4fe23851SShenghao Ding unsigned long total_sz = 0;
165*4fe23851SShenghao Ding unsigned int attr, size;
166*4fe23851SShenghao Ding unsigned char *data;
167*4fe23851SShenghao Ding efi_status_t status;
168*4fe23851SShenghao Ding
169*4fe23851SShenghao Ding if (hda->catlog_id < LENOVO)
170*4fe23851SShenghao Ding efi_guid = tasdev_fct_efi_guid[hda->catlog_id];
171*4fe23851SShenghao Ding
172*4fe23851SShenghao Ding cali_data->cali_dat_sz_per_dev = 20;
173*4fe23851SShenghao Ding size = p->ndev * (cali_data->cali_dat_sz_per_dev + 1);
174*4fe23851SShenghao Ding /* Get real size of UEFI variable */
175*4fe23851SShenghao Ding status = efi.get_variable(efi_name, &efi_guid, &attr, &total_sz, NULL);
176*4fe23851SShenghao Ding cali_data->total_sz = total_sz > size ? total_sz : size;
177*4fe23851SShenghao Ding if (status == EFI_BUFFER_TOO_SMALL) {
178*4fe23851SShenghao Ding /* Allocate data buffer of data_size bytes */
179*4fe23851SShenghao Ding data = p->cali_data.data = devm_kzalloc(p->dev,
180*4fe23851SShenghao Ding p->cali_data.total_sz, GFP_KERNEL);
181*4fe23851SShenghao Ding if (!data) {
182*4fe23851SShenghao Ding p->cali_data.total_sz = 0;
183*4fe23851SShenghao Ding return -ENOMEM;
184*4fe23851SShenghao Ding }
185*4fe23851SShenghao Ding /* Get variable contents into buffer */
186*4fe23851SShenghao Ding status = efi.get_variable(efi_name, &efi_guid, &attr,
187*4fe23851SShenghao Ding &p->cali_data.total_sz, data);
188*4fe23851SShenghao Ding }
189*4fe23851SShenghao Ding if (status != EFI_SUCCESS) {
190*4fe23851SShenghao Ding p->cali_data.total_sz = 0;
191*4fe23851SShenghao Ding return status;
192*4fe23851SShenghao Ding }
193*4fe23851SShenghao Ding
194*4fe23851SShenghao Ding tas2781_apply_calib(p);
195*4fe23851SShenghao Ding
196*4fe23851SShenghao Ding return 0;
197*4fe23851SShenghao Ding }
198*4fe23851SShenghao Ding EXPORT_SYMBOL_NS_GPL(tas2781_save_calibration, "SND_HDA_SCODEC_TAS2781");
199*4fe23851SShenghao Ding
tas2781_hda_remove(struct device * dev,const struct component_ops * ops)20028a09d9eSShenghao Ding void tas2781_hda_remove(struct device *dev,
20128a09d9eSShenghao Ding const struct component_ops *ops)
20228a09d9eSShenghao Ding {
20328a09d9eSShenghao Ding struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
20428a09d9eSShenghao Ding
20528a09d9eSShenghao Ding component_del(tas_hda->dev, ops);
20628a09d9eSShenghao Ding
20728a09d9eSShenghao Ding pm_runtime_get_sync(tas_hda->dev);
20828a09d9eSShenghao Ding pm_runtime_disable(tas_hda->dev);
20928a09d9eSShenghao Ding
21028a09d9eSShenghao Ding pm_runtime_put_noidle(tas_hda->dev);
21128a09d9eSShenghao Ding
21228a09d9eSShenghao Ding tasdevice_remove(tas_hda->priv);
21328a09d9eSShenghao Ding }
21428a09d9eSShenghao Ding EXPORT_SYMBOL_NS_GPL(tas2781_hda_remove, "SND_HDA_SCODEC_TAS2781");
21528a09d9eSShenghao Ding
tasdevice_info_profile(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)21628a09d9eSShenghao Ding int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
21728a09d9eSShenghao Ding struct snd_ctl_elem_info *uinfo)
21828a09d9eSShenghao Ding {
21928a09d9eSShenghao Ding struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
22028a09d9eSShenghao Ding
22128a09d9eSShenghao Ding uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
22228a09d9eSShenghao Ding uinfo->count = 1;
22328a09d9eSShenghao Ding uinfo->value.integer.min = 0;
22428a09d9eSShenghao Ding uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1;
22528a09d9eSShenghao Ding
22628a09d9eSShenghao Ding return 0;
22728a09d9eSShenghao Ding }
22828a09d9eSShenghao Ding EXPORT_SYMBOL_NS_GPL(tasdevice_info_profile, "SND_HDA_SCODEC_TAS2781");
22928a09d9eSShenghao Ding
tasdevice_info_programs(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)23028a09d9eSShenghao Ding int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
23128a09d9eSShenghao Ding struct snd_ctl_elem_info *uinfo)
23228a09d9eSShenghao Ding {
23328a09d9eSShenghao Ding struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
23428a09d9eSShenghao Ding
23528a09d9eSShenghao Ding uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
23628a09d9eSShenghao Ding uinfo->count = 1;
23728a09d9eSShenghao Ding uinfo->value.integer.min = 0;
23828a09d9eSShenghao Ding uinfo->value.integer.max = tas_priv->fmw->nr_programs - 1;
23928a09d9eSShenghao Ding
24028a09d9eSShenghao Ding return 0;
24128a09d9eSShenghao Ding }
24228a09d9eSShenghao Ding EXPORT_SYMBOL_NS_GPL(tasdevice_info_programs, "SND_HDA_SCODEC_TAS2781");
24328a09d9eSShenghao Ding
tasdevice_info_config(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)24428a09d9eSShenghao Ding int tasdevice_info_config(struct snd_kcontrol *kcontrol,
24528a09d9eSShenghao Ding struct snd_ctl_elem_info *uinfo)
24628a09d9eSShenghao Ding {
24728a09d9eSShenghao Ding struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
24828a09d9eSShenghao Ding struct tasdevice_fw *tas_fw = tas_priv->fmw;
24928a09d9eSShenghao Ding
25028a09d9eSShenghao Ding uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
25128a09d9eSShenghao Ding uinfo->count = 1;
25228a09d9eSShenghao Ding uinfo->value.integer.min = 0;
25328a09d9eSShenghao Ding uinfo->value.integer.max = tas_fw->nr_configurations - 1;
25428a09d9eSShenghao Ding
25528a09d9eSShenghao Ding return 0;
25628a09d9eSShenghao Ding }
25728a09d9eSShenghao Ding EXPORT_SYMBOL_NS_GPL(tasdevice_info_config, "SND_HDA_SCODEC_TAS2781");
25828a09d9eSShenghao Ding
tasdevice_get_profile_id(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)25928a09d9eSShenghao Ding int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
26028a09d9eSShenghao Ding struct snd_ctl_elem_value *ucontrol)
26128a09d9eSShenghao Ding {
26228a09d9eSShenghao Ding struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
26328a09d9eSShenghao Ding
26428a09d9eSShenghao Ding ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id;
26528a09d9eSShenghao Ding
26628a09d9eSShenghao Ding dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
26728a09d9eSShenghao Ding kcontrol->id.name, tas_priv->rcabin.profile_cfg_id);
26828a09d9eSShenghao Ding
26928a09d9eSShenghao Ding return 0;
27028a09d9eSShenghao Ding }
27128a09d9eSShenghao Ding EXPORT_SYMBOL_NS_GPL(tasdevice_get_profile_id, "SND_HDA_SCODEC_TAS2781");
27228a09d9eSShenghao Ding
tasdevice_set_profile_id(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)27328a09d9eSShenghao Ding int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
27428a09d9eSShenghao Ding struct snd_ctl_elem_value *ucontrol)
27528a09d9eSShenghao Ding {
27628a09d9eSShenghao Ding struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
27728a09d9eSShenghao Ding int profile_id = ucontrol->value.integer.value[0];
27828a09d9eSShenghao Ding int max = tas_priv->rcabin.ncfgs - 1;
27928a09d9eSShenghao Ding int val, ret = 0;
28028a09d9eSShenghao Ding
28128a09d9eSShenghao Ding val = clamp(profile_id, 0, max);
28228a09d9eSShenghao Ding
28328a09d9eSShenghao Ding guard(mutex)(&tas_priv->codec_lock);
28428a09d9eSShenghao Ding
28528a09d9eSShenghao Ding dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
28628a09d9eSShenghao Ding kcontrol->id.name, tas_priv->rcabin.profile_cfg_id, val);
28728a09d9eSShenghao Ding
28828a09d9eSShenghao Ding if (tas_priv->rcabin.profile_cfg_id != val) {
28928a09d9eSShenghao Ding tas_priv->rcabin.profile_cfg_id = val;
29028a09d9eSShenghao Ding ret = 1;
29128a09d9eSShenghao Ding }
29228a09d9eSShenghao Ding
29328a09d9eSShenghao Ding return ret;
29428a09d9eSShenghao Ding }
29528a09d9eSShenghao Ding EXPORT_SYMBOL_NS_GPL(tasdevice_set_profile_id, "SND_HDA_SCODEC_TAS2781");
29628a09d9eSShenghao Ding
tasdevice_program_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)29728a09d9eSShenghao Ding int tasdevice_program_get(struct snd_kcontrol *kcontrol,
29828a09d9eSShenghao Ding struct snd_ctl_elem_value *ucontrol)
29928a09d9eSShenghao Ding {
30028a09d9eSShenghao Ding struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
30128a09d9eSShenghao Ding
30228a09d9eSShenghao Ding ucontrol->value.integer.value[0] = tas_priv->cur_prog;
30328a09d9eSShenghao Ding
30428a09d9eSShenghao Ding dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
30528a09d9eSShenghao Ding kcontrol->id.name, tas_priv->cur_prog);
30628a09d9eSShenghao Ding
30728a09d9eSShenghao Ding return 0;
30828a09d9eSShenghao Ding }
30928a09d9eSShenghao Ding EXPORT_SYMBOL_NS_GPL(tasdevice_program_get, "SND_HDA_SCODEC_TAS2781");
31028a09d9eSShenghao Ding
tasdevice_program_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)31128a09d9eSShenghao Ding int tasdevice_program_put(struct snd_kcontrol *kcontrol,
31228a09d9eSShenghao Ding struct snd_ctl_elem_value *ucontrol)
31328a09d9eSShenghao Ding {
31428a09d9eSShenghao Ding struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
31528a09d9eSShenghao Ding struct tasdevice_fw *tas_fw = tas_priv->fmw;
31628a09d9eSShenghao Ding int nr_program = ucontrol->value.integer.value[0];
31728a09d9eSShenghao Ding int max = tas_fw->nr_programs - 1;
31828a09d9eSShenghao Ding int val, ret = 0;
31928a09d9eSShenghao Ding
32028a09d9eSShenghao Ding val = clamp(nr_program, 0, max);
32128a09d9eSShenghao Ding
32228a09d9eSShenghao Ding guard(mutex)(&tas_priv->codec_lock);
32328a09d9eSShenghao Ding
32428a09d9eSShenghao Ding dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
32528a09d9eSShenghao Ding kcontrol->id.name, tas_priv->cur_prog, val);
32628a09d9eSShenghao Ding
32728a09d9eSShenghao Ding if (tas_priv->cur_prog != val) {
32828a09d9eSShenghao Ding tas_priv->cur_prog = val;
32928a09d9eSShenghao Ding ret = 1;
33028a09d9eSShenghao Ding }
33128a09d9eSShenghao Ding
33228a09d9eSShenghao Ding return ret;
33328a09d9eSShenghao Ding }
33428a09d9eSShenghao Ding EXPORT_SYMBOL_NS_GPL(tasdevice_program_put, "SND_HDA_SCODEC_TAS2781");
33528a09d9eSShenghao Ding
tasdevice_config_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)33628a09d9eSShenghao Ding int tasdevice_config_get(struct snd_kcontrol *kcontrol,
33728a09d9eSShenghao Ding struct snd_ctl_elem_value *ucontrol)
33828a09d9eSShenghao Ding {
33928a09d9eSShenghao Ding struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
34028a09d9eSShenghao Ding
34128a09d9eSShenghao Ding ucontrol->value.integer.value[0] = tas_priv->cur_conf;
34228a09d9eSShenghao Ding
34328a09d9eSShenghao Ding dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
34428a09d9eSShenghao Ding kcontrol->id.name, tas_priv->cur_conf);
34528a09d9eSShenghao Ding
34628a09d9eSShenghao Ding return 0;
34728a09d9eSShenghao Ding }
34828a09d9eSShenghao Ding EXPORT_SYMBOL_NS_GPL(tasdevice_config_get, "SND_HDA_SCODEC_TAS2781");
34928a09d9eSShenghao Ding
tasdevice_config_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)35028a09d9eSShenghao Ding int tasdevice_config_put(struct snd_kcontrol *kcontrol,
35128a09d9eSShenghao Ding struct snd_ctl_elem_value *ucontrol)
35228a09d9eSShenghao Ding {
35328a09d9eSShenghao Ding struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
35428a09d9eSShenghao Ding struct tasdevice_fw *tas_fw = tas_priv->fmw;
35528a09d9eSShenghao Ding int nr_config = ucontrol->value.integer.value[0];
35628a09d9eSShenghao Ding int max = tas_fw->nr_configurations - 1;
35728a09d9eSShenghao Ding int val, ret = 0;
35828a09d9eSShenghao Ding
35928a09d9eSShenghao Ding val = clamp(nr_config, 0, max);
36028a09d9eSShenghao Ding
36128a09d9eSShenghao Ding guard(mutex)(&tas_priv->codec_lock);
36228a09d9eSShenghao Ding
36328a09d9eSShenghao Ding dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
36428a09d9eSShenghao Ding kcontrol->id.name, tas_priv->cur_conf, val);
36528a09d9eSShenghao Ding
36628a09d9eSShenghao Ding if (tas_priv->cur_conf != val) {
36728a09d9eSShenghao Ding tas_priv->cur_conf = val;
36828a09d9eSShenghao Ding ret = 1;
36928a09d9eSShenghao Ding }
37028a09d9eSShenghao Ding
37128a09d9eSShenghao Ding return ret;
37228a09d9eSShenghao Ding }
37328a09d9eSShenghao Ding EXPORT_SYMBOL_NS_GPL(tasdevice_config_put, "SND_HDA_SCODEC_TAS2781");
37428a09d9eSShenghao Ding
37528a09d9eSShenghao Ding MODULE_DESCRIPTION("TAS2781 HDA Driver");
37628a09d9eSShenghao Ding MODULE_LICENSE("GPL");
37728a09d9eSShenghao Ding MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
378