summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/sgtl5000.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-07-04 02:52:22 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-04 02:52:22 (GMT)
commit1286da8bc009cb2aee7f285e94623fc974c0c983 (patch)
tree51ec0a79c3de63fa809b831ae0cbb5b85e44482f /sound/soc/codecs/sgtl5000.c
parent9e220385c4eb8b7e66174a60ea0e15b6b296f228 (diff)
parent1ba65ae4bdbd43265c51ee4c30ff21a48124b6d8 (diff)
downloadlinux-fsl-qoriq-1286da8bc009cb2aee7f285e94623fc974c0c983.tar.xz
Merge tag 'sound-3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "A relative calm release at this time with a flat diffstat. The only significant change in the ALSA core side is the support for more than 32 card instances, configurable via kconfig. Other than that, in both ASoC and other parts, mostly some improvements and fixes on the driver side. - hda: More quirks for ALC269-variants on Dell & co, VIA codec fixes - hda: Haswell HDMI audio fixes, runtime PM improvements - hda: Intel BayTrail support, ALC5505 DSP support - es1968: MediaForte M56VAP support - usb-audio: Improved support for Yamaha/Roland devices - usb-audio: M2Tech hiFace, Audio Advantage Micro II support - hdspm: wordclock fixes - ASoC: Pending fixes for WM8962 - ASoC: Cleanups and fixes for Blackfin, SGTL5000 and UX500 - ASoC: Generalisation of the Bluetooth and HDMI stub drivers - ASoC: SSM2518 and RT5640 codec drivers. - ASoC: Tegra CPUs with RT5640 machine driver - ASoC: AC'97 refactoring bug fixes - ASoC: ADAU1701 driver fixes - Clean up of *_set_drvdata() in a wide range of drivers" * tag 'sound-3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (284 commits) ALSA: vmaster: Fix the regression of missing vmaster hook call ALSA: hda - Add Dell SSID to support Headset Mic recording ASoC: adau1701: remove control_data assignment ASoC: adau1701: more direct regmap usage ASoC: ac97: fixup multi-platform AC'97 module build failure ASoC: pxa2xx: fixup multi-platform AC'97 build failures ASoC: tegra20-ac97: Remove unused variable ASoC: tegra20-ac97: Remove duplicate error message ALSA: usb-audio: Add Audio Advantage Micro II ASoC: tas5086: fix Mid-Z implementation ASoC: tas5086: fix TAS5086_CLOCK_CONTROL register size ALSA: Replace the magic number 44 with const ALSA: hda - Fix the max length of control name in generic parser ALSA: hda - Guess what, it's two more Dell headset mic quirks ALSA: hda - Yet another Dell headset mic quirk ALSA: hda - Add support for ALC5505 DSP power-save mode ASoC: mfld: Remove unused variable ALSA: usb-audio: add quirks for Roland QUAD/OCTO-CAPTURE ALSA: usb-audio: claim autodetected PCM interfaces all at once ALSA: usb-audio: remove superfluous Roland quirks ...
Diffstat (limited to 'sound/soc/codecs/sgtl5000.c')
-rw-r--r--sound/soc/codecs/sgtl5000.c267
1 files changed, 208 insertions, 59 deletions
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 92bbfec..d441559 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -16,6 +16,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/clk.h>
+#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/consumer.h>
@@ -34,30 +35,30 @@
#define SGTL5000_MAX_REG_OFFSET 0x013A
/* default value of sgtl5000 registers */
-static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET] = {
- [SGTL5000_CHIP_CLK_CTRL] = 0x0008,
- [SGTL5000_CHIP_I2S_CTRL] = 0x0010,
- [SGTL5000_CHIP_SSS_CTRL] = 0x0008,
- [SGTL5000_CHIP_DAC_VOL] = 0x3c3c,
- [SGTL5000_CHIP_PAD_STRENGTH] = 0x015f,
- [SGTL5000_CHIP_ANA_HP_CTRL] = 0x1818,
- [SGTL5000_CHIP_ANA_CTRL] = 0x0111,
- [SGTL5000_CHIP_LINE_OUT_VOL] = 0x0404,
- [SGTL5000_CHIP_ANA_POWER] = 0x7060,
- [SGTL5000_CHIP_PLL_CTRL] = 0x5000,
- [SGTL5000_DAP_BASS_ENHANCE] = 0x0040,
- [SGTL5000_DAP_BASS_ENHANCE_CTRL] = 0x051f,
- [SGTL5000_DAP_SURROUND] = 0x0040,
- [SGTL5000_DAP_EQ_BASS_BAND0] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND1] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND2] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND3] = 0x002f,
- [SGTL5000_DAP_EQ_BASS_BAND4] = 0x002f,
- [SGTL5000_DAP_MAIN_CHAN] = 0x8000,
- [SGTL5000_DAP_AVC_CTRL] = 0x0510,
- [SGTL5000_DAP_AVC_THRESHOLD] = 0x1473,
- [SGTL5000_DAP_AVC_ATTACK] = 0x0028,
- [SGTL5000_DAP_AVC_DECAY] = 0x0050,
+static const struct reg_default sgtl5000_reg_defaults[] = {
+ { SGTL5000_CHIP_CLK_CTRL, 0x0008 },
+ { SGTL5000_CHIP_I2S_CTRL, 0x0010 },
+ { SGTL5000_CHIP_SSS_CTRL, 0x0008 },
+ { SGTL5000_CHIP_DAC_VOL, 0x3c3c },
+ { SGTL5000_CHIP_PAD_STRENGTH, 0x015f },
+ { SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 },
+ { SGTL5000_CHIP_ANA_CTRL, 0x0111 },
+ { SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 },
+ { SGTL5000_CHIP_ANA_POWER, 0x7060 },
+ { SGTL5000_CHIP_PLL_CTRL, 0x5000 },
+ { SGTL5000_DAP_BASS_ENHANCE, 0x0040 },
+ { SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f },
+ { SGTL5000_DAP_SURROUND, 0x0040 },
+ { SGTL5000_DAP_EQ_BASS_BAND0, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND1, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND2, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND3, 0x002f },
+ { SGTL5000_DAP_EQ_BASS_BAND4, 0x002f },
+ { SGTL5000_DAP_MAIN_CHAN, 0x8000 },
+ { SGTL5000_DAP_AVC_CTRL, 0x0510 },
+ { SGTL5000_DAP_AVC_THRESHOLD, 0x1473 },
+ { SGTL5000_DAP_AVC_ATTACK, 0x0028 },
+ { SGTL5000_DAP_AVC_DECAY, 0x0050 },
};
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
@@ -112,6 +113,8 @@ struct sgtl5000_priv {
int fmt; /* i2s data format */
struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
struct ldo_regulator *ldo;
+ struct regmap *regmap;
+ struct clk *mclk;
};
/*
@@ -151,12 +154,12 @@ static int power_vag_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
break;
- case SND_SOC_DAPM_POST_PMD:
+ case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP, 0);
msleep(400);
@@ -217,12 +220,11 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
0, SGTL5000_CHIP_DIG_POWER,
1, 0),
- SND_SOC_DAPM_SUPPLY("VAG_POWER", SGTL5000_CHIP_ANA_POWER, 7, 0,
- power_vag_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
-
SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0),
SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0),
+
+ SND_SOC_DAPM_PRE("VAG_POWER_PRE", power_vag_event),
+ SND_SOC_DAPM_POST("VAG_POWER_POST", power_vag_event),
};
/* routes for sgtl5000 */
@@ -230,16 +232,13 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = {
{"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */
{"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */
- {"ADC", NULL, "VAG_POWER"},
{"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */
{"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */
- {"DAC", NULL, "VAG_POWER"},
{"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */
{"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */
{"LO", NULL, "DAC"}, /* dac --> line_out */
- {"LINE_IN", NULL, "VAG_POWER"},
{"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
{"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */
@@ -909,10 +908,25 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
if (ret)
return ret;
udelay(10);
+
+ regcache_cache_only(sgtl5000->regmap, false);
+
+ ret = regcache_sync(sgtl5000->regmap);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to restore cache: %d\n", ret);
+
+ regcache_cache_only(sgtl5000->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
+
+ return ret;
+ }
}
break;
case SND_SOC_BIAS_OFF:
+ regcache_cache_only(sgtl5000->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
break;
@@ -958,17 +972,76 @@ static struct snd_soc_dai_driver sgtl5000_dai = {
.symmetric_rates = 1,
};
-static int sgtl5000_volatile_register(struct snd_soc_codec *codec,
- unsigned int reg)
+static bool sgtl5000_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case SGTL5000_CHIP_ID:
case SGTL5000_CHIP_ADCDAC_CTRL:
case SGTL5000_CHIP_ANA_STATUS:
- return 1;
+ return true;
}
- return 0;
+ return false;
+}
+
+static bool sgtl5000_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SGTL5000_CHIP_ID:
+ case SGTL5000_CHIP_DIG_POWER:
+ case SGTL5000_CHIP_CLK_CTRL:
+ case SGTL5000_CHIP_I2S_CTRL:
+ case SGTL5000_CHIP_SSS_CTRL:
+ case SGTL5000_CHIP_ADCDAC_CTRL:
+ case SGTL5000_CHIP_DAC_VOL:
+ case SGTL5000_CHIP_PAD_STRENGTH:
+ case SGTL5000_CHIP_ANA_ADC_CTRL:
+ case SGTL5000_CHIP_ANA_HP_CTRL:
+ case SGTL5000_CHIP_ANA_CTRL:
+ case SGTL5000_CHIP_LINREG_CTRL:
+ case SGTL5000_CHIP_REF_CTRL:
+ case SGTL5000_CHIP_MIC_CTRL:
+ case SGTL5000_CHIP_LINE_OUT_CTRL:
+ case SGTL5000_CHIP_LINE_OUT_VOL:
+ case SGTL5000_CHIP_ANA_POWER:
+ case SGTL5000_CHIP_PLL_CTRL:
+ case SGTL5000_CHIP_CLK_TOP_CTRL:
+ case SGTL5000_CHIP_ANA_STATUS:
+ case SGTL5000_CHIP_SHORT_CTRL:
+ case SGTL5000_CHIP_ANA_TEST2:
+ case SGTL5000_DAP_CTRL:
+ case SGTL5000_DAP_PEQ:
+ case SGTL5000_DAP_BASS_ENHANCE:
+ case SGTL5000_DAP_BASS_ENHANCE_CTRL:
+ case SGTL5000_DAP_AUDIO_EQ:
+ case SGTL5000_DAP_SURROUND:
+ case SGTL5000_DAP_FLT_COEF_ACCESS:
+ case SGTL5000_DAP_COEF_WR_B0_MSB:
+ case SGTL5000_DAP_COEF_WR_B0_LSB:
+ case SGTL5000_DAP_EQ_BASS_BAND0:
+ case SGTL5000_DAP_EQ_BASS_BAND1:
+ case SGTL5000_DAP_EQ_BASS_BAND2:
+ case SGTL5000_DAP_EQ_BASS_BAND3:
+ case SGTL5000_DAP_EQ_BASS_BAND4:
+ case SGTL5000_DAP_MAIN_CHAN:
+ case SGTL5000_DAP_MIX_CHAN:
+ case SGTL5000_DAP_AVC_CTRL:
+ case SGTL5000_DAP_AVC_THRESHOLD:
+ case SGTL5000_DAP_AVC_ATTACK:
+ case SGTL5000_DAP_AVC_DECAY:
+ case SGTL5000_DAP_COEF_WR_B1_MSB:
+ case SGTL5000_DAP_COEF_WR_B1_LSB:
+ case SGTL5000_DAP_COEF_WR_B2_MSB:
+ case SGTL5000_DAP_COEF_WR_B2_LSB:
+ case SGTL5000_DAP_COEF_WR_A1_MSB:
+ case SGTL5000_DAP_COEF_WR_A1_LSB:
+ case SGTL5000_DAP_COEF_WR_A2_MSB:
+ case SGTL5000_DAP_COEF_WR_A2_LSB:
+ return true;
+
+ default:
+ return false;
+ }
}
#ifdef CONFIG_SUSPEND
@@ -1214,7 +1287,7 @@ static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec)
static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
{
- u16 reg;
+ int reg;
int ret;
int rev;
int i;
@@ -1242,23 +1315,17 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
/* wait for all power rails bring up */
udelay(10);
- /* read chip information */
- reg = snd_soc_read(codec, SGTL5000_CHIP_ID);
- if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
- SGTL5000_PARTID_PART_ID) {
- dev_err(codec->dev,
- "Device with ID register %x is not a sgtl5000\n", reg);
- ret = -ENODEV;
- goto err_regulator_disable;
- }
-
- rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
- dev_info(codec->dev, "sgtl5000 revision 0x%x\n", rev);
-
/*
* workaround for revision 0x11 and later,
* roll back to use internal LDO
*/
+
+ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
+ if (ret)
+ goto err_regulator_disable;
+
+ rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+
if (external_vddd && rev >= 0x11) {
/* disable all regulator first */
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
@@ -1300,7 +1367,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
/* setup i2c data ops */
- ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
+ codec->control_data = sgtl5000->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
@@ -1391,11 +1459,6 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
.suspend = sgtl5000_suspend,
.resume = sgtl5000_resume,
.set_bias_level = sgtl5000_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(sgtl5000_regs),
- .reg_word_size = sizeof(u16),
- .reg_cache_step = 2,
- .reg_cache_default = sgtl5000_regs,
- .volatile_register = sgtl5000_volatile_register,
.controls = sgtl5000_snd_controls,
.num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
.dapm_widgets = sgtl5000_dapm_widgets,
@@ -1404,28 +1467,114 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
.num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes),
};
+static const struct regmap_config sgtl5000_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = SGTL5000_MAX_REG_OFFSET,
+ .volatile_reg = sgtl5000_volatile,
+ .readable_reg = sgtl5000_readable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = sgtl5000_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(sgtl5000_reg_defaults),
+};
+
+/*
+ * Write all the default values from sgtl5000_reg_defaults[] array into the
+ * sgtl5000 registers, to make sure we always start with the sane registers
+ * values as stated in the datasheet.
+ *
+ * Since sgtl5000 does not have a reset line, nor a reset command in software,
+ * we follow this approach to guarantee we always start from the default values
+ * and avoid problems like, not being able to probe after an audio playback
+ * followed by a system reset or a 'reboot' command in Linux
+ */
+static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
+{
+ int i, ret, val, index;
+
+ for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) {
+ val = sgtl5000_reg_defaults[i].def;
+ index = sgtl5000_reg_defaults[i].reg;
+ ret = regmap_write(sgtl5000->regmap, index, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int sgtl5000_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct sgtl5000_priv *sgtl5000;
- int ret;
+ int ret, reg, rev;
sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv),
GFP_KERNEL);
if (!sgtl5000)
return -ENOMEM;
+ sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap);
+ if (IS_ERR(sgtl5000->regmap)) {
+ ret = PTR_ERR(sgtl5000->regmap);
+ dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ sgtl5000->mclk = devm_clk_get(&client->dev, NULL);
+ if (IS_ERR(sgtl5000->mclk)) {
+ ret = PTR_ERR(sgtl5000->mclk);
+ dev_err(&client->dev, "Failed to get mclock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(sgtl5000->mclk);
+ if (ret)
+ return ret;
+
+ /* read chip information */
+ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
+ if (ret)
+ goto disable_clk;
+
+ if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
+ SGTL5000_PARTID_PART_ID) {
+ dev_err(&client->dev,
+ "Device with ID register %x is not a sgtl5000\n", reg);
+ ret = -ENODEV;
+ goto disable_clk;
+ }
+
+ rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+ dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
+
i2c_set_clientdata(client, sgtl5000);
+ /* Ensure sgtl5000 will start with sane register values */
+ ret = sgtl5000_fill_defaults(sgtl5000);
+ if (ret)
+ goto disable_clk;
+
ret = snd_soc_register_codec(&client->dev,
&sgtl5000_driver, &sgtl5000_dai, 1);
+ if (ret)
+ goto disable_clk;
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(sgtl5000->mclk);
return ret;
}
static int sgtl5000_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
+ struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
+ snd_soc_unregister_codec(&client->dev);
+ clk_disable_unprepare(sgtl5000->mclk);
return 0;
}