1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers 4 // 5 // Copyright 2023 - 2025 Texas Instruments, Inc. 6 // 7 // Author: Shenghao Ding <shenghao-ding@ti.com> 8 9 #include <linux/crc8.h> 10 #include <linux/dev_printk.h> 11 #include <linux/firmware.h> 12 #include <linux/gpio/consumer.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_irq.h> 18 #include <linux/regmap.h> 19 #include <linux/slab.h> 20 #include <sound/tas2781.h> 21 22 int tasdevice_dev_read(struct tasdevice_priv *tas_priv, 23 unsigned short chn, unsigned int reg, unsigned int *val) 24 { 25 int ret = 0; 26 27 if (chn < tas_priv->ndev) { 28 struct regmap *map = tas_priv->regmap; 29 30 ret = tas_priv->change_chn_book(tas_priv, chn, 31 TASDEVICE_BOOK_ID(reg)); 32 if (ret < 0) 33 goto out; 34 35 ret = regmap_read(map, TASDEVICE_PGRG(reg), val); 36 if (ret < 0) 37 dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); 38 } else { 39 ret = -EINVAL; 40 dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, 41 chn); 42 } 43 44 out: 45 return ret; 46 } 47 EXPORT_SYMBOL_GPL(tasdevice_dev_read); 48 49 int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv, 50 unsigned short chn, unsigned int reg, unsigned char *data, 51 unsigned int len) 52 { 53 int ret = 0; 54 55 if (chn < tas_priv->ndev) { 56 struct regmap *map = tas_priv->regmap; 57 58 ret = tas_priv->change_chn_book(tas_priv, chn, 59 TASDEVICE_BOOK_ID(reg)); 60 if (ret < 0) 61 goto out; 62 63 ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len); 64 if (ret < 0) 65 dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); 66 } else 67 dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, 68 chn); 69 70 out: 71 return ret; 72 } 73 EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read); 74 75 int tasdevice_dev_write(struct tasdevice_priv *tas_priv, 76 unsigned short chn, unsigned int reg, unsigned int value) 77 { 78 int ret = 0; 79 80 if (chn < tas_priv->ndev) { 81 struct regmap *map = tas_priv->regmap; 82 83 ret = tas_priv->change_chn_book(tas_priv, chn, 84 TASDEVICE_BOOK_ID(reg)); 85 if (ret < 0) 86 goto out; 87 88 ret = regmap_write(map, TASDEVICE_PGRG(reg), 89 value); 90 if (ret < 0) 91 dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); 92 } else { 93 ret = -EINVAL; 94 dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, 95 chn); 96 } 97 98 out: 99 return ret; 100 } 101 EXPORT_SYMBOL_GPL(tasdevice_dev_write); 102 103 int tasdevice_dev_bulk_write( 104 struct tasdevice_priv *tas_priv, unsigned short chn, 105 unsigned int reg, unsigned char *data, 106 unsigned int len) 107 { 108 int ret = 0; 109 110 if (chn < tas_priv->ndev) { 111 struct regmap *map = tas_priv->regmap; 112 113 ret = tas_priv->change_chn_book(tas_priv, chn, 114 TASDEVICE_BOOK_ID(reg)); 115 if (ret < 0) 116 goto out; 117 118 ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg), 119 data, len); 120 if (ret < 0) 121 dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); 122 } else { 123 ret = -EINVAL; 124 dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, 125 chn); 126 } 127 128 out: 129 return ret; 130 } 131 EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write); 132 133 static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog) 134 { 135 struct tasdevice_data *tas_dt; 136 struct tasdev_blk *blk; 137 unsigned int i; 138 139 if (!prog) 140 return; 141 142 tas_dt = &(prog->dev_data); 143 144 if (!tas_dt->dev_blks) 145 return; 146 147 for (i = 0; i < tas_dt->nr_blk; i++) { 148 blk = &(tas_dt->dev_blks[i]); 149 kfree(blk->data); 150 } 151 kfree(tas_dt->dev_blks); 152 } 153 154 static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog, 155 unsigned short nr) 156 { 157 int i; 158 159 for (i = 0; i < nr; i++) 160 tasdev_dsp_prog_blk_remove(&prog[i]); 161 kfree(prog); 162 } 163 164 static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg) 165 { 166 struct tasdevice_data *tas_dt; 167 struct tasdev_blk *blk; 168 unsigned int i; 169 170 if (cfg) { 171 tas_dt = &(cfg->dev_data); 172 173 if (!tas_dt->dev_blks) 174 return; 175 176 for (i = 0; i < tas_dt->nr_blk; i++) { 177 blk = &(tas_dt->dev_blks[i]); 178 kfree(blk->data); 179 } 180 kfree(tas_dt->dev_blks); 181 } 182 } 183 184 static void tasdev_dsp_cfg_remove(struct tasdevice_config *config, 185 unsigned short nr) 186 { 187 int i; 188 189 for (i = 0; i < nr; i++) 190 tasdev_dsp_cfg_blk_remove(&config[i]); 191 kfree(config); 192 } 193 194 void tasdevice_dsp_remove(void *context) 195 { 196 struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context; 197 struct tasdevice_fw *tas_fmw = tas_dev->fmw; 198 199 if (!tas_dev->fmw) 200 return; 201 202 if (tas_fmw->programs) 203 tasdev_dsp_prog_remove(tas_fmw->programs, 204 tas_fmw->nr_programs); 205 if (tas_fmw->configs) 206 tasdev_dsp_cfg_remove(tas_fmw->configs, 207 tas_fmw->nr_configurations); 208 kfree(tas_fmw); 209 tas_dev->fmw = NULL; 210 } 211 EXPORT_SYMBOL_GPL(tasdevice_dsp_remove); 212 213 void tasdevice_remove(struct tasdevice_priv *tas_priv) 214 { 215 mutex_destroy(&tas_priv->codec_lock); 216 } 217 EXPORT_SYMBOL_GPL(tasdevice_remove); 218 219 MODULE_DESCRIPTION("TAS2781 common library"); 220 MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); 221 MODULE_LICENSE("GPL"); 222