diff --git a/asoc/codecs/wcd938x/internal.h b/asoc/codecs/wcd938x/internal.h index 2c67ff80f6..8c2ff0df61 100644 --- a/asoc/codecs/wcd938x/internal.h +++ b/asoc/codecs/wcd938x/internal.h @@ -11,6 +11,8 @@ #include #include "wcd938x-mbhc.h" +#define SWR_SCP_CONTROL 0x44 +#define SWR_SCP_HOST_CLK_DIV2_CTL_BANK 0xE0 #define WCD938X_MAX_MICBIAS 4 /* Convert from vout ctl to micbias voltage in mV */ diff --git a/asoc/codecs/wcd938x/wcd938x.c b/asoc/codecs/wcd938x/wcd938x.c index 036f97ad87..dc4a0fb172 100644 --- a/asoc/codecs/wcd938x/wcd938x.c +++ b/asoc/codecs/wcd938x/wcd938x.c @@ -25,6 +25,7 @@ #include "wcd938x-registers.h" #include "wcd938x.h" + #define WCD938X_DRV_NAME "wcd938x_codec" #define NUM_SWRS_DT_PARAMS 5 #define WCD938X_VARIANT_ENTRY_SIZE 32 @@ -126,6 +127,45 @@ static int wcd938x_handle_post_irq(void *data) return IRQ_HANDLED; } +static int wcd938x_swr_slv_get_current_bank(struct swr_device *dev, u8 devnum) +{ + int ret = 0; + int bank = 0; + + ret = swr_read(dev, devnum, SWR_SCP_CONTROL, &bank, 1); + if (ret) + return -EINVAL; + + return ((bank & 0x40) ? 1: 0); +} + +static int wcd938x_swr_slv_set_host_clk_div2(struct swr_device *dev, + u8 devnum, int bank) +{ + u8 val = (bank ? 1 : 0); + + return (swr_write(dev, devnum, + (SWR_SCP_HOST_CLK_DIV2_CTL_BANK + (0x10 * bank)), &val)); +} + +static int wcd938x_set_swr_clk_rate(struct snd_soc_component *component, + int mode, int bank) +{ + u8 mask = (bank ? 0xF0 : 0x0F); + u8 val = 0; + + if ((mode == ADC_MODE_ULP1) || (mode == ADC_MODE_ULP2)) + val = (bank ? 0x60 : 0x06); + else + val = 0x00; + + snd_soc_component_update_bits(component, + WCD938X_DIGITAL_SWR_TX_CLK_RATE, + mask, val); + + return 0; +} + static int wcd938x_init_reg(struct snd_soc_component *component) { snd_soc_component_update_bits(component, WCD938X_SLEEP_CTL, 0x0E, 0x0E); @@ -1335,17 +1375,33 @@ static int wcd938x_tx_swr_ctrl(struct snd_soc_dapm_widget *w, snd_soc_dapm_to_component(w->dapm); struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); int ret = 0; + int bank = 0; + int mode = 0; + bank = wcd938x_swr_slv_get_current_bank(wcd938x->tx_swr_dev, + wcd938x->tx_swr_dev->dev_num); + wcd938x_swr_slv_set_host_clk_div2(wcd938x->tx_swr_dev, + wcd938x->tx_swr_dev->dev_num, bank); switch (event) { case SND_SOC_DAPM_PRE_PMU: ret = swr_slvdev_datapath_control(wcd938x->tx_swr_dev, wcd938x->tx_swr_dev->dev_num, true); + if (test_bit(WCD_ADC1, &wcd938x->status_mask)) + mode |= wcd938x->tx_mode[WCD_ADC1]; + if (test_bit(WCD_ADC2, &wcd938x->status_mask)) + mode |= wcd938x->tx_mode[WCD_ADC2]; + if (test_bit(WCD_ADC3, &wcd938x->status_mask)) + mode |= wcd938x->tx_mode[WCD_ADC3]; + if (test_bit(WCD_ADC4, &wcd938x->status_mask)) + mode |= wcd938x->tx_mode[WCD_ADC4]; + wcd938x_set_swr_clk_rate(component, mode, bank); break; case SND_SOC_DAPM_POST_PMD: ret = swr_slvdev_datapath_control(wcd938x->tx_swr_dev, wcd938x->tx_swr_dev->dev_num, false); + wcd938x_set_swr_clk_rate(component, ADC_MODE_INVALID, bank); break; };