diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-28 21:23:12 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-28 21:23:12 +0200 |
commit | 0723ab4a97a19bf9da135d68529977aeba17570d (patch) | |
tree | 453051ed67b556ddd13a5921742ba228c22d980a /sound/soc/codecs | |
parent | Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/... (diff) | |
parent | Merge tag 'asoc-v4.7-2' of git://git.kernel.org/pub/scm/linux/kernel/git/broo... (diff) | |
download | linux-0723ab4a97a19bf9da135d68529977aeba17570d.tar.xz linux-0723ab4a97a19bf9da135d68529977aeba17570d.zip |
Merge tag 'sound-4.7-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull more sound updates from Takashi Iwai:
"This is the second update round for 4.7-rc1. Most of changes are
about the pending ASoC updates and fixes, including a few new drivers.
Below are some highlights:
ASoC:
- New drivers for MAX98371 and TAS5720
- SPI support for TLV320AIC32x4, along with the module split
- TDM support for STI Uniperf IPs
- Remaining topology API fixes / updates
HDA:
- A couple of Dell quirks and new Realtek codec support"
* tag 'sound-4.7-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (63 commits)
ALSA: hda - Fix headset mic detection problem for one Dell machine
spi: spi-ep93xx: Fix the PTR_ERR() argument
ALSA: hda/realtek - Add support for ALC295/ALC3254
ASoC: kirkwood: fix build failure
ALSA: hda - Fix headphone noise on Dell XPS 13 9360
ASoC: ak4642: Enable cache usage to fix crashes on resume
ASoC: twl6040: Disconnect AUX output pads on digital mute
ASoC: tlv320aic32x4: Properly implement the positive and negative pins into the mixers
rcar: src: skip disabled-SRC nodes
ASoC: max98371 Remove duplicate entry in max98371_reg
ASoC: twl6040: Select LPPLL during standby
ASoC: rsnd: don't use prohibited number to PDMACHCRn.SRS
ASoC: simple-card: Add pm callbacks to platform driver
ASoC: pxa: Fix module autoload for platform drivers
ASoC: topology: Fix memory leak in widget creation
ASoC: Add max98371 codec driver
ASoC: rsnd: count .probe/.remove for rsnd_mod_call()
ASoC: topology: Check size mismatch of ABI objects before parsing
ASoC: topology: Check failure to create a widget
ASoC: add support for TAS5720 digital amplifier
...
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/Kconfig | 38 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 11 | ||||
-rw-r--r-- | sound/soc/codecs/ak4642.c | 3 | ||||
-rw-r--r-- | sound/soc/codecs/max98371.c | 441 | ||||
-rw-r--r-- | sound/soc/codecs/max98371.h | 67 | ||||
-rw-r--r-- | sound/soc/codecs/rt298.c | 51 | ||||
-rw-r--r-- | sound/soc/codecs/rt298.h | 2 | ||||
-rw-r--r-- | sound/soc/codecs/rt5677.c | 24 | ||||
-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 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic31xx.c | 10 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic32x4-i2c.c | 74 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic32x4-spi.c | 76 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic32x4.c | 279 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic32x4.h | 7 | ||||
-rw-r--r-- | sound/soc/codecs/twl6040.c | 16 | ||||
-rw-r--r-- | sound/soc/codecs/wm8962.c | 9 | ||||
-rw-r--r-- | sound/soc/codecs/wm8962.h | 6 |
20 files changed, 1824 insertions, 163 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b3afae990e39..4d82a58ff6b0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -43,6 +43,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AK5386 select SND_SOC_ALC5623 if I2C select SND_SOC_ALC5632 if I2C + select SND_SOC_BT_SCO select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS35L32 if I2C select SND_SOC_CS42L51_I2C if I2C @@ -64,7 +65,6 @@ config SND_SOC_ALL_CODECS select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C select SND_SOC_DMIC - select SND_SOC_BT_SCO select SND_SOC_ES8328_SPI if SPI_MASTER select SND_SOC_ES8328_I2C if I2C select SND_SOC_GTM601 @@ -79,6 +79,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX98090 if I2C select SND_SOC_MAX98095 if I2C select SND_SOC_MAX98357A if GPIOLIB + select SND_SOC_MAX98371 if I2C select SND_SOC_MAX9867 if I2C select SND_SOC_MAX98925 if I2C select SND_SOC_MAX98926 if I2C @@ -126,12 +127,14 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C select SND_SOC_TAS571X if I2C + select SND_SOC_TAS5720 if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC31XX if I2C - select SND_SOC_TLV320AIC32X4 if I2C + select SND_SOC_TLV320AIC32X4_I2C if I2C + select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C @@ -367,6 +370,9 @@ config SND_SOC_ALC5623 config SND_SOC_ALC5632 tristate +config SND_SOC_BT_SCO + tristate + config SND_SOC_CQ0093VC tristate @@ -473,9 +479,6 @@ config SND_SOC_DA732X config SND_SOC_DA9055 tristate -config SND_SOC_BT_SCO - tristate - config SND_SOC_DMIC tristate @@ -529,6 +532,9 @@ config SND_SOC_MAX98095 config SND_SOC_MAX98357A tristate +config SND_SOC_MAX98371 + tristate + config SND_SOC_MAX9867 tristate @@ -748,9 +754,16 @@ config SND_SOC_TAS5086 depends on I2C config SND_SOC_TAS571X - tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers" + tristate "Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 power amplifiers" depends on I2C +config SND_SOC_TAS5720 + tristate "Texas Instruments TAS5720 Mono Audio amplifier" + depends on I2C + help + Enable support for Texas Instruments TAS5720L/M high-efficiency mono + Class-D audio power amplifiers. + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C @@ -780,6 +793,16 @@ config SND_SOC_TLV320AIC31XX config SND_SOC_TLV320AIC32X4 tristate +config SND_SOC_TLV320AIC32X4_I2C + tristate + depends on I2C + select SND_SOC_TLV320AIC32X4 + +config SND_SOC_TLV320AIC32X4_SPI + tristate + depends on SPI_MASTER + select SND_SOC_TLV320AIC32X4 + config SND_SOC_TLV320AIC3X tristate "Texas Instruments TLV320AIC3x CODECs" depends on I2C @@ -920,7 +943,8 @@ config SND_SOC_WM8955 tristate config SND_SOC_WM8960 - tristate + tristate "Wolfson Microelectronics WM8960 CODEC" + depends on I2C config SND_SOC_WM8961 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b7b99416537f..0f548fd34ca3 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -32,6 +32,7 @@ snd-soc-ak4642-objs := ak4642.o snd-soc-ak4671-objs := ak4671.o snd-soc-ak5386-objs := ak5386.o snd-soc-arizona-objs := arizona.o +snd-soc-bt-sco-objs := bt-sco.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs35l32-objs := cs35l32.o snd-soc-cs42l51-objs := cs42l51.o @@ -55,7 +56,6 @@ snd-soc-da7218-objs := da7218.o snd-soc-da7219-objs := da7219.o da7219-aad.o snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o -snd-soc-bt-sco-objs := bt-sco.o snd-soc-dmic-objs := dmic.o snd-soc-es8328-objs := es8328.o snd-soc-es8328-i2c-objs := es8328-i2c.o @@ -74,6 +74,7 @@ snd-soc-max98088-objs := max98088.o snd-soc-max98090-objs := max98090.o snd-soc-max98095-objs := max98095.o snd-soc-max98357a-objs := max98357a.o +snd-soc-max98371-objs := max98371.o snd-soc-max9867-objs := max9867.o snd-soc-max98925-objs := max98925.o snd-soc-max98926-objs := max98926.o @@ -131,6 +132,7 @@ snd-soc-stac9766-objs := stac9766.o snd-soc-sti-sas-objs := sti-sas.o snd-soc-tas5086-objs := tas5086.o snd-soc-tas571x-objs := tas571x.o +snd-soc-tas5720-objs := tas5720.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -138,6 +140,8 @@ snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o +snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o +snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-ts3a227e-objs := ts3a227e.o @@ -243,6 +247,7 @@ obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o +obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o @@ -266,7 +271,6 @@ obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o -obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o @@ -339,6 +343,7 @@ obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o +obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o @@ -346,6 +351,8 @@ obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o +obj-$(CONFIG_SND_SOC_TLV320AIC32X4_I2C) += snd-soc-tlv320aic32x4-i2c.o +obj-$(CONFIG_SND_SOC_TLV320AIC32X4_SPI) += snd-soc-tlv320aic32x4-spi.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 1ee8506c06c7..4d8b9e49e8d6 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -560,6 +560,7 @@ static const struct regmap_config ak4642_regmap = { .max_register = FIL1_3, .reg_defaults = ak4642_reg, .num_reg_defaults = NUM_AK4642_REG_DEFAULTS, + .cache_type = REGCACHE_RBTREE, }; static const struct regmap_config ak4643_regmap = { @@ -568,6 +569,7 @@ static const struct regmap_config ak4643_regmap = { .max_register = SPK_MS, .reg_defaults = ak4643_reg, .num_reg_defaults = ARRAY_SIZE(ak4643_reg), + .cache_type = REGCACHE_RBTREE, }; static const struct regmap_config ak4648_regmap = { @@ -576,6 +578,7 @@ static const struct regmap_config ak4648_regmap = { .max_register = EQ_FBEQE, .reg_defaults = ak4648_reg, .num_reg_defaults = ARRAY_SIZE(ak4648_reg), + .cache_type = REGCACHE_RBTREE, }; static const struct ak4642_drvdata ak4642_drvdata = { diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c new file mode 100644 index 000000000000..cf0a39bb631a --- /dev/null +++ b/sound/soc/codecs/max98371.c @@ -0,0 +1,441 @@ +/* + * max98371.c -- ALSA SoC Stereo MAX98371 driver + * + * Copyright 2015-16 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "max98371.h" + +static const char *const monomix_text[] = { + "Left", "Right", "LeftRightDiv2", +}; + +static const char *const hpf_cutoff_txt[] = { + "Disable", "DC Block", "50Hz", + "100Hz", "200Hz", "400Hz", "800Hz", +}; + +static SOC_ENUM_SINGLE_DECL(max98371_monomix, MAX98371_MONOMIX_CFG, 0, + monomix_text); + +static SOC_ENUM_SINGLE_DECL(max98371_hpf_cutoff, MAX98371_HPF, 0, + hpf_cutoff_txt); + +static const DECLARE_TLV_DB_RANGE(max98371_dht_min_gain, + 0, 1, TLV_DB_SCALE_ITEM(537, 66, 0), + 2, 3, TLV_DB_SCALE_ITEM(677, 82, 0), + 4, 5, TLV_DB_SCALE_ITEM(852, 104, 0), + 6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0), + 8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0), + 10, 11, TLV_DB_SCALE_ITEM(1699, 101, 0), +); + +static const DECLARE_TLV_DB_RANGE(max98371_dht_max_gain, + 0, 1, TLV_DB_SCALE_ITEM(537, 66, 0), + 2, 3, TLV_DB_SCALE_ITEM(677, 82, 0), + 4, 5, TLV_DB_SCALE_ITEM(852, 104, 0), + 6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0), + 8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0), + 10, 11, TLV_DB_SCALE_ITEM(1699, 208, 0), +); + +static const DECLARE_TLV_DB_RANGE(max98371_dht_rot_gain, + 0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0), + 2, 6, TLV_DB_SCALE_ITEM(-100, -100, 0), + 7, 8, TLV_DB_SCALE_ITEM(-800, -200, 0), + 9, 11, TLV_DB_SCALE_ITEM(-1200, -300, 0), + 12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0), + 14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0), +); + +static const struct reg_default max98371_reg[] = { + { 0x01, 0x00 }, + { 0x02, 0x00 }, + { 0x03, 0x00 }, + { 0x04, 0x00 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x00 }, + { 0x0A, 0x00 }, + { 0x10, 0x06 }, + { 0x11, 0x08 }, + { 0x14, 0x80 }, + { 0x15, 0x00 }, + { 0x16, 0x00 }, + { 0x18, 0x00 }, + { 0x19, 0x00 }, + { 0x1C, 0x00 }, + { 0x1D, 0x00 }, + { 0x1E, 0x00 }, + { 0x1F, 0x00 }, + { 0x20, 0x00 }, + { 0x21, 0x00 }, + { 0x22, 0x00 }, + { 0x23, 0x00 }, + { 0x24, 0x00 }, + { 0x25, 0x00 }, + { 0x26, 0x00 }, + { 0x27, 0x00 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2A, 0x00 }, + { 0x2B, 0x00 }, + { 0x2C, 0x00 }, + { 0x2D, 0x00 }, + { 0x2E, 0x0B }, + { 0x31, 0x00 }, + { 0x32, 0x18 }, + { 0x33, 0x00 }, + { 0x34, 0x00 }, + { 0x36, 0x00 }, + { 0x37, 0x00 }, + { 0x38, 0x00 }, + { 0x39, 0x00 }, + { 0x3A, 0x00 }, + { 0x3B, 0x00 }, + { 0x3C, 0x00 }, + { 0x3D, 0x00 }, + { 0x3E, 0x00 }, + { 0x3F, 0x00 }, + { 0x40, 0x00 }, + { 0x41, 0x00 }, + { 0x42, 0x00 }, + { 0x43, 0x00 }, + { 0x4A, 0x00 }, + { 0x4B, 0x00 }, + { 0x4C, 0x00 }, + { 0x4D, 0x00 }, + { 0x4E, 0x00 }, + { 0x50, 0x00 }, + { 0x51, 0x00 }, + { 0x55, 0x00 }, + { 0x58, 0x00 }, + { 0x59, 0x00 }, + { 0x5C, 0x00 }, + { 0xFF, 0x43 }, +}; + +static bool max98371_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98371_IRQ_CLEAR1: + case MAX98371_IRQ_CLEAR2: + case MAX98371_IRQ_CLEAR3: + case MAX98371_VERSION: + return true; + default: + return false; + } +} + +static bool max98371_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98371_SOFT_RESET: + return false; + default: + return true; + } +}; + +static const DECLARE_TLV_DB_RANGE(max98371_gain_tlv, + 0, 7, TLV_DB_SCALE_ITEM(0, 50, 0), + 8, 10, TLV_DB_SCALE_ITEM(400, 100, 0) +); + +static const DECLARE_TLV_DB_RANGE(max98371_noload_gain_tlv, + 0, 11, TLV_DB_SCALE_ITEM(950, 100, 0), +); + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -6300, 50, 1); + +static const struct snd_kcontrol_new max98371_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", MAX98371_GAIN, + MAX98371_GAIN_SHIFT, (1<<MAX98371_GAIN_WIDTH)-1, 0, + max98371_gain_tlv), + SOC_SINGLE_TLV("Digital Volume", MAX98371_DIGITAL_GAIN, 0, + (1<<MAX98371_DIGITAL_GAIN_WIDTH)-1, 1, digital_tlv), + SOC_SINGLE_TLV("Speaker DHT Max Volume", MAX98371_GAIN, + 0, (1<<MAX98371_DHT_MAX_WIDTH)-1, 0, + max98371_dht_max_gain), + SOC_SINGLE_TLV("Speaker DHT Min Volume", MAX98371_DHT_GAIN, + 0, (1<<MAX98371_DHT_GAIN_WIDTH)-1, 0, + max98371_dht_min_gain), + SOC_SINGLE_TLV("Speaker DHT Rotation Volume", MAX98371_DHT_GAIN, + 0, (1<<MAX98371_DHT_ROT_WIDTH)-1, 0, + max98371_dht_rot_gain), + SOC_SINGLE("DHT Attack Step", MAX98371_DHT, MAX98371_DHT_STEP, 3, 0), + SOC_SINGLE("DHT Attack Rate", MAX98371_DHT, 0, 7, 0), + SOC_ENUM("Monomix Select", max98371_monomix), + SOC_ENUM("HPF Cutoff", max98371_hpf_cutoff), +}; + +static int max98371_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98371_priv *max98371 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + dev_err(codec->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val |= 0; + break; + case SND_SOC_DAIFMT_RIGHT_J: + val |= MAX98371_DAI_RIGHT; + break; + case SND_SOC_DAIFMT_LEFT_J: + val |= MAX98371_DAI_LEFT; + break; + default: + dev_err(codec->dev, "DAI wrong mode unsupported"); + return -EINVAL; + } + regmap_update_bits(max98371->regmap, MAX98371_FMT, + MAX98371_FMT_MODE_MASK, val); + return 0; +} + +static int max98371_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98371_priv *max98371 = snd_soc_codec_get_drvdata(codec); + int blr_clk_ratio, ch_size, channels = params_channels(params); + int rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + regmap_update_bits(max98371->regmap, MAX98371_FMT, + MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16); + ch_size = 8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + regmap_update_bits(max98371->regmap, MAX98371_FMT, + MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16); + ch_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + regmap_update_bits(max98371->regmap, MAX98371_FMT, + MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32); + ch_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + regmap_update_bits(max98371->regmap, MAX98371_FMT, + MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32); + ch_size = 32; + break; + default: + return -EINVAL; + } + + /* BCLK/LRCLK ratio calculation */ + blr_clk_ratio = channels * ch_size; + switch (blr_clk_ratio) { + case 32: + regmap_update_bits(max98371->regmap, + MAX98371_DAI_CLK, + MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_32); + break; + case 48: + regmap_update_bits(max98371->regmap, + MAX98371_DAI_CLK, + MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_48); + break; + case 64: + regmap_update_bits(max98371->regmap, + MAX98371_DAI_CLK, + MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_64); + break; + default: + return -EINVAL; + } + + switch (rate) { + case 32000: + regmap_update_bits(max98371->regmap, + MAX98371_SPK_SR, + MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_32); + break; + case 44100: + regmap_update_bits(max98371->regmap, + MAX98371_SPK_SR, + MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_44); + break; + case 48000: + regmap_update_bits(max98371->regmap, + MAX98371_SPK_SR, + MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_48); + break; + case 88200: + regmap_update_bits(max98371->regmap, + MAX98371_SPK_SR, + MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_88); + break; + case 96000: + regmap_update_bits(max98371->regmap, + MAX98371_SPK_SR, + MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_96); + break; + default: + return -EINVAL; + } + + /* enabling both the RX channels*/ + regmap_update_bits(max98371->regmap, MAX98371_MONOMIX_SRC, + MAX98371_MONOMIX_SRC_MASK, MONOMIX_RX_0_1); + regmap_update_bits(max98371->regmap, MAX98371_DAI_CHANNEL, + MAX98371_CHANNEL_MASK, MAX98371_CHANNEL_MASK); + return 0; +} + +static const struct snd_soc_dapm_widget max98371_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", NULL, MAX98371_SPK_ENABLE, 0, 0), + SND_SOC_DAPM_SUPPLY("Global Enable", MAX98371_GLOBAL_ENABLE, + 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPK_OUT"), +}; + +static const struct snd_soc_dapm_route max98371_audio_map[] = { + {"DAC", NULL, "HiFi Playback"}, + {"SPK_OUT", NULL, "DAC"}, + {"SPK_OUT", NULL, "Global Enable"}, +}; + +#define MAX98371_RATES SNDRV_PCM_RATE_8000_48000 +#define MAX98371_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE) + +static const struct snd_soc_dai_ops max98371_dai_ops = { + .set_fmt = max98371_dai_set_fmt, + .hw_params = max98371_dai_hw_params, +}; + +static struct snd_soc_dai_driver max98371_dai[] = { + { + .name = "max98371-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98371_FORMATS, + }, + .ops = &max98371_dai_ops, + } +}; + +static const struct snd_soc_codec_driver max98371_codec = { + .controls = max98371_snd_controls, + .num_controls = ARRAY_SIZE(max98371_snd_controls), + .dapm_routes = max98371_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98371_audio_map), + .dapm_widgets = max98371_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98371_dapm_widgets), +}; + +static const struct regmap_config max98371_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX98371_VERSION, + .reg_defaults = max98371_reg, + .num_reg_defaults = ARRAY_SIZE(max98371_reg), + .volatile_reg = max98371_volatile_register, + .readable_reg = max98371_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98371_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max98371_priv *max98371; + int ret, reg; + + max98371 = devm_kzalloc(&i2c->dev, + sizeof(*max98371), GFP_KERNEL); + if (!max98371) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98371); + max98371->regmap = devm_regmap_init_i2c(i2c, &max98371_regmap); + if (IS_ERR(max98371->regmap)) { + ret = PTR_ERR(max98371->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + + ret = regmap_read(max98371->regmap, MAX98371_VERSION, ®); + if (ret < 0) { + dev_info(&i2c->dev, "device error %d\n", ret); + return ret; + } + dev_info(&i2c->dev, "device version %x\n", reg); + + ret = snd_soc_register_codec(&i2c->dev, &max98371_codec, + max98371_dai, ARRAY_SIZE(max98371_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + return ret; + } + return ret; +} + +static int max98371_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id max98371_i2c_id[] = { + { "max98371", 0 }, +}; + +MODULE_DEVICE_TABLE(i2c, max98371_i2c_id); + +static const struct of_device_id max98371_of_match[] = { + { .compatible = "maxim,max98371", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98371_of_match); + +static struct i2c_driver max98371_i2c_driver = { + .driver = { + .name = "max98371", + .owner = THIS_MODULE, + .pm = NULL, + .of_match_table = of_match_ptr(max98371_of_match), + }, + .probe = max98371_i2c_probe, + .remove = max98371_i2c_remove, + .id_table = max98371_i2c_id, +}; + +module_i2c_driver(max98371_i2c_driver); + +MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>"); +MODULE_DESCRIPTION("ALSA SoC MAX98371 driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98371.h b/sound/soc/codecs/max98371.h new file mode 100644 index 000000000000..9f6330964d98 --- /dev/null +++ b/sound/soc/codecs/max98371.h @@ -0,0 +1,67 @@ +/* + * max98371.h -- MAX98371 ALSA SoC Audio driver + * + * Copyright 2011-2012 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX98371_H +#define _MAX98371_H + +#define MAX98371_IRQ_CLEAR1 0x01 +#define MAX98371_IRQ_CLEAR2 0x02 +#define MAX98371_IRQ_CLEAR3 0x03 +#define MAX98371_DAI_CLK 0x10 +#define MAX98371_DAI_BSEL_MASK 0xF +#define MAX98371_DAI_BSEL_32 2 +#define MAX98371_DAI_BSEL_48 3 +#define MAX98371_DAI_BSEL_64 4 +#define MAX98371_SPK_SR 0x11 +#define MAX98371_SPK_SR_MASK 0xF +#define MAX98371_SPK_SR_32 6 +#define MAX98371_SPK_SR_44 7 +#define MAX98371_SPK_SR_48 8 +#define MAX98371_SPK_SR_88 10 +#define MAX98371_SPK_SR_96 11 +#define MAX98371_DAI_CHANNEL 0x15 +#define MAX98371_CHANNEL_MASK 0x3 +#define MAX98371_MONOMIX_SRC 0x18 +#define MAX98371_MONOMIX_CFG 0x19 +#define MAX98371_HPF 0x1C +#define MAX98371_MONOMIX_SRC_MASK 0xFF +#define MONOMIX_RX_0_1 ((0x1)<<(4)) +#define M98371_DAI_CHANNEL_I2S 0x3 +#define MAX98371_DIGITAL_GAIN 0x2D +#define MAX98371_DIGITAL_GAIN_WIDTH 0x7 +#define MAX98371_GAIN 0x2E +#define MAX98371_GAIN_SHIFT 0x4 +#define MAX98371_GAIN_WIDTH 0x4 +#define MAX98371_DHT_MAX_WIDTH 4 +#define MAX98371_FMT 0x14 +#define MAX98371_CHANSZ_WIDTH 6 +#define MAX98371_FMT_MASK ((0x3)<<(MAX98371_CHANSZ_WIDTH)) +#define MAX98371_FMT_MODE_MASK ((0x7)<<(3)) +#define MAX98371_DAI_LEFT ((0x1)<<(3)) +#define MAX98371_DAI_RIGHT ((0x2)<<(3)) +#define MAX98371_DAI_CHANSZ_16 ((1)<<(MAX98371_CHANSZ_WIDTH)) +#define MAX98371_DAI_CHANSZ_24 ((2)<<(MAX98371_CHANSZ_WIDTH)) +#define MAX98371_DAI_CHANSZ_32 ((3)<<(MAX98371_CHANSZ_WIDTH)) +#define MAX98371_DHT 0x32 +#define MAX98371_DHT_STEP 0x3 +#define MAX98371_DHT_GAIN 0x31 +#define MAX98371_DHT_GAIN_WIDTH 0x4 +#define MAX98371_DHT_ROT_WIDTH 0x4 +#define MAX98371_SPK_ENABLE 0x4A +#define MAX98371_GLOBAL_ENABLE 0x50 +#define MAX98371_SOFT_RESET 0x51 +#define MAX98371_VERSION 0xFF + + +struct max98371_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; +}; +#endif diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index a1aaffc20862..f80cfe4d2ef2 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -276,6 +276,8 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic) } else { *mic = false; regmap_write(rt298->regmap, RT298_SET_MIC1, 0x20); + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, 0x0400, 0x0000); } } else { regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf); @@ -482,6 +484,26 @@ static int rt298_adc_event(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0), 0x7080, 0x7000); + /* If MCLK doesn't exist, reset AD filter */ + if (!(snd_soc_read(codec, RT298_VAD_CTRL) & 0x200)) { + pr_info("NO MCLK\n"); + switch (nid) { + case RT298_ADC_IN1: + snd_soc_update_bits(codec, + RT298_D_FILTER_CTRL, 0x2, 0x2); + mdelay(10); + snd_soc_update_bits(codec, + RT298_D_FILTER_CTRL, 0x2, 0x0); + break; + case RT298_ADC_IN2: + snd_soc_update_bits(codec, + RT298_D_FILTER_CTRL, 0x4, 0x4); + mdelay(10); + snd_soc_update_bits(codec, + RT298_D_FILTER_CTRL, 0x4, 0x0); + break; + } + } break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, @@ -520,30 +542,12 @@ static int rt298_mic1_event(struct snd_soc_dapm_widget *w, return 0; } -static int rt298_vref_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, - RT298_CBJ_CTRL1, 0x0400, 0x0000); - mdelay(50); - break; - default: - return 0; - } - - return 0; -} - static const struct snd_soc_dapm_widget rt298_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY_S("HV", 1, RT298_POWER_CTRL1, 12, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("VREF", RT298_POWER_CTRL1, - 0, 1, rt298_vref_event, SND_SOC_DAPM_PRE_PMU), + 0, 1, NULL, 0), SND_SOC_DAPM_SUPPLY_S("BG_MBIAS", 1, RT298_POWER_CTRL2, 1, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT298_POWER_CTRL2, @@ -934,18 +938,9 @@ static int rt298_set_bias_level(struct snd_soc_codec *codec, } break; - case SND_SOC_BIAS_ON: - mdelay(30); - snd_soc_update_bits(codec, - RT298_CBJ_CTRL1, 0x0400, 0x0400); - - break; - case SND_SOC_BIAS_STANDBY: snd_soc_write(codec, RT298_SET_AUDIO_POWER, AC_PWRST_D3); - snd_soc_update_bits(codec, - RT298_CBJ_CTRL1, 0x0400, 0x0000); break; default: diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h index d66f8847b676..3638f3d61209 100644 --- a/sound/soc/codecs/rt298.h +++ b/sound/soc/codecs/rt298.h @@ -137,6 +137,7 @@ #define RT298_A_BIAS_CTRL2 0x02 #define RT298_POWER_CTRL1 0x03 #define RT298_A_BIAS_CTRL3 0x04 +#define RT298_D_FILTER_CTRL 0x05 #define RT298_POWER_CTRL2 0x08 #define RT298_I2S_CTRL1 0x09 #define RT298_I2S_CTRL2 0x0a @@ -148,6 +149,7 @@ #define RT298_IRQ_CTRL 0x33 #define RT298_WIND_FILTER_CTRL 0x46 #define RT298_PLL_CTRL1 0x49 +#define RT298_VAD_CTRL 0x4e #define RT298_CBJ_CTRL1 0x4f #define RT298_CBJ_CTRL2 0x50 #define RT298_PLL_CTRL 0x63 diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 60212266d5d1..da9483c1c6fb 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -1241,60 +1241,46 @@ static int rt5677_dmic_use_asrc(struct snd_soc_dapm_widget *source, regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); asrc_setting = (asrc_setting & RT5677_AD_STO1_CLK_SEL_MASK) >> RT5677_AD_STO1_CLK_SEL_SFT; - if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && - asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) - return 1; break; case 10: regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); asrc_setting = (asrc_setting & RT5677_AD_STO2_CLK_SEL_MASK) >> RT5677_AD_STO2_CLK_SEL_SFT; - if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && - asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) - return 1; break; case 9: regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); asrc_setting = (asrc_setting & RT5677_AD_STO3_CLK_SEL_MASK) >> RT5677_AD_STO3_CLK_SEL_SFT; - if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && - asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) - return 1; break; case 8: regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); asrc_setting = (asrc_setting & RT5677_AD_STO4_CLK_SEL_MASK) >> RT5677_AD_STO4_CLK_SEL_SFT; - if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && - asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) - return 1; break; case 7: regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting); asrc_setting = (asrc_setting & RT5677_AD_MONOL_CLK_SEL_MASK) >> RT5677_AD_MONOL_CLK_SEL_SFT; - if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && - asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) - return 1; break; case 6: regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting); asrc_setting = (asrc_setting & RT5677_AD_MONOR_CLK_SEL_MASK) >> RT5677_AD_MONOR_CLK_SEL_SFT; - if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && - asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) - return 1; break; default: - break; + return 0; } + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + return 0; } 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 @@ * Copyright (C) 2015 Google, Inc. * Copyright (c) 2013 Daniel Mack <zonque@gmail.com> * + * TAS5721 support: + * Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * 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) case TAS571X_CH1_VOL_REG: case TAS571X_CH2_VOL_REG: return priv->chip->vol_reg_size; + case TAS571X_INPUT_MUX_REG: + case TAS571X_CH4_SRC_SELECT_REG: + case TAS571X_PWM_MUX_REG: + return 4; default: return 1; } @@ -167,6 +174,23 @@ static int tas571x_hw_params(struct snd_pcm_substream *substream, TAS571X_SDI_FMT_MASK, val); } +static int tas571x_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u8 sysctl2; + int ret; + + sysctl2 = mute ? TAS571X_SYS_CTRL_2_SDN_MASK : 0; + + ret = snd_soc_update_bits(codec, + TAS571X_SYS_CTRL_2_REG, + TAS571X_SYS_CTRL_2_SDN_MASK, + sysctl2); + usleep_range(1000, 2000); + + return ret; +} + static int tas571x_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { @@ -214,6 +238,7 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec, static const struct snd_soc_dai_ops tas571x_dai_ops = { .set_fmt = tas571x_set_dai_fmt, .hw_params = tas571x_hw_params, + .digital_mute = tas571x_mute, }; static const char *const tas5711_supply_names[] = { @@ -241,6 +266,26 @@ static const struct snd_kcontrol_new tas5711_controls[] = { 1, 1), }; +static const struct regmap_range tas571x_readonly_regs_range[] = { + regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_DEV_ID_REG), +}; + +static const struct regmap_range tas571x_volatile_regs_range[] = { + regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG), + regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG), +}; + +static const struct regmap_access_table tas571x_write_regs = { + .no_ranges = tas571x_readonly_regs_range, + .n_no_ranges = ARRAY_SIZE(tas571x_readonly_regs_range), +}; + +static const struct regmap_access_table tas571x_volatile_regs = { + .yes_ranges = tas571x_volatile_regs_range, + .n_yes_ranges = ARRAY_SIZE(tas571x_volatile_regs_range), + +}; + static const struct reg_default tas5711_reg_defaults[] = { { 0x04, 0x05 }, { 0x05, 0x40 }, @@ -260,6 +305,8 @@ static const struct regmap_config tas5711_regmap_config = { .reg_defaults = tas5711_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas571x_volatile_regs, }; static const struct tas571x_chip tas5711_chip = { @@ -314,6 +361,8 @@ static const struct regmap_config tas5717_regmap_config = { .reg_defaults = tas5717_reg_defaults, .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas571x_volatile_regs, }; /* This entry is reused for tas5719 as the software interface is identical. */ @@ -326,6 +375,77 @@ static const struct tas571x_chip tas5717_chip = { .vol_reg_size = 2, }; +static const char *const tas5721_supply_names[] = { + "AVDD", + "DVDD", + "DRVDD", + "PVDD", +}; + +static const struct snd_kcontrol_new tas5721_controls[] = { + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, + TAS571X_CH2_VOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), +}; + +static const struct reg_default tas5721_reg_defaults[] = { + {TAS571X_CLK_CTRL_REG, 0x6c}, + {TAS571X_DEV_ID_REG, 0x00}, + {TAS571X_ERR_STATUS_REG, 0x00}, + {TAS571X_SYS_CTRL_1_REG, 0xa0}, + {TAS571X_SDI_REG, 0x05}, + {TAS571X_SYS_CTRL_2_REG, 0x40}, + {TAS571X_SOFT_MUTE_REG, 0x00}, + {TAS571X_MVOL_REG, 0xff}, + {TAS571X_CH1_VOL_REG, 0x30}, + {TAS571X_CH2_VOL_REG, 0x30}, + {TAS571X_CH3_VOL_REG, 0x30}, + {TAS571X_VOL_CFG_REG, 0x91}, + {TAS571X_MODULATION_LIMIT_REG, 0x02}, + {TAS571X_IC_DELAY_CH1_REG, 0xac}, + {TAS571X_IC_DELAY_CH2_REG, 0x54}, + {TAS571X_IC_DELAY_CH3_REG, 0xac}, + {TAS571X_IC_DELAY_CH4_REG, 0x54}, + {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30}, + {TAS571X_START_STOP_PERIOD_REG, 0x0f}, + {TAS571X_OSC_TRIM_REG, 0x82}, + {TAS571X_BKND_ERR_REG, 0x02}, + {TAS571X_INPUT_MUX_REG, 0x17772}, + {TAS571X_CH4_SRC_SELECT_REG, 0x4303}, + {TAS571X_PWM_MUX_REG, 0x1021345}, +}; + +static const struct regmap_config tas5721_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5721_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5721_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas571x_volatile_regs, +}; + + +static const struct tas571x_chip tas5721_chip = { + .supply_names = tas5721_supply_names, + .num_supply_names = ARRAY_SIZE(tas5721_supply_names), + .controls = tas5711_controls, + .num_controls = ARRAY_SIZE(tas5711_controls), + .regmap_config = &tas5721_regmap_config, + .vol_reg_size = 1, +}; + static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), @@ -386,11 +506,10 @@ static int tas571x_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, priv); of_id = of_match_device(tas571x_of_match, dev); - if (!of_id) { - dev_err(dev, "Unknown device type\n"); - return -EINVAL; - } - priv->chip = of_id->data; + if (of_id) + priv->chip = of_id->data; + else + priv->chip = (void *) id->driver_data; priv->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { @@ -445,10 +564,6 @@ static int tas571x_i2c_probe(struct i2c_client *client, if (ret) return ret; - ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG, - TAS571X_SYS_CTRL_2_SDN_MASK, 0); - if (ret) - return ret; memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); priv->codec_driver.controls = priv->chip->controls; @@ -486,14 +601,16 @@ static const struct of_device_id tas571x_of_match[] = { { .compatible = "ti,tas5711", .data = &tas5711_chip, }, { .compatible = "ti,tas5717", .data = &tas5717_chip, }, { .compatible = "ti,tas5719", .data = &tas5717_chip, }, + { .compatible = "ti,tas5721", .data = &tas5721_chip, }, { } }; MODULE_DEVICE_TABLE(of, tas571x_of_match); static const struct i2c_device_id tas571x_i2c_id[] = { - { "tas5711", 0 }, - { "tas5717", 0 }, - { "tas5719", 0 }, + { "tas5711", (kernel_ulong_t) &tas5711_chip }, + { "tas5717", (kernel_ulong_t) &tas5717_chip }, + { "tas5719", (kernel_ulong_t) &tas5717_chip }, + { "tas5721", (kernel_ulong_t) &tas5721_chip }, { } }; 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 @@ #define _TAS571X_H /* device registers */ +#define TAS571X_CLK_CTRL_REG 0x00 +#define TAS571X_DEV_ID_REG 0x01 +#define TAS571X_ERR_STATUS_REG 0x02 +#define TAS571X_SYS_CTRL_1_REG 0x03 #define TAS571X_SDI_REG 0x04 #define TAS571X_SDI_FMT_MASK 0x0f @@ -27,7 +31,25 @@ #define TAS571X_MVOL_REG 0x07 #define TAS571X_CH1_VOL_REG 0x08 #define TAS571X_CH2_VOL_REG 0x09 +#define TAS571X_CH3_VOL_REG 0x0a +#define TAS571X_VOL_CFG_REG 0x0e +#define TAS571X_MODULATION_LIMIT_REG 0x10 +#define TAS571X_IC_DELAY_CH1_REG 0x11 +#define TAS571X_IC_DELAY_CH2_REG 0x12 +#define TAS571X_IC_DELAY_CH3_REG 0x13 +#define TAS571X_IC_DELAY_CH4_REG 0x14 +#define TAS571X_PWM_CH_SDN_GROUP_REG 0x19 /* N/A on TAS5717, TAS5719 */ +#define TAS571X_PWM_CH1_SDN_MASK (1<<0) +#define TAS571X_PWM_CH2_SDN_SHIFT (1<<1) +#define TAS571X_PWM_CH3_SDN_SHIFT (1<<2) +#define TAS571X_PWM_CH4_SDN_SHIFT (1<<3) + +#define TAS571X_START_STOP_PERIOD_REG 0x1a #define TAS571X_OSC_TRIM_REG 0x1b +#define TAS571X_BKND_ERR_REG 0x1c +#define TAS571X_INPUT_MUX_REG 0x20 +#define TAS571X_CH4_SRC_SELECT_REG 0x21 +#define TAS571X_PWM_MUX_REG 0x25 #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 @@ +/* + * tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier + * + * Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Andreas Dannenberg <dannenberg@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> + +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "tas5720.h" + +/* Define how often to check (and clear) the fault status register (in ms) */ +#define TAS5720_FAULT_CHECK_INTERVAL 200 + +static const char * const tas5720_supply_names[] = { + "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ + "pvdd", /* Class-D amp and analog power supply (connected). */ +}; + +#define TAS5720_NUM_SUPPLIES ARRAY_SIZE(tas5720_supply_names) + +struct tas5720_data { + struct snd_soc_codec *codec; + struct regmap *regmap; + struct i2c_client *tas5720_client; + struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES]; + struct delayed_work fault_check_work; + unsigned int last_fault; +}; + +static int tas5720_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int rate = params_rate(params); + bool ssz_ds; + int ret; + + switch (rate) { + case 44100: + case 48000: + ssz_ds = false; + break; + case 88200: + case 96000: + ssz_ds = true; + break; + default: + dev_err(codec->dev, "unsupported sample rate: %u\n", rate); + return -EINVAL; + } + + ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG, + TAS5720_SSZ_DS, ssz_ds); + if (ret < 0) { + dev_err(codec->dev, "error setting sample rate: %d\n", ret); + return ret; + } + + return 0; +} + +static int tas5720_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + u8 serial_format; + int ret; + + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_vdbg(codec->dev, "DAI Format master is not found\n"); + return -EINVAL; + } + + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | + SND_SOC_DAIFMT_INV_MASK)) { + case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): + /* 1st data bit occur one BCLK cycle after the frame sync */ + serial_format = TAS5720_SAIF_I2S; + break; + case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF): + /* + * Note that although the TAS5720 does not have a dedicated DSP + * mode it doesn't care about the LRCLK duty cycle during TDM + * operation. Therefore we can use the device's I2S mode with + * its delaying of the 1st data bit to receive DSP_A formatted + * data. See device datasheet for additional details. + */ + serial_format = TAS5720_SAIF_I2S; + break; + case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF): + /* + * Similar to DSP_A, we can use the fact that the TAS5720 does + * not care about the LRCLK duty cycle during TDM to receive + * DSP_B formatted data in LEFTJ mode (no delaying of the 1st + * data bit). + */ + serial_format = TAS5720_SAIF_LEFTJ; + break; + case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): + /* No delay after the frame sync */ + serial_format = TAS5720_SAIF_LEFTJ; + break; + default: + dev_vdbg(codec->dev, "DAI Format is not found\n"); + return -EINVAL; + } + + ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG, + TAS5720_SAIF_FORMAT_MASK, + serial_format); + if (ret < 0) { + dev_err(codec->dev, "error setting SAIF format: %d\n", ret); + return ret; + } + + return 0; +} + +static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int first_slot; + int ret; + + if (!tx_mask) { + dev_err(codec->dev, "tx masks must not be 0\n"); + return -EINVAL; + } + + /* + * Determine the first slot that is being requested. We will only + * use the first slot that is found since the TAS5720 is a mono + * amplifier. + */ + first_slot = __ffs(tx_mask); + + if (first_slot > 7) { + dev_err(codec->dev, "slot selection out of bounds (%u)\n", + first_slot); + return -EINVAL; + } + + /* Enable manual TDM slot selection (instead of I2C ID based) */ + ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG, + TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC); + if (ret < 0) + goto error_snd_soc_update_bits; + + /* Configure the TDM slot to process audio from */ + ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG, + TAS5720_TDM_SLOT_SEL_MASK, first_slot); + if (ret < 0) + goto error_snd_soc_update_bits; + + return 0; + +error_snd_soc_update_bits: + dev_err(codec->dev, "error configuring TDM mode: %d\n", ret); + return ret; +} + +static int tas5720_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int ret; + + ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG, + TAS5720_MUTE, mute ? TAS5720_MUTE : 0); + if (ret < 0) { + dev_err(codec->dev, "error (un-)muting device: %d\n", ret); + return ret; + } + + return 0; +} + +static void tas5720_fault_check_work(struct work_struct *work) +{ + struct tas5720_data *tas5720 = container_of(work, struct tas5720_data, + fault_check_work.work); + struct device *dev = tas5720->codec->dev; + unsigned int curr_fault; + int ret; + + ret = regmap_read(tas5720->regmap, TAS5720_FAULT_REG, &curr_fault); + if (ret < 0) { + dev_err(dev, "failed to read FAULT register: %d\n", ret); + goto out; + } + + /* Check/handle all errors except SAIF clock errors */ + curr_fault &= TAS5720_OCE | TAS5720_DCE | TAS5720_OTE; + + /* + * Only flag errors once for a given occurrence. This is needed as + * the TAS5720 will take time clearing the fault condition internally + * during which we don't want to bombard the system with the same + * error message over and over. + */ + if ((curr_fault & TAS5720_OCE) && !(tas5720->last_fault & TAS5720_OCE)) + dev_crit(dev, "experienced an over current hardware fault\n"); + + if ((curr_fault & TAS5720_DCE) && !(tas5720->last_fault & TAS5720_DCE)) + dev_crit(dev, "experienced a DC detection fault\n"); + + if ((curr_fault & TAS5720_OTE) && !(tas5720->last_fault & TAS5720_OTE)) + dev_crit(dev, "experienced an over temperature fault\n"); + + /* Store current fault value so we can detect any changes next time */ + tas5720->last_fault = curr_fault; + + if (!curr_fault) + goto out; + + /* + * Periodically toggle SDZ (shutdown bit) H->L->H to clear any latching + * faults as long as a fault condition persists. Always going through + * the full sequence no matter the first return value to minimizes + * chances for the device to end up in shutdown mode. + */ + ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG, + TAS5720_SDZ, 0); + if (ret < 0) + dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret); + + ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG, + TAS5720_SDZ, TAS5720_SDZ); + if (ret < 0) + dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret); + +out: + /* Schedule the next fault check at the specified interval */ + schedule_delayed_work(&tas5720->fault_check_work, + msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL)); +} + +static int tas5720_codec_probe(struct snd_soc_codec *codec) +{ + struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); + unsigned int device_id; + int ret; + + tas5720->codec = codec; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies), + tas5720->supplies); + if (ret != 0) { + dev_err(codec->dev, "failed to enable supplies: %d\n", ret); + return ret; + } + + ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id); + if (ret < 0) { + dev_err(codec->dev, "failed to read device ID register: %d\n", + ret); + goto probe_fail; + } + + if (device_id != TAS5720_DEVICE_ID) { + dev_err(codec->dev, "wrong device ID. expected: %u read: %u\n", + TAS5720_DEVICE_ID, device_id); + ret = -ENODEV; + goto probe_fail; + } + + /* Set device to mute */ + ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG, + TAS5720_MUTE, TAS5720_MUTE); + if (ret < 0) + goto error_snd_soc_update_bits; + + /* + * Enter shutdown mode - our default when not playing audio - to + * minimize current consumption. On the TAS5720 there is no real down + * side doing so as all device registers are preserved and the wakeup + * of the codec is rather quick which we do using a dapm widget. + */ + ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG, + TAS5720_SDZ, 0); + if (ret < 0) + goto error_snd_soc_update_bits; + + INIT_DELAYED_WORK(&tas5720->fault_check_work, tas5720_fault_check_work); + + return 0; + +error_snd_soc_update_bits: + dev_err(codec->dev, "error configuring device registers: %d\n", ret); + +probe_fail: + regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), + tas5720->supplies); + return ret; +} + +static int tas5720_codec_remove(struct snd_soc_codec *codec) +{ + struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); + int ret; + + cancel_delayed_work_sync(&tas5720->fault_check_work); + + ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), + tas5720->supplies); + if (ret < 0) + dev_err(codec->dev, "failed to disable supplies: %d\n", ret); + + return ret; +}; + +static int tas5720_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); + int ret; + + if (event & SND_SOC_DAPM_POST_PMU) { + /* Take TAS5720 out of shutdown mode */ + ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG, + TAS5720_SDZ, TAS5720_SDZ); + if (ret < 0) { + dev_err(codec->dev, "error waking codec: %d\n", ret); + return ret; + } + + /* + * Observe codec shutdown-to-active time. The datasheet only + * lists a nominal value however just use-it as-is without + * additional padding to minimize the delay introduced in + * starting to play audio (actually there is other setup done + * by the ASoC framework that will provide additional delays, + * so we should always be safe). + */ + msleep(25); + + /* Turn on TAS5720 periodic fault checking/handling */ + tas5720->last_fault = 0; + schedule_delayed_work(&tas5720->fault_check_work, + msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL)); + } else if (event & SND_SOC_DAPM_PRE_PMD) { + /* Disable TAS5720 periodic fault checking/handling */ + cancel_delayed_work_sync(&tas5720->fault_check_work); + + /* Place TAS5720 in shutdown mode to minimize current draw */ + ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG, + TAS5720_SDZ, 0); + if (ret < 0) { + dev_err(codec->dev, "error shutting down codec: %d\n", + ret); + return ret; + } + } + + return 0; +} + +#ifdef CONFIG_PM +static int tas5720_suspend(struct snd_soc_codec *codec) +{ + struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); + int ret; + + regcache_cache_only(tas5720->regmap, true); + regcache_mark_dirty(tas5720->regmap); + + ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), + tas5720->supplies); + if (ret < 0) + dev_err(codec->dev, "failed to disable supplies: %d\n", ret); + + return ret; +} + +static int tas5720_resume(struct snd_soc_codec *codec) +{ + struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies), + tas5720->supplies); + if (ret < 0) { + dev_err(codec->dev, "failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_cache_only(tas5720->regmap, false); + + ret = regcache_sync(tas5720->regmap); + if (ret < 0) { + dev_err(codec->dev, "failed to sync regcache: %d\n", ret); + return ret; + } + + return 0; +} +#else +#define tas5720_suspend NULL +#define tas5720_resume NULL +#endif + +static bool tas5720_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS5720_DEVICE_ID_REG: + case TAS5720_FAULT_REG: + return true; + default: + return false; + } +} + +static const struct regmap_config tas5720_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = TAS5720_MAX_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = tas5720_is_volatile_reg, +}; + +/* + * DAC analog gain. There are four discrete values to select from, ranging + * from 19.2 dB to 26.3dB. + */ +static const DECLARE_TLV_DB_RANGE(dac_analog_tlv, + 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0), + 0x1, 0x1, TLV_DB_SCALE_ITEM(2070, 0, 0), + 0x2, 0x2, TLV_DB_SCALE_ITEM(2350, 0, 0), + 0x3, 0x3, TLV_DB_SCALE_ITEM(2630, 0, 0), +); + +/* + * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that + * setting the gain below -100 dB (register value <0x7) is effectively a MUTE + * as per device datasheet. + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0); + +static const struct snd_kcontrol_new tas5720_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Driver Playback Volume", + TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, dac_tlv), + SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, + TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), +}; + +static const struct snd_soc_dapm_widget tas5720_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas5720_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT") +}; + +static const struct snd_soc_dapm_route tas5720_audio_map[] = { + { "DAC", NULL, "DAC IN" }, + { "OUT", NULL, "DAC" }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_tas5720 = { + .probe = tas5720_codec_probe, + .remove = tas5720_codec_remove, + .suspend = tas5720_suspend, + .resume = tas5720_resume, + + .controls = tas5720_snd_controls, + .num_controls = ARRAY_SIZE(tas5720_snd_controls), + .dapm_widgets = tas5720_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), + .dapm_routes = tas5720_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), +}; + +/* PCM rates supported by the TAS5720 driver */ +#define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +/* Formats supported by TAS5720 driver */ +#define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops tas5720_speaker_dai_ops = { + .hw_params = tas5720_hw_params, + .set_fmt = tas5720_set_dai_fmt, + .set_tdm_slot = tas5720_set_dai_tdm_slot, + .digital_mute = tas5720_mute, +}; + +/* + * TAS5720 DAI structure + * + * Note that were are advertising .playback.channels_max = 2 despite this being + * a mono amplifier. The reason for that is that some serial ports such as TI's + * McASP module have a minimum number of channels (2) that they can output. + * Advertising more channels than we have will allow us to interface with such + * a serial port without really any negative side effects as the TAS5720 will + * simply ignore any extra channel(s) asides from the one channel that is + * configured to be played back. + */ +static struct snd_soc_dai_driver tas5720_dai[] = { + { + .name = "tas5720-amplifier", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAS5720_RATES, + .formats = TAS5720_FORMATS, + }, + .ops = &tas5720_speaker_dai_ops, + }, +}; + +static int tas5720_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct tas5720_data *data; + int ret; + int i; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->tas5720_client = client; + data->regmap = devm_regmap_init_i2c(client, &tas5720_regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + dev_err(dev, "failed to allocate register map: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(data->supplies); i++) + data->supplies[i].supply = tas5720_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), + data->supplies); + if (ret != 0) { + dev_err(dev, "failed to request supplies: %d\n", ret); + return ret; + } + + dev_set_drvdata(dev, data); + + ret = snd_soc_register_codec(&client->dev, + &soc_codec_dev_tas5720, + tas5720_dai, ARRAY_SIZE(tas5720_dai)); + if (ret < 0) { + dev_err(dev, "failed to register codec: %d\n", ret); + return ret; + } + + return 0; +} + +static int tas5720_remove(struct i2c_client *client) +{ + struct device *dev = &client->dev; + + snd_soc_unregister_codec(dev); + + return 0; +} + +static const struct i2c_device_id tas5720_id[] = { + { "tas5720", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas5720_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tas5720_of_match[] = { + { .compatible = "ti,tas5720", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tas5720_of_match); +#endif + +static struct i2c_driver tas5720_i2c_driver = { + .driver = { + .name = "tas5720", + .of_match_table = of_match_ptr(tas5720_of_match), + }, + .probe = tas5720_probe, + .remove = tas5720_remove, + .id_table = tas5720_id, +}; + +module_i2c_driver(tas5720_i2c_driver); + +MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); +MODULE_DESCRIPTION("TAS5720 Audio amplifier driver"); +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 @@ +/* + * tas5720.h - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier + * + * Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Andreas Dannenberg <dannenberg@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __TAS5720_H__ +#define __TAS5720_H__ + +/* Register Address Map */ +#define TAS5720_DEVICE_ID_REG 0x00 +#define TAS5720_POWER_CTRL_REG 0x01 +#define TAS5720_DIGITAL_CTRL1_REG 0x02 +#define TAS5720_DIGITAL_CTRL2_REG 0x03 +#define TAS5720_VOLUME_CTRL_REG 0x04 +#define TAS5720_ANALOG_CTRL_REG 0x06 +#define TAS5720_FAULT_REG 0x08 +#define TAS5720_DIGITAL_CLIP2_REG 0x10 +#define TAS5720_DIGITAL_CLIP1_REG 0x11 +#define TAS5720_MAX_REG TAS5720_DIGITAL_CLIP1_REG + +/* TAS5720_DEVICE_ID_REG */ +#define TAS5720_DEVICE_ID 0x01 + +/* TAS5720_POWER_CTRL_REG */ +#define TAS5720_DIG_CLIP_MASK GENMASK(7, 2) +#define TAS5720_SLEEP BIT(1) +#define TAS5720_SDZ BIT(0) + +/* TAS5720_DIGITAL_CTRL1_REG */ +#define TAS5720_HPF_BYPASS BIT(7) +#define TAS5720_TDM_CFG_SRC BIT(6) +#define TAS5720_SSZ_DS BIT(3) +#define TAS5720_SAIF_RIGHTJ_24BIT (0x0) +#define TAS5720_SAIF_RIGHTJ_20BIT (0x1) +#define TAS5720_SAIF_RIGHTJ_18BIT (0x2) +#define TAS5720_SAIF_RIGHTJ_16BIT (0x3) +#define TAS5720_SAIF_I2S (0x4) +#define TAS5720_SAIF_LEFTJ (0x5) +#define TAS5720_SAIF_FORMAT_MASK GENMASK(2, 0) + +/* TAS5720_DIGITAL_CTRL2_REG */ +#define TAS5720_MUTE BIT(4) +#define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0) + +/* TAS5720_ANALOG_CTRL_REG */ +#define TAS5720_PWM_RATE_6_3_FSYNC (0x0 << 4) +#define TAS5720_PWM_RATE_8_4_FSYNC (0x1 << 4) +#define TAS5720_PWM_RATE_10_5_FSYNC (0x2 << 4) +#define TAS5720_PWM_RATE_12_6_FSYNC (0x3 << 4) +#define TAS5720_PWM_RATE_14_7_FSYNC (0x4 << 4) +#define TAS5720_PWM_RATE_16_8_FSYNC (0x5 << 4) +#define TAS5720_PWM_RATE_20_10_FSYNC (0x6 << 4) +#define TAS5720_PWM_RATE_24_12_FSYNC (0x7 << 4) +#define TAS5720_PWM_RATE_MASK GENMASK(6, 4) +#define TAS5720_ANALOG_GAIN_19_2DBV (0x0 << 2) +#define TAS5720_ANALOG_GAIN_20_7DBV (0x1 << 2) +#define TAS5720_ANALOG_GAIN_23_5DBV (0x2 << 2) +#define TAS5720_ANALOG_GAIN_26_3DBV (0x3 << 2) +#define TAS5720_ANALOG_GAIN_MASK GENMASK(3, 2) +#define TAS5720_ANALOG_GAIN_SHIFT (0x2) + +/* TAS5720_FAULT_REG */ +#define TAS5720_OC_THRESH_100PCT (0x0 << 4) +#define TAS5720_OC_THRESH_75PCT (0x1 << 4) +#define TAS5720_OC_THRESH_50PCT (0x2 << 4) +#define TAS5720_OC_THRESH_25PCT (0x3 << 4) +#define TAS5720_OC_THRESH_MASK GENMASK(5, 4) +#define TAS5720_CLKE BIT(3) +#define TAS5720_OCE BIT(2) +#define TAS5720_DCE BIT(1) +#define TAS5720_OTE BIT(0) +#define TAS5720_FAULT_MASK GENMASK(3, 0) + +/* TAS5720_DIGITAL_CLIP1_REG */ +#define TAS5720_CLIP1_MASK GENMASK(7, 2) +#define TAS5720_CLIP1_SHIFT (0x2) + +#endif /* __TAS5720_H__ */ diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index ee4def4f819f..3c5e1df01c19 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -28,6 +28,7 @@ #include <linux/i2c.h> #include <linux/gpio.h> #include <linux/regulator/consumer.h> +#include <linux/acpi.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/slab.h> @@ -1280,10 +1281,19 @@ static const struct i2c_device_id aic31xx_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id); +#ifdef CONFIG_ACPI +static const struct acpi_device_id aic31xx_acpi_match[] = { + { "10TI3100", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match); +#endif + static struct i2c_driver aic31xx_i2c_driver = { .driver = { .name = "tlv320aic31xx-codec", .of_match_table = of_match_ptr(tlv320aic31xx_of_match), + .acpi_match_table = ACPI_PTR(aic31xx_acpi_match), }, .probe = aic31xx_i2c_probe, .remove = aic31xx_i2c_remove, diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c new file mode 100644 index 000000000000..59606cf3008f --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -0,0 +1,74 @@ +/* + * linux/sound/soc/codecs/tlv320aic32x4-i2c.c + * + * Copyright 2011 NW Digital Radio + * + * Author: Jeremy McDermond <nh6z@nh6z.net> + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <sound/soc.h> + +#include "tlv320aic32x4.h" + +static int aic32x4_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + struct regmap_config config; + + config = aic32x4_regmap_config; + config.reg_bits = 8; + config.val_bits = 8; + + regmap = devm_regmap_init_i2c(i2c, &config); + return aic32x4_probe(&i2c->dev, regmap); +} + +static int aic32x4_i2c_remove(struct i2c_client *i2c) +{ + return aic32x4_remove(&i2c->dev); +} + +static const struct i2c_device_id aic32x4_i2c_id[] = { + { "tlv320aic32x4", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); + +static const struct of_device_id aic32x4_of_id[] = { + { .compatible = "ti,tlv320aic32x4", }, + { /* senitel */ } +}; +MODULE_DEVICE_TABLE(of, aic32x4_of_id); + +static struct i2c_driver aic32x4_i2c_driver = { + .driver = { + .name = "tlv320aic32x4", + .of_match_table = aic32x4_of_id, + }, + .probe = aic32x4_i2c_probe, + .remove = aic32x4_i2c_remove, + .id_table = aic32x4_i2c_id, +}; + +module_i2c_driver(aic32x4_i2c_driver); + +MODULE_DESCRIPTION("ASoC TLV320AIC32x4 codec driver I2C"); +MODULE_AUTHOR("Jeremy McDermond <nh6z@nh6z.net>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c new file mode 100644 index 000000000000..724fcdd491b2 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4-spi.c @@ -0,0 +1,76 @@ +/* + * linux/sound/soc/codecs/tlv320aic32x4-spi.c + * + * Copyright 2011 NW Digital Radio + * + * Author: Jeremy McDermond <nh6z@nh6z.net> + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <sound/soc.h> + +#include "tlv320aic32x4.h" + +static int aic32x4_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + struct regmap_config config; + + config = aic32x4_regmap_config; + config.reg_bits = 7; + config.pad_bits = 1; + config.val_bits = 8; + config.read_flag_mask = 0x01; + + regmap = devm_regmap_init_spi(spi, &config); + return aic32x4_probe(&spi->dev, regmap); +} + +static int aic32x4_spi_remove(struct spi_device *spi) +{ + return aic32x4_remove(&spi->dev); +} + +static const struct spi_device_id aic32x4_spi_id[] = { + { "tlv320aic32x4", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, aic32x4_spi_id); + +static const struct of_device_id aic32x4_of_id[] = { + { .compatible = "ti,tlv320aic32x4", }, + { /* senitel */ } +}; +MODULE_DEVICE_TABLE(of, aic32x4_of_id); + +static struct spi_driver aic32x4_spi_driver = { + .driver = { + .name = "tlv320aic32x4", + .owner = THIS_MODULE, + .of_match_table = aic32x4_of_id, + }, + .probe = aic32x4_spi_probe, + .remove = aic32x4_spi_remove, + .id_table = aic32x4_spi_id, +}; + +module_spi_driver(aic32x4_spi_driver); + +MODULE_DESCRIPTION("ASoC TLV320AIC32x4 codec driver SPI"); +MODULE_AUTHOR("Jeremy McDermond <nh6z@nh6z.net>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index f2d3191961e1..85d4978d0384 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -30,7 +30,6 @@ #include <linux/pm.h> #include <linux/gpio.h> #include <linux/of_gpio.h> -#include <linux/i2c.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/clk.h> @@ -160,7 +159,10 @@ static const struct aic32x4_rate_divs aic32x4_divs[] = { /* 48k rate */ {AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, {AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, - {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4} + {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}, + + /* 96k rate */ + {AIC32X4_FREQ_25000000, 96000, 2, 7, 8643, 64, 4, 4, 64, 4, 4, 1}, }; static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { @@ -181,16 +183,71 @@ static const struct snd_kcontrol_new lor_output_mixer_controls[] = { SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0), }; -static const struct snd_kcontrol_new left_input_mixer_controls[] = { - SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0), - SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0), - SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0), +static const char * const resistor_text[] = { + "Off", "10 kOhm", "20 kOhm", "40 kOhm", }; -static const struct snd_kcontrol_new right_input_mixer_controls[] = { - SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0), - SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0), - SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0), +/* Left mixer pins */ +static SOC_ENUM_SINGLE_DECL(in1l_lpga_p_enum, AIC32X4_LMICPGAPIN, 6, resistor_text); +static SOC_ENUM_SINGLE_DECL(in2l_lpga_p_enum, AIC32X4_LMICPGAPIN, 4, resistor_text); +static SOC_ENUM_SINGLE_DECL(in3l_lpga_p_enum, AIC32X4_LMICPGAPIN, 2, resistor_text); +static SOC_ENUM_SINGLE_DECL(in1r_lpga_p_enum, AIC32X4_LMICPGAPIN, 0, resistor_text); + +static SOC_ENUM_SINGLE_DECL(cml_lpga_n_enum, AIC32X4_LMICPGANIN, 6, resistor_text); +static SOC_ENUM_SINGLE_DECL(in2r_lpga_n_enum, AIC32X4_LMICPGANIN, 4, resistor_text); +static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_text); + +static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum), +}; +static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum), +}; +static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum), +}; +static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum), +}; +static const struct snd_kcontrol_new cml_to_lmixer_controls[] = { + SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum), +}; +static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum), +}; +static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum), +}; + +/* Right mixer pins */ +static SOC_ENUM_SINGLE_DECL(in1r_rpga_p_enum, AIC32X4_RMICPGAPIN, 6, resistor_text); +static SOC_ENUM_SINGLE_DECL(in2r_rpga_p_enum, AIC32X4_RMICPGAPIN, 4, resistor_text); +static SOC_ENUM_SINGLE_DECL(in3r_rpga_p_enum, AIC32X4_RMICPGAPIN, 2, resistor_text); +static SOC_ENUM_SINGLE_DECL(in2l_rpga_p_enum, AIC32X4_RMICPGAPIN, 0, resistor_text); +static SOC_ENUM_SINGLE_DECL(cmr_rpga_n_enum, AIC32X4_RMICPGANIN, 6, resistor_text); +static SOC_ENUM_SINGLE_DECL(in1l_rpga_n_enum, AIC32X4_RMICPGANIN, 4, resistor_text); +static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_text); + +static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum), +}; +static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum), +}; +static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum), +}; +static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum), +}; +static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = { + SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum), +}; +static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum), +}; +static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum), }; static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { @@ -214,14 +271,39 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { &lor_output_mixer_controls[0], ARRAY_SIZE(lor_output_mixer_controls)), SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0), - SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0, - &left_input_mixer_controls[0], - ARRAY_SIZE(left_input_mixer_controls)), - SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0, - &right_input_mixer_controls[0], - ARRAY_SIZE(right_input_mixer_controls)), - SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), + SND_SOC_DAPM_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in1r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in2r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in3r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in2l_to_rmixer_controls), + SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + cmr_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + in1l_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + in3l_to_rmixer_controls), + + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), + SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in1l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in2l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in3l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in1r_to_lmixer_controls), + SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + cml_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + in2r_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + in3r_to_lmixer_controls), + SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0), SND_SOC_DAPM_OUTPUT("HPL"), @@ -261,19 +343,77 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { {"LOR Power", NULL, "LOR Output Mixer"}, {"LOR", NULL, "LOR Power"}, - /* Left input */ - {"Left Input Mixer", "IN1_L P Switch", "IN1_L"}, - {"Left Input Mixer", "IN2_L P Switch", "IN2_L"}, - {"Left Input Mixer", "IN3_L P Switch", "IN3_L"}, - - {"Left ADC", NULL, "Left Input Mixer"}, - /* Right Input */ - {"Right Input Mixer", "IN1_R P Switch", "IN1_R"}, - {"Right Input Mixer", "IN2_R P Switch", "IN2_R"}, - {"Right Input Mixer", "IN3_R P Switch", "IN3_R"}, - - {"Right ADC", NULL, "Right Input Mixer"}, + {"Right ADC", NULL, "IN1_R to Right Mixer Positive Resistor"}, + {"IN1_R to Right Mixer Positive Resistor", "10 kOhm", "IN1_R"}, + {"IN1_R to Right Mixer Positive Resistor", "20 kOhm", "IN1_R"}, + {"IN1_R to Right Mixer Positive Resistor", "40 kOhm", "IN1_R"}, + + {"Right ADC", NULL, "IN2_R to Right Mixer Positive Resistor"}, + {"IN2_R to Right Mixer Positive Resistor", "10 kOhm", "IN2_R"}, + {"IN2_R to Right Mixer Positive Resistor", "20 kOhm", "IN2_R"}, + {"IN2_R to Right Mixer Positive Resistor", "40 kOhm", "IN2_R"}, + + {"Right ADC", NULL, "IN3_R to Right Mixer Positive Resistor"}, + {"IN3_R to Right Mixer Positive Resistor", "10 kOhm", "IN3_R"}, + {"IN3_R to Right Mixer Positive Resistor", "20 kOhm", "IN3_R"}, + {"IN3_R to Right Mixer Positive Resistor", "40 kOhm", "IN3_R"}, + + {"Right ADC", NULL, "IN2_L to Right Mixer Positive Resistor"}, + {"IN2_L to Right Mixer Positive Resistor", "10 kOhm", "IN2_L"}, + {"IN2_L to Right Mixer Positive Resistor", "20 kOhm", "IN2_L"}, + {"IN2_L to Right Mixer Positive Resistor", "40 kOhm", "IN2_L"}, + + {"Right ADC", NULL, "CM_R to Right Mixer Negative Resistor"}, + {"CM_R to Right Mixer Negative Resistor", "10 kOhm", "CM_R"}, + {"CM_R to Right Mixer Negative Resistor", "20 kOhm", "CM_R"}, + {"CM_R to Right Mixer Negative Resistor", "40 kOhm", "CM_R"}, + + {"Right ADC", NULL, "IN1_L to Right Mixer Negative Resistor"}, + {"IN1_L to Right Mixer Negative Resistor", "10 kOhm", "IN1_L"}, + {"IN1_L to Right Mixer Negative Resistor", "20 kOhm", "IN1_L"}, + {"IN1_L to Right Mixer Negative Resistor", "40 kOhm", "IN1_L"}, + + {"Right ADC", NULL, "IN3_L to Right Mixer Negative Resistor"}, + {"IN3_L to Right Mixer Negative Resistor", "10 kOhm", "IN3_L"}, + {"IN3_L to Right Mixer Negative Resistor", "20 kOhm", "IN3_L"}, + {"IN3_L to Right Mixer Negative Resistor", "40 kOhm", "IN3_L"}, + + /* Left Input */ + {"Left ADC", NULL, "IN1_L to Left Mixer Positive Resistor"}, + {"IN1_L to Left Mixer Positive Resistor", "10 kOhm", "IN1_L"}, + {"IN1_L to Left Mixer Positive Resistor", "20 kOhm", "IN1_L"}, + {"IN1_L to Left Mixer Positive Resistor", "40 kOhm", "IN1_L"}, + + {"Left ADC", NULL, "IN2_L to Left Mixer Positive Resistor"}, + {"IN2_L to Left Mixer Positive Resistor", "10 kOhm", "IN2_L"}, + {"IN2_L to Left Mixer Positive Resistor", "20 kOhm", "IN2_L"}, + {"IN2_L to Left Mixer Positive Resistor", "40 kOhm", "IN2_L"}, + + {"Left ADC", NULL, "IN3_L to Left Mixer Positive Resistor"}, + {"IN3_L to Left Mixer Positive Resistor", "10 kOhm", "IN3_L"}, + {"IN3_L to Left Mixer Positive Resistor", "20 kOhm", "IN3_L"}, + {"IN3_L to Left Mixer Positive Resistor", "40 kOhm", "IN3_L"}, + + {"Left ADC", NULL, "IN1_R to Left Mixer Positive Resistor"}, + {"IN1_R to Left Mixer Positive Resistor", "10 kOhm", "IN1_R"}, + {"IN1_R to Left Mixer Positive Resistor", "20 kOhm", "IN1_R"}, + {"IN1_R to Left Mixer Positive Resistor", "40 kOhm", "IN1_R"}, + + {"Left ADC", NULL, "CM_L to Left Mixer Negative Resistor"}, + {"CM_L to Left Mixer Negative Resistor", "10 kOhm", "CM_L"}, + {"CM_L to Left Mixer Negative Resistor", "20 kOhm", "CM_L"}, + {"CM_L to Left Mixer Negative Resistor", "40 kOhm", "CM_L"}, + + {"Left ADC", NULL, "IN2_R to Left Mixer Negative Resistor"}, + {"IN2_R to Left Mixer Negative Resistor", "10 kOhm", "IN2_R"}, + {"IN2_R to Left Mixer Negative Resistor", "20 kOhm", "IN2_R"}, + {"IN2_R to Left Mixer Negative Resistor", "40 kOhm", "IN2_R"}, + + {"Left ADC", NULL, "IN3_R to Left Mixer Negative Resistor"}, + {"IN3_R to Left Mixer Negative Resistor", "10 kOhm", "IN3_R"}, + {"IN3_R to Left Mixer Negative Resistor", "20 kOhm", "IN3_R"}, + {"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"}, }; static const struct regmap_range_cfg aic32x4_regmap_pages[] = { @@ -287,14 +427,12 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = { }, }; -static const struct regmap_config aic32x4_regmap = { - .reg_bits = 8, - .val_bits = 8, - +const struct regmap_config aic32x4_regmap_config = { .max_register = AIC32X4_RMICPGAVOL, .ranges = aic32x4_regmap_pages, .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages), }; +EXPORT_SYMBOL(aic32x4_regmap_config); static inline int aic32x4_get_divs(int mclk, int rate) { @@ -567,7 +705,7 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec, return 0; } -#define AIC32X4_RATES SNDRV_PCM_RATE_8000_48000 +#define AIC32X4_RATES SNDRV_PCM_RATE_8000_96000 #define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) @@ -596,7 +734,7 @@ static struct snd_soc_dai_driver aic32x4_dai = { .symmetric_rates = 1, }; -static int aic32x4_probe(struct snd_soc_codec *codec) +static int aic32x4_codec_probe(struct snd_soc_codec *codec) { struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); u32 tmp_reg; @@ -655,7 +793,7 @@ static int aic32x4_probe(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { - .probe = aic32x4_probe, + .probe = aic32x4_codec_probe, .set_bias_level = aic32x4_set_bias_level, .suspend_bias_off = true, @@ -777,24 +915,22 @@ error_ldo: return ret; } -static int aic32x4_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +int aic32x4_probe(struct device *dev, struct regmap *regmap) { - struct aic32x4_pdata *pdata = i2c->dev.platform_data; struct aic32x4_priv *aic32x4; - struct device_node *np = i2c->dev.of_node; + struct aic32x4_pdata *pdata = dev->platform_data; + struct device_node *np = dev->of_node; int ret; - aic32x4 = devm_kzalloc(&i2c->dev, sizeof(struct aic32x4_priv), + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), GFP_KERNEL); if (aic32x4 == NULL) return -ENOMEM; - aic32x4->regmap = devm_regmap_init_i2c(i2c, &aic32x4_regmap); - if (IS_ERR(aic32x4->regmap)) - return PTR_ERR(aic32x4->regmap); - - i2c_set_clientdata(i2c, aic32x4); + dev_set_drvdata(dev, aic32x4); if (pdata) { aic32x4->power_cfg = pdata->power_cfg; @@ -804,7 +940,7 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c, } else if (np) { ret = aic32x4_parse_dt(aic32x4, np); if (ret) { - dev_err(&i2c->dev, "Failed to parse DT node\n"); + dev_err(dev, "Failed to parse DT node\n"); return ret; } } else { @@ -814,71 +950,48 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c, aic32x4->rstn_gpio = -1; } - aic32x4->mclk = devm_clk_get(&i2c->dev, "mclk"); + aic32x4->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(aic32x4->mclk)) { - dev_err(&i2c->dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n"); + dev_err(dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n"); return PTR_ERR(aic32x4->mclk); } if (gpio_is_valid(aic32x4->rstn_gpio)) { - ret = devm_gpio_request_one(&i2c->dev, aic32x4->rstn_gpio, + ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio, GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn"); if (ret != 0) return ret; } - ret = aic32x4_setup_regulators(&i2c->dev, aic32x4); + ret = aic32x4_setup_regulators(dev, aic32x4); if (ret) { - dev_err(&i2c->dev, "Failed to setup regulators\n"); + dev_err(dev, "Failed to setup regulators\n"); return ret; } - ret = snd_soc_register_codec(&i2c->dev, + ret = snd_soc_register_codec(dev, &soc_codec_dev_aic32x4, &aic32x4_dai, 1); if (ret) { - dev_err(&i2c->dev, "Failed to register codec\n"); + dev_err(dev, "Failed to register codec\n"); aic32x4_disable_regulators(aic32x4); return ret; } - i2c_set_clientdata(i2c, aic32x4); - return 0; } +EXPORT_SYMBOL(aic32x4_probe); -static int aic32x4_i2c_remove(struct i2c_client *client) +int aic32x4_remove(struct device *dev) { - struct aic32x4_priv *aic32x4 = i2c_get_clientdata(client); + struct aic32x4_priv *aic32x4 = dev_get_drvdata(dev); aic32x4_disable_regulators(aic32x4); - snd_soc_unregister_codec(&client->dev); + snd_soc_unregister_codec(dev); + return 0; } - -static const struct i2c_device_id aic32x4_i2c_id[] = { - { "tlv320aic32x4", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); - -static const struct of_device_id aic32x4_of_id[] = { - { .compatible = "ti,tlv320aic32x4", }, - { /* senitel */ } -}; -MODULE_DEVICE_TABLE(of, aic32x4_of_id); - -static struct i2c_driver aic32x4_i2c_driver = { - .driver = { - .name = "tlv320aic32x4", - .of_match_table = aic32x4_of_id, - }, - .probe = aic32x4_i2c_probe, - .remove = aic32x4_i2c_remove, - .id_table = aic32x4_i2c_id, -}; - -module_i2c_driver(aic32x4_i2c_driver); +EXPORT_SYMBOL(aic32x4_remove); MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver"); MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index 995f033a855d..a197dd51addc 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -10,6 +10,13 @@ #ifndef _TLV320AIC32X4_H #define _TLV320AIC32X4_H +struct device; +struct regmap_config; + +extern const struct regmap_config aic32x4_regmap_config; +int aic32x4_probe(struct device *dev, struct regmap *regmap); +int aic32x4_remove(struct device *dev); + /* tlv320aic32x4 register space (in decimal to match datasheet) */ #define AIC32X4_PAGE1 128 diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index bc3de2e844e6..1f7081043566 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -824,7 +824,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, { struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int ret; + int ret = 0; switch (level) { case SND_SOC_BIAS_ON: @@ -832,12 +832,16 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (priv->codec_powered) + if (priv->codec_powered) { + /* Select low power PLL in standby */ + ret = twl6040_set_pll(twl6040, TWL6040_SYSCLK_SEL_LPPLL, + 32768, 19200000); break; + } ret = twl6040_power(twl6040, 1); if (ret) - return ret; + break; priv->codec_powered = 1; @@ -853,7 +857,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, break; } - return 0; + return ret; } static int twl6040_startup(struct snd_pcm_substream *substream, @@ -983,9 +987,9 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i if (mute) { /* Power down drivers and DACs */ hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA | - TWL6040_HFDRVENA); + TWL6040_HFDRVENA | TWL6040_HFSWENA); hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA | - TWL6040_HFDRVENA); + TWL6040_HFDRVENA | TWL6040_HFSWENA); } twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl); diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index fc164d69a557..f3109da24769 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3793,9 +3793,8 @@ static int wm8962_runtime_resume(struct device *dev) ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies); if (ret != 0) { - dev_err(dev, - "Failed to enable supplies: %d\n", ret); - return ret; + dev_err(dev, "Failed to enable supplies: %d\n", ret); + goto disable_clock; } regcache_cache_only(wm8962->regmap, false); @@ -3833,6 +3832,10 @@ static int wm8962_runtime_resume(struct device *dev) msleep(5); return 0; + +disable_clock: + clk_disable_unprepare(wm8962->pdata.mclk); + return ret; } static int wm8962_runtime_suspend(struct device *dev) diff --git a/sound/soc/codecs/wm8962.h b/sound/soc/codecs/wm8962.h index 910aafd09d21..e63a318a3015 100644 --- a/sound/soc/codecs/wm8962.h +++ b/sound/soc/codecs/wm8962.h @@ -16,9 +16,9 @@ #include <asm/types.h> #include <sound/soc.h> -#define WM8962_SYSCLK_MCLK 1 -#define WM8962_SYSCLK_FLL 2 -#define WM8962_SYSCLK_PLL3 3 +#define WM8962_SYSCLK_MCLK 0 +#define WM8962_SYSCLK_FLL 1 +#define WM8962_SYSCLK_PLL3 2 #define WM8962_FLL 1 |