From 07dc0bbc6818366c0e8f4b00e8916f987c526548 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lothar=20Wa=C3=9Fmann?= Date: Mon, 16 Jun 2014 13:33:36 +0200 Subject: [PATCH] ASoC: sgtl5000: backport driver from mainline to fix permanent failure after reboot --- sound/soc/codecs/sgtl5000.c | 437 ++++++++++++++++++++++-------------- 1 file changed, 263 insertions(+), 174 deletions(-) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index ea479388fb5c..daa915fd6c76 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -34,30 +35,45 @@ #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] = 0x0010, - [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_DIG_POWER, 0x0000 }, + { SGTL5000_CHIP_CLK_CTRL, 0x0008 }, + { SGTL5000_CHIP_I2S_CTRL, 0x0010 }, + { SGTL5000_CHIP_SSS_CTRL, 0x0010 }, + { SGTL5000_CHIP_ADCDAC_CTRL, 0x020c }, + { SGTL5000_CHIP_DAC_VOL, 0x3c3c }, + { SGTL5000_CHIP_PAD_STRENGTH, 0x015f }, + { SGTL5000_CHIP_ANA_ADC_CTRL, 0x0000 }, + { SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 }, + { SGTL5000_CHIP_ANA_CTRL, 0x0111 }, + { SGTL5000_CHIP_LINREG_CTRL, 0x0000 }, + { SGTL5000_CHIP_REF_CTRL, 0x0000 }, + { SGTL5000_CHIP_MIC_CTRL, 0x0000 }, + { SGTL5000_CHIP_LINE_OUT_CTRL, 0x0000 }, + { SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 }, + { SGTL5000_CHIP_ANA_POWER, 0x7060 }, + { SGTL5000_CHIP_PLL_CTRL, 0x5000 }, + { SGTL5000_CHIP_CLK_TOP_CTRL, 0x0000 }, + { SGTL5000_CHIP_ANA_STATUS, 0x0000 }, + { SGTL5000_CHIP_SHORT_CTRL, 0x0000 }, + { SGTL5000_CHIP_ANA_TEST2, 0x0000 }, + { SGTL5000_DAP_CTRL, 0x0000 }, + { SGTL5000_DAP_PEQ, 0x0000 }, + { SGTL5000_DAP_BASS_ENHANCE, 0x0040 }, + { SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f }, + { SGTL5000_DAP_AUDIO_EQ, 0x0000 }, + { 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_MIX_CHAN, 0x0000 }, + { 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 +128,9 @@ 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; + int revision; }; /* @@ -150,16 +169,26 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, static int power_vag_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + const u32 mask = SGTL5000_DAC_POWERUP | SGTL5000_ADC_POWERUP; + 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: - snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, - SGTL5000_VAG_POWERUP, 0); - msleep(400); + case SND_SOC_DAPM_PRE_PMD: + /* + * Don't clear VAG_POWERUP, when both DAC and ADC are + * operational to prevent inadvertently starving the + * other one of them. + */ + if ((snd_soc_read(w->codec, SGTL5000_CHIP_ANA_POWER) & + mask) != mask) { + snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, 0); + msleep(400); + } break; default: break; @@ -217,12 +246,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 +258,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 */ @@ -389,7 +414,7 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0), SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)", SGTL5000_CHIP_ANA_ADC_CTRL, - 8, 2, 0, capture_6db_attenuate), + 8, 1, 0, capture_6db_attenuate), SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0), SOC_DOUBLE_TLV("Headphone Playback Volume", @@ -645,16 +670,19 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP); + + /* if using pll, clk_ctrl must be set after pll power up */ + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); } else { + /* otherwise, clk_ctrl must be set before pll power down */ + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); + /* power down pll */ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, 0); } - /* if using pll, clk_ctrl must be set after pll power up */ - snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); - return 0; } @@ -909,10 +937,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,82 +1001,83 @@ 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; } -#ifdef CONFIG_SUSPEND -static int sgtl5000_suspend(struct snd_soc_codec *codec) +static bool sgtl5000_readable(struct device *dev, unsigned int reg) { - sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF); + 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; - return 0; + default: + return false; + } } -/* - * restore all sgtl5000 registers, - * since a big hole between dap and regular registers, - * we will restore them respectively. - */ -static int sgtl5000_restore_regs(struct snd_soc_codec *codec) +#ifdef CONFIG_SUSPEND +static int sgtl5000_suspend(struct snd_soc_codec *codec) { - u16 *cache = codec->reg_cache; - u16 reg; - - /* restore regular registers */ - for (reg = 0; reg <= SGTL5000_CHIP_SHORT_CTRL; reg += 2) { - - /* These regs should restore in particular order */ - if (reg == SGTL5000_CHIP_ANA_POWER || - reg == SGTL5000_CHIP_CLK_CTRL || - reg == SGTL5000_CHIP_LINREG_CTRL || - reg == SGTL5000_CHIP_LINE_OUT_CTRL || - reg == SGTL5000_CHIP_REF_CTRL) - continue; - - snd_soc_write(codec, reg, cache[reg]); - } - - /* restore dap registers */ - for (reg = SGTL5000_DAP_REG_OFFSET; reg < SGTL5000_MAX_REG_OFFSET; reg += 2) - snd_soc_write(codec, reg, cache[reg]); - - /* - * restore these regs according to the power setting sequence in - * sgtl5000_set_power_regs() and clock setting sequence in - * sgtl5000_set_clock(). - * - * The order of restore is: - * 1. SGTL5000_CHIP_CLK_CTRL MCLK_FREQ bits (1:0) should be restore after - * SGTL5000_CHIP_ANA_POWER PLL bits set - * 2. SGTL5000_CHIP_LINREG_CTRL should be set before - * SGTL5000_CHIP_ANA_POWER LINREG_D restored - * 3. SGTL5000_CHIP_REF_CTRL controls Analog Ground Voltage, - * prefer to resotre it after SGTL5000_CHIP_ANA_POWER restored - */ - snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, - cache[SGTL5000_CHIP_LINREG_CTRL]); - - snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, - cache[SGTL5000_CHIP_ANA_POWER]); - - snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, - cache[SGTL5000_CHIP_CLK_CTRL]); - - snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL, - cache[SGTL5000_CHIP_REF_CTRL]); + sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF); - snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL, - cache[SGTL5000_CHIP_LINE_OUT_CTRL]); return 0; } @@ -1042,8 +1086,6 @@ static int sgtl5000_resume(struct snd_soc_codec *codec) /* Bring the codec back up to standby to enable regulators */ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* Restore registers by cached in memory */ - sgtl5000_restore_regs(codec); return 0; } #else @@ -1199,96 +1241,57 @@ static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec) sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; - ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - - if (ret) { - ldo_regulator_remove(codec); - dev_err(codec->dev, "Failed to request supplies: %d\n", ret); - return ret; - } - dev_info(codec->dev, "Using internal LDO instead of VDDD\n"); return 0; } static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) { - u16 reg; int ret; - int rev; int i; int external_vddd = 0; struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct regulator *vddd; for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++) sgtl5000->supplies[i].supply = supply_names[i]; - ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - if (!ret) - external_vddd = 1; - else { + /* External VDDD only works before revision 0x11 */ + if (sgtl5000->revision < 0x11) { + vddd = regulator_get(codec->dev, "VDDD"); + if (IS_ERR(vddd)) { + /* See if it's just not registered yet */ + if (PTR_ERR(vddd) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } else { + external_vddd = 1; + regulator_put(vddd); + } + } + + if (!external_vddd) { ret = sgtl5000_replace_vddd_with_ldo(codec); if (ret) return ret; } + ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + goto err_ldo_remove; + ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); if (ret) - goto err_regulator_free; + goto err_ldo_remove; /* 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 - */ - if (external_vddd && rev >= 0x11) { - /* disable all regulator first */ - regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - /* free VDDD regulator */ - regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - - ret = sgtl5000_replace_vddd_with_ldo(codec); - if (ret) - return ret; - - ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - if (ret) - goto err_regulator_free; - - /* wait for all power rails bring up */ - udelay(10); - } - return 0; -err_regulator_disable: - regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); -err_regulator_free: - regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - if (external_vddd) +err_ldo_remove: + if (!external_vddd) ldo_regulator_remove(codec); return ret; @@ -1363,8 +1366,6 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) err: regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); - regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); ldo_regulator_remove(codec); return ret; @@ -1378,8 +1379,6 @@ static int sgtl5000_remove(struct snd_soc_codec *codec) regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); - regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); ldo_regulator_remove(codec); return 0; @@ -1391,11 +1390,10 @@ 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_cache_size = ARRAY_SIZE(sgtl5000_reg_defaults), .reg_word_size = sizeof(u16), .reg_cache_step = 2, - .reg_cache_default = sgtl5000_regs, - .volatile_register = sgtl5000_volatile_register, + .reg_cache_default = sgtl5000_reg_defaults, .controls = sgtl5000_snd_controls, .num_controls = ARRAY_SIZE(sgtl5000_snd_controls), .dapm_widgets = sgtl5000_dapm_widgets, @@ -1404,28 +1402,119 @@ 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, + .reg_stride = 2, + + .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); + /* Defer the probe to see if the clk will be provided later */ + if (ret == -ENOENT) + return -EPROBE_DEFER; + return ret; + } + + ret = clk_prepare_enable(sgtl5000->mclk); + if (ret) + return ret; + + /* read chip information */ + ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); + 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); + sgtl5000->revision = 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; } -- 2.39.2