From f6e384c5c169507663aef0d4c773b792e20b61ab Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Thu, 29 Jun 2017 14:02:38 -0700 Subject: [PATCH 1/2] ASoC: wcd934x: add low power mode support for codec Codec goes to digital power collapse state and disables the external clock when there is no audio usecase active. It is possible certain low power audio usecases need the codec to be out of digital core power collapse but can run using the internal codec clock. Change adds support to turn on codec digital core without turning on the external clock if the clock mode is set to internal. Change-Id: I74c296d9b967ea56c3f03514022b259856533d9b Signed-off-by: Bhalchandra Gajare --- sound/soc/codecs/wcd934x/wcd934x.c | 86 ++++++++++++++++++++++++++++++ sound/soc/codecs/wcd934x/wcd934x.h | 1 + 2 files changed, 87 insertions(+) diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index ca16ed8c8ae3..ee9b60d8f5d3 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -180,6 +180,8 @@ enum { ANC_MIC_AMIC2, ANC_MIC_AMIC3, ANC_MIC_AMIC4, + CLK_INTERNAL, + CLK_MODE, }; enum { @@ -1071,6 +1073,40 @@ err: return ret; } +static int tavil_get_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + if (test_bit(CLK_MODE, &tavil_p->status_mask)) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + dev_dbg(codec->dev, "%s: is_low_power_clock: %s\n", __func__, + test_bit(CLK_MODE, &tavil_p->status_mask) ? "true" : "false"); + + return 0; +} + +static int tavil_put_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.enumerated.item[0]) + set_bit(CLK_MODE, &tavil_p->status_mask); + else + clear_bit(CLK_MODE, &tavil_p->status_mask); + + dev_dbg(codec->dev, "%s: is_low_power_clock: %s\n", __func__, + test_bit(CLK_MODE, &tavil_p->status_mask) ? "true" : "false"); + + return 0; +} + static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -5547,6 +5583,9 @@ static const char *const tavil_anc_func_text[] = {"OFF", "ON"}; static const struct soc_enum tavil_anc_func_enum = SOC_ENUM_SINGLE_EXT(2, tavil_anc_func_text); +static const char *const tavil_clkmode_text[] = {"EXTERNAL", "INTERNAL"}; +static SOC_ENUM_SINGLE_EXT_DECL(tavil_clkmode_enum, tavil_clkmode_text); + /* Cutoff frequency for high pass filter */ static const char * const cf_text[] = { "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" @@ -5726,6 +5765,9 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = { SOC_ENUM_EXT("ANC Function", tavil_anc_func_enum, tavil_get_anc_func, tavil_put_anc_func), + SOC_ENUM_EXT("CLK MODE", tavil_clkmode_enum, tavil_get_clkmode, + tavil_put_clkmode), + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), @@ -8316,6 +8358,50 @@ static int tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec, return ret; } +/* + * tavil_cdc_mclk_tx_enable: Enable/Disable codec's clock for TX path + * @codec: Handle to codec + * @enable: Indicates whether clock should be enabled or disabled + */ +int tavil_cdc_mclk_tx_enable(struct snd_soc_codec *codec, bool enable) +{ + struct tavil_priv *tavil_p; + int ret = 0; + bool clk_mode; + bool clk_internal; + + if (!codec) + return -EINVAL; + + tavil_p = snd_soc_codec_get_drvdata(codec); + clk_mode = test_bit(CLK_MODE, &tavil_p->status_mask); + clk_internal = test_bit(CLK_INTERNAL, &tavil_p->status_mask); + + dev_dbg(codec->dev, "%s: clkmode: %d, enable: %d, clk_internal: %d\n", + __func__, clk_mode, enable, clk_internal); + + if (clk_mode || clk_internal) { + if (enable) { + wcd_resmgr_enable_master_bias(tavil_p->resmgr); + tavil_dig_core_power_collapse(tavil_p, POWER_RESUME); + tavil_vote_svs(tavil_p, true); + ret = tavil_codec_internal_rco_ctrl(codec, enable); + set_bit(CLK_INTERNAL, &tavil_p->status_mask); + } else { + clear_bit(CLK_INTERNAL, &tavil_p->status_mask); + tavil_codec_internal_rco_ctrl(codec, enable); + tavil_vote_svs(tavil_p, false); + tavil_dig_core_power_collapse(tavil_p, POWER_COLLAPSE); + wcd_resmgr_disable_master_bias(tavil_p->resmgr); + } + } else { + ret = __tavil_cdc_mclk_enable(tavil_p, enable); + } + + return ret; +} +EXPORT_SYMBOL(tavil_cdc_mclk_tx_enable); + static const struct wcd_resmgr_cb tavil_resmgr_cb = { .cdc_rco_ctrl = __tavil_codec_internal_rco_ctrl, }; diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/sound/soc/codecs/wcd934x/wcd934x.h index c3bf50a4ffdb..27c21f103533 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.h +++ b/sound/soc/codecs/wcd934x/wcd934x.h @@ -137,6 +137,7 @@ struct tavil_reg_mask_val { extern void *tavil_get_afe_config(struct snd_soc_codec *codec, enum afe_config_type config_type); extern int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable); +extern int tavil_cdc_mclk_tx_enable(struct snd_soc_codec *codec, bool enable); extern int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode); extern int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset); extern struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev); From eef10ca63b6c85450ee935c6c965c5a5b9eaefed Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Thu, 29 Jun 2017 14:09:09 -0700 Subject: [PATCH 2/2] ASoC: sdm845: add independent clock support on codec WCD934X codec has single master clock supply widget for both playback and recording paths. Adding separate clock supply for playback and recording paths will help handle low power audio recording usecases without affecting playback usecases. Change is to enable separate clock supply widgets for playback and recording paths. Change-Id: I8823294c4bf4892d1a7cac3d67afc30ad9a90210 Signed-off-by: Bhalchandra Gajare --- sound/soc/msm/sdm845.c | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/sound/soc/msm/sdm845.c b/sound/soc/msm/sdm845.c index d3c4e05c65c8..9248766e107a 100644 --- a/sound/soc/msm/sdm845.c +++ b/sound/soc/msm/sdm845.c @@ -538,10 +538,10 @@ static struct wcd_mbhc_config wcd_mbhc_cfg = { }; static struct snd_soc_dapm_route wcd_audio_paths[] = { - {"MIC BIAS1", NULL, "MCLK"}, - {"MIC BIAS2", NULL, "MCLK"}, - {"MIC BIAS3", NULL, "MCLK"}, - {"MIC BIAS4", NULL, "MCLK"}, + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, }; static struct afe_clk_set mi2s_clk[MI2S_MAX] = { @@ -2784,6 +2784,38 @@ static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, return ret; } +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tavil_codec")) { + ret = tavil_cdc_mclk_tx_enable(codec, enable); + } else { + dev_err(codec->dev, "%s: unknown codec to enable TX ext clk\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +static int msm_mclk_tx_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); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(codec, 0, true); + } + return 0; +} + static int msm_mclk_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -2840,7 +2872,7 @@ static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, - NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), SND_SOC_DAPM_SPK("Lineout_2 amp", NULL),