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