diff options
Diffstat (limited to 'sound/soc/codecs/wcd9335.c')
| -rw-r--r--[-rwxr-xr-x] | sound/soc/codecs/wcd9335.c | 231 |
1 files changed, 207 insertions, 24 deletions
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index f46057d027e0..11993bb9a639 100755..100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -369,6 +369,7 @@ enum { AIF_MIX1_PB, AIF4_MAD_TX, AIF4_VIFEED, + AIF5_CPE_TX, NUM_CODEC_DAIS, }; @@ -477,15 +478,18 @@ static const struct wcd9xxx_ch tasha_tx_chs[TASHA_TX_MAX] = { }; static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { - 0, /* AIF1_PB */ - BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */ - 0, /* AIF2_PB */ - BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */ - 0, /* AIF3_PB */ - BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */ - 0, /* AIF4_PB */ - 0, /* AIF_MIX1_PB */ - BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP), /* AIF4_MAD_TX */ + /* Needs to define in the same order of DAI enum definitions */ + 0, + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), }; static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = { @@ -584,6 +588,8 @@ static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv); static int tasha_config_compander(struct snd_soc_codec *, int, int); static void tasha_codec_set_tx_hold(struct snd_soc_codec *, u16, bool); +static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable); /* Hold instance to soundwire platform device */ struct tasha_swr_ctrl_data { @@ -837,6 +843,8 @@ struct tasha_priv { int hph_r_gain; int rx_7_count; int rx_8_count; + bool clk_mode; + bool clk_internal; }; static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec, @@ -2286,6 +2294,30 @@ static int tasha_put_anc_func(struct snd_kcontrol *kcontrol, return 0; } +static int tasha_get_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = tasha->clk_mode; + dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode); + + return 0; +} + +static int tasha_put_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha->clk_mode = ucontrol->value.enumerated.item[0]; + dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode); + + return 0; +} + static int tasha_get_iir_enable_audio_mixer( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2520,6 +2552,7 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, } break; case AIF4_MAD_TX: + case AIF5_CPE_TX: break; default: pr_err("Unknown AIF %d\n", dai_id); @@ -3289,7 +3322,7 @@ static int __tasha_codec_enable_slimtx(struct snd_soc_codec *codec, ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list, dai->grph); - pr_debug("%s: Disconnect RX port, ret = %d\n", + pr_debug("%s: Disconnect TX port, ret = %d\n", __func__, ret); } @@ -3971,13 +4004,16 @@ static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, set_bit(HPH_PA_DELAY, &tasha->status_mask); break; case SND_SOC_DAPM_POST_PMU: - if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) != 0xC0) - /* - * If PA_EN is not set (potentially in ANC case) then - * do nothing for POST_PMU and let left channel handle - * everything. - */ - break; + if (!(strcmp(w->name, "ANC HPHR PA"))) { + if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let left + * channel handle everything. + */ + break; + } /* * 7ms sleep is required after PA is enabled as per * HW requirement @@ -4062,13 +4098,16 @@ static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, set_bit(HPH_PA_DELAY, &tasha->status_mask); break; case SND_SOC_DAPM_POST_PMU: - if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) != 0xC0) - /* - * If PA_EN is not set (potentially in ANC case) then - * do nothing for POST_PMU and let right channel handle - * everything. - */ - break; + if (!(strcmp(w->name, "ANC HPHL PA"))) { + if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let right + * channel handle everything. + */ + break; + } /* * 7ms sleep is required after PA is enabled as per * HW requirement @@ -5781,6 +5820,18 @@ static u32 tasha_get_dmic_sample_rate(struct snd_soc_codec *codec, tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F; dmic_fs = tx_stream_fs <= 4 ? WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ : WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + + /* + * Check for ECPP path selection and DEC1 not connected to + * any other audio path to apply ECPP DMIC sample rate + */ + if ((adc_mux_index == 1) && + ((snd_soc_read(codec, WCD9335_CPE_SS_US_EC_MUX_CFG) + & 0x0F) == 0x0A) && + ((snd_soc_read(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0) + & 0x0C) == 0x00)) { + dmic_fs = pdata->ecpp_dmic_sample_rate; + } } else { dmic_fs = pdata->dmic_sample_rate; } @@ -6078,6 +6129,9 @@ static const char *const tasha_anc_func_text[] = {"OFF", "ON"}; static const struct soc_enum tasha_anc_func_enum = SOC_ENUM_SINGLE_EXT(2, tasha_anc_func_text); +static const char *const tasha_clkmode_text[] = {"EXTERNAL", "INTERNAL"}; +static SOC_ENUM_SINGLE_EXT_DECL(tasha_clkmode_enum, tasha_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" @@ -6199,6 +6253,9 @@ static const struct snd_soc_dapm_route audio_map[] = { {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, {"AIF4 MAD", NULL, "AIF4"}, + {"EC BUF MUX INP", "DEC1", "ADC MUX1"}, + {"AIF5 CPE", NULL, "EC BUF MUX INP"}, + /* SLIMBUS Connections */ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, @@ -8203,6 +8260,9 @@ static const struct snd_kcontrol_new tasha_snd_controls[] = { SOC_ENUM_EXT("ANC Function", tasha_anc_func_enum, tasha_get_anc_func, tasha_put_anc_func), + SOC_ENUM_EXT("CLK MODE", tasha_clkmode_enum, tasha_get_clkmode, + tasha_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), @@ -9960,6 +10020,45 @@ static const struct snd_kcontrol_new anc0_fb_mux = static const struct snd_kcontrol_new anc1_fb_mux = SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum); +static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d name = %s\n", + __func__, event, w->name); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x28); + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x08); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x40); + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00); + break; + } + + return 0; +}; + +static const char * const ec_buf_mux_text[] = { + "ZERO", "RXMIXEC", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3", + "I2S_RX_SD0_L", "I2S_RX_SD0_R", "I2S_RX_SD1_L", "I2S_RX_SD1_R", + "DEC1" +}; + +static SOC_ENUM_SINGLE_DECL(ec_buf_mux_enum, WCD9335_CPE_SS_US_EC_MUX_CFG, + 0, ec_buf_mux_text); + +static const struct snd_kcontrol_new ec_buf_mux = + SOC_DAPM_ENUM("EC BUF Mux", ec_buf_mux_enum); + static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("EAR"), SND_SOC_DAPM_OUTPUT("ANC EAR"), @@ -10449,6 +10548,14 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), SND_SOC_DAPM_INPUT("VIINPUT"), + + SND_SOC_DAPM_AIF_OUT("AIF5 CPE", "AIF5 CPE TX", 0, SND_SOC_NOPM, + AIF5_CPE_TX, 0), + + SND_SOC_DAPM_MUX_E("EC BUF MUX INP", SND_SOC_NOPM, 0, 0, &ec_buf_mux, + tasha_codec_ec_buf_mux_enable, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + /* Digital Mic Inputs */ SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | @@ -11432,6 +11539,19 @@ static struct snd_soc_dai_driver tasha_dai[] = { }, .ops = &tasha_dai_ops, }, + { + .name = "tasha_cpe", + .id = AIF5_CPE_TX, + .capture = { + .stream_name = "AIF5 CPE TX", + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_min = 16000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + }, + }, }; static struct snd_soc_dai_driver tasha_i2s_dai[] = { @@ -11681,6 +11801,44 @@ int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, bool dapm) } EXPORT_SYMBOL(tasha_cdc_mclk_enable); +int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, bool dapm) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(tasha->dev, "%s: clk_mode: %d, enable: %d, clk_internal: %d\n", + __func__, tasha->clk_mode, enable, tasha->clk_internal); + if (tasha->clk_mode || tasha->clk_internal) { + if (enable) { + tasha_cdc_sido_ccl_enable(tasha, true); + wcd_resmgr_enable_master_bias(tasha->resmgr); + tasha_dig_core_power_collapse(tasha, POWER_RESUME); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + set_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + SIDO_VOLTAGE_NOMINAL_MV); + tasha->clk_internal = true; + } else { + tasha->clk_internal = false; + clear_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + sido_buck_svs_voltage); + tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE); + wcd_resmgr_disable_master_bias(tasha->resmgr); + tasha_cdc_sido_ccl_enable(tasha, false); + } + } else { + ret = __tasha_cdc_mclk_enable(tasha, enable); + } + return ret; +} +EXPORT_SYMBOL(tasha_cdc_mclk_tx_enable); + static ssize_t tasha_codec_version_read(struct snd_info_entry *entry, void *file_private_data, struct file *file, char __user *buf, size_t count, loff_t pos) @@ -12330,6 +12488,17 @@ static int tasha_handle_pdata(struct tasha_priv *tasha, */ pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate; } + if (pdata->ecpp_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, + "%s: ecpp_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for ECPP DMIC + * if ecpp dmic sample rate is undefined + */ + pdata->ecpp_dmic_sample_rate = pdata->dmic_sample_rate; + } if (pdata->dmic_clk_drv == WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) { @@ -13098,6 +13267,20 @@ static int tasha_codec_probe(struct snd_soc_codec *codec) control->tx_chs = ptr + sizeof(tasha_rx_chs); memcpy(control->tx_chs, tasha_tx_chs, sizeof(tasha_tx_chs)); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF Mix Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + } + snd_soc_dapm_sync(dapm); ret = tasha_setup_irqs(tasha); |
