diff options
author | Mark Brown <broonie@kernel.org> | 2016-05-27 08:45:37 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-05-27 08:45:37 -0400 |
commit | aaa36d3a98e20b858438eb16cd5c81f27be6dfd5 (patch) | |
tree | ae69b084091c10cfa7acda48bc274d159662877e /sound | |
parent | 4c1c16d9a90a7dc3e2a950f7a8629e85a7e114e6 (diff) | |
parent | bd023ada36a66fe3d2fde619926b49b9a0428133 (diff) |
Merge remote-tracking branch 'asoc/topic/tas5270' into asoc-next
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/Kconfig | 10 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/tas571x.c | 141 | ||||
-rw-r--r-- | sound/soc/codecs/tas571x.h | 22 | ||||
-rw-r--r-- | sound/soc/codecs/tas5720.c | 620 | ||||
-rw-r--r-- | sound/soc/codecs/tas5720.h | 90 |
6 files changed, 872 insertions, 13 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f22e86e063b9..03242a4dc5a8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -127,6 +127,7 @@ config SND_SOC_ALL_CODECS | |||
127 | select SND_SOC_TAS2552 if I2C | 127 | select SND_SOC_TAS2552 if I2C |
128 | select SND_SOC_TAS5086 if I2C | 128 | select SND_SOC_TAS5086 if I2C |
129 | select SND_SOC_TAS571X if I2C | 129 | select SND_SOC_TAS571X if I2C |
130 | select SND_SOC_TAS5720 if I2C | ||
130 | select SND_SOC_TFA9879 if I2C | 131 | select SND_SOC_TFA9879 if I2C |
131 | select SND_SOC_TLV320AIC23_I2C if I2C | 132 | select SND_SOC_TLV320AIC23_I2C if I2C |
132 | select SND_SOC_TLV320AIC23_SPI if SPI_MASTER | 133 | select SND_SOC_TLV320AIC23_SPI if SPI_MASTER |
@@ -753,9 +754,16 @@ config SND_SOC_TAS5086 | |||
753 | depends on I2C | 754 | depends on I2C |
754 | 755 | ||
755 | config SND_SOC_TAS571X | 756 | config SND_SOC_TAS571X |
756 | tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers" | 757 | tristate "Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 power amplifiers" |
757 | depends on I2C | 758 | depends on I2C |
758 | 759 | ||
760 | config SND_SOC_TAS5720 | ||
761 | tristate "Texas Instruments TAS5720 Mono Audio amplifier" | ||
762 | depends on I2C | ||
763 | help | ||
764 | Enable support for Texas Instruments TAS5720L/M high-efficiency mono | ||
765 | Class-D audio power amplifiers. | ||
766 | |||
759 | config SND_SOC_TFA9879 | 767 | config SND_SOC_TFA9879 |
760 | tristate "NXP Semiconductors TFA9879 amplifier" | 768 | tristate "NXP Semiconductors TFA9879 amplifier" |
761 | depends on I2C | 769 | depends on I2C |
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index a2fdfdc86750..2620ee001071 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -132,6 +132,7 @@ snd-soc-stac9766-objs := stac9766.o | |||
132 | snd-soc-sti-sas-objs := sti-sas.o | 132 | snd-soc-sti-sas-objs := sti-sas.o |
133 | snd-soc-tas5086-objs := tas5086.o | 133 | snd-soc-tas5086-objs := tas5086.o |
134 | snd-soc-tas571x-objs := tas571x.o | 134 | snd-soc-tas571x-objs := tas571x.o |
135 | snd-soc-tas5720-objs := tas5720.o | ||
135 | snd-soc-tfa9879-objs := tfa9879.o | 136 | snd-soc-tfa9879-objs := tfa9879.o |
136 | snd-soc-tlv320aic23-objs := tlv320aic23.o | 137 | snd-soc-tlv320aic23-objs := tlv320aic23.o |
137 | snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o | 138 | snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o |
@@ -342,6 +343,7 @@ obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o | |||
342 | obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o | 343 | obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o |
343 | obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o | 344 | obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o |
344 | obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o | 345 | obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o |
346 | obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o | ||
345 | obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o | 347 | obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o |
346 | obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o | 348 | obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o |
347 | obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o | 349 | obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o |
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index 39307ad41a34..b8d19b77bde9 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c | |||
@@ -4,6 +4,9 @@ | |||
4 | * Copyright (C) 2015 Google, Inc. | 4 | * Copyright (C) 2015 Google, Inc. |
5 | * Copyright (c) 2013 Daniel Mack <zonque@gmail.com> | 5 | * Copyright (c) 2013 Daniel Mack <zonque@gmail.com> |
6 | * | 6 | * |
7 | * TAS5721 support: | ||
8 | * Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com> | ||
9 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by | 11 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or | 12 | * the Free Software Foundation; either version 2 of the License, or |
@@ -57,6 +60,10 @@ static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg) | |||
57 | case TAS571X_CH1_VOL_REG: | 60 | case TAS571X_CH1_VOL_REG: |
58 | case TAS571X_CH2_VOL_REG: | 61 | case TAS571X_CH2_VOL_REG: |
59 | return priv->chip->vol_reg_size; | 62 | return priv->chip->vol_reg_size; |
63 | case TAS571X_INPUT_MUX_REG: | ||
64 | case TAS571X_CH4_SRC_SELECT_REG: | ||
65 | case TAS571X_PWM_MUX_REG: | ||
66 | return 4; | ||
60 | default: | 67 | default: |
61 | return 1; | 68 | return 1; |
62 | } | 69 | } |
@@ -167,6 +174,23 @@ static int tas571x_hw_params(struct snd_pcm_substream *substream, | |||
167 | TAS571X_SDI_FMT_MASK, val); | 174 | TAS571X_SDI_FMT_MASK, val); |
168 | } | 175 | } |
169 | 176 | ||
177 | static int tas571x_mute(struct snd_soc_dai *dai, int mute) | ||
178 | { | ||
179 | struct snd_soc_codec *codec = dai->codec; | ||
180 | u8 sysctl2; | ||
181 | int ret; | ||
182 | |||
183 | sysctl2 = mute ? TAS571X_SYS_CTRL_2_SDN_MASK : 0; | ||
184 | |||
185 | ret = snd_soc_update_bits(codec, | ||
186 | TAS571X_SYS_CTRL_2_REG, | ||
187 | TAS571X_SYS_CTRL_2_SDN_MASK, | ||
188 | sysctl2); | ||
189 | usleep_range(1000, 2000); | ||
190 | |||
191 | return ret; | ||
192 | } | ||
193 | |||
170 | static int tas571x_set_bias_level(struct snd_soc_codec *codec, | 194 | static int tas571x_set_bias_level(struct snd_soc_codec *codec, |
171 | enum snd_soc_bias_level level) | 195 | enum snd_soc_bias_level level) |
172 | { | 196 | { |
@@ -214,6 +238,7 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec, | |||
214 | static const struct snd_soc_dai_ops tas571x_dai_ops = { | 238 | static const struct snd_soc_dai_ops tas571x_dai_ops = { |
215 | .set_fmt = tas571x_set_dai_fmt, | 239 | .set_fmt = tas571x_set_dai_fmt, |
216 | .hw_params = tas571x_hw_params, | 240 | .hw_params = tas571x_hw_params, |
241 | .digital_mute = tas571x_mute, | ||
217 | }; | 242 | }; |
218 | 243 | ||
219 | static const char *const tas5711_supply_names[] = { | 244 | static const char *const tas5711_supply_names[] = { |
@@ -241,6 +266,26 @@ static const struct snd_kcontrol_new tas5711_controls[] = { | |||
241 | 1, 1), | 266 | 1, 1), |
242 | }; | 267 | }; |
243 | 268 | ||
269 | static const struct regmap_range tas571x_readonly_regs_range[] = { | ||
270 | regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_DEV_ID_REG), | ||
271 | }; | ||
272 | |||
273 | static const struct regmap_range tas571x_volatile_regs_range[] = { | ||
274 | regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG), | ||
275 | regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG), | ||
276 | }; | ||
277 | |||
278 | static const struct regmap_access_table tas571x_write_regs = { | ||
279 | .no_ranges = tas571x_readonly_regs_range, | ||
280 | .n_no_ranges = ARRAY_SIZE(tas571x_readonly_regs_range), | ||
281 | }; | ||
282 | |||
283 | static const struct regmap_access_table tas571x_volatile_regs = { | ||
284 | .yes_ranges = tas571x_volatile_regs_range, | ||
285 | .n_yes_ranges = ARRAY_SIZE(tas571x_volatile_regs_range), | ||
286 | |||
287 | }; | ||
288 | |||
244 | static const struct reg_default tas5711_reg_defaults[] = { | 289 | static const struct reg_default tas5711_reg_defaults[] = { |
245 | { 0x04, 0x05 }, | 290 | { 0x04, 0x05 }, |
246 | { 0x05, 0x40 }, | 291 | { 0x05, 0x40 }, |
@@ -260,6 +305,8 @@ static const struct regmap_config tas5711_regmap_config = { | |||
260 | .reg_defaults = tas5711_reg_defaults, | 305 | .reg_defaults = tas5711_reg_defaults, |
261 | .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), | 306 | .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), |
262 | .cache_type = REGCACHE_RBTREE, | 307 | .cache_type = REGCACHE_RBTREE, |
308 | .wr_table = &tas571x_write_regs, | ||
309 | .volatile_table = &tas571x_volatile_regs, | ||
263 | }; | 310 | }; |
264 | 311 | ||
265 | static const struct tas571x_chip tas5711_chip = { | 312 | static const struct tas571x_chip tas5711_chip = { |
@@ -314,6 +361,8 @@ static const struct regmap_config tas5717_regmap_config = { | |||
314 | .reg_defaults = tas5717_reg_defaults, | 361 | .reg_defaults = tas5717_reg_defaults, |
315 | .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), | 362 | .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), |
316 | .cache_type = REGCACHE_RBTREE, | 363 | .cache_type = REGCACHE_RBTREE, |
364 | .wr_table = &tas571x_write_regs, | ||
365 | .volatile_table = &tas571x_volatile_regs, | ||
317 | }; | 366 | }; |
318 | 367 | ||
319 | /* This entry is reused for tas5719 as the software interface is identical. */ | 368 | /* This entry is reused for tas5719 as the software interface is identical. */ |
@@ -326,6 +375,77 @@ static const struct tas571x_chip tas5717_chip = { | |||
326 | .vol_reg_size = 2, | 375 | .vol_reg_size = 2, |
327 | }; | 376 | }; |
328 | 377 | ||
378 | static const char *const tas5721_supply_names[] = { | ||
379 | "AVDD", | ||
380 | "DVDD", | ||
381 | "DRVDD", | ||
382 | "PVDD", | ||
383 | }; | ||
384 | |||
385 | static const struct snd_kcontrol_new tas5721_controls[] = { | ||
386 | SOC_SINGLE_TLV("Master Volume", | ||
387 | TAS571X_MVOL_REG, | ||
388 | 0, 0xff, 1, tas5711_volume_tlv), | ||
389 | SOC_DOUBLE_R_TLV("Speaker Volume", | ||
390 | TAS571X_CH1_VOL_REG, | ||
391 | TAS571X_CH2_VOL_REG, | ||
392 | 0, 0xff, 1, tas5711_volume_tlv), | ||
393 | SOC_DOUBLE("Speaker Switch", | ||
394 | TAS571X_SOFT_MUTE_REG, | ||
395 | TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, | ||
396 | 1, 1), | ||
397 | }; | ||
398 | |||
399 | static const struct reg_default tas5721_reg_defaults[] = { | ||
400 | {TAS571X_CLK_CTRL_REG, 0x6c}, | ||
401 | {TAS571X_DEV_ID_REG, 0x00}, | ||
402 | {TAS571X_ERR_STATUS_REG, 0x00}, | ||
403 | {TAS571X_SYS_CTRL_1_REG, 0xa0}, | ||
404 | {TAS571X_SDI_REG, 0x05}, | ||
405 | {TAS571X_SYS_CTRL_2_REG, 0x40}, | ||
406 | {TAS571X_SOFT_MUTE_REG, 0x00}, | ||
407 | {TAS571X_MVOL_REG, 0xff}, | ||
408 | {TAS571X_CH1_VOL_REG, 0x30}, | ||
409 | {TAS571X_CH2_VOL_REG, 0x30}, | ||
410 | {TAS571X_CH3_VOL_REG, 0x30}, | ||
411 | {TAS571X_VOL_CFG_REG, 0x91}, | ||
412 | {TAS571X_MODULATION_LIMIT_REG, 0x02}, | ||
413 | {TAS571X_IC_DELAY_CH1_REG, 0xac}, | ||
414 | {TAS571X_IC_DELAY_CH2_REG, 0x54}, | ||
415 | {TAS571X_IC_DELAY_CH3_REG, 0xac}, | ||
416 | {TAS571X_IC_DELAY_CH4_REG, 0x54}, | ||
417 | {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30}, | ||
418 | {TAS571X_START_STOP_PERIOD_REG, 0x0f}, | ||
419 | {TAS571X_OSC_TRIM_REG, 0x82}, | ||
420 | {TAS571X_BKND_ERR_REG, 0x02}, | ||
421 | {TAS571X_INPUT_MUX_REG, 0x17772}, | ||
422 | {TAS571X_CH4_SRC_SELECT_REG, 0x4303}, | ||
423 | {TAS571X_PWM_MUX_REG, 0x1021345}, | ||
424 | }; | ||
425 | |||
426 | static const struct regmap_config tas5721_regmap_config = { | ||
427 | .reg_bits = 8, | ||
428 | .val_bits = 32, | ||
429 | .max_register = 0xff, | ||
430 | .reg_read = tas571x_reg_read, | ||
431 | .reg_write = tas571x_reg_write, | ||
432 | .reg_defaults = tas5721_reg_defaults, | ||
433 | .num_reg_defaults = ARRAY_SIZE(tas5721_reg_defaults), | ||
434 | .cache_type = REGCACHE_RBTREE, | ||
435 | .wr_table = &tas571x_write_regs, | ||
436 | .volatile_table = &tas571x_volatile_regs, | ||
437 | }; | ||
438 | |||
439 | |||
440 | static const struct tas571x_chip tas5721_chip = { | ||
441 | .supply_names = tas5721_supply_names, | ||
442 | .num_supply_names = ARRAY_SIZE(tas5721_supply_names), | ||
443 | .controls = tas5711_controls, | ||
444 | .num_controls = ARRAY_SIZE(tas5711_controls), | ||
445 | .regmap_config = &tas5721_regmap_config, | ||
446 | .vol_reg_size = 1, | ||
447 | }; | ||
448 | |||
329 | static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { | 449 | static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { |
330 | SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), | 450 | SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), |
331 | SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), | 451 | SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), |
@@ -386,11 +506,10 @@ static int tas571x_i2c_probe(struct i2c_client *client, | |||
386 | i2c_set_clientdata(client, priv); | 506 | i2c_set_clientdata(client, priv); |
387 | 507 | ||
388 | of_id = of_match_device(tas571x_of_match, dev); | 508 | of_id = of_match_device(tas571x_of_match, dev); |
389 | if (!of_id) { | 509 | if (of_id) |
390 | dev_err(dev, "Unknown device type\n"); | 510 | priv->chip = of_id->data; |
391 | return -EINVAL; | 511 | else |
392 | } | 512 | priv->chip = (void *) id->driver_data; |
393 | priv->chip = of_id->data; | ||
394 | 513 | ||
395 | priv->mclk = devm_clk_get(dev, "mclk"); | 514 | priv->mclk = devm_clk_get(dev, "mclk"); |
396 | if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { | 515 | if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { |
@@ -445,10 +564,6 @@ static int tas571x_i2c_probe(struct i2c_client *client, | |||
445 | if (ret) | 564 | if (ret) |
446 | return ret; | 565 | return ret; |
447 | 566 | ||
448 | ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG, | ||
449 | TAS571X_SYS_CTRL_2_SDN_MASK, 0); | ||
450 | if (ret) | ||
451 | return ret; | ||
452 | 567 | ||
453 | memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); | 568 | memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); |
454 | priv->codec_driver.controls = priv->chip->controls; | 569 | priv->codec_driver.controls = priv->chip->controls; |
@@ -486,14 +601,16 @@ static const struct of_device_id tas571x_of_match[] = { | |||
486 | { .compatible = "ti,tas5711", .data = &tas5711_chip, }, | 601 | { .compatible = "ti,tas5711", .data = &tas5711_chip, }, |
487 | { .compatible = "ti,tas5717", .data = &tas5717_chip, }, | 602 | { .compatible = "ti,tas5717", .data = &tas5717_chip, }, |
488 | { .compatible = "ti,tas5719", .data = &tas5717_chip, }, | 603 | { .compatible = "ti,tas5719", .data = &tas5717_chip, }, |
604 | { .compatible = "ti,tas5721", .data = &tas5721_chip, }, | ||
489 | { } | 605 | { } |
490 | }; | 606 | }; |
491 | MODULE_DEVICE_TABLE(of, tas571x_of_match); | 607 | MODULE_DEVICE_TABLE(of, tas571x_of_match); |
492 | 608 | ||
493 | static const struct i2c_device_id tas571x_i2c_id[] = { | 609 | static const struct i2c_device_id tas571x_i2c_id[] = { |
494 | { "tas5711", 0 }, | 610 | { "tas5711", (kernel_ulong_t) &tas5711_chip }, |
495 | { "tas5717", 0 }, | 611 | { "tas5717", (kernel_ulong_t) &tas5717_chip }, |
496 | { "tas5719", 0 }, | 612 | { "tas5719", (kernel_ulong_t) &tas5717_chip }, |
613 | { "tas5721", (kernel_ulong_t) &tas5721_chip }, | ||
497 | { } | 614 | { } |
498 | }; | 615 | }; |
499 | MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); | 616 | MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); |
diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h index 0aee471232cd..cf800c364f0f 100644 --- a/sound/soc/codecs/tas571x.h +++ b/sound/soc/codecs/tas571x.h | |||
@@ -13,6 +13,10 @@ | |||
13 | #define _TAS571X_H | 13 | #define _TAS571X_H |
14 | 14 | ||
15 | /* device registers */ | 15 | /* device registers */ |
16 | #define TAS571X_CLK_CTRL_REG 0x00 | ||
17 | #define TAS571X_DEV_ID_REG 0x01 | ||
18 | #define TAS571X_ERR_STATUS_REG 0x02 | ||
19 | #define TAS571X_SYS_CTRL_1_REG 0x03 | ||
16 | #define TAS571X_SDI_REG 0x04 | 20 | #define TAS571X_SDI_REG 0x04 |
17 | #define TAS571X_SDI_FMT_MASK 0x0f | 21 | #define TAS571X_SDI_FMT_MASK 0x0f |
18 | 22 | ||
@@ -27,7 +31,25 @@ | |||
27 | #define TAS571X_MVOL_REG 0x07 | 31 | #define TAS571X_MVOL_REG 0x07 |
28 | #define TAS571X_CH1_VOL_REG 0x08 | 32 | #define TAS571X_CH1_VOL_REG 0x08 |
29 | #define TAS571X_CH2_VOL_REG 0x09 | 33 | #define TAS571X_CH2_VOL_REG 0x09 |
34 | #define TAS571X_CH3_VOL_REG 0x0a | ||
35 | #define TAS571X_VOL_CFG_REG 0x0e | ||
36 | #define TAS571X_MODULATION_LIMIT_REG 0x10 | ||
37 | #define TAS571X_IC_DELAY_CH1_REG 0x11 | ||
38 | #define TAS571X_IC_DELAY_CH2_REG 0x12 | ||
39 | #define TAS571X_IC_DELAY_CH3_REG 0x13 | ||
40 | #define TAS571X_IC_DELAY_CH4_REG 0x14 | ||
30 | 41 | ||
42 | #define TAS571X_PWM_CH_SDN_GROUP_REG 0x19 /* N/A on TAS5717, TAS5719 */ | ||
43 | #define TAS571X_PWM_CH1_SDN_MASK (1<<0) | ||
44 | #define TAS571X_PWM_CH2_SDN_SHIFT (1<<1) | ||
45 | #define TAS571X_PWM_CH3_SDN_SHIFT (1<<2) | ||
46 | #define TAS571X_PWM_CH4_SDN_SHIFT (1<<3) | ||
47 | |||
48 | #define TAS571X_START_STOP_PERIOD_REG 0x1a | ||
31 | #define TAS571X_OSC_TRIM_REG 0x1b | 49 | #define TAS571X_OSC_TRIM_REG 0x1b |
50 | #define TAS571X_BKND_ERR_REG 0x1c | ||
51 | #define TAS571X_INPUT_MUX_REG 0x20 | ||
52 | #define TAS571X_CH4_SRC_SELECT_REG 0x21 | ||
53 | #define TAS571X_PWM_MUX_REG 0x25 | ||
32 | 54 | ||
33 | #endif /* _TAS571X_H */ | 55 | #endif /* _TAS571X_H */ |
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c new file mode 100644 index 000000000000..f54fb46b77c2 --- /dev/null +++ b/sound/soc/codecs/tas5720.c | |||
@@ -0,0 +1,620 @@ | |||
1 | /* | ||
2 | * tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier | ||
3 | * | ||
4 | * Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com | ||
5 | * | ||
6 | * Author: Andreas Dannenberg <dannenberg@ti.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/i2c.h> | ||
22 | #include <linux/pm_runtime.h> | ||
23 | #include <linux/regmap.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/regulator/consumer.h> | ||
26 | #include <linux/delay.h> | ||
27 | |||
28 | #include <sound/pcm.h> | ||
29 | #include <sound/pcm_params.h> | ||
30 | #include <sound/soc.h> | ||
31 | #include <sound/soc-dapm.h> | ||
32 | #include <sound/tlv.h> | ||
33 | |||
34 | #include "tas5720.h" | ||
35 | |||
36 | /* Define how often to check (and clear) the fault status register (in ms) */ | ||
37 | #define TAS5720_FAULT_CHECK_INTERVAL 200 | ||
38 | |||
39 | static const char * const tas5720_supply_names[] = { | ||
40 | "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ | ||
41 | "pvdd", /* Class-D amp and analog power supply (connected). */ | ||
42 | }; | ||
43 | |||
44 | #define TAS5720_NUM_SUPPLIES ARRAY_SIZE(tas5720_supply_names) | ||
45 | |||
46 | struct tas5720_data { | ||
47 | struct snd_soc_codec *codec; | ||
48 | struct regmap *regmap; | ||
49 | struct i2c_client *tas5720_client; | ||
50 | struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES]; | ||
51 | struct delayed_work fault_check_work; | ||
52 | unsigned int last_fault; | ||
53 | }; | ||
54 | |||
55 | static int tas5720_hw_params(struct snd_pcm_substream *substream, | ||
56 | struct snd_pcm_hw_params *params, | ||
57 | struct snd_soc_dai *dai) | ||
58 | { | ||
59 | struct snd_soc_codec *codec = dai->codec; | ||
60 | unsigned int rate = params_rate(params); | ||
61 | bool ssz_ds; | ||
62 | int ret; | ||
63 | |||
64 | switch (rate) { | ||
65 | case 44100: | ||
66 | case 48000: | ||
67 | ssz_ds = false; | ||
68 | break; | ||
69 | case 88200: | ||
70 | case 96000: | ||
71 | ssz_ds = true; | ||
72 | break; | ||
73 | default: | ||
74 | dev_err(codec->dev, "unsupported sample rate: %u\n", rate); | ||
75 | return -EINVAL; | ||
76 | } | ||
77 | |||
78 | ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG, | ||
79 | TAS5720_SSZ_DS, ssz_ds); | ||
80 | if (ret < 0) { | ||
81 | dev_err(codec->dev, "error setting sample rate: %d\n", ret); | ||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int tas5720_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) | ||
89 | { | ||
90 | struct snd_soc_codec *codec = dai->codec; | ||
91 | u8 serial_format; | ||
92 | int ret; | ||
93 | |||
94 | if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { | ||
95 | dev_vdbg(codec->dev, "DAI Format master is not found\n"); | ||
96 | return -EINVAL; | ||
97 | } | ||
98 | |||
99 | switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | | ||
100 | SND_SOC_DAIFMT_INV_MASK)) { | ||
101 | case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): | ||
102 | /* 1st data bit occur one BCLK cycle after the frame sync */ | ||
103 | serial_format = TAS5720_SAIF_I2S; | ||
104 | break; | ||
105 | case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF): | ||
106 | /* | ||
107 | * Note that although the TAS5720 does not have a dedicated DSP | ||
108 | * mode it doesn't care about the LRCLK duty cycle during TDM | ||
109 | * operation. Therefore we can use the device's I2S mode with | ||
110 | * its delaying of the 1st data bit to receive DSP_A formatted | ||
111 | * data. See device datasheet for additional details. | ||
112 | */ | ||
113 | serial_format = TAS5720_SAIF_I2S; | ||
114 | break; | ||
115 | case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF): | ||
116 | /* | ||
117 | * Similar to DSP_A, we can use the fact that the TAS5720 does | ||
118 | * not care about the LRCLK duty cycle during TDM to receive | ||
119 | * DSP_B formatted data in LEFTJ mode (no delaying of the 1st | ||
120 | * data bit). | ||
121 | */ | ||
122 | serial_format = TAS5720_SAIF_LEFTJ; | ||
123 | break; | ||
124 | case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): | ||
125 | /* No delay after the frame sync */ | ||
126 | serial_format = TAS5720_SAIF_LEFTJ; | ||
127 | break; | ||
128 | default: | ||
129 | dev_vdbg(codec->dev, "DAI Format is not found\n"); | ||
130 | return -EINVAL; | ||
131 | } | ||
132 | |||
133 | ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG, | ||
134 | TAS5720_SAIF_FORMAT_MASK, | ||
135 | serial_format); | ||
136 | if (ret < 0) { | ||
137 | dev_err(codec->dev, "error setting SAIF format: %d\n", ret); | ||
138 | return ret; | ||
139 | } | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai, | ||
145 | unsigned int tx_mask, unsigned int rx_mask, | ||
146 | int slots, int slot_width) | ||
147 | { | ||
148 | struct snd_soc_codec *codec = dai->codec; | ||
149 | unsigned int first_slot; | ||
150 | int ret; | ||
151 | |||
152 | if (!tx_mask) { | ||
153 | dev_err(codec->dev, "tx masks must not be 0\n"); | ||
154 | return -EINVAL; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * Determine the first slot that is being requested. We will only | ||
159 | * use the first slot that is found since the TAS5720 is a mono | ||
160 | * amplifier. | ||
161 | */ | ||
162 | first_slot = __ffs(tx_mask); | ||
163 | |||
164 | if (first_slot > 7) { | ||
165 | dev_err(codec->dev, "slot selection out of bounds (%u)\n", | ||
166 | first_slot); | ||
167 | return -EINVAL; | ||
168 | } | ||
169 | |||
170 | /* Enable manual TDM slot selection (instead of I2C ID based) */ | ||
171 | ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG, | ||
172 | TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC); | ||
173 | if (ret < 0) | ||
174 | goto error_snd_soc_update_bits; | ||
175 | |||
176 | /* Configure the TDM slot to process audio from */ | ||
177 | ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG, | ||
178 | TAS5720_TDM_SLOT_SEL_MASK, first_slot); | ||
179 | if (ret < 0) | ||
180 | goto error_snd_soc_update_bits; | ||
181 | |||
182 | return 0; | ||
183 | |||
184 | error_snd_soc_update_bits: | ||
185 | dev_err(codec->dev, "error configuring TDM mode: %d\n", ret); | ||
186 | return ret; | ||
187 | } | ||
188 | |||
189 | static int tas5720_mute(struct snd_soc_dai *dai, int mute) | ||
190 | { | ||
191 | struct snd_soc_codec *codec = dai->codec; | ||
192 | int ret; | ||
193 | |||
194 | ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG, | ||
195 | TAS5720_MUTE, mute ? TAS5720_MUTE : 0); | ||
196 | if (ret < 0) { | ||
197 | dev_err(codec->dev, "error (un-)muting device: %d\n", ret); | ||
198 | return ret; | ||
199 | } | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static void tas5720_fault_check_work(struct work_struct *work) | ||
205 | { | ||
206 | struct tas5720_data *tas5720 = container_of(work, struct tas5720_data, | ||
207 | fault_check_work.work); | ||
208 | struct device *dev = tas5720->codec->dev; | ||
209 | unsigned int curr_fault; | ||
210 | int ret; | ||
211 | |||
212 | ret = regmap_read(tas5720->regmap, TAS5720_FAULT_REG, &curr_fault); | ||
213 | if (ret < 0) { | ||
214 | dev_err(dev, "failed to read FAULT register: %d\n", ret); | ||
215 | goto out; | ||
216 | } | ||
217 | |||
218 | /* Check/handle all errors except SAIF clock errors */ | ||
219 | curr_fault &= TAS5720_OCE | TAS5720_DCE | TAS5720_OTE; | ||
220 | |||
221 | /* | ||
222 | * Only flag errors once for a given occurrence. This is needed as | ||
223 | * the TAS5720 will take time clearing the fault condition internally | ||
224 | * during which we don't want to bombard the system with the same | ||
225 | * error message over and over. | ||
226 | */ | ||
227 | if ((curr_fault & TAS5720_OCE) && !(tas5720->last_fault & TAS5720_OCE)) | ||
228 | dev_crit(dev, "experienced an over current hardware fault\n"); | ||
229 | |||
230 | if ((curr_fault & TAS5720_DCE) && !(tas5720->last_fault & TAS5720_DCE)) | ||
231 | dev_crit(dev, "experienced a DC detection fault\n"); | ||
232 | |||
233 | if ((curr_fault & TAS5720_OTE) && !(tas5720->last_fault & TAS5720_OTE)) | ||
234 | dev_crit(dev, "experienced an over temperature fault\n"); | ||
235 | |||
236 | /* Store current fault value so we can detect any changes next time */ | ||
237 | tas5720->last_fault = curr_fault; | ||
238 | |||
239 | if (!curr_fault) | ||
240 | goto out; | ||
241 | |||
242 | /* | ||
243 | * Periodically toggle SDZ (shutdown bit) H->L->H to clear any latching | ||
244 | * faults as long as a fault condition persists. Always going through | ||
245 | * the full sequence no matter the first return value to minimizes | ||
246 | * chances for the device to end up in shutdown mode. | ||
247 | */ | ||
248 | ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG, | ||
249 | TAS5720_SDZ, 0); | ||
250 | if (ret < 0) | ||
251 | dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret); | ||
252 | |||
253 | ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG, | ||
254 | TAS5720_SDZ, TAS5720_SDZ); | ||
255 | if (ret < 0) | ||
256 | dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret); | ||
257 | |||
258 | out: | ||
259 | /* Schedule the next fault check at the specified interval */ | ||
260 | schedule_delayed_work(&tas5720->fault_check_work, | ||
261 | msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL)); | ||
262 | } | ||
263 | |||
264 | static int tas5720_codec_probe(struct snd_soc_codec *codec) | ||
265 | { | ||
266 | struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); | ||
267 | unsigned int device_id; | ||
268 | int ret; | ||
269 | |||
270 | tas5720->codec = codec; | ||
271 | |||
272 | ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies), | ||
273 | tas5720->supplies); | ||
274 | if (ret != 0) { | ||
275 | dev_err(codec->dev, "failed to enable supplies: %d\n", ret); | ||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id); | ||
280 | if (ret < 0) { | ||
281 | dev_err(codec->dev, "failed to read device ID register: %d\n", | ||
282 | ret); | ||
283 | goto probe_fail; | ||
284 | } | ||
285 | |||
286 | if (device_id != TAS5720_DEVICE_ID) { | ||
287 | dev_err(codec->dev, "wrong device ID. expected: %u read: %u\n", | ||
288 | TAS5720_DEVICE_ID, device_id); | ||
289 | ret = -ENODEV; | ||
290 | goto probe_fail; | ||
291 | } | ||
292 | |||
293 | /* Set device to mute */ | ||
294 | ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG, | ||
295 | TAS5720_MUTE, TAS5720_MUTE); | ||
296 | if (ret < 0) | ||
297 | goto error_snd_soc_update_bits; | ||
298 | |||
299 | /* | ||
300 | * Enter shutdown mode - our default when not playing audio - to | ||
301 | * minimize current consumption. On the TAS5720 there is no real down | ||
302 | * side doing so as all device registers are preserved and the wakeup | ||
303 | * of the codec is rather quick which we do using a dapm widget. | ||
304 | */ | ||
305 | ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG, | ||
306 | TAS5720_SDZ, 0); | ||
307 | if (ret < 0) | ||
308 | goto error_snd_soc_update_bits; | ||
309 | |||
310 | INIT_DELAYED_WORK(&tas5720->fault_check_work, tas5720_fault_check_work); | ||
311 | |||
312 | return 0; | ||
313 | |||
314 | error_snd_soc_update_bits: | ||
315 | dev_err(codec->dev, "error configuring device registers: %d\n", ret); | ||
316 | |||
317 | probe_fail: | ||
318 | regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), | ||
319 | tas5720->supplies); | ||
320 | return ret; | ||
321 | } | ||
322 | |||
323 | static int tas5720_codec_remove(struct snd_soc_codec *codec) | ||
324 | { | ||
325 | struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); | ||
326 | int ret; | ||
327 | |||
328 | cancel_delayed_work_sync(&tas5720->fault_check_work); | ||
329 | |||
330 | ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), | ||
331 | tas5720->supplies); | ||
332 | if (ret < 0) | ||
333 | dev_err(codec->dev, "failed to disable supplies: %d\n", ret); | ||
334 | |||
335 | return ret; | ||
336 | }; | ||
337 | |||
338 | static int tas5720_dac_event(struct snd_soc_dapm_widget *w, | ||
339 | struct snd_kcontrol *kcontrol, int event) | ||
340 | { | ||
341 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
342 | struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); | ||
343 | int ret; | ||
344 | |||
345 | if (event & SND_SOC_DAPM_POST_PMU) { | ||
346 | /* Take TAS5720 out of shutdown mode */ | ||
347 | ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG, | ||
348 | TAS5720_SDZ, TAS5720_SDZ); | ||
349 | if (ret < 0) { | ||
350 | dev_err(codec->dev, "error waking codec: %d\n", ret); | ||
351 | return ret; | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * Observe codec shutdown-to-active time. The datasheet only | ||
356 | * lists a nominal value however just use-it as-is without | ||
357 | * additional padding to minimize the delay introduced in | ||
358 | * starting to play audio (actually there is other setup done | ||
359 | * by the ASoC framework that will provide additional delays, | ||
360 | * so we should always be safe). | ||
361 | */ | ||
362 | msleep(25); | ||
363 | |||
364 | /* Turn on TAS5720 periodic fault checking/handling */ | ||
365 | tas5720->last_fault = 0; | ||
366 | schedule_delayed_work(&tas5720->fault_check_work, | ||
367 | msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL)); | ||
368 | } else if (event & SND_SOC_DAPM_PRE_PMD) { | ||
369 | /* Disable TAS5720 periodic fault checking/handling */ | ||
370 | cancel_delayed_work_sync(&tas5720->fault_check_work); | ||
371 | |||
372 | /* Place TAS5720 in shutdown mode to minimize current draw */ | ||
373 | ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG, | ||
374 | TAS5720_SDZ, 0); | ||
375 | if (ret < 0) { | ||
376 | dev_err(codec->dev, "error shutting down codec: %d\n", | ||
377 | ret); | ||
378 | return ret; | ||
379 | } | ||
380 | } | ||
381 | |||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | #ifdef CONFIG_PM | ||
386 | static int tas5720_suspend(struct snd_soc_codec *codec) | ||
387 | { | ||
388 | struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); | ||
389 | int ret; | ||
390 | |||
391 | regcache_cache_only(tas5720->regmap, true); | ||
392 | regcache_mark_dirty(tas5720->regmap); | ||
393 | |||
394 | ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), | ||
395 | tas5720->supplies); | ||
396 | if (ret < 0) | ||
397 | dev_err(codec->dev, "failed to disable supplies: %d\n", ret); | ||
398 | |||
399 | return ret; | ||
400 | } | ||
401 | |||
402 | static int tas5720_resume(struct snd_soc_codec *codec) | ||
403 | { | ||
404 | struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); | ||
405 | int ret; | ||
406 | |||
407 | ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies), | ||
408 | tas5720->supplies); | ||
409 | if (ret < 0) { | ||
410 | dev_err(codec->dev, "failed to enable supplies: %d\n", ret); | ||
411 | return ret; | ||
412 | } | ||
413 | |||
414 | regcache_cache_only(tas5720->regmap, false); | ||
415 | |||
416 | ret = regcache_sync(tas5720->regmap); | ||
417 | if (ret < 0) { | ||
418 | dev_err(codec->dev, "failed to sync regcache: %d\n", ret); | ||
419 | return ret; | ||
420 | } | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | #else | ||
425 | #define tas5720_suspend NULL | ||
426 | #define tas5720_resume NULL | ||
427 | #endif | ||
428 | |||
429 | static bool tas5720_is_volatile_reg(struct device *dev, unsigned int reg) | ||
430 | { | ||
431 | switch (reg) { | ||
432 | case TAS5720_DEVICE_ID_REG: | ||
433 | case TAS5720_FAULT_REG: | ||
434 | return true; | ||
435 | default: | ||
436 | return false; | ||
437 | } | ||
438 | } | ||
439 | |||
440 | static const struct regmap_config tas5720_regmap_config = { | ||
441 | .reg_bits = 8, | ||
442 | .val_bits = 8, | ||
443 | |||
444 | .max_register = TAS5720_MAX_REG, | ||
445 | .cache_type = REGCACHE_RBTREE, | ||
446 | .volatile_reg = tas5720_is_volatile_reg, | ||
447 | }; | ||
448 | |||
449 | /* | ||
450 | * DAC analog gain. There are four discrete values to select from, ranging | ||
451 | * from 19.2 dB to 26.3dB. | ||
452 | */ | ||
453 | static const DECLARE_TLV_DB_RANGE(dac_analog_tlv, | ||
454 | 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0), | ||
455 | 0x1, 0x1, TLV_DB_SCALE_ITEM(2070, 0, 0), | ||
456 | 0x2, 0x2, TLV_DB_SCALE_ITEM(2350, 0, 0), | ||
457 | 0x3, 0x3, TLV_DB_SCALE_ITEM(2630, 0, 0), | ||
458 | ); | ||
459 | |||
460 | /* | ||
461 | * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that | ||
462 | * setting the gain below -100 dB (register value <0x7) is effectively a MUTE | ||
463 | * as per device datasheet. | ||
464 | */ | ||
465 | static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0); | ||
466 | |||
467 | static const struct snd_kcontrol_new tas5720_snd_controls[] = { | ||
468 | SOC_SINGLE_TLV("Speaker Driver Playback Volume", | ||
469 | TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, dac_tlv), | ||
470 | SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, | ||
471 | TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), | ||
472 | }; | ||
473 | |||
474 | static const struct snd_soc_dapm_widget tas5720_dapm_widgets[] = { | ||
475 | SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), | ||
476 | SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas5720_dac_event, | ||
477 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), | ||
478 | SND_SOC_DAPM_OUTPUT("OUT") | ||
479 | }; | ||
480 | |||
481 | static const struct snd_soc_dapm_route tas5720_audio_map[] = { | ||
482 | { "DAC", NULL, "DAC IN" }, | ||
483 | { "OUT", NULL, "DAC" }, | ||
484 | }; | ||
485 | |||
486 | static struct snd_soc_codec_driver soc_codec_dev_tas5720 = { | ||
487 | .probe = tas5720_codec_probe, | ||
488 | .remove = tas5720_codec_remove, | ||
489 | .suspend = tas5720_suspend, | ||
490 | .resume = tas5720_resume, | ||
491 | |||
492 | .controls = tas5720_snd_controls, | ||
493 | .num_controls = ARRAY_SIZE(tas5720_snd_controls), | ||
494 | .dapm_widgets = tas5720_dapm_widgets, | ||
495 | .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), | ||
496 | .dapm_routes = tas5720_audio_map, | ||
497 | .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), | ||
498 | }; | ||
499 | |||
500 | /* PCM rates supported by the TAS5720 driver */ | ||
501 | #define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ | ||
502 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | ||
503 | |||
504 | /* Formats supported by TAS5720 driver */ | ||
505 | #define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\ | ||
506 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) | ||
507 | |||
508 | static struct snd_soc_dai_ops tas5720_speaker_dai_ops = { | ||
509 | .hw_params = tas5720_hw_params, | ||
510 | .set_fmt = tas5720_set_dai_fmt, | ||
511 | .set_tdm_slot = tas5720_set_dai_tdm_slot, | ||
512 | .digital_mute = tas5720_mute, | ||
513 | }; | ||
514 | |||
515 | /* | ||
516 | * TAS5720 DAI structure | ||
517 | * | ||
518 | * Note that were are advertising .playback.channels_max = 2 despite this being | ||
519 | * a mono amplifier. The reason for that is that some serial ports such as TI's | ||
520 | * McASP module have a minimum number of channels (2) that they can output. | ||
521 | * Advertising more channels than we have will allow us to interface with such | ||
522 | * a serial port without really any negative side effects as the TAS5720 will | ||
523 | * simply ignore any extra channel(s) asides from the one channel that is | ||
524 | * configured to be played back. | ||
525 | */ | ||
526 | static struct snd_soc_dai_driver tas5720_dai[] = { | ||
527 | { | ||
528 | .name = "tas5720-amplifier", | ||
529 | .playback = { | ||
530 | .stream_name = "Playback", | ||
531 | .channels_min = 1, | ||
532 | .channels_max = 2, | ||
533 | .rates = TAS5720_RATES, | ||
534 | .formats = TAS5720_FORMATS, | ||
535 | }, | ||
536 | .ops = &tas5720_speaker_dai_ops, | ||
537 | }, | ||
538 | }; | ||
539 | |||
540 | static int tas5720_probe(struct i2c_client *client, | ||
541 | const struct i2c_device_id *id) | ||
542 | { | ||
543 | struct device *dev = &client->dev; | ||
544 | struct tas5720_data *data; | ||
545 | int ret; | ||
546 | int i; | ||
547 | |||
548 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||
549 | if (!data) | ||
550 | return -ENOMEM; | ||
551 | |||
552 | data->tas5720_client = client; | ||
553 | data->regmap = devm_regmap_init_i2c(client, &tas5720_regmap_config); | ||
554 | if (IS_ERR(data->regmap)) { | ||
555 | ret = PTR_ERR(data->regmap); | ||
556 | dev_err(dev, "failed to allocate register map: %d\n", ret); | ||
557 | return ret; | ||
558 | } | ||
559 | |||
560 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) | ||
561 | data->supplies[i].supply = tas5720_supply_names[i]; | ||
562 | |||
563 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), | ||
564 | data->supplies); | ||
565 | if (ret != 0) { | ||
566 | dev_err(dev, "failed to request supplies: %d\n", ret); | ||
567 | return ret; | ||
568 | } | ||
569 | |||
570 | dev_set_drvdata(dev, data); | ||
571 | |||
572 | ret = snd_soc_register_codec(&client->dev, | ||
573 | &soc_codec_dev_tas5720, | ||
574 | tas5720_dai, ARRAY_SIZE(tas5720_dai)); | ||
575 | if (ret < 0) { | ||
576 | dev_err(dev, "failed to register codec: %d\n", ret); | ||
577 | return ret; | ||
578 | } | ||
579 | |||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static int tas5720_remove(struct i2c_client *client) | ||
584 | { | ||
585 | struct device *dev = &client->dev; | ||
586 | |||
587 | snd_soc_unregister_codec(dev); | ||
588 | |||
589 | return 0; | ||
590 | } | ||
591 | |||
592 | static const struct i2c_device_id tas5720_id[] = { | ||
593 | { "tas5720", 0 }, | ||
594 | { } | ||
595 | }; | ||
596 | MODULE_DEVICE_TABLE(i2c, tas5720_id); | ||
597 | |||
598 | #if IS_ENABLED(CONFIG_OF) | ||
599 | static const struct of_device_id tas5720_of_match[] = { | ||
600 | { .compatible = "ti,tas5720", }, | ||
601 | { }, | ||
602 | }; | ||
603 | MODULE_DEVICE_TABLE(of, tas5720_of_match); | ||
604 | #endif | ||
605 | |||
606 | static struct i2c_driver tas5720_i2c_driver = { | ||
607 | .driver = { | ||
608 | .name = "tas5720", | ||
609 | .of_match_table = of_match_ptr(tas5720_of_match), | ||
610 | }, | ||
611 | .probe = tas5720_probe, | ||
612 | .remove = tas5720_remove, | ||
613 | .id_table = tas5720_id, | ||
614 | }; | ||
615 | |||
616 | module_i2c_driver(tas5720_i2c_driver); | ||
617 | |||
618 | MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); | ||
619 | MODULE_DESCRIPTION("TAS5720 Audio amplifier driver"); | ||
620 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/tas5720.h b/sound/soc/codecs/tas5720.h new file mode 100644 index 000000000000..3d077c779b12 --- /dev/null +++ b/sound/soc/codecs/tas5720.h | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * tas5720.h - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier | ||
3 | * | ||
4 | * Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com | ||
5 | * | ||
6 | * Author: Andreas Dannenberg <dannenberg@ti.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #ifndef __TAS5720_H__ | ||
19 | #define __TAS5720_H__ | ||
20 | |||
21 | /* Register Address Map */ | ||
22 | #define TAS5720_DEVICE_ID_REG 0x00 | ||
23 | #define TAS5720_POWER_CTRL_REG 0x01 | ||
24 | #define TAS5720_DIGITAL_CTRL1_REG 0x02 | ||
25 | #define TAS5720_DIGITAL_CTRL2_REG 0x03 | ||
26 | #define TAS5720_VOLUME_CTRL_REG 0x04 | ||
27 | #define TAS5720_ANALOG_CTRL_REG 0x06 | ||
28 | #define TAS5720_FAULT_REG 0x08 | ||
29 | #define TAS5720_DIGITAL_CLIP2_REG 0x10 | ||
30 | #define TAS5720_DIGITAL_CLIP1_REG 0x11 | ||
31 | #define TAS5720_MAX_REG TAS5720_DIGITAL_CLIP1_REG | ||
32 | |||
33 | /* TAS5720_DEVICE_ID_REG */ | ||
34 | #define TAS5720_DEVICE_ID 0x01 | ||
35 | |||
36 | /* TAS5720_POWER_CTRL_REG */ | ||
37 | #define TAS5720_DIG_CLIP_MASK GENMASK(7, 2) | ||
38 | #define TAS5720_SLEEP BIT(1) | ||
39 | #define TAS5720_SDZ BIT(0) | ||
40 | |||
41 | /* TAS5720_DIGITAL_CTRL1_REG */ | ||
42 | #define TAS5720_HPF_BYPASS BIT(7) | ||
43 | #define TAS5720_TDM_CFG_SRC BIT(6) | ||
44 | #define TAS5720_SSZ_DS BIT(3) | ||
45 | #define TAS5720_SAIF_RIGHTJ_24BIT (0x0) | ||
46 | #define TAS5720_SAIF_RIGHTJ_20BIT (0x1) | ||
47 | #define TAS5720_SAIF_RIGHTJ_18BIT (0x2) | ||
48 | #define TAS5720_SAIF_RIGHTJ_16BIT (0x3) | ||
49 | #define TAS5720_SAIF_I2S (0x4) | ||
50 | #define TAS5720_SAIF_LEFTJ (0x5) | ||
51 | #define TAS5720_SAIF_FORMAT_MASK GENMASK(2, 0) | ||
52 | |||
53 | /* TAS5720_DIGITAL_CTRL2_REG */ | ||
54 | #define TAS5720_MUTE BIT(4) | ||
55 | #define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0) | ||
56 | |||
57 | /* TAS5720_ANALOG_CTRL_REG */ | ||
58 | #define TAS5720_PWM_RATE_6_3_FSYNC (0x0 << 4) | ||
59 | #define TAS5720_PWM_RATE_8_4_FSYNC (0x1 << 4) | ||
60 | #define TAS5720_PWM_RATE_10_5_FSYNC (0x2 << 4) | ||
61 | #define TAS5720_PWM_RATE_12_6_FSYNC (0x3 << 4) | ||
62 | #define TAS5720_PWM_RATE_14_7_FSYNC (0x4 << 4) | ||
63 | #define TAS5720_PWM_RATE_16_8_FSYNC (0x5 << 4) | ||
64 | #define TAS5720_PWM_RATE_20_10_FSYNC (0x6 << 4) | ||
65 | #define TAS5720_PWM_RATE_24_12_FSYNC (0x7 << 4) | ||
66 | #define TAS5720_PWM_RATE_MASK GENMASK(6, 4) | ||
67 | #define TAS5720_ANALOG_GAIN_19_2DBV (0x0 << 2) | ||
68 | #define TAS5720_ANALOG_GAIN_20_7DBV (0x1 << 2) | ||
69 | #define TAS5720_ANALOG_GAIN_23_5DBV (0x2 << 2) | ||
70 | #define TAS5720_ANALOG_GAIN_26_3DBV (0x3 << 2) | ||
71 | #define TAS5720_ANALOG_GAIN_MASK GENMASK(3, 2) | ||
72 | #define TAS5720_ANALOG_GAIN_SHIFT (0x2) | ||
73 | |||
74 | /* TAS5720_FAULT_REG */ | ||
75 | #define TAS5720_OC_THRESH_100PCT (0x0 << 4) | ||
76 | #define TAS5720_OC_THRESH_75PCT (0x1 << 4) | ||
77 | #define TAS5720_OC_THRESH_50PCT (0x2 << 4) | ||
78 | #define TAS5720_OC_THRESH_25PCT (0x3 << 4) | ||
79 | #define TAS5720_OC_THRESH_MASK GENMASK(5, 4) | ||
80 | #define TAS5720_CLKE BIT(3) | ||
81 | #define TAS5720_OCE BIT(2) | ||
82 | #define TAS5720_DCE BIT(1) | ||
83 | #define TAS5720_OTE BIT(0) | ||
84 | #define TAS5720_FAULT_MASK GENMASK(3, 0) | ||
85 | |||
86 | /* TAS5720_DIGITAL_CLIP1_REG */ | ||
87 | #define TAS5720_CLIP1_MASK GENMASK(7, 2) | ||
88 | #define TAS5720_CLIP1_SHIFT (0x2) | ||
89 | |||
90 | #endif /* __TAS5720_H__ */ | ||