diff options
author | Petr Kulhavy <brain@jikos.cz> | 2016-05-26 13:26:17 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-05-30 11:21:01 -0400 |
commit | 4b9e385b9dac5c84640b13e70dbbd8e2bb669c8d (patch) | |
tree | e1ac4f764df5197508a0dc590e94782aa9892195 | |
parent | dca3fed85e922239f31e9864e37f3fc92651142d (diff) |
ASoC: tas571x: add biquads for TAS5717/19
TAS571x features multiple biquad filters. Their coefficients
are stored in 20-byte registers, which cannot be supported by
regmap.
This patch adds read and write functions for multi-word (32-bit) register
access and mixer controls for the biquads. The multi-word read/write
functions can be used in the future to implement other features like DRC or
output mixer.
Signed-off-by: Petr Kulhavy <brain@jikos.cz>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/codecs/tas571x.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index b8d19b77bde9..bc1fbafb8ea4 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <sound/pcm_params.h> | 28 | #include <sound/pcm_params.h> |
29 | #include <sound/soc.h> | 29 | #include <sound/soc.h> |
30 | #include <sound/tlv.h> | 30 | #include <sound/tlv.h> |
31 | #include <asm/unaligned.h> | ||
31 | 32 | ||
32 | #include "tas571x.h" | 33 | #include "tas571x.h" |
33 | 34 | ||
@@ -135,6 +136,129 @@ static int tas571x_reg_read(void *context, unsigned int reg, | |||
135 | return 0; | 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 | static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) | 262 | static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) |
139 | { | 263 | { |
140 | struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); | 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 | .digital_mute = tas571x_mute, | 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 | static const char *const tas5711_supply_names[] = { | 377 | static const char *const tas5711_supply_names[] = { |
245 | "AVDD", | 378 | "AVDD", |
246 | "DVDD", | 379 | "DVDD", |
@@ -340,6 +473,43 @@ static const struct snd_kcontrol_new tas5717_controls[] = { | |||
340 | TAS571X_SOFT_MUTE_REG, | 473 | TAS571X_SOFT_MUTE_REG, |
341 | TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, | 474 | TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, |
342 | 1, 1), | 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 | static const struct reg_default tas5717_reg_defaults[] = { | 515 | static const struct reg_default tas5717_reg_defaults[] = { |