kernel: update kernel 4.4 to 4.4.52
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.9 / 0119-Revert-Revert-Added-driver-for-HiFiBerry-Amp-amplifi.patch
1 From 20779cac1f8632b2315e1421fdecc960dac7f1c3 Mon Sep 17 00:00:00 2001
2 From: Phil Elwell <phil@raspberrypi.org>
3 Date: Mon, 9 Jan 2017 09:23:06 +0000
4 Subject: [PATCH] Revert "Revert "Added driver for HiFiBerry Amp amplifier
5 add-on board""
6
7 This reverts commit bf84babd8fffcb79c60f1342c2416f8e1e4b7af9.
8 ---
9 sound/soc/bcm/Kconfig | 7 +
10 sound/soc/bcm/Makefile | 2 +
11 sound/soc/bcm/hifiberry_amp.c | 129 +++++++++++++++
12 sound/soc/codecs/Kconfig | 4 +
13 sound/soc/codecs/Makefile | 2 +
14 sound/soc/codecs/tas5713.c | 369 ++++++++++++++++++++++++++++++++++++++++++
15 sound/soc/codecs/tas5713.h | 210 ++++++++++++++++++++++++
16 7 files changed, 723 insertions(+)
17 create mode 100644 sound/soc/bcm/hifiberry_amp.c
18 create mode 100644 sound/soc/codecs/tas5713.c
19 create mode 100644 sound/soc/codecs/tas5713.h
20
21 --- a/sound/soc/bcm/Kconfig
22 +++ b/sound/soc/bcm/Kconfig
23 @@ -38,6 +38,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DIGI
24 help
25 Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board.
26
27 +config SND_BCM2708_SOC_HIFIBERRY_AMP
28 + tristate "Support for the HifiBerry Amp"
29 + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
30 + select SND_SOC_TAS5713
31 + help
32 + Say Y or M if you want to add support for the HifiBerry Amp amplifier board.
33 +
34 config SND_BCM2708_SOC_RPI_DAC
35 tristate "Support for RPi-DAC"
36 depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
37 --- a/sound/soc/bcm/Makefile
38 +++ b/sound/soc/bcm/Makefile
39 @@ -10,6 +10,7 @@ obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-
40
41 # BCM2708 Machine Support
42 snd-soc-adau1977-adc-objs := adau1977-adc.o
43 +snd-soc-hifiberry-amp-objs := hifiberry_amp.o
44 snd-soc-hifiberry-dac-objs := hifiberry_dac.o
45 snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o
46 snd-soc-hifiberry-digi-objs := hifiberry_digi.o
47 @@ -27,6 +28,7 @@ snd-soc-allo-piano-dac-objs := allo-pian
48 snd-soc-pisound-objs := pisound.o
49
50 obj-$(CONFIG_SND_BCM2708_SOC_ADAU1977_ADC) += snd-soc-adau1977-adc.o
51 +obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) += snd-soc-hifiberry-amp.o
52 obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o
53 obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o
54 obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o
55 --- /dev/null
56 +++ b/sound/soc/bcm/hifiberry_amp.c
57 @@ -0,0 +1,129 @@
58 +/*
59 + * ASoC Driver for HifiBerry AMP
60 + *
61 + * Author: Sebastian Eickhoff <basti.eickhoff@googlemail.com>
62 + * Copyright 2014
63 + *
64 + * This program is free software; you can redistribute it and/or
65 + * modify it under the terms of the GNU General Public License
66 + * version 2 as published by the Free Software Foundation.
67 + *
68 + * This program is distributed in the hope that it will be useful, but
69 + * WITHOUT ANY WARRANTY; without even the implied warranty of
70 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
71 + * General Public License for more details.
72 + */
73 +
74 +#include <linux/module.h>
75 +#include <linux/platform_device.h>
76 +
77 +#include <sound/core.h>
78 +#include <sound/pcm.h>
79 +#include <sound/pcm_params.h>
80 +#include <sound/soc.h>
81 +#include <sound/jack.h>
82 +
83 +static int snd_rpi_hifiberry_amp_init(struct snd_soc_pcm_runtime *rtd)
84 +{
85 + // ToDo: init of the dsp-registers.
86 + return 0;
87 +}
88 +
89 +static int snd_rpi_hifiberry_amp_hw_params( struct snd_pcm_substream *substream,
90 + struct snd_pcm_hw_params *params )
91 +{
92 + struct snd_soc_pcm_runtime *rtd = substream->private_data;
93 + struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
94 +
95 + return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
96 +}
97 +
98 +static struct snd_soc_ops snd_rpi_hifiberry_amp_ops = {
99 + .hw_params = snd_rpi_hifiberry_amp_hw_params,
100 +};
101 +
102 +static struct snd_soc_dai_link snd_rpi_hifiberry_amp_dai[] = {
103 + {
104 + .name = "HifiBerry AMP",
105 + .stream_name = "HifiBerry AMP HiFi",
106 + .cpu_dai_name = "bcm2708-i2s.0",
107 + .codec_dai_name = "tas5713-hifi",
108 + .platform_name = "bcm2708-i2s.0",
109 + .codec_name = "tas5713.1-001b",
110 + .dai_fmt = SND_SOC_DAIFMT_I2S |
111 + SND_SOC_DAIFMT_NB_NF |
112 + SND_SOC_DAIFMT_CBS_CFS,
113 + .ops = &snd_rpi_hifiberry_amp_ops,
114 + .init = snd_rpi_hifiberry_amp_init,
115 + },
116 +};
117 +
118 +
119 +static struct snd_soc_card snd_rpi_hifiberry_amp = {
120 + .name = "snd_rpi_hifiberry_amp",
121 + .driver_name = "HifiberryAmp",
122 + .owner = THIS_MODULE,
123 + .dai_link = snd_rpi_hifiberry_amp_dai,
124 + .num_links = ARRAY_SIZE(snd_rpi_hifiberry_amp_dai),
125 +};
126 +
127 +static const struct of_device_id snd_rpi_hifiberry_amp_of_match[] = {
128 + { .compatible = "hifiberry,hifiberry-amp", },
129 + {},
130 +};
131 +MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_amp_of_match);
132 +
133 +
134 +static int snd_rpi_hifiberry_amp_probe(struct platform_device *pdev)
135 +{
136 + int ret = 0;
137 +
138 + snd_rpi_hifiberry_amp.dev = &pdev->dev;
139 +
140 + if (pdev->dev.of_node) {
141 + struct device_node *i2s_node;
142 + struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_amp_dai[0];
143 + i2s_node = of_parse_phandle(pdev->dev.of_node,
144 + "i2s-controller", 0);
145 +
146 + if (i2s_node) {
147 + dai->cpu_dai_name = NULL;
148 + dai->cpu_of_node = i2s_node;
149 + dai->platform_name = NULL;
150 + dai->platform_of_node = i2s_node;
151 + }
152 + }
153 +
154 + ret = snd_soc_register_card(&snd_rpi_hifiberry_amp);
155 +
156 + if (ret != 0) {
157 + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
158 + }
159 +
160 + return ret;
161 +}
162 +
163 +
164 +static int snd_rpi_hifiberry_amp_remove(struct platform_device *pdev)
165 +{
166 + return snd_soc_unregister_card(&snd_rpi_hifiberry_amp);
167 +}
168 +
169 +
170 +static struct platform_driver snd_rpi_hifiberry_amp_driver = {
171 + .driver = {
172 + .name = "snd-hifiberry-amp",
173 + .owner = THIS_MODULE,
174 + .of_match_table = snd_rpi_hifiberry_amp_of_match,
175 + },
176 + .probe = snd_rpi_hifiberry_amp_probe,
177 + .remove = snd_rpi_hifiberry_amp_remove,
178 +};
179 +
180 +
181 +module_platform_driver(snd_rpi_hifiberry_amp_driver);
182 +
183 +
184 +MODULE_AUTHOR("Sebastian Eickhoff <basti.eickhoff@googlemail.com>");
185 +MODULE_DESCRIPTION("ASoC driver for HiFiBerry-AMP");
186 +MODULE_LICENSE("GPL v2");
187 --- a/sound/soc/codecs/Kconfig
188 +++ b/sound/soc/codecs/Kconfig
189 @@ -139,6 +139,7 @@ config SND_SOC_ALL_CODECS
190 select SND_SOC_TFA9879 if I2C
191 select SND_SOC_TLV320AIC23_I2C if I2C
192 select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
193 + select SND_SOC_TAS5713 if I2C
194 select SND_SOC_TLV320AIC26 if SPI_MASTER
195 select SND_SOC_TLV320AIC31XX if I2C
196 select SND_SOC_TLV320AIC32X4_I2C if I2C
197 @@ -821,6 +822,9 @@ config SND_SOC_TFA9879
198 tristate "NXP Semiconductors TFA9879 amplifier"
199 depends on I2C
200
201 +config SND_SOC_TAS5713
202 + tristate
203 +
204 config SND_SOC_TLV320AIC23
205 tristate
206
207 --- a/sound/soc/codecs/Makefile
208 +++ b/sound/soc/codecs/Makefile
209 @@ -144,6 +144,7 @@ snd-soc-tas5086-objs := tas5086.o
210 snd-soc-tas571x-objs := tas571x.o
211 snd-soc-tas5720-objs := tas5720.o
212 snd-soc-tfa9879-objs := tfa9879.o
213 +snd-soc-tas5713-objs := tas5713.o
214 snd-soc-tlv320aic23-objs := tlv320aic23.o
215 snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
216 snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
217 @@ -366,6 +367,7 @@ obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc
218 obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
219 obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
220 obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
221 +obj-$(CONFIG_SND_SOC_TAS5713) += snd-soc-tas5713.o
222 obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
223 obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
224 obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o
225 --- /dev/null
226 +++ b/sound/soc/codecs/tas5713.c
227 @@ -0,0 +1,369 @@
228 +/*
229 + * ASoC Driver for TAS5713
230 + *
231 + * Author: Sebastian Eickhoff <basti.eickhoff@googlemail.com>
232 + * Copyright 2014
233 + *
234 + * This program is free software; you can redistribute it and/or
235 + * modify it under the terms of the GNU General Public License
236 + * version 2 as published by the Free Software Foundation.
237 + *
238 + * This program is distributed in the hope that it will be useful, but
239 + * WITHOUT ANY WARRANTY; without even the implied warranty of
240 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
241 + * General Public License for more details.
242 + */
243 +
244 +#include <linux/module.h>
245 +#include <linux/moduleparam.h>
246 +#include <linux/init.h>
247 +#include <linux/delay.h>
248 +#include <linux/pm.h>
249 +#include <linux/i2c.h>
250 +#include <linux/of_device.h>
251 +#include <linux/spi/spi.h>
252 +#include <linux/regmap.h>
253 +#include <linux/regulator/consumer.h>
254 +#include <linux/slab.h>
255 +#include <sound/core.h>
256 +#include <sound/pcm.h>
257 +#include <sound/pcm_params.h>
258 +#include <sound/soc.h>
259 +#include <sound/initval.h>
260 +#include <sound/tlv.h>
261 +
262 +#include <linux/kernel.h>
263 +#include <linux/string.h>
264 +#include <linux/fs.h>
265 +#include <asm/uaccess.h>
266 +
267 +#include "tas5713.h"
268 +
269 +
270 +static struct i2c_client *i2c;
271 +
272 +struct tas5713_priv {
273 + struct regmap *regmap;
274 + int mclk_div;
275 + struct snd_soc_codec *codec;
276 +};
277 +
278 +static struct tas5713_priv *priv_data;
279 +
280 +
281 +
282 +
283 +/*
284 + * _ _ ___ _ ___ _ _
285 + * /_\ | | / __| /_\ / __|___ _ _| |_ _ _ ___| |___
286 + * / _ \| |__\__ \/ _ \ | (__/ _ \ ' \ _| '_/ _ \ (_-<
287 + * /_/ \_\____|___/_/ \_\ \___\___/_||_\__|_| \___/_/__/
288 + *
289 + */
290 +
291 +static const DECLARE_TLV_DB_SCALE(tas5713_vol_tlv, -10000, 50, 1);
292 +
293 +
294 +static const struct snd_kcontrol_new tas5713_snd_controls[] = {
295 + SOC_SINGLE_TLV ("Master" , TAS5713_VOL_MASTER, 0, 248, 1, tas5713_vol_tlv),
296 + SOC_DOUBLE_R_TLV("Channels" , TAS5713_VOL_CH1, TAS5713_VOL_CH2, 0, 248, 1, tas5713_vol_tlv)
297 +};
298 +
299 +
300 +
301 +
302 +/*
303 + * __ __ _ _ ___ _
304 + * | \/ |__ _ __| |_ (_)_ _ ___ | \ _ _(_)_ _____ _ _
305 + * | |\/| / _` / _| ' \| | ' \/ -_) | |) | '_| \ V / -_) '_|
306 + * |_| |_\__,_\__|_||_|_|_||_\___| |___/|_| |_|\_/\___|_|
307 + *
308 + */
309 +
310 +static int tas5713_hw_params(struct snd_pcm_substream *substream,
311 + struct snd_pcm_hw_params *params,
312 + struct snd_soc_dai *dai)
313 +{
314 + u16 blen = 0x00;
315 +
316 + struct snd_soc_codec *codec;
317 + codec = dai->codec;
318 + priv_data->codec = dai->codec;
319 +
320 + switch (params_format(params)) {
321 + case SNDRV_PCM_FORMAT_S16_LE:
322 + blen = 0x03;
323 + break;
324 + case SNDRV_PCM_FORMAT_S20_3LE:
325 + blen = 0x1;
326 + break;
327 + case SNDRV_PCM_FORMAT_S24_LE:
328 + blen = 0x04;
329 + break;
330 + case SNDRV_PCM_FORMAT_S32_LE:
331 + blen = 0x05;
332 + break;
333 + default:
334 + dev_err(dai->dev, "Unsupported word length: %u\n",
335 + params_format(params));
336 + return -EINVAL;
337 + }
338 +
339 + // set word length
340 + snd_soc_update_bits(codec, TAS5713_SERIAL_DATA_INTERFACE, 0x7, blen);
341 +
342 + return 0;
343 +}
344 +
345 +
346 +static int tas5713_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
347 +{
348 + unsigned int val = 0;
349 +
350 + struct tas5713_priv *tas5713;
351 + struct snd_soc_codec *codec = dai->codec;
352 + tas5713 = snd_soc_codec_get_drvdata(codec);
353 +
354 + if (mute) {
355 + val = TAS5713_SOFT_MUTE_ALL;
356 + }
357 +
358 + return regmap_write(tas5713->regmap, TAS5713_SOFT_MUTE, val);
359 +}
360 +
361 +
362 +static const struct snd_soc_dai_ops tas5713_dai_ops = {
363 + .hw_params = tas5713_hw_params,
364 + .mute_stream = tas5713_mute_stream,
365 +};
366 +
367 +
368 +static struct snd_soc_dai_driver tas5713_dai = {
369 + .name = "tas5713-hifi",
370 + .playback = {
371 + .stream_name = "Playback",
372 + .channels_min = 2,
373 + .channels_max = 2,
374 + .rates = SNDRV_PCM_RATE_8000_48000,
375 + .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE ),
376 + },
377 + .ops = &tas5713_dai_ops,
378 +};
379 +
380 +
381 +
382 +
383 +/*
384 + * ___ _ ___ _
385 + * / __|___ __| |___ __ | \ _ _(_)_ _____ _ _
386 + * | (__/ _ \/ _` / -_) _| | |) | '_| \ V / -_) '_|
387 + * \___\___/\__,_\___\__| |___/|_| |_|\_/\___|_|
388 + *
389 + */
390 +
391 +static int tas5713_remove(struct snd_soc_codec *codec)
392 +{
393 + struct tas5713_priv *tas5713;
394 +
395 + tas5713 = snd_soc_codec_get_drvdata(codec);
396 +
397 + return 0;
398 +}
399 +
400 +
401 +static int tas5713_probe(struct snd_soc_codec *codec)
402 +{
403 + struct tas5713_priv *tas5713;
404 + int i, ret;
405 +
406 + i2c = container_of(codec->dev, struct i2c_client, dev);
407 +
408 + tas5713 = snd_soc_codec_get_drvdata(codec);
409 +
410 + // Reset error
411 + ret = snd_soc_write(codec, TAS5713_ERROR_STATUS, 0x00);
412 + if (ret < 0) return ret;
413 +
414 + // Trim oscillator
415 + ret = snd_soc_write(codec, TAS5713_OSC_TRIM, 0x00);
416 + if (ret < 0) return ret;
417 + msleep(1000);
418 +
419 + // Reset error
420 + ret = snd_soc_write(codec, TAS5713_ERROR_STATUS, 0x00);
421 + if (ret < 0) return ret;
422 +
423 + // Clock mode: 44/48kHz, MCLK=64xfs
424 + ret = snd_soc_write(codec, TAS5713_CLOCK_CTRL, 0x60);
425 + if (ret < 0) return ret;
426 +
427 + // I2S 24bit
428 + ret = snd_soc_write(codec, TAS5713_SERIAL_DATA_INTERFACE, 0x05);
429 + if (ret < 0) return ret;
430 +
431 + // Unmute
432 + ret = snd_soc_write(codec, TAS5713_SYSTEM_CTRL2, 0x00);
433 + if (ret < 0) return ret;
434 + ret = snd_soc_write(codec, TAS5713_SOFT_MUTE, 0x00);
435 + if (ret < 0) return ret;
436 +
437 + // Set volume to 0db
438 + ret = snd_soc_write(codec, TAS5713_VOL_MASTER, 0x00);
439 + if (ret < 0) return ret;
440 +
441 + // Now start programming the default initialization sequence
442 + for (i = 0; i < ARRAY_SIZE(tas5713_init_sequence); ++i) {
443 + ret = i2c_master_send(i2c,
444 + tas5713_init_sequence[i].data,
445 + tas5713_init_sequence[i].size);
446 + if (ret < 0) {
447 + printk(KERN_INFO "TAS5713 CODEC PROBE: InitSeq returns: %d\n", ret);
448 + }
449 + }
450 +
451 + // Unmute
452 + ret = snd_soc_write(codec, TAS5713_SYSTEM_CTRL2, 0x00);
453 + if (ret < 0) return ret;
454 +
455 + return 0;
456 +}
457 +
458 +
459 +static struct snd_soc_codec_driver soc_codec_dev_tas5713 = {
460 + .probe = tas5713_probe,
461 + .remove = tas5713_remove,
462 + .controls = tas5713_snd_controls,
463 + .num_controls = ARRAY_SIZE(tas5713_snd_controls),
464 +};
465 +
466 +
467 +
468 +
469 +/*
470 + * ___ ___ ___ ___ _
471 + * |_ _|_ ) __| | \ _ _(_)_ _____ _ _
472 + * | | / / (__ | |) | '_| \ V / -_) '_|
473 + * |___/___\___| |___/|_| |_|\_/\___|_|
474 + *
475 + */
476 +
477 +static const struct reg_default tas5713_reg_defaults[] = {
478 + { 0x07 ,0x80 }, // R7 - VOL_MASTER - -40dB
479 + { 0x08 , 30 }, // R8 - VOL_CH1 - 0dB
480 + { 0x09 , 30 }, // R9 - VOL_CH2 - 0dB
481 + { 0x0A ,0x80 }, // R10 - VOL_HEADPHONE - -40dB
482 +};
483 +
484 +
485 +static bool tas5713_reg_volatile(struct device *dev, unsigned int reg)
486 +{
487 + switch (reg) {
488 + case TAS5713_DEVICE_ID:
489 + case TAS5713_ERROR_STATUS:
490 + return true;
491 + default:
492 + return false;
493 + }
494 +}
495 +
496 +
497 +static const struct of_device_id tas5713_of_match[] = {
498 + { .compatible = "ti,tas5713", },
499 + { }
500 +};
501 +MODULE_DEVICE_TABLE(of, tas5713_of_match);
502 +
503 +
504 +static struct regmap_config tas5713_regmap_config = {
505 + .reg_bits = 8,
506 + .val_bits = 8,
507 +
508 + .max_register = TAS5713_MAX_REGISTER,
509 + .volatile_reg = tas5713_reg_volatile,
510 +
511 + .cache_type = REGCACHE_RBTREE,
512 + .reg_defaults = tas5713_reg_defaults,
513 + .num_reg_defaults = ARRAY_SIZE(tas5713_reg_defaults),
514 +};
515 +
516 +
517 +static int tas5713_i2c_probe(struct i2c_client *i2c,
518 + const struct i2c_device_id *id)
519 +{
520 + int ret;
521 +
522 + priv_data = devm_kzalloc(&i2c->dev, sizeof *priv_data, GFP_KERNEL);
523 + if (!priv_data)
524 + return -ENOMEM;
525 +
526 + priv_data->regmap = devm_regmap_init_i2c(i2c, &tas5713_regmap_config);
527 + if (IS_ERR(priv_data->regmap)) {
528 + ret = PTR_ERR(priv_data->regmap);
529 + return ret;
530 + }
531 +
532 + i2c_set_clientdata(i2c, priv_data);
533 +
534 + ret = snd_soc_register_codec(&i2c->dev,
535 + &soc_codec_dev_tas5713, &tas5713_dai, 1);
536 +
537 + return ret;
538 +}
539 +
540 +
541 +static int tas5713_i2c_remove(struct i2c_client *i2c)
542 +{
543 + snd_soc_unregister_codec(&i2c->dev);
544 + i2c_set_clientdata(i2c, NULL);
545 +
546 + kfree(priv_data);
547 +
548 + return 0;
549 +}
550 +
551 +
552 +static const struct i2c_device_id tas5713_i2c_id[] = {
553 + { "tas5713", 0 },
554 + { }
555 +};
556 +
557 +MODULE_DEVICE_TABLE(i2c, tas5713_i2c_id);
558 +
559 +
560 +static struct i2c_driver tas5713_i2c_driver = {
561 + .driver = {
562 + .name = "tas5713",
563 + .owner = THIS_MODULE,
564 + .of_match_table = tas5713_of_match,
565 + },
566 + .probe = tas5713_i2c_probe,
567 + .remove = tas5713_i2c_remove,
568 + .id_table = tas5713_i2c_id
569 +};
570 +
571 +
572 +static int __init tas5713_modinit(void)
573 +{
574 + int ret = 0;
575 +
576 + ret = i2c_add_driver(&tas5713_i2c_driver);
577 + if (ret) {
578 + printk(KERN_ERR "Failed to register tas5713 I2C driver: %d\n",
579 + ret);
580 + }
581 +
582 + return ret;
583 +}
584 +module_init(tas5713_modinit);
585 +
586 +
587 +static void __exit tas5713_exit(void)
588 +{
589 + i2c_del_driver(&tas5713_i2c_driver);
590 +}
591 +module_exit(tas5713_exit);
592 +
593 +
594 +MODULE_AUTHOR("Sebastian Eickhoff <basti.eickhoff@googlemail.com>");
595 +MODULE_DESCRIPTION("ASoC driver for TAS5713");
596 +MODULE_LICENSE("GPL v2");
597 --- /dev/null
598 +++ b/sound/soc/codecs/tas5713.h
599 @@ -0,0 +1,210 @@
600 +/*
601 + * ASoC Driver for TAS5713
602 + *
603 + * Author: Sebastian Eickhoff <basti.eickhoff@googlemail.com>
604 + * Copyright 2014
605 + *
606 + * This program is free software; you can redistribute it and/or
607 + * modify it under the terms of the GNU General Public License
608 + * version 2 as published by the Free Software Foundation.
609 + *
610 + * This program is distributed in the hope that it will be useful, but
611 + * WITHOUT ANY WARRANTY; without even the implied warranty of
612 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
613 + * General Public License for more details.
614 + */
615 +
616 +#ifndef _TAS5713_H
617 +#define _TAS5713_H
618 +
619 +
620 +// TAS5713 I2C-bus register addresses
621 +
622 +#define TAS5713_CLOCK_CTRL 0x00
623 +#define TAS5713_DEVICE_ID 0x01
624 +#define TAS5713_ERROR_STATUS 0x02
625 +#define TAS5713_SYSTEM_CTRL1 0x03
626 +#define TAS5713_SERIAL_DATA_INTERFACE 0x04
627 +#define TAS5713_SYSTEM_CTRL2 0x05
628 +#define TAS5713_SOFT_MUTE 0x06
629 +#define TAS5713_VOL_MASTER 0x07
630 +#define TAS5713_VOL_CH1 0x08
631 +#define TAS5713_VOL_CH2 0x09
632 +#define TAS5713_VOL_HEADPHONE 0x0A
633 +#define TAS5713_VOL_CONFIG 0x0E
634 +#define TAS5713_MODULATION_LIMIT 0x10
635 +#define TAS5713_IC_DLY_CH1 0x11
636 +#define TAS5713_IC_DLY_CH2 0x12
637 +#define TAS5713_IC_DLY_CH3 0x13
638 +#define TAS5713_IC_DLY_CH4 0x14
639 +
640 +#define TAS5713_START_STOP_PERIOD 0x1A
641 +#define TAS5713_OSC_TRIM 0x1B
642 +#define TAS5713_BKND_ERR 0x1C
643 +
644 +#define TAS5713_INPUT_MUX 0x20
645 +#define TAS5713_SRC_SELECT_CH4 0x21
646 +#define TAS5713_PWM_MUX 0x25
647 +
648 +#define TAS5713_CH1_BQ0 0x29
649 +#define TAS5713_CH1_BQ1 0x2A
650 +#define TAS5713_CH1_BQ2 0x2B
651 +#define TAS5713_CH1_BQ3 0x2C
652 +#define TAS5713_CH1_BQ4 0x2D
653 +#define TAS5713_CH1_BQ5 0x2E
654 +#define TAS5713_CH1_BQ6 0x2F
655 +#define TAS5713_CH1_BQ7 0x58
656 +#define TAS5713_CH1_BQ8 0x59
657 +
658 +#define TAS5713_CH2_BQ0 0x30
659 +#define TAS5713_CH2_BQ1 0x31
660 +#define TAS5713_CH2_BQ2 0x32
661 +#define TAS5713_CH2_BQ3 0x33
662 +#define TAS5713_CH2_BQ4 0x34
663 +#define TAS5713_CH2_BQ5 0x35
664 +#define TAS5713_CH2_BQ6 0x36
665 +#define TAS5713_CH2_BQ7 0x5C
666 +#define TAS5713_CH2_BQ8 0x5D
667 +
668 +#define TAS5713_CH4_BQ0 0x5A
669 +#define TAS5713_CH4_BQ1 0x5B
670 +#define TAS5713_CH3_BQ0 0x5E
671 +#define TAS5713_CH3_BQ1 0x5F
672 +
673 +#define TAS5713_DRC1_SOFTENING_FILTER_ALPHA_OMEGA 0x3B
674 +#define TAS5713_DRC1_ATTACK_RELEASE_RATE 0x3C
675 +#define TAS5713_DRC2_SOFTENING_FILTER_ALPHA_OMEGA 0x3E
676 +#define TAS5713_DRC2_ATTACK_RELEASE_RATE 0x3F
677 +#define TAS5713_DRC1_ATTACK_RELEASE_THRES 0x40
678 +#define TAS5713_DRC2_ATTACK_RELEASE_THRES 0x43
679 +#define TAS5713_DRC_CTRL 0x46
680 +
681 +#define TAS5713_BANK_SW_CTRL 0x50
682 +#define TAS5713_CH1_OUTPUT_MIXER 0x51
683 +#define TAS5713_CH2_OUTPUT_MIXER 0x52
684 +#define TAS5713_CH1_INPUT_MIXER 0x53
685 +#define TAS5713_CH2_INPUT_MIXER 0x54
686 +#define TAS5713_OUTPUT_POST_SCALE 0x56
687 +#define TAS5713_OUTPUT_PRESCALE 0x57
688 +
689 +#define TAS5713_IDF_POST_SCALE 0x62
690 +
691 +#define TAS5713_CH1_INLINE_MIXER 0x70
692 +#define TAS5713_CH1_INLINE_DRC_EN_MIXER 0x71
693 +#define TAS5713_CH1_R_CHANNEL_MIXER 0x72
694 +#define TAS5713_CH1_L_CHANNEL_MIXER 0x73
695 +#define TAS5713_CH2_INLINE_MIXER 0x74
696 +#define TAS5713_CH2_INLINE_DRC_EN_MIXER 0x75
697 +#define TAS5713_CH2_L_CHANNEL_MIXER 0x76
698 +#define TAS5713_CH2_R_CHANNEL_MIXER 0x77
699 +
700 +#define TAS5713_UPDATE_DEV_ADDR_KEY 0xF8
701 +#define TAS5713_UPDATE_DEV_ADDR_REG 0xF9
702 +
703 +#define TAS5713_REGISTER_COUNT 0x46
704 +#define TAS5713_MAX_REGISTER 0xF9
705 +
706 +
707 +// Bitmasks for registers
708 +#define TAS5713_SOFT_MUTE_ALL 0x07
709 +
710 +
711 +
712 +struct tas5713_init_command {
713 + const int size;
714 + const char *const data;
715 +};
716 +
717 +static const struct tas5713_init_command tas5713_init_sequence[] = {
718 +
719 + // Trim oscillator
720 + { .size = 2, .data = "\x1B\x00" },
721 + // System control register 1 (0x03): block DC
722 + { .size = 2, .data = "\x03\x80" },
723 + // Mute everything
724 + { .size = 2, .data = "\x05\x40" },
725 + // Modulation limit register (0x10): 97.7%
726 + { .size = 2, .data = "\x10\x02" },
727 + // Interchannel delay registers
728 + // (0x11, 0x12, 0x13, and 0x14): BD mode
729 + { .size = 2, .data = "\x11\xB8" },
730 + { .size = 2, .data = "\x12\x60" },
731 + { .size = 2, .data = "\x13\xA0" },
732 + { .size = 2, .data = "\x14\x48" },
733 + // PWM shutdown group register (0x19): no shutdown
734 + { .size = 2, .data = "\x19\x00" },
735 + // Input multiplexer register (0x20): BD mode
736 + { .size = 2, .data = "\x20\x00\x89\x77\x72" },
737 + // PWM output mux register (0x25)
738 + // Channel 1 --> OUTA, channel 1 neg --> OUTB
739 + // Channel 2 --> OUTC, channel 2 neg --> OUTD
740 + { .size = 5, .data = "\x25\x01\x02\x13\x45" },
741 + // DRC control (0x46): DRC off
742 + { .size = 5, .data = "\x46\x00\x00\x00\x00" },
743 + // BKND_ERR register (0x1C): 299ms reset period
744 + { .size = 2, .data = "\x1C\x07" },
745 + // Mute channel 3
746 + { .size = 2, .data = "\x0A\xFF" },
747 + // Volume configuration register (0x0E): volume slew 512 steps
748 + { .size = 2, .data = "\x0E\x90" },
749 + // Clock control register (0x00): 44/48kHz, MCLK=64xfs
750 + { .size = 2, .data = "\x00\x60" },
751 + // Bank switch and eq control (0x50): no bank switching
752 + { .size = 5, .data = "\x50\x00\x00\x00\x00" },
753 + // Volume registers (0x07, 0x08, 0x09, 0x0A)
754 + { .size = 2, .data = "\x07\x20" },
755 + { .size = 2, .data = "\x08\x30" },
756 + { .size = 2, .data = "\x09\x30" },
757 + { .size = 2, .data = "\x0A\xFF" },
758 + // 0x72, 0x73, 0x76, 0x77 input mixer:
759 + // no intermix between channels
760 + { .size = 5, .data = "\x72\x00\x00\x00\x00" },
761 + { .size = 5, .data = "\x73\x00\x80\x00\x00" },
762 + { .size = 5, .data = "\x76\x00\x00\x00\x00" },
763 + { .size = 5, .data = "\x77\x00\x80\x00\x00" },
764 + // 0x70, 0x71, 0x74, 0x75 inline DRC mixer:
765 + // no inline DRC inmix
766 + { .size = 5, .data = "\x70\x00\x80\x00\x00" },
767 + { .size = 5, .data = "\x71\x00\x00\x00\x00" },
768 + { .size = 5, .data = "\x74\x00\x80\x00\x00" },
769 + { .size = 5, .data = "\x75\x00\x00\x00\x00" },
770 + // 0x56, 0x57 Output scale
771 + { .size = 5, .data = "\x56\x00\x80\x00\x00" },
772 + { .size = 5, .data = "\x57\x00\x02\x00\x00" },
773 + // 0x3B, 0x3c
774 + { .size = 9, .data = "\x3B\x00\x08\x00\x00\x00\x78\x00\x00" },
775 + { .size = 9, .data = "\x3C\x00\x00\x01\x00\xFF\xFF\xFF\x00" },
776 + { .size = 9, .data = "\x3E\x00\x08\x00\x00\x00\x78\x00\x00" },
777 + { .size = 9, .data = "\x3F\x00\x00\x01\x00\xFF\xFF\xFF\x00" },
778 + { .size = 9, .data = "\x40\x00\x00\x01\x00\xFF\xFF\xFF\x00" },
779 + { .size = 9, .data = "\x43\x00\x00\x01\x00\xFF\xFF\xFF\x00" },
780 + // 0x51, 0x52: output mixer
781 + { .size = 9, .data = "\x51\x00\x80\x00\x00\x00\x00\x00\x00" },
782 + { .size = 9, .data = "\x52\x00\x80\x00\x00\x00\x00\x00\x00" },
783 + // PEQ defaults
784 + { .size = 21, .data = "\x29\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
785 + { .size = 21, .data = "\x2A\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
786 + { .size = 21, .data = "\x2B\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
787 + { .size = 21, .data = "\x2C\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
788 + { .size = 21, .data = "\x2D\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
789 + { .size = 21, .data = "\x2E\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
790 + { .size = 21, .data = "\x2F\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
791 + { .size = 21, .data = "\x30\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
792 + { .size = 21, .data = "\x31\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
793 + { .size = 21, .data = "\x32\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
794 + { .size = 21, .data = "\x33\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
795 + { .size = 21, .data = "\x34\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
796 + { .size = 21, .data = "\x35\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
797 + { .size = 21, .data = "\x36\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
798 + { .size = 21, .data = "\x58\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
799 + { .size = 21, .data = "\x59\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
800 + { .size = 21, .data = "\x5C\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
801 + { .size = 21, .data = "\x5D\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
802 + { .size = 21, .data = "\x5E\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
803 + { .size = 21, .data = "\x5F\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
804 + { .size = 21, .data = "\x5A\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
805 + { .size = 21, .data = "\x5B\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
806 +};
807 +
808 +
809 +#endif /* _TAS5713_H */