|
28 | 28 | #include <sound/pcm_params.h>
|
29 | 29 | #include <sound/soc.h>
|
30 | 30 | #include <sound/tlv.h>
|
| 31 | +#include <asm/unaligned.h> |
31 | 32 |
|
32 | 33 | #include "tas571x.h"
|
33 | 34 |
|
@@ -135,6 +136,129 @@ static int tas571x_reg_read(void *context, unsigned int reg,
|
135 | 136 | return 0;
|
136 | 137 | }
|
137 | 138 |
|
| 139 | +/* |
| 140 | + * register write for 8- and 20-byte registers |
| 141 | + */ |
| 142 | +static int tas571x_reg_write_multiword(struct i2c_client *client, |
| 143 | + unsigned int reg, const long values[], size_t len) |
| 144 | +{ |
| 145 | + size_t i; |
| 146 | + uint8_t *buf, *p; |
| 147 | + int ret; |
| 148 | + size_t send_size = 1 + len * sizeof(uint32_t); |
| 149 | + |
| 150 | + buf = kzalloc(send_size, GFP_KERNEL | GFP_DMA); |
| 151 | + if (!buf) |
| 152 | + return -ENOMEM; |
| 153 | + buf[0] = reg; |
| 154 | + |
| 155 | + for (i = 0, p = buf + 1; i < len; i++, p += sizeof(uint32_t)) |
| 156 | + put_unaligned_be32(values[i], p); |
| 157 | + |
| 158 | + ret = i2c_master_send(client, buf, send_size); |
| 159 | + |
| 160 | + kfree(buf); |
| 161 | + |
| 162 | + if (ret == send_size) |
| 163 | + return 0; |
| 164 | + else if (ret < 0) |
| 165 | + return ret; |
| 166 | + else |
| 167 | + return -EIO; |
| 168 | +} |
| 169 | + |
| 170 | +/* |
| 171 | + * register read for 8- and 20-byte registers |
| 172 | + */ |
| 173 | +static int tas571x_reg_read_multiword(struct i2c_client *client, |
| 174 | + unsigned int reg, long values[], size_t len) |
| 175 | +{ |
| 176 | + unsigned int i; |
| 177 | + uint8_t send_buf; |
| 178 | + uint8_t *recv_buf, *p; |
| 179 | + struct i2c_msg msgs[2]; |
| 180 | + unsigned int recv_size = len * sizeof(uint32_t); |
| 181 | + int ret; |
| 182 | + |
| 183 | + recv_buf = kzalloc(recv_size, GFP_KERNEL | GFP_DMA); |
| 184 | + if (!recv_buf) |
| 185 | + return -ENOMEM; |
| 186 | + |
| 187 | + send_buf = reg; |
| 188 | + |
| 189 | + msgs[0].addr = client->addr; |
| 190 | + msgs[0].len = sizeof(send_buf); |
| 191 | + msgs[0].buf = &send_buf; |
| 192 | + msgs[0].flags = 0; |
| 193 | + |
| 194 | + msgs[1].addr = client->addr; |
| 195 | + msgs[1].len = recv_size; |
| 196 | + msgs[1].buf = recv_buf; |
| 197 | + msgs[1].flags = I2C_M_RD; |
| 198 | + |
| 199 | + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); |
| 200 | + if (ret < 0) |
| 201 | + goto err_ret; |
| 202 | + else if (ret != ARRAY_SIZE(msgs)) { |
| 203 | + ret = -EIO; |
| 204 | + goto err_ret; |
| 205 | + } |
| 206 | + |
| 207 | + for (i = 0, p = recv_buf; i < len; i++, p += sizeof(uint32_t)) |
| 208 | + values[i] = get_unaligned_be32(p); |
| 209 | + |
| 210 | +err_ret: |
| 211 | + kfree(recv_buf); |
| 212 | + return ret; |
| 213 | +} |
| 214 | + |
| 215 | +/* |
| 216 | + * Integer array controls for setting biquad, mixer, DRC coefficients. |
| 217 | + * According to the datasheet each coefficient is effectively 26bits, |
| 218 | + * i.e. stored as 32bits, where bits [31:26] are ignored. |
| 219 | + * TI's TAS57xx Graphical Development Environment tool however produces |
| 220 | + * coefficients with more than 26 bits. For this reason we allow values |
| 221 | + * in the full 32-bits reange. |
| 222 | + * The coefficients are ordered as given in the TAS571x data sheet: |
| 223 | + * b0, b1, b2, a1, a2 |
| 224 | + */ |
| 225 | + |
| 226 | +static int tas571x_coefficient_info(struct snd_kcontrol *kcontrol, |
| 227 | + struct snd_ctl_elem_info *uinfo) |
| 228 | +{ |
| 229 | + int numcoef = kcontrol->private_value >> 16; |
| 230 | + |
| 231 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
| 232 | + uinfo->count = numcoef; |
| 233 | + uinfo->value.integer.min = 0; |
| 234 | + uinfo->value.integer.max = 0xffffffff; |
| 235 | + return 0; |
| 236 | +} |
| 237 | + |
| 238 | +static int tas571x_coefficient_get(struct snd_kcontrol *kcontrol, |
| 239 | + struct snd_ctl_elem_value *ucontrol) |
| 240 | +{ |
| 241 | + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); |
| 242 | + struct i2c_client *i2c = to_i2c_client(codec->dev); |
| 243 | + int numcoef = kcontrol->private_value >> 16; |
| 244 | + int index = kcontrol->private_value & 0xffff; |
| 245 | + |
| 246 | + return tas571x_reg_read_multiword(i2c, index, |
| 247 | + ucontrol->value.integer.value, numcoef); |
| 248 | +} |
| 249 | + |
| 250 | +static int tas571x_coefficient_put(struct snd_kcontrol *kcontrol, |
| 251 | + struct snd_ctl_elem_value *ucontrol) |
| 252 | +{ |
| 253 | + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); |
| 254 | + struct i2c_client *i2c = to_i2c_client(codec->dev); |
| 255 | + int numcoef = kcontrol->private_value >> 16; |
| 256 | + int index = kcontrol->private_value & 0xffff; |
| 257 | + |
| 258 | + return tas571x_reg_write_multiword(i2c, index, |
| 259 | + ucontrol->value.integer.value, numcoef); |
| 260 | +} |
| 261 | + |
138 | 262 | static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
|
139 | 263 | {
|
140 | 264 | struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
|
@@ -241,6 +365,15 @@ static const struct snd_soc_dai_ops tas571x_dai_ops = {
|
241 | 365 | .digital_mute = tas571x_mute,
|
242 | 366 | };
|
243 | 367 |
|
| 368 | + |
| 369 | +#define BIQUAD_COEFS(xname, reg) \ |
| 370 | +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ |
| 371 | + .info = tas571x_coefficient_info, \ |
| 372 | + .get = tas571x_coefficient_get,\ |
| 373 | + .put = tas571x_coefficient_put, \ |
| 374 | + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ |
| 375 | + .private_value = reg | (5 << 16) } |
| 376 | + |
244 | 377 | static const char *const tas5711_supply_names[] = {
|
245 | 378 | "AVDD",
|
246 | 379 | "DVDD",
|
@@ -340,6 +473,43 @@ static const struct snd_kcontrol_new tas5717_controls[] = {
|
340 | 473 | TAS571X_SOFT_MUTE_REG,
|
341 | 474 | TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
|
342 | 475 | 1, 1),
|
| 476 | + |
| 477 | + /* |
| 478 | + * The biquads are named according to the register names. |
| 479 | + * Please note that TI's TAS57xx Graphical Development Environment |
| 480 | + * tool names them different. |
| 481 | + */ |
| 482 | + BIQUAD_COEFS("CH1 - Biquad 0", TAS5717_CH1_BQ0_REG), |
| 483 | + BIQUAD_COEFS("CH1 - Biquad 1", TAS5717_CH1_BQ1_REG), |
| 484 | + BIQUAD_COEFS("CH1 - Biquad 2", TAS5717_CH1_BQ2_REG), |
| 485 | + BIQUAD_COEFS("CH1 - Biquad 3", TAS5717_CH1_BQ3_REG), |
| 486 | + BIQUAD_COEFS("CH1 - Biquad 4", TAS5717_CH1_BQ4_REG), |
| 487 | + BIQUAD_COEFS("CH1 - Biquad 5", TAS5717_CH1_BQ5_REG), |
| 488 | + BIQUAD_COEFS("CH1 - Biquad 6", TAS5717_CH1_BQ6_REG), |
| 489 | + BIQUAD_COEFS("CH1 - Biquad 7", TAS5717_CH1_BQ7_REG), |
| 490 | + BIQUAD_COEFS("CH1 - Biquad 8", TAS5717_CH1_BQ8_REG), |
| 491 | + BIQUAD_COEFS("CH1 - Biquad 9", TAS5717_CH1_BQ9_REG), |
| 492 | + BIQUAD_COEFS("CH1 - Biquad 10", TAS5717_CH1_BQ10_REG), |
| 493 | + BIQUAD_COEFS("CH1 - Biquad 11", TAS5717_CH1_BQ11_REG), |
| 494 | + |
| 495 | + BIQUAD_COEFS("CH2 - Biquad 0", TAS5717_CH2_BQ0_REG), |
| 496 | + BIQUAD_COEFS("CH2 - Biquad 1", TAS5717_CH2_BQ1_REG), |
| 497 | + BIQUAD_COEFS("CH2 - Biquad 2", TAS5717_CH2_BQ2_REG), |
| 498 | + BIQUAD_COEFS("CH2 - Biquad 3", TAS5717_CH2_BQ3_REG), |
| 499 | + BIQUAD_COEFS("CH2 - Biquad 4", TAS5717_CH2_BQ4_REG), |
| 500 | + BIQUAD_COEFS("CH2 - Biquad 5", TAS5717_CH2_BQ5_REG), |
| 501 | + BIQUAD_COEFS("CH2 - Biquad 6", TAS5717_CH2_BQ6_REG), |
| 502 | + BIQUAD_COEFS("CH2 - Biquad 7", TAS5717_CH2_BQ7_REG), |
| 503 | + BIQUAD_COEFS("CH2 - Biquad 8", TAS5717_CH2_BQ8_REG), |
| 504 | + BIQUAD_COEFS("CH2 - Biquad 9", TAS5717_CH2_BQ9_REG), |
| 505 | + BIQUAD_COEFS("CH2 - Biquad 10", TAS5717_CH2_BQ10_REG), |
| 506 | + BIQUAD_COEFS("CH2 - Biquad 11", TAS5717_CH2_BQ11_REG), |
| 507 | + |
| 508 | + BIQUAD_COEFS("CH3 - Biquad 0", TAS5717_CH3_BQ0_REG), |
| 509 | + BIQUAD_COEFS("CH3 - Biquad 1", TAS5717_CH3_BQ1_REG), |
| 510 | + |
| 511 | + BIQUAD_COEFS("CH4 - Biquad 0", TAS5717_CH4_BQ0_REG), |
| 512 | + BIQUAD_COEFS("CH4 - Biquad 1", TAS5717_CH4_BQ1_REG), |
343 | 513 | };
|
344 | 514 |
|
345 | 515 | static const struct reg_default tas5717_reg_defaults[] = {
|
|
0 commit comments