1ef3bcde7SShenghao Ding // SPDX-License-Identifier: GPL-2.0
2ef3bcde7SShenghao Ding //
3ef3bcde7SShenghao Ding // tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers
4ef3bcde7SShenghao Ding //
5ef3bcde7SShenghao Ding // Copyright 2023 Texas Instruments, Inc.
6ef3bcde7SShenghao Ding //
7ef3bcde7SShenghao Ding // Author: Shenghao Ding <shenghao-ding@ti.com>
8ef3bcde7SShenghao Ding
9ef3bcde7SShenghao Ding #include <linux/crc8.h>
10ef3bcde7SShenghao Ding #include <linux/firmware.h>
11ef3bcde7SShenghao Ding #include <linux/gpio/consumer.h>
12ef3bcde7SShenghao Ding #include <linux/i2c.h>
13ef3bcde7SShenghao Ding #include <linux/init.h>
14ef3bcde7SShenghao Ding #include <linux/interrupt.h>
15ef3bcde7SShenghao Ding #include <linux/module.h>
16ef3bcde7SShenghao Ding #include <linux/of.h>
17ef3bcde7SShenghao Ding #include <linux/of_irq.h>
18ef3bcde7SShenghao Ding #include <linux/regmap.h>
19ef3bcde7SShenghao Ding #include <linux/slab.h>
20ef3bcde7SShenghao Ding #include <sound/pcm_params.h>
21ef3bcde7SShenghao Ding #include <sound/soc.h>
22ef3bcde7SShenghao Ding #include <sound/tas2781.h>
23ef3bcde7SShenghao Ding
24ef3bcde7SShenghao Ding #define TASDEVICE_CRC8_POLYNOMIAL 0x4d
25ef3bcde7SShenghao Ding
26ef3bcde7SShenghao Ding static const struct regmap_range_cfg tasdevice_ranges[] = {
27ef3bcde7SShenghao Ding {
28ef3bcde7SShenghao Ding .range_min = 0,
29ef3bcde7SShenghao Ding .range_max = 256 * 128,
30ef3bcde7SShenghao Ding .selector_reg = TASDEVICE_PAGE_SELECT,
31ef3bcde7SShenghao Ding .selector_mask = 0xff,
32ef3bcde7SShenghao Ding .selector_shift = 0,
33ef3bcde7SShenghao Ding .window_start = 0,
34ef3bcde7SShenghao Ding .window_len = 128,
35ef3bcde7SShenghao Ding },
36ef3bcde7SShenghao Ding };
37ef3bcde7SShenghao Ding
38ef3bcde7SShenghao Ding static const struct regmap_config tasdevice_regmap = {
39ef3bcde7SShenghao Ding .reg_bits = 8,
40ef3bcde7SShenghao Ding .val_bits = 8,
41aee67bbeSGergo Koteles .cache_type = REGCACHE_NONE,
42ef3bcde7SShenghao Ding .ranges = tasdevice_ranges,
43ef3bcde7SShenghao Ding .num_ranges = ARRAY_SIZE(tasdevice_ranges),
44ef3bcde7SShenghao Ding .max_register = 256 * 128,
45ef3bcde7SShenghao Ding };
46ef3bcde7SShenghao Ding
tasdevice_change_chn_book(struct tasdevice_priv * tas_priv,unsigned short chn,int book)47ef3bcde7SShenghao Ding static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv,
48ef3bcde7SShenghao Ding unsigned short chn, int book)
49ef3bcde7SShenghao Ding {
50ef3bcde7SShenghao Ding struct i2c_client *client = (struct i2c_client *)tas_priv->client;
51ef3bcde7SShenghao Ding int ret = 0;
52ef3bcde7SShenghao Ding
53ef3bcde7SShenghao Ding if (chn < tas_priv->ndev) {
54ef3bcde7SShenghao Ding struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
55ef3bcde7SShenghao Ding struct regmap *map = tas_priv->regmap;
56ef3bcde7SShenghao Ding
57ef3bcde7SShenghao Ding if (client->addr != tasdev->dev_addr) {
58ef3bcde7SShenghao Ding client->addr = tasdev->dev_addr;
593d521f9fSShenghao Ding /* All tas2781s share the same regmap, clear the page
603d521f9fSShenghao Ding * inside regmap once switching to another tas2781.
613d521f9fSShenghao Ding * Register 0 at any pages and any books inside tas2781
623d521f9fSShenghao Ding * is the same one for page-switching.
633d521f9fSShenghao Ding */
643d521f9fSShenghao Ding ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
65ef3bcde7SShenghao Ding if (ret < 0) {
66ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, E=%d\n",
67ef3bcde7SShenghao Ding __func__, ret);
68ef3bcde7SShenghao Ding goto out;
69ef3bcde7SShenghao Ding }
70ef3bcde7SShenghao Ding }
71ef3bcde7SShenghao Ding
72ef3bcde7SShenghao Ding if (tasdev->cur_book != book) {
73ef3bcde7SShenghao Ding ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
74ef3bcde7SShenghao Ding if (ret < 0) {
75ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, E=%d\n",
76ef3bcde7SShenghao Ding __func__, ret);
77ef3bcde7SShenghao Ding goto out;
78ef3bcde7SShenghao Ding }
79ef3bcde7SShenghao Ding tasdev->cur_book = book;
80ef3bcde7SShenghao Ding }
81ef3bcde7SShenghao Ding } else {
82ef3bcde7SShenghao Ding ret = -EINVAL;
83ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
84ef3bcde7SShenghao Ding chn);
85ef3bcde7SShenghao Ding }
86ef3bcde7SShenghao Ding
87ef3bcde7SShenghao Ding out:
88ef3bcde7SShenghao Ding return ret;
89ef3bcde7SShenghao Ding }
90ef3bcde7SShenghao Ding
tasdevice_dev_read(struct tasdevice_priv * tas_priv,unsigned short chn,unsigned int reg,unsigned int * val)91ef3bcde7SShenghao Ding int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
92ef3bcde7SShenghao Ding unsigned short chn, unsigned int reg, unsigned int *val)
93ef3bcde7SShenghao Ding {
94ef3bcde7SShenghao Ding int ret = 0;
95ef3bcde7SShenghao Ding
96ef3bcde7SShenghao Ding if (chn < tas_priv->ndev) {
97ef3bcde7SShenghao Ding struct regmap *map = tas_priv->regmap;
98ef3bcde7SShenghao Ding
99ef3bcde7SShenghao Ding ret = tasdevice_change_chn_book(tas_priv, chn,
100ef3bcde7SShenghao Ding TASDEVICE_BOOK_ID(reg));
101ef3bcde7SShenghao Ding if (ret < 0)
102ef3bcde7SShenghao Ding goto out;
103ef3bcde7SShenghao Ding
104ef3bcde7SShenghao Ding ret = regmap_read(map, TASDEVICE_PGRG(reg), val);
105ef3bcde7SShenghao Ding if (ret < 0)
106ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
107ef3bcde7SShenghao Ding } else {
108ef3bcde7SShenghao Ding ret = -EINVAL;
109ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
110ef3bcde7SShenghao Ding chn);
111ef3bcde7SShenghao Ding }
112ef3bcde7SShenghao Ding
113ef3bcde7SShenghao Ding out:
114ef3bcde7SShenghao Ding return ret;
115ef3bcde7SShenghao Ding }
116ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_dev_read);
117ef3bcde7SShenghao Ding
tasdevice_dev_write(struct tasdevice_priv * tas_priv,unsigned short chn,unsigned int reg,unsigned int value)118ef3bcde7SShenghao Ding int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
119ef3bcde7SShenghao Ding unsigned short chn, unsigned int reg, unsigned int value)
120ef3bcde7SShenghao Ding {
121ef3bcde7SShenghao Ding int ret = 0;
122ef3bcde7SShenghao Ding
123ef3bcde7SShenghao Ding if (chn < tas_priv->ndev) {
124ef3bcde7SShenghao Ding struct regmap *map = tas_priv->regmap;
125ef3bcde7SShenghao Ding
126ef3bcde7SShenghao Ding ret = tasdevice_change_chn_book(tas_priv, chn,
127ef3bcde7SShenghao Ding TASDEVICE_BOOK_ID(reg));
128ef3bcde7SShenghao Ding if (ret < 0)
129ef3bcde7SShenghao Ding goto out;
130ef3bcde7SShenghao Ding
131ef3bcde7SShenghao Ding ret = regmap_write(map, TASDEVICE_PGRG(reg),
132ef3bcde7SShenghao Ding value);
133ef3bcde7SShenghao Ding if (ret < 0)
134ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
135ef3bcde7SShenghao Ding } else {
136ef3bcde7SShenghao Ding ret = -EINVAL;
137ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
138ef3bcde7SShenghao Ding chn);
139ef3bcde7SShenghao Ding }
140ef3bcde7SShenghao Ding
141ef3bcde7SShenghao Ding out:
142ef3bcde7SShenghao Ding return ret;
143ef3bcde7SShenghao Ding }
144ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_dev_write);
145ef3bcde7SShenghao Ding
tasdevice_dev_bulk_write(struct tasdevice_priv * tas_priv,unsigned short chn,unsigned int reg,unsigned char * data,unsigned int len)146ef3bcde7SShenghao Ding int tasdevice_dev_bulk_write(
147ef3bcde7SShenghao Ding struct tasdevice_priv *tas_priv, unsigned short chn,
148ef3bcde7SShenghao Ding unsigned int reg, unsigned char *data,
149ef3bcde7SShenghao Ding unsigned int len)
150ef3bcde7SShenghao Ding {
151ef3bcde7SShenghao Ding int ret = 0;
152ef3bcde7SShenghao Ding
153ef3bcde7SShenghao Ding if (chn < tas_priv->ndev) {
154ef3bcde7SShenghao Ding struct regmap *map = tas_priv->regmap;
155ef3bcde7SShenghao Ding
156ef3bcde7SShenghao Ding ret = tasdevice_change_chn_book(tas_priv, chn,
157ef3bcde7SShenghao Ding TASDEVICE_BOOK_ID(reg));
158ef3bcde7SShenghao Ding if (ret < 0)
159ef3bcde7SShenghao Ding goto out;
160ef3bcde7SShenghao Ding
161ef3bcde7SShenghao Ding ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg),
162ef3bcde7SShenghao Ding data, len);
163ef3bcde7SShenghao Ding if (ret < 0)
164ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
165ef3bcde7SShenghao Ding } else {
166ef3bcde7SShenghao Ding ret = -EINVAL;
167ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
168ef3bcde7SShenghao Ding chn);
169ef3bcde7SShenghao Ding }
170ef3bcde7SShenghao Ding
171ef3bcde7SShenghao Ding out:
172ef3bcde7SShenghao Ding return ret;
173ef3bcde7SShenghao Ding }
174ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write);
175ef3bcde7SShenghao Ding
tasdevice_dev_bulk_read(struct tasdevice_priv * tas_priv,unsigned short chn,unsigned int reg,unsigned char * data,unsigned int len)176ef3bcde7SShenghao Ding int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
177ef3bcde7SShenghao Ding unsigned short chn, unsigned int reg, unsigned char *data,
178ef3bcde7SShenghao Ding unsigned int len)
179ef3bcde7SShenghao Ding {
180ef3bcde7SShenghao Ding int ret = 0;
181ef3bcde7SShenghao Ding
182ef3bcde7SShenghao Ding if (chn < tas_priv->ndev) {
183ef3bcde7SShenghao Ding struct regmap *map = tas_priv->regmap;
184ef3bcde7SShenghao Ding
185ef3bcde7SShenghao Ding ret = tasdevice_change_chn_book(tas_priv, chn,
186ef3bcde7SShenghao Ding TASDEVICE_BOOK_ID(reg));
187ef3bcde7SShenghao Ding if (ret < 0)
188ef3bcde7SShenghao Ding goto out;
189ef3bcde7SShenghao Ding
190ef3bcde7SShenghao Ding ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
191ef3bcde7SShenghao Ding if (ret < 0)
192ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
193ef3bcde7SShenghao Ding } else
194ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
195ef3bcde7SShenghao Ding chn);
196ef3bcde7SShenghao Ding
197ef3bcde7SShenghao Ding out:
198ef3bcde7SShenghao Ding return ret;
199ef3bcde7SShenghao Ding }
200ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read);
201ef3bcde7SShenghao Ding
tasdevice_dev_update_bits(struct tasdevice_priv * tas_priv,unsigned short chn,unsigned int reg,unsigned int mask,unsigned int value)202ef3bcde7SShenghao Ding int tasdevice_dev_update_bits(
203ef3bcde7SShenghao Ding struct tasdevice_priv *tas_priv, unsigned short chn,
204ef3bcde7SShenghao Ding unsigned int reg, unsigned int mask, unsigned int value)
205ef3bcde7SShenghao Ding {
206ef3bcde7SShenghao Ding int ret = 0;
207ef3bcde7SShenghao Ding
208ef3bcde7SShenghao Ding if (chn < tas_priv->ndev) {
209ef3bcde7SShenghao Ding struct regmap *map = tas_priv->regmap;
210ef3bcde7SShenghao Ding
211ef3bcde7SShenghao Ding ret = tasdevice_change_chn_book(tas_priv, chn,
212ef3bcde7SShenghao Ding TASDEVICE_BOOK_ID(reg));
213ef3bcde7SShenghao Ding if (ret < 0)
214ef3bcde7SShenghao Ding goto out;
215ef3bcde7SShenghao Ding
216ef3bcde7SShenghao Ding ret = regmap_update_bits(map, TASDEVICE_PGRG(reg),
217ef3bcde7SShenghao Ding mask, value);
218ef3bcde7SShenghao Ding if (ret < 0)
219ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
220ef3bcde7SShenghao Ding } else {
221ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
222ef3bcde7SShenghao Ding chn);
223ef3bcde7SShenghao Ding ret = -EINVAL;
224ef3bcde7SShenghao Ding }
225ef3bcde7SShenghao Ding
226ef3bcde7SShenghao Ding out:
227ef3bcde7SShenghao Ding return ret;
228ef3bcde7SShenghao Ding }
229ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits);
230ef3bcde7SShenghao Ding
tasdevice_kzalloc(struct i2c_client * i2c)231ef3bcde7SShenghao Ding struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c)
232ef3bcde7SShenghao Ding {
233ef3bcde7SShenghao Ding struct tasdevice_priv *tas_priv;
234ef3bcde7SShenghao Ding
235ef3bcde7SShenghao Ding tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL);
236ef3bcde7SShenghao Ding if (!tas_priv)
237ef3bcde7SShenghao Ding return NULL;
238ef3bcde7SShenghao Ding tas_priv->dev = &i2c->dev;
239ef3bcde7SShenghao Ding tas_priv->client = (void *)i2c;
240ef3bcde7SShenghao Ding
241ef3bcde7SShenghao Ding return tas_priv;
242ef3bcde7SShenghao Ding }
243ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_kzalloc);
244ef3bcde7SShenghao Ding
tas2781_reset(struct tasdevice_priv * tas_dev)245ef3bcde7SShenghao Ding void tas2781_reset(struct tasdevice_priv *tas_dev)
246ef3bcde7SShenghao Ding {
247ef3bcde7SShenghao Ding int ret, i;
248ef3bcde7SShenghao Ding
249ef3bcde7SShenghao Ding if (tas_dev->reset) {
250ef3bcde7SShenghao Ding gpiod_set_value_cansleep(tas_dev->reset, 0);
251ef3bcde7SShenghao Ding usleep_range(500, 1000);
252ef3bcde7SShenghao Ding gpiod_set_value_cansleep(tas_dev->reset, 1);
253ef3bcde7SShenghao Ding } else {
254ef3bcde7SShenghao Ding for (i = 0; i < tas_dev->ndev; i++) {
255ef3bcde7SShenghao Ding ret = tasdevice_dev_write(tas_dev, i,
256ef3bcde7SShenghao Ding TAS2781_REG_SWRESET,
257ef3bcde7SShenghao Ding TAS2781_REG_SWRESET_RESET);
258ef3bcde7SShenghao Ding if (ret < 0)
259ef3bcde7SShenghao Ding dev_err(tas_dev->dev,
260ef3bcde7SShenghao Ding "dev %d swreset fail, %d\n",
261ef3bcde7SShenghao Ding i, ret);
262ef3bcde7SShenghao Ding }
263ef3bcde7SShenghao Ding }
264ef3bcde7SShenghao Ding usleep_range(1000, 1050);
265ef3bcde7SShenghao Ding }
266ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tas2781_reset);
267ef3bcde7SShenghao Ding
tascodec_init(struct tasdevice_priv * tas_priv,void * codec,struct module * module,void (* cont)(const struct firmware * fw,void * context))268ef3bcde7SShenghao Ding int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
269eb06fca2SGergo Koteles struct module *module,
270ef3bcde7SShenghao Ding void (*cont)(const struct firmware *fw, void *context))
271ef3bcde7SShenghao Ding {
272ef3bcde7SShenghao Ding int ret = 0;
273ef3bcde7SShenghao Ding
274ef3bcde7SShenghao Ding /* Codec Lock Hold to ensure that codec_probe and firmware parsing and
275ef3bcde7SShenghao Ding * loading do not simultaneously execute.
276ef3bcde7SShenghao Ding */
277ef3bcde7SShenghao Ding mutex_lock(&tas_priv->codec_lock);
278ef3bcde7SShenghao Ding
279ef3bcde7SShenghao Ding scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin",
280ef3bcde7SShenghao Ding tas_priv->dev_name, tas_priv->ndev);
281ef3bcde7SShenghao Ding crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
282ef3bcde7SShenghao Ding tas_priv->codec = codec;
283eb06fca2SGergo Koteles ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
284ef3bcde7SShenghao Ding tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
285ef3bcde7SShenghao Ding cont);
286ef3bcde7SShenghao Ding if (ret)
287ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
288ef3bcde7SShenghao Ding ret);
289ef3bcde7SShenghao Ding
290ef3bcde7SShenghao Ding /* Codec Lock Release*/
291ef3bcde7SShenghao Ding mutex_unlock(&tas_priv->codec_lock);
292ef3bcde7SShenghao Ding return ret;
293ef3bcde7SShenghao Ding }
294ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tascodec_init);
295ef3bcde7SShenghao Ding
tasdevice_init(struct tasdevice_priv * tas_priv)296ef3bcde7SShenghao Ding int tasdevice_init(struct tasdevice_priv *tas_priv)
297ef3bcde7SShenghao Ding {
298ef3bcde7SShenghao Ding int ret = 0;
299ef3bcde7SShenghao Ding int i;
300ef3bcde7SShenghao Ding
301ef3bcde7SShenghao Ding tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client,
302ef3bcde7SShenghao Ding &tasdevice_regmap);
303ef3bcde7SShenghao Ding if (IS_ERR(tas_priv->regmap)) {
304ef3bcde7SShenghao Ding ret = PTR_ERR(tas_priv->regmap);
305ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "Failed to allocate register map: %d\n",
306ef3bcde7SShenghao Ding ret);
307ef3bcde7SShenghao Ding goto out;
308ef3bcde7SShenghao Ding }
309ef3bcde7SShenghao Ding
310ef3bcde7SShenghao Ding tas_priv->cur_prog = -1;
311ef3bcde7SShenghao Ding tas_priv->cur_conf = -1;
312ef3bcde7SShenghao Ding
313ef3bcde7SShenghao Ding for (i = 0; i < tas_priv->ndev; i++) {
314ef3bcde7SShenghao Ding tas_priv->tasdevice[i].cur_book = -1;
315ef3bcde7SShenghao Ding tas_priv->tasdevice[i].cur_prog = -1;
316ef3bcde7SShenghao Ding tas_priv->tasdevice[i].cur_conf = -1;
317ef3bcde7SShenghao Ding }
318ef3bcde7SShenghao Ding
319ef3bcde7SShenghao Ding mutex_init(&tas_priv->codec_lock);
320ef3bcde7SShenghao Ding
321ef3bcde7SShenghao Ding out:
322ef3bcde7SShenghao Ding return ret;
323ef3bcde7SShenghao Ding }
324ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_init);
325ef3bcde7SShenghao Ding
tasdev_dsp_prog_blk_remove(struct tasdevice_prog * prog)326ef3bcde7SShenghao Ding static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog)
327ef3bcde7SShenghao Ding {
328ef3bcde7SShenghao Ding struct tasdevice_data *tas_dt;
329ef3bcde7SShenghao Ding struct tasdev_blk *blk;
330ef3bcde7SShenghao Ding unsigned int i;
331ef3bcde7SShenghao Ding
332ef3bcde7SShenghao Ding if (!prog)
333ef3bcde7SShenghao Ding return;
334ef3bcde7SShenghao Ding
335ef3bcde7SShenghao Ding tas_dt = &(prog->dev_data);
336ef3bcde7SShenghao Ding
337ef3bcde7SShenghao Ding if (!tas_dt->dev_blks)
338ef3bcde7SShenghao Ding return;
339ef3bcde7SShenghao Ding
340ef3bcde7SShenghao Ding for (i = 0; i < tas_dt->nr_blk; i++) {
341ef3bcde7SShenghao Ding blk = &(tas_dt->dev_blks[i]);
342ef3bcde7SShenghao Ding kfree(blk->data);
343ef3bcde7SShenghao Ding }
344ef3bcde7SShenghao Ding kfree(tas_dt->dev_blks);
345ef3bcde7SShenghao Ding }
346ef3bcde7SShenghao Ding
tasdev_dsp_prog_remove(struct tasdevice_prog * prog,unsigned short nr)347ef3bcde7SShenghao Ding static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog,
348ef3bcde7SShenghao Ding unsigned short nr)
349ef3bcde7SShenghao Ding {
350ef3bcde7SShenghao Ding int i;
351ef3bcde7SShenghao Ding
352ef3bcde7SShenghao Ding for (i = 0; i < nr; i++)
353ef3bcde7SShenghao Ding tasdev_dsp_prog_blk_remove(&prog[i]);
354ef3bcde7SShenghao Ding kfree(prog);
355ef3bcde7SShenghao Ding }
356ef3bcde7SShenghao Ding
tasdev_dsp_cfg_blk_remove(struct tasdevice_config * cfg)357ef3bcde7SShenghao Ding static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg)
358ef3bcde7SShenghao Ding {
359ef3bcde7SShenghao Ding struct tasdevice_data *tas_dt;
360ef3bcde7SShenghao Ding struct tasdev_blk *blk;
361ef3bcde7SShenghao Ding unsigned int i;
362ef3bcde7SShenghao Ding
363ef3bcde7SShenghao Ding if (cfg) {
364ef3bcde7SShenghao Ding tas_dt = &(cfg->dev_data);
365ef3bcde7SShenghao Ding
366ef3bcde7SShenghao Ding if (!tas_dt->dev_blks)
367ef3bcde7SShenghao Ding return;
368ef3bcde7SShenghao Ding
369ef3bcde7SShenghao Ding for (i = 0; i < tas_dt->nr_blk; i++) {
370ef3bcde7SShenghao Ding blk = &(tas_dt->dev_blks[i]);
371ef3bcde7SShenghao Ding kfree(blk->data);
372ef3bcde7SShenghao Ding }
373ef3bcde7SShenghao Ding kfree(tas_dt->dev_blks);
374ef3bcde7SShenghao Ding }
375ef3bcde7SShenghao Ding }
376ef3bcde7SShenghao Ding
tasdev_dsp_cfg_remove(struct tasdevice_config * config,unsigned short nr)377ef3bcde7SShenghao Ding static void tasdev_dsp_cfg_remove(struct tasdevice_config *config,
378ef3bcde7SShenghao Ding unsigned short nr)
379ef3bcde7SShenghao Ding {
380ef3bcde7SShenghao Ding int i;
381ef3bcde7SShenghao Ding
382ef3bcde7SShenghao Ding for (i = 0; i < nr; i++)
383ef3bcde7SShenghao Ding tasdev_dsp_cfg_blk_remove(&config[i]);
384ef3bcde7SShenghao Ding kfree(config);
385ef3bcde7SShenghao Ding }
386ef3bcde7SShenghao Ding
tasdevice_dsp_remove(void * context)387ef3bcde7SShenghao Ding void tasdevice_dsp_remove(void *context)
388ef3bcde7SShenghao Ding {
389ef3bcde7SShenghao Ding struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
390ef3bcde7SShenghao Ding struct tasdevice_fw *tas_fmw = tas_dev->fmw;
391ef3bcde7SShenghao Ding
392ef3bcde7SShenghao Ding if (!tas_dev->fmw)
393ef3bcde7SShenghao Ding return;
394ef3bcde7SShenghao Ding
395ef3bcde7SShenghao Ding if (tas_fmw->programs)
396ef3bcde7SShenghao Ding tasdev_dsp_prog_remove(tas_fmw->programs,
397ef3bcde7SShenghao Ding tas_fmw->nr_programs);
398ef3bcde7SShenghao Ding if (tas_fmw->configs)
399ef3bcde7SShenghao Ding tasdev_dsp_cfg_remove(tas_fmw->configs,
400ef3bcde7SShenghao Ding tas_fmw->nr_configurations);
401ef3bcde7SShenghao Ding kfree(tas_fmw);
402ef3bcde7SShenghao Ding tas_dev->fmw = NULL;
403ef3bcde7SShenghao Ding }
404ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_dsp_remove);
405ef3bcde7SShenghao Ding
tasdevice_remove(struct tasdevice_priv * tas_priv)406ef3bcde7SShenghao Ding void tasdevice_remove(struct tasdevice_priv *tas_priv)
407ef3bcde7SShenghao Ding {
408ef3bcde7SShenghao Ding mutex_destroy(&tas_priv->codec_lock);
409ef3bcde7SShenghao Ding }
410ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_remove);
411ef3bcde7SShenghao Ding
tasdevice_save_calibration(struct tasdevice_priv * tas_priv)412*939dbde0SGergo Koteles int tasdevice_save_calibration(struct tasdevice_priv *tas_priv)
413*939dbde0SGergo Koteles {
414*939dbde0SGergo Koteles if (tas_priv->save_calibration)
415*939dbde0SGergo Koteles return tas_priv->save_calibration(tas_priv);
416*939dbde0SGergo Koteles return -EINVAL;
417*939dbde0SGergo Koteles }
418*939dbde0SGergo Koteles EXPORT_SYMBOL_GPL(tasdevice_save_calibration);
419*939dbde0SGergo Koteles
tasdevice_apply_calibration(struct tasdevice_priv * tas_priv)420*939dbde0SGergo Koteles void tasdevice_apply_calibration(struct tasdevice_priv *tas_priv)
421*939dbde0SGergo Koteles {
422*939dbde0SGergo Koteles if (tas_priv->apply_calibration && tas_priv->cali_data.total_sz)
423*939dbde0SGergo Koteles tas_priv->apply_calibration(tas_priv);
424*939dbde0SGergo Koteles }
425*939dbde0SGergo Koteles EXPORT_SYMBOL_GPL(tasdevice_apply_calibration);
426*939dbde0SGergo Koteles
tasdevice_clamp(int val,int max,unsigned int invert)427ef3bcde7SShenghao Ding static int tasdevice_clamp(int val, int max, unsigned int invert)
428ef3bcde7SShenghao Ding {
429ef3bcde7SShenghao Ding if (val > max)
430ef3bcde7SShenghao Ding val = max;
431ef3bcde7SShenghao Ding if (invert)
432ef3bcde7SShenghao Ding val = max - val;
433ef3bcde7SShenghao Ding if (val < 0)
434ef3bcde7SShenghao Ding val = 0;
435ef3bcde7SShenghao Ding return val;
436ef3bcde7SShenghao Ding }
437ef3bcde7SShenghao Ding
tasdevice_amp_putvol(struct tasdevice_priv * tas_priv,struct snd_ctl_elem_value * ucontrol,struct soc_mixer_control * mc)438ef3bcde7SShenghao Ding int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv,
439ef3bcde7SShenghao Ding struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
440ef3bcde7SShenghao Ding {
441ef3bcde7SShenghao Ding unsigned int invert = mc->invert;
442ef3bcde7SShenghao Ding unsigned char mask;
443ef3bcde7SShenghao Ding int max = mc->max;
444ef3bcde7SShenghao Ding int err_cnt = 0;
445ef3bcde7SShenghao Ding int val, i, ret;
446ef3bcde7SShenghao Ding
447ef3bcde7SShenghao Ding mask = (1 << fls(max)) - 1;
448ef3bcde7SShenghao Ding mask <<= mc->shift;
449ef3bcde7SShenghao Ding val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
450ef3bcde7SShenghao Ding for (i = 0; i < tas_priv->ndev; i++) {
451ef3bcde7SShenghao Ding ret = tasdevice_dev_update_bits(tas_priv, i,
452ef3bcde7SShenghao Ding mc->reg, mask, (unsigned int)(val << mc->shift));
453ef3bcde7SShenghao Ding if (!ret)
454ef3bcde7SShenghao Ding continue;
455ef3bcde7SShenghao Ding err_cnt++;
456ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i);
457ef3bcde7SShenghao Ding }
458ef3bcde7SShenghao Ding
459ef3bcde7SShenghao Ding /* All the devices set error, return 0 */
460ef3bcde7SShenghao Ding return (err_cnt == tas_priv->ndev) ? 0 : 1;
461ef3bcde7SShenghao Ding }
462ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_amp_putvol);
463ef3bcde7SShenghao Ding
tasdevice_amp_getvol(struct tasdevice_priv * tas_priv,struct snd_ctl_elem_value * ucontrol,struct soc_mixer_control * mc)464ef3bcde7SShenghao Ding int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv,
465ef3bcde7SShenghao Ding struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
466ef3bcde7SShenghao Ding {
467ef3bcde7SShenghao Ding unsigned int invert = mc->invert;
468ef3bcde7SShenghao Ding unsigned char mask = 0;
469ef3bcde7SShenghao Ding int max = mc->max;
470ef3bcde7SShenghao Ding int ret = 0;
471ef3bcde7SShenghao Ding int val;
472ef3bcde7SShenghao Ding
473ef3bcde7SShenghao Ding /* Read the primary device */
474ef3bcde7SShenghao Ding ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
475ef3bcde7SShenghao Ding if (ret) {
476ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
477ef3bcde7SShenghao Ding goto out;
478ef3bcde7SShenghao Ding }
479ef3bcde7SShenghao Ding
480ef3bcde7SShenghao Ding mask = (1 << fls(max)) - 1;
481ef3bcde7SShenghao Ding mask <<= mc->shift;
482ef3bcde7SShenghao Ding val = (val & mask) >> mc->shift;
483ef3bcde7SShenghao Ding val = tasdevice_clamp(val, max, invert);
484ef3bcde7SShenghao Ding ucontrol->value.integer.value[0] = val;
485ef3bcde7SShenghao Ding
486ef3bcde7SShenghao Ding out:
487ef3bcde7SShenghao Ding return ret;
488ef3bcde7SShenghao Ding
489ef3bcde7SShenghao Ding }
490ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_amp_getvol);
491ef3bcde7SShenghao Ding
tasdevice_digital_putvol(struct tasdevice_priv * tas_priv,struct snd_ctl_elem_value * ucontrol,struct soc_mixer_control * mc)492ef3bcde7SShenghao Ding int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv,
493ef3bcde7SShenghao Ding struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
494ef3bcde7SShenghao Ding {
495ef3bcde7SShenghao Ding unsigned int invert = mc->invert;
496ef3bcde7SShenghao Ding int max = mc->max;
497ef3bcde7SShenghao Ding int err_cnt = 0;
498ef3bcde7SShenghao Ding int ret;
499ef3bcde7SShenghao Ding int val, i;
500ef3bcde7SShenghao Ding
501ef3bcde7SShenghao Ding val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
502ef3bcde7SShenghao Ding
503ef3bcde7SShenghao Ding for (i = 0; i < tas_priv->ndev; i++) {
504ef3bcde7SShenghao Ding ret = tasdevice_dev_write(tas_priv, i, mc->reg,
505ef3bcde7SShenghao Ding (unsigned int)val);
506ef3bcde7SShenghao Ding if (!ret)
507ef3bcde7SShenghao Ding continue;
508ef3bcde7SShenghao Ding err_cnt++;
509ef3bcde7SShenghao Ding dev_err(tas_priv->dev,
510ef3bcde7SShenghao Ding "set digital vol err in dev %d\n", i);
511ef3bcde7SShenghao Ding }
512ef3bcde7SShenghao Ding
513ef3bcde7SShenghao Ding /* All the devices set error, return 0 */
514ef3bcde7SShenghao Ding return (err_cnt == tas_priv->ndev) ? 0 : 1;
515ef3bcde7SShenghao Ding
516ef3bcde7SShenghao Ding }
517ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_digital_putvol);
518ef3bcde7SShenghao Ding
tasdevice_digital_getvol(struct tasdevice_priv * tas_priv,struct snd_ctl_elem_value * ucontrol,struct soc_mixer_control * mc)519ef3bcde7SShenghao Ding int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv,
520ef3bcde7SShenghao Ding struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
521ef3bcde7SShenghao Ding {
522ef3bcde7SShenghao Ding unsigned int invert = mc->invert;
523ef3bcde7SShenghao Ding int max = mc->max;
524ef3bcde7SShenghao Ding int ret, val;
525ef3bcde7SShenghao Ding
526ef3bcde7SShenghao Ding /* Read the primary device as the whole */
527ef3bcde7SShenghao Ding ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
528ef3bcde7SShenghao Ding if (ret) {
529ef3bcde7SShenghao Ding dev_err(tas_priv->dev, "%s, get digital vol error\n",
530ef3bcde7SShenghao Ding __func__);
531ef3bcde7SShenghao Ding goto out;
532ef3bcde7SShenghao Ding }
533ef3bcde7SShenghao Ding
534ef3bcde7SShenghao Ding val = tasdevice_clamp(val, max, invert);
535ef3bcde7SShenghao Ding ucontrol->value.integer.value[0] = val;
536ef3bcde7SShenghao Ding
537ef3bcde7SShenghao Ding out:
538ef3bcde7SShenghao Ding return ret;
539ef3bcde7SShenghao Ding
540ef3bcde7SShenghao Ding }
541ef3bcde7SShenghao Ding EXPORT_SYMBOL_GPL(tasdevice_digital_getvol);
542ef3bcde7SShenghao Ding
543ef3bcde7SShenghao Ding MODULE_DESCRIPTION("TAS2781 common library");
544ef3bcde7SShenghao Ding MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
545ef3bcde7SShenghao Ding MODULE_LICENSE("GPL");
546