diff options
Diffstat (limited to 'sound/soc/codecs/sgtl5000.c')
| -rw-r--r-- | sound/soc/codecs/sgtl5000.c | 650 |
1 files changed, 487 insertions, 163 deletions
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 80f6d1da7095..320312f8db92 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1,12 +1,8 @@ -/* - * sgtl5000.c -- SGTL5000 ALSA SoC Audio driver - * - * Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// sgtl5000.c -- SGTL5000 ALSA SoC Audio driver +// +// Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. #include <linux/module.h> #include <linux/moduleparam.h> @@ -17,11 +13,11 @@ #include <linux/i2c.h> #include <linux/clk.h> #include <linux/log2.h> +#include <linux/of.h> #include <linux/regmap.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/consumer.h> -#include <linux/of_device.h> #include <sound/core.h> #include <sound/tlv.h> #include <sound/pcm.h> @@ -35,6 +31,13 @@ #define SGTL5000_DAP_REG_OFFSET 0x0100 #define SGTL5000_MAX_REG_OFFSET 0x013A +/* Delay for the VAG ramp up */ +#define SGTL5000_VAG_POWERUP_DELAY 500 /* ms */ +/* Delay for the VAG ramp down */ +#define SGTL5000_VAG_POWERDOWN_DELAY 500 /* ms */ + +#define SGTL5000_OUTPUTS_MUTE (SGTL5000_HP_MUTE | SGTL5000_LINE_OUT_MUTE) + /* default value of sgtl5000 registers */ static const struct reg_default sgtl5000_reg_defaults[] = { { SGTL5000_CHIP_DIG_POWER, 0x0000 }, @@ -68,7 +71,7 @@ static const struct reg_default sgtl5000_reg_defaults[] = { { SGTL5000_DAP_EQ_BASS_BAND4, 0x002f }, { SGTL5000_DAP_MAIN_CHAN, 0x8000 }, { SGTL5000_DAP_MIX_CHAN, 0x0000 }, - { SGTL5000_DAP_AVC_CTRL, 0x0510 }, + { SGTL5000_DAP_AVC_CTRL, 0x5100 }, { SGTL5000_DAP_AVC_THRESHOLD, 0x1473 }, { SGTL5000_DAP_AVC_ATTACK, 0x0028 }, { SGTL5000_DAP_AVC_DECAY, 0x0050 }, @@ -120,6 +123,20 @@ enum { I2S_LRCLK_STRENGTH_HIGH, }; +enum { + I2S_SCLK_STRENGTH_DISABLE, + I2S_SCLK_STRENGTH_LOW, + I2S_SCLK_STRENGTH_MEDIUM, + I2S_SCLK_STRENGTH_HIGH, +}; + +enum { + HP_POWER_EVENT, + DAC_POWER_EVENT, + ADC_POWER_EVENT, + LAST_POWER_EVENT = ADC_POWER_EVENT +}; + /* sgtl5000 private structure in codec */ struct sgtl5000_priv { int sysclk; /* sysclk rate */ @@ -133,8 +150,110 @@ struct sgtl5000_priv { u8 micbias_resistor; u8 micbias_voltage; u8 lrclk_strength; + u8 sclk_strength; + u16 mute_state[LAST_POWER_EVENT + 1]; }; +static inline int hp_sel_input(struct snd_soc_component *component) +{ + return (snd_soc_component_read(component, SGTL5000_CHIP_ANA_CTRL) & + SGTL5000_HP_SEL_MASK) >> SGTL5000_HP_SEL_SHIFT; +} + +static inline u16 mute_output(struct snd_soc_component *component, + u16 mute_mask) +{ + u16 mute_reg = snd_soc_component_read(component, + SGTL5000_CHIP_ANA_CTRL); + + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL, + mute_mask, mute_mask); + return mute_reg; +} + +static inline void restore_output(struct snd_soc_component *component, + u16 mute_mask, u16 mute_reg) +{ + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL, + mute_mask, mute_reg); +} + +static void vag_power_on(struct snd_soc_component *component, u32 source) +{ + if (snd_soc_component_read(component, SGTL5000_CHIP_ANA_POWER) & + SGTL5000_VAG_POWERUP) + return; + + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP); + + /* When VAG powering on to get local loop from Line-In, the sleep + * is required to avoid loud pop. + */ + if (hp_sel_input(component) == SGTL5000_HP_SEL_LINE_IN && + source == HP_POWER_EVENT) + msleep(SGTL5000_VAG_POWERUP_DELAY); +} + +static int vag_power_consumers(struct snd_soc_component *component, + u16 ana_pwr_reg, u32 source) +{ + int consumers = 0; + + /* count dac/adc consumers unconditional */ + if (ana_pwr_reg & SGTL5000_DAC_POWERUP) + consumers++; + if (ana_pwr_reg & SGTL5000_ADC_POWERUP) + consumers++; + + /* + * If the event comes from HP and Line-In is selected, + * current action is 'DAC to be powered down'. + * As HP_POWERUP is not set when HP muxed to line-in, + * we need to keep VAG power ON. + */ + if (source == HP_POWER_EVENT) { + if (hp_sel_input(component) == SGTL5000_HP_SEL_LINE_IN) + consumers++; + } else { + if (ana_pwr_reg & SGTL5000_HP_POWERUP) + consumers++; + } + + return consumers; +} + +static void vag_power_off(struct snd_soc_component *component, u32 source) +{ + u16 ana_pwr = snd_soc_component_read(component, + SGTL5000_CHIP_ANA_POWER); + + if (!(ana_pwr & SGTL5000_VAG_POWERUP)) + return; + + /* + * This function calls when any of VAG power consumers is disappearing. + * Thus, if there is more than one consumer at the moment, as minimum + * one consumer will definitely stay after the end of the current + * event. + * Don't clear VAG_POWERUP if 2 or more consumers of VAG present: + * - LINE_IN (for HP events) / HP (for DAC/ADC events) + * - DAC + * - ADC + * (the current consumer is disappearing right now) + */ + if (vag_power_consumers(component, ana_pwr, source) >= 2) + return; + + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, 0); + /* In power down case, we need wait 400-1000 ms + * when VAG fully ramped down. + * As longer we wait, as smaller pop we've got. + */ + msleep(SGTL5000_VAG_POWERDOWN_DELAY); +} + /* * mic_bias power on/off share the same register bits with * output impedance of mic bias, when power on mic bias, we @@ -147,55 +266,65 @@ struct sgtl5000_priv { static int mic_bias_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 sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); switch (event) { case SND_SOC_DAPM_POST_PMU: /* change mic bias resistor */ - snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL, + snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL, SGTL5000_BIAS_R_MASK, sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT); break; case SND_SOC_DAPM_PRE_PMD: - snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL, + snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL, SGTL5000_BIAS_R_MASK, 0); break; } return 0; } -/* - * As manual described, ADC/DAC only works when VAG powerup, - * So enabled VAG before ADC/DAC up. - * In power down case, we need wait 400ms when vag fully ramped down. - */ -static int power_vag_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static int vag_and_mute_control(struct snd_soc_component *component, + int event, int event_source) { - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - const u32 mask = SGTL5000_DAC_POWERUP | SGTL5000_ADC_POWERUP; + static const u16 mute_mask[] = { + /* + * Mask for HP_POWER_EVENT. + * Muxing Headphones have to be wrapped with mute/unmute + * headphones only. + */ + SGTL5000_HP_MUTE, + /* + * Masks for DAC_POWER_EVENT/ADC_POWER_EVENT. + * Muxing DAC or ADC block have to wrapped with mute/unmute + * both headphones and line-out. + */ + SGTL5000_OUTPUTS_MUTE, + SGTL5000_OUTPUTS_MUTE + }; + + struct sgtl5000_priv *sgtl5000 = + snd_soc_component_get_drvdata(component); switch (event) { + case SND_SOC_DAPM_PRE_PMU: + sgtl5000->mute_state[event_source] = + mute_output(component, mute_mask[event_source]); + break; case SND_SOC_DAPM_POST_PMU: - snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, - SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP); - msleep(400); + vag_power_on(component, event_source); + restore_output(component, mute_mask[event_source], + sgtl5000->mute_state[event_source]); break; - 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(codec, SGTL5000_CHIP_ANA_POWER) & - mask) != mask) { - snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, - SGTL5000_VAG_POWERUP, 0); - msleep(400); - } + sgtl5000->mute_state[event_source] = + mute_output(component, mute_mask[event_source]); + vag_power_off(component, event_source); + break; + case SND_SOC_DAPM_POST_PMD: + restore_output(component, mute_mask[event_source], + sgtl5000->mute_state[event_source]); break; default: break; @@ -204,6 +333,41 @@ static int power_vag_event(struct snd_soc_dapm_widget *w, return 0; } +/* + * Mute Headphone when power it up/down. + * Control VAG power on HP power path. + */ +static int headphone_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + return vag_and_mute_control(component, event, HP_POWER_EVENT); +} + +/* As manual describes, ADC/DAC powering up/down requires + * to mute outputs to avoid pops. + * Control VAG power on ADC/DAC power path. + */ +static int adc_updown_depop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + return vag_and_mute_control(component, event, ADC_POWER_EVENT); +} + +static int dac_updown_depop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + return vag_and_mute_control(component, event, DAC_POWER_EVENT); +} + /* input sources for ADC */ static const char *adc_mux_text[] = { "MIC_IN", "LINE_IN" @@ -216,17 +380,54 @@ static SOC_ENUM_SINGLE_DECL(adc_enum, static const struct snd_kcontrol_new adc_mux = SOC_DAPM_ENUM("Capture Mux", adc_enum); +/* input sources for headphone */ +static const char *hp_mux_text[] = { + "DAC", "LINE_IN" +}; + +static SOC_ENUM_SINGLE_DECL(hp_enum, + SGTL5000_CHIP_ANA_CTRL, 6, + hp_mux_text); + +static const struct snd_kcontrol_new hp_mux = +SOC_DAPM_ENUM("Headphone Mux", hp_enum); + /* input sources for DAC */ static const char *dac_mux_text[] = { - "DAC", "LINE_IN" + "ADC", "I2S", "Rsvrd", "DAP" }; static SOC_ENUM_SINGLE_DECL(dac_enum, - SGTL5000_CHIP_ANA_CTRL, 6, + SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAC_SEL_SHIFT, dac_mux_text); static const struct snd_kcontrol_new dac_mux = -SOC_DAPM_ENUM("Headphone Mux", dac_enum); +SOC_DAPM_ENUM("Digital Input Mux", dac_enum); + +/* input sources for DAP */ +static const char *dap_mux_text[] = { + "ADC", "I2S" +}; + +static SOC_ENUM_SINGLE_DECL(dap_enum, + SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAP_SEL_SHIFT, + dap_mux_text); + +static const struct snd_kcontrol_new dap_mux = +SOC_DAPM_ENUM("DAP Mux", dap_enum); + +/* input sources for DAP mix */ +static const char *dapmix_mux_text[] = { + "ADC", "I2S" +}; + +static SOC_ENUM_SINGLE_DECL(dapmix_enum, + SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAP_MIX_SEL_SHIFT, + dapmix_mux_text); + +static const struct snd_kcontrol_new dapmix_mux = +SOC_DAPM_ENUM("DAP MIX Mux", dapmix_enum); + static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = { SND_SOC_DAPM_INPUT("LINE_IN"), @@ -239,11 +440,19 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = { mic_bias_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_PGA("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0, + headphone_pga_event, + SND_SOC_DAPM_PRE_POST_PMU | + SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_PGA("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0), SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux), - SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &dac_mux), + SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &hp_mux), + SND_SOC_DAPM_MUX("Digital Input Mux", SND_SOC_NOPM, 0, 0, &dac_mux), + SND_SOC_DAPM_MUX("DAP Mux", SGTL5000_DAP_CTRL, 0, 0, &dap_mux), + SND_SOC_DAPM_MUX("DAP MIX Mux", SGTL5000_DAP_CTRL, 4, 0, &dapmix_mux), + SND_SOC_DAPM_MIXER("DAP", SGTL5000_CHIP_DIG_POWER, 4, 0, NULL, 0), + /* aif for i2s input */ SND_SOC_DAPM_AIF_IN("AIFIN", "Playback", @@ -255,11 +464,12 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = { 0, SGTL5000_CHIP_DIG_POWER, 1, 0), - 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), + SND_SOC_DAPM_ADC_E("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0, + adc_updown_depop, SND_SOC_DAPM_PRE_POST_PMU | + SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0, + dac_updown_depop, SND_SOC_DAPM_PRE_POST_PMU | + SND_SOC_DAPM_PRE_POST_PMD), }; /* routes for sgtl5000 */ @@ -270,7 +480,19 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = { {"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */ {"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */ - {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */ + {"DAP Mux", "ADC", "ADC"}, /* adc --> DAP mux */ + {"DAP Mux", NULL, "AIFIN"}, /* i2s --> DAP mux */ + {"DAP", NULL, "DAP Mux"}, /* DAP mux --> dap */ + + {"DAP MIX Mux", "ADC", "ADC"}, /* adc --> DAP MIX mux */ + {"DAP MIX Mux", NULL, "AIFIN"}, /* i2s --> DAP MIX mux */ + {"DAP", NULL, "DAP MIX Mux"}, /* DAP MIX mux --> dap */ + + {"Digital Input Mux", "ADC", "ADC"}, /* adc --> audio mux */ + {"Digital Input Mux", NULL, "AIFIN"}, /* i2s --> audio mux */ + {"Digital Input Mux", NULL, "DAP"}, /* dap --> audio mux */ + {"DAC", NULL, "Digital Input Mux"}, /* audio mux --> dac */ + {"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */ {"LO", NULL, "DAC"}, /* dac --> line_out */ @@ -318,12 +540,12 @@ static int dac_info_volsw(struct snd_kcontrol *kcontrol, static int dac_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); int reg; int l; int r; - reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL); + reg = snd_soc_component_read(component, SGTL5000_CHIP_DAC_VOL); /* get left channel volume */ l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT; @@ -371,7 +593,7 @@ static int dac_get_volsw(struct snd_kcontrol *kcontrol, static int dac_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); int reg; int l; int r; @@ -391,7 +613,7 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol, reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT | r << SGTL5000_DAC_VOL_RIGHT_SHIFT; - snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg); + snd_soc_component_write(component, SGTL5000_CHIP_DAC_VOL, reg); return 0; } @@ -403,15 +625,15 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol, * avc_put_threshold function: register_value = 10^(dB/20) * 0.636 * 2^15 ==> * dB = ( fls(register_value) - 14.347 ) * 6.02 * - * As this calculation is expensive and the threshold dB values may not exeed + * As this calculation is expensive and the threshold dB values may not exceed * 0 to 96 we use pre-calculated values. */ static int avc_get_threshold(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); int db, i; - u16 reg = snd_soc_read(codec, SGTL5000_DAP_AVC_THRESHOLD); + u16 reg = snd_soc_component_read(component, SGTL5000_DAP_AVC_THRESHOLD); /* register value 0 => -96dB */ if (!reg) { @@ -436,13 +658,13 @@ static int avc_get_threshold(struct snd_kcontrol *kcontrol, * * The register value is calculated by following formula: * register_value = 10^(dB/20) * 0.636 * 2^15 - * As this calculation is expensive and the threshold dB values may not exeed + * As this calculation is expensive and the threshold dB values may not exceed * 0 to 96 we use pre-calculated values. */ static int avc_put_threshold(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); int db; u16 reg; @@ -450,7 +672,7 @@ static int avc_put_threshold(struct snd_kcontrol *kcontrol, if (db < 0 || db > 96) return -EINVAL; reg = avc_thr_db2reg[db]; - snd_soc_write(codec, SGTL5000_DAP_AVC_THRESHOLD, reg); + snd_soc_component_write(component, SGTL5000_DAP_AVC_THRESHOLD, reg); return 0; } @@ -463,6 +685,12 @@ static const DECLARE_TLV_DB_RANGE(mic_gain_tlv, 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0) ); +/* tlv for DAP channels, 0% - 100% - 200% */ +static const DECLARE_TLV_DB_SCALE(dap_volume, 0, 1, 0); + +/* tlv for bass bands, -11.75db to 12.0db, step .25db */ +static const DECLARE_TLV_DB_SCALE(bass_band, -1175, 25, 0); + /* tlv for hp volume, -51.5db to 12.0db, step .5db */ static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0); @@ -492,6 +720,7 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { SGTL5000_CHIP_ANA_ADC_CTRL, 8, 1, 0, capture_6db_attenuate), SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0), + SOC_SINGLE("Capture Switch", SGTL5000_CHIP_ANA_CTRL, 0, 1, 1), SOC_DOUBLE_TLV("Headphone Playback Volume", SGTL5000_CHIP_ANA_HP_CTRL, @@ -514,6 +743,11 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { lineout_volume), SOC_SINGLE("Lineout Playback Switch", SGTL5000_CHIP_ANA_CTRL, 8, 1, 1), + SOC_SINGLE_TLV("DAP Main channel", SGTL5000_DAP_MAIN_CHAN, + 0, 0xffff, 0, dap_volume), + + SOC_SINGLE_TLV("DAP Mix channel", SGTL5000_DAP_MIX_CHAN, + 0, 0xffff, 0, dap_volume), /* Automatic Volume Control (DAP AVC) */ SOC_SINGLE("AVC Switch", SGTL5000_DAP_AVC_CTRL, 0, 1, 0), SOC_SINGLE("AVC Hard Limiter Switch", SGTL5000_DAP_AVC_CTRL, 5, 1, 0), @@ -523,16 +757,36 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { SOC_SINGLE_EXT_TLV("AVC Threshold Volume", SGTL5000_DAP_AVC_THRESHOLD, 0, 96, 0, avc_get_threshold, avc_put_threshold, avc_threshold), + + SOC_SINGLE_TLV("BASS 0", SGTL5000_DAP_EQ_BASS_BAND0, + 0, 0x5F, 0, bass_band), + + SOC_SINGLE_TLV("BASS 1", SGTL5000_DAP_EQ_BASS_BAND1, + 0, 0x5F, 0, bass_band), + + SOC_SINGLE_TLV("BASS 2", SGTL5000_DAP_EQ_BASS_BAND2, + 0, 0x5F, 0, bass_band), + + SOC_SINGLE_TLV("BASS 3", SGTL5000_DAP_EQ_BASS_BAND3, + 0, 0x5F, 0, bass_band), + + SOC_SINGLE_TLV("BASS 4", SGTL5000_DAP_EQ_BASS_BAND4, + 0, 0x5F, 0, bass_band), }; /* mute the codec used by alsa core */ -static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute) +static int sgtl5000_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction) { - struct snd_soc_codec *codec = codec_dai->codec; - u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT; + struct snd_soc_component *component = codec_dai->component; + u16 i2s_pwr = SGTL5000_I2S_IN_POWERUP; - snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL, - adcdac_ctrl, mute ? adcdac_ctrl : 0); + /* + * During 'digital mute' do not mute DAC + * because LINE_IN would be muted aswell. We want to mute + * only I2S block - this can be done by powering it off + */ + snd_soc_component_update_bits(component, SGTL5000_CHIP_DIG_POWER, + i2s_pwr, mute ? 0 : i2s_pwr); return 0; } @@ -540,8 +794,8 @@ static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute) /* set codec format */ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; - struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = codec_dai->component; + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); u16 i2sctl = 0; sgtl5000->master = 0; @@ -552,9 +806,9 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) * - clock and frame master */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: i2sctl |= SGTL5000_I2S_MASTER; sgtl5000->master = 1; break; @@ -599,7 +853,7 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } - snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl); + snd_soc_component_write(component, SGTL5000_CHIP_I2S_CTRL, i2sctl); return 0; } @@ -608,8 +862,8 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { - struct snd_soc_codec *codec = codec_dai->codec; - struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = codec_dai->component; + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); switch (clk_id) { case SGTL5000_SYSCLK: @@ -635,9 +889,9 @@ static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai, * and above. * 3. usage of sys_mclk is preferred over pll to save power. */ -static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) +static int sgtl5000_set_clock(struct snd_soc_component *component, int frame_rate) { - struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); int clk_ctl = 0; int sys_fs; /* sample freq */ @@ -690,7 +944,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT; break; default: - dev_err(codec->dev, "frame rate %d not supported\n", + dev_err(component->dev, "frame rate %d not supported\n", frame_rate); return -EINVAL; } @@ -719,9 +973,9 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) clk_ctl |= SGTL5000_MCLK_FREQ_PLL << SGTL5000_MCLK_FREQ_SHIFT; } else { - dev_err(codec->dev, + dev_err(component->dev, "PLL not supported in slave mode\n"); - dev_err(codec->dev, "%d ratio is not supported. " + dev_err(component->dev, "%d ratio is not supported. " "SYS_MCLK needs to be 256, 384 or 512 * fs\n", sgtl5000->sysclk / frame_rate); return -EINVAL; @@ -754,31 +1008,31 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT | frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT; - snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl); + snd_soc_component_write(component, SGTL5000_CHIP_PLL_CTRL, pll_ctl); if (div2) - snd_soc_update_bits(codec, + snd_soc_component_update_bits(component, SGTL5000_CHIP_CLK_TOP_CTRL, SGTL5000_INPUT_FREQ_DIV2, SGTL5000_INPUT_FREQ_DIV2); else - snd_soc_update_bits(codec, + snd_soc_component_update_bits(component, SGTL5000_CHIP_CLK_TOP_CTRL, SGTL5000_INPUT_FREQ_DIV2, 0); /* power up pll */ - snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + snd_soc_component_update_bits(component, 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); + snd_soc_component_write(component, 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); + snd_soc_component_write(component, SGTL5000_CHIP_CLK_CTRL, clk_ctl); /* power down pll */ - snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, 0); } @@ -794,8 +1048,8 @@ static int sgtl5000_pcm_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 sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); int channels = params_channels(params); int i2s_ctl = 0; int stereo; @@ -803,7 +1057,7 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, /* sysclk should already set */ if (!sgtl5000->sysclk) { - dev_err(codec->dev, "%s: set sysclk first!\n", __func__); + dev_err(component->dev, "%s: set sysclk first!\n", __func__); return -EFAULT; } @@ -813,11 +1067,11 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, stereo = SGTL5000_ADC_STEREO; /* set mono to save power */ - snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, stereo, + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, stereo, channels == 1 ? 0 : stereo); /* set codec clock base on lrclk */ - ret = sgtl5000_set_clock(codec, params_rate(params)); + ret = sgtl5000_set_clock(component, params_rate(params)); if (ret) return ret; @@ -851,7 +1105,7 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - snd_soc_update_bits(codec, SGTL5000_CHIP_I2S_CTRL, + snd_soc_component_update_bits(component, SGTL5000_CHIP_I2S_CTRL, SGTL5000_I2S_DLEN_MASK | SGTL5000_I2S_SCLKFREQ_MASK, i2s_ctl); @@ -868,19 +1122,30 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, * stop: * on --> prepare --> standby */ -static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, +static int sgtl5000_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { + struct sgtl5000_priv *sgtl = snd_soc_component_get_drvdata(component); + int ret; + switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_STANDBY: - snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + regcache_cache_only(sgtl->regmap, false); + ret = regcache_sync(sgtl->regmap); + if (ret) { + regcache_cache_only(sgtl->regmap, true); + return ret; + } + + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, SGTL5000_REFTOP_POWERUP, SGTL5000_REFTOP_POWERUP); break; case SND_SOC_BIAS_OFF: - snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + regcache_cache_only(sgtl->regmap, true); + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, SGTL5000_REFTOP_POWERUP, 0); break; } @@ -895,9 +1160,10 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, static const struct snd_soc_dai_ops sgtl5000_ops = { .hw_params = sgtl5000_pcm_hw_params, - .digital_mute = sgtl5000_digital_mute, + .mute_stream = sgtl5000_mute_stream, .set_fmt = sgtl5000_set_dai_fmt, .set_sysclk = sgtl5000_set_dai_sysclk, + .no_capture_mute = 1, }; static struct snd_soc_dai_driver sgtl5000_dai = { @@ -921,7 +1187,7 @@ static struct snd_soc_dai_driver sgtl5000_dai = { .formats = SGTL5000_FORMATS, }, .ops = &sgtl5000_ops, - .symmetric_rates = 1, + .symmetric_rate = 1, }; static bool sgtl5000_volatile(struct device *dev, unsigned int reg) @@ -1020,7 +1286,7 @@ static const u8 vol_quot_table[] = { * 1. vddd provided by external or not * 2. vdda and vddio voltage value. > 3.1v or not */ -static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) +static int sgtl5000_set_power_regs(struct snd_soc_component *component) { int vddd; int vdda; @@ -1032,7 +1298,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) int vol_quot; int lo_vol; size_t i; - struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer); vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer); @@ -1045,14 +1311,14 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) vddd = vddd / 1000; if (vdda <= 0 || vddio <= 0 || vddd < 0) { - dev_err(codec->dev, "regulator voltage not set correctly\n"); + dev_err(component->dev, "regulator voltage not set correctly\n"); return -EINVAL; } /* according to datasheet, maximum voltage of supplies */ if (vdda > 3600 || vddio > 3600 || vddd > 1980) { - dev_err(codec->dev, + dev_err(component->dev, "exceed max voltage vdda %dmV vddio %dmV vddd %dmV\n", vdda, vddio, vddd); @@ -1060,30 +1326,36 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) } /* reset value */ - ana_pwr = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER); + ana_pwr = snd_soc_component_read(component, SGTL5000_CHIP_ANA_POWER); ana_pwr |= SGTL5000_DAC_STEREO | SGTL5000_ADC_STEREO | SGTL5000_REFTOP_POWERUP; - lreg_ctrl = snd_soc_read(codec, SGTL5000_CHIP_LINREG_CTRL); + lreg_ctrl = snd_soc_component_read(component, SGTL5000_CHIP_LINREG_CTRL); if (vddio < 3100 && vdda < 3100) { /* enable internal oscillator used for charge pump */ - snd_soc_update_bits(codec, SGTL5000_CHIP_CLK_TOP_CTRL, + snd_soc_component_update_bits(component, SGTL5000_CHIP_CLK_TOP_CTRL, SGTL5000_INT_OSC_EN, SGTL5000_INT_OSC_EN); /* Enable VDDC charge pump */ ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP; - } else if (vddio >= 3100 && vdda >= 3100) { + } else { ana_pwr &= ~SGTL5000_VDDC_CHRGPMP_POWERUP; - /* VDDC use VDDIO rail */ - lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD; - lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO << - SGTL5000_VDDC_MAN_ASSN_SHIFT; + /* + * if vddio == vdda the source of charge pump should be + * assigned manually to VDDIO + */ + if (regulator_is_equal(sgtl5000->supplies[VDDA].consumer, + sgtl5000->supplies[VDDIO].consumer)) { + lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD; + lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO << + SGTL5000_VDDC_MAN_ASSN_SHIFT; + } } - snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl); + snd_soc_component_write(component, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl); - snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr); + snd_soc_component_write(component, SGTL5000_CHIP_ANA_POWER, ana_pwr); /* * set ADC/DAC VAG to vdda / 2, @@ -1098,7 +1370,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) else vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP; - snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL, + snd_soc_component_update_bits(component, SGTL5000_CHIP_REF_CTRL, SGTL5000_ANA_GND_MASK, vag << SGTL5000_ANA_GND_SHIFT); /* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */ @@ -1112,7 +1384,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) lo_vag = (lo_vag - SGTL5000_LINE_OUT_GND_BASE) / SGTL5000_LINE_OUT_GND_STP; - snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL, + snd_soc_component_update_bits(component, SGTL5000_CHIP_LINE_OUT_CTRL, SGTL5000_LINE_OUT_CURRENT_MASK | SGTL5000_LINE_OUT_GND_MASK, lo_vag << SGTL5000_LINE_OUT_GND_SHIFT | @@ -1126,7 +1398,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) * Searching for a suitable index solving this formula: * idx = 40 * log10(vag_val / lo_cagcntrl) + 15 */ - vol_quot = (vag * 100) / lo_vag; + vol_quot = lo_vag ? (vag * 100) / lo_vag : 0; lo_vol = 0; for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) { if (vol_quot >= vol_quot_table[i]) @@ -1135,7 +1407,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) break; } - snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_VOL, + snd_soc_component_update_bits(component, SGTL5000_CHIP_LINE_OUT_VOL, SGTL5000_LINE_OUT_VOL_RIGHT_MASK | SGTL5000_LINE_OUT_VOL_LEFT_MASK, lo_vol << SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT | @@ -1183,59 +1455,59 @@ static int sgtl5000_enable_regulators(struct i2c_client *client) return ret; } -static int sgtl5000_probe(struct snd_soc_codec *codec) +static int sgtl5000_probe(struct snd_soc_component *component) { int ret; u16 reg; - struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); + unsigned int zcd_mask = SGTL5000_HP_ZCD_EN | SGTL5000_ADC_ZCD_EN; /* power up sgtl5000 */ - ret = sgtl5000_set_power_regs(codec); + ret = sgtl5000_set_power_regs(component); if (ret) goto err; /* enable small pop, introduce 400ms delay in turning off */ - snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL, - SGTL5000_SMALL_POP, 1); + snd_soc_component_update_bits(component, SGTL5000_CHIP_REF_CTRL, + SGTL5000_SMALL_POP, SGTL5000_SMALL_POP); /* disable short cut detector */ - snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0); + snd_soc_component_write(component, SGTL5000_CHIP_SHORT_CTRL, 0); - /* - * set i2s as default input of sound switch - * TODO: add sound switch to control and dapm widge. - */ - snd_soc_write(codec, SGTL5000_CHIP_SSS_CTRL, - SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT); - snd_soc_write(codec, SGTL5000_CHIP_DIG_POWER, + snd_soc_component_write(component, SGTL5000_CHIP_DIG_POWER, SGTL5000_ADC_EN | SGTL5000_DAC_EN); /* enable dac volume ramp by default */ - snd_soc_write(codec, SGTL5000_CHIP_ADCDAC_CTRL, + snd_soc_component_write(component, SGTL5000_CHIP_ADCDAC_CTRL, SGTL5000_DAC_VOL_RAMP_EN | SGTL5000_DAC_MUTE_RIGHT | SGTL5000_DAC_MUTE_LEFT); - reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT | 0x5f); - snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, reg); + reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT | + (sgtl5000->sclk_strength) << SGTL5000_PAD_I2S_SCLK_SHIFT | + 0x1f); + snd_soc_component_write(component, SGTL5000_CHIP_PAD_STRENGTH, reg); - snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL, - SGTL5000_HP_ZCD_EN | - SGTL5000_ADC_ZCD_EN); + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL, + zcd_mask, zcd_mask); - snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL, + snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL, SGTL5000_BIAS_R_MASK, sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT); - snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL, + snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL, SGTL5000_BIAS_VOLT_MASK, sgtl5000->micbias_voltage << SGTL5000_BIAS_VOLT_SHIFT); /* - * disable DAP + * enable DAP Graphic EQ * TODO: - * Enable DAP in kcontrol and dapm. + * Add control for changing between PEQ/Tone Control/GEQ */ - snd_soc_write(codec, SGTL5000_DAP_CTRL, 0); + snd_soc_component_write(component, SGTL5000_DAP_AUDIO_EQ, SGTL5000_DAP_SEL_GEQ); + + /* Unmute DAC after start */ + snd_soc_component_update_bits(component, SGTL5000_CHIP_ADCDAC_CTRL, + SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT, 0); return 0; @@ -1243,24 +1515,27 @@ err: return ret; } -static int sgtl5000_remove(struct snd_soc_codec *codec) +static int sgtl5000_of_xlate_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) { + /* return dai id 0, whatever the endpoint index */ return 0; } -static struct snd_soc_codec_driver sgtl5000_driver = { - .probe = sgtl5000_probe, - .remove = sgtl5000_remove, - .set_bias_level = sgtl5000_set_bias_level, - .suspend_bias_off = true, - .component_driver = { - .controls = sgtl5000_snd_controls, - .num_controls = ARRAY_SIZE(sgtl5000_snd_controls), - .dapm_widgets = sgtl5000_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets), - .dapm_routes = sgtl5000_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes), - }, +static const struct snd_soc_component_driver sgtl5000_driver = { + .probe = sgtl5000_probe, + .set_bias_level = sgtl5000_set_bias_level, + .controls = sgtl5000_snd_controls, + .num_controls = ARRAY_SIZE(sgtl5000_snd_controls), + .dapm_widgets = sgtl5000_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets), + .dapm_routes = sgtl5000_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes), + .of_xlate_dai_id = sgtl5000_of_xlate_dai_id, + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, }; static const struct regmap_config sgtl5000_regmap = { @@ -1303,8 +1578,7 @@ static void sgtl5000_fill_defaults(struct i2c_client *client) } } -static int sgtl5000_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sgtl5000_i2c_probe(struct i2c_client *client) { struct sgtl5000_priv *sgtl5000; int ret, reg, rev; @@ -1332,10 +1606,12 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, 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) ret = -EPROBE_DEFER; + + dev_err_probe(&client->dev, ret, "Failed to get mclock\n"); + goto disable_regs; } @@ -1375,6 +1651,40 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, dev_err(&client->dev, "Error %d initializing CHIP_CLK_CTRL\n", ret); + /* Mute everything to avoid pop from the following power-up */ + ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_CTRL, + SGTL5000_CHIP_ANA_CTRL_DEFAULT); + if (ret) { + dev_err(&client->dev, + "Error %d muting outputs via CHIP_ANA_CTRL\n", ret); + goto disable_clk; + } + + /* + * If VAG is powered-on (e.g. from previous boot), it would be disabled + * by the write to ANA_POWER in later steps of the probe code. This + * may create a loud pop even with all outputs muted. The proper way + * to circumvent this is disabling the bit first and waiting the proper + * cool-down time. + */ + ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, &value); + if (ret) { + dev_err(&client->dev, "Failed to read ANA_POWER: %d\n", ret); + goto disable_clk; + } + if (value & SGTL5000_VAG_POWERUP) { + ret = regmap_update_bits(sgtl5000->regmap, + SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, + 0); + if (ret) { + dev_err(&client->dev, "Error %d disabling VAG\n", ret); + goto disable_clk; + } + + msleep(SGTL5000_VAG_POWERDOWN_DELAY); + } + /* Follow section 2.2.1.1 of AN3663 */ ana_pwr = SGTL5000_ANA_POWER_DEFAULT; if (sgtl5000->num_supplies <= VDDD) { @@ -1389,7 +1699,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, ana_pwr |= SGTL5000_LINEREG_D_POWERUP; dev_info(&client->dev, - "Using internal LDO instead of VDDD: check ER1\n"); + "Using internal LDO instead of VDDD: check ER1 erratum\n"); } else { /* using external LDO for VDDD * Clear startup powerup and simple powerup @@ -1453,10 +1763,17 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, sgtl5000->lrclk_strength = value; } + sgtl5000->sclk_strength = I2S_SCLK_STRENGTH_LOW; + if (!of_property_read_u32(np, "sclk-strength", &value)) { + if (value > I2S_SCLK_STRENGTH_HIGH) + value = I2S_SCLK_STRENGTH_LOW; + sgtl5000->sclk_strength = value; + } + /* Ensure sgtl5000 will start with sane register values */ sgtl5000_fill_defaults(client); - ret = snd_soc_register_codec(&client->dev, + ret = devm_snd_soc_register_component(&client->dev, &sgtl5000_driver, &sgtl5000_dai, 1); if (ret) goto disable_clk; @@ -1473,20 +1790,26 @@ disable_regs: return ret; } -static int sgtl5000_i2c_remove(struct i2c_client *client) +static void sgtl5000_i2c_remove(struct i2c_client *client) { struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client); - snd_soc_unregister_codec(&client->dev); + regmap_write(sgtl5000->regmap, SGTL5000_CHIP_CLK_CTRL, SGTL5000_CHIP_CLK_CTRL_DEFAULT); + regmap_write(sgtl5000->regmap, SGTL5000_CHIP_DIG_POWER, SGTL5000_DIG_POWER_DEFAULT); + regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, SGTL5000_ANA_POWER_DEFAULT); + clk_disable_unprepare(sgtl5000->mclk); regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies); regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies); +} - return 0; +static void sgtl5000_i2c_shutdown(struct i2c_client *client) +{ + sgtl5000_i2c_remove(client); } static const struct i2c_device_id sgtl5000_id[] = { - {"sgtl5000", 0}, + {"sgtl5000"}, {}, }; @@ -1500,11 +1823,12 @@ MODULE_DEVICE_TABLE(of, sgtl5000_dt_ids); static struct i2c_driver sgtl5000_i2c_driver = { .driver = { - .name = "sgtl5000", - .of_match_table = sgtl5000_dt_ids, - }, + .name = "sgtl5000", + .of_match_table = sgtl5000_dt_ids, + }, .probe = sgtl5000_i2c_probe, .remove = sgtl5000_i2c_remove, + .shutdown = sgtl5000_i2c_shutdown, .id_table = sgtl5000_id, }; |
