xref: /openbmc/linux/sound/soc/codecs/tas2781-comlib.c (revision 34d6f206a88c2651d216bd3487ac956a40b2ba8e)
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