On Thu, 6 Jun 2019 13:42:42 +0100 Srinivas Kandagatla <srinivas.kandagatla@xxxxxxxxxx> wrote: > This patch adds sidetone support via one of the 3 RX Mix paths > using IIR1 and IIR2. > IIR1 can be feed by any Decimators or RX paths, and IIRx can also be > looped back to RX mixers to provide sidetone functionality. > Two IIR filters are used for Side tone equalization and each filter > is 5 stage. > > Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@xxxxxxxxxx> > --- > Changes since v1: > - added missing break in switch > - Moved IIR bands enable to switch controls > > sound/soc/codecs/msm8916-wcd-digital.c | 282 > +++++++++++++++++++++++++ 1 file changed, 282 insertions(+) > > diff --git a/sound/soc/codecs/msm8916-wcd-digital.c > b/sound/soc/codecs/msm8916-wcd-digital.c index > a63961861e55..1db7e43ec203 100644 --- > a/sound/soc/codecs/msm8916-wcd-digital.c +++ > b/sound/soc/codecs/msm8916-wcd-digital.c @@ -187,6 +187,43 @@ > #define MSM8916_WCD_DIGITAL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ > SNDRV_PCM_FMTBIT_S32_LE) > > +/* Codec supports 2 IIR filters */ > +enum { > + IIR1 = 0, > + IIR2, > + IIR_MAX, > +}; > + > +/* Codec supports 5 bands */ > +enum { > + BAND1 = 0, > + BAND2, > + BAND3, > + BAND4, > + BAND5, > + BAND_MAX, > +}; > + > +#define WCD_IIR_FILTER_SIZE (sizeof(u32)*BAND_MAX) > + > +#define WCD_IIR_FILTER_CTL(xname, iidx, bidx) \ > +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ > + .info = wcd_iir_filter_info, \ > + .get = msm8x16_wcd_get_iir_band_audio_mixer, \ > + .put = msm8x16_wcd_put_iir_band_audio_mixer, \ > + .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) > { \ > + .iir_idx = iidx, \ > + .band_idx = bidx, \ > + .bytes_ext = {.max = WCD_IIR_FILTER_SIZE, }, \ > + } \ > +} > + > +struct wcd_iir_filter_ctl { > + unsigned int iir_idx; > + unsigned int band_idx; > + struct soc_bytes_ext bytes_ext; > +}; > + > struct msm8916_wcd_digital_priv { > struct clk *ahbclk, *mclk; > }; > @@ -298,6 +335,161 @@ static > SOC_ENUM_SINGLE_DECL(rx2_dcb_cutoff_enum, LPASS_CDC_RX2_B4_CTL, 0, > static SOC_ENUM_SINGLE_DECL(rx3_dcb_cutoff_enum, > LPASS_CDC_RX3_B4_CTL, 0, dc_blocker_cutoff_text); > +static int msm8x16_wcd_codec_set_iir_gain(struct snd_soc_dapm_widget > *w, > + struct snd_kcontrol *kcontrol, int event) > +{ > + struct snd_soc_component *component = > + snd_soc_dapm_to_component(w->dapm); > + int value = 0, reg = 0; > + > + switch (event) { > + case SND_SOC_DAPM_POST_PMU: > + if (w->shift == 0) > + reg = LPASS_CDC_IIR1_GAIN_B1_CTL; > + else if (w->shift == 1) > + reg = LPASS_CDC_IIR2_GAIN_B1_CTL; > + value = snd_soc_component_read32(component, reg); > + snd_soc_component_write(component, reg, value); > + break; > + default: > + break; > + } > + return 0; > +} > + > +static uint32_t get_iir_band_coeff(struct snd_soc_component > *component, > + int iir_idx, int band_idx, > + int coeff_idx) > +{ > + uint32_t value = 0; > + > + /* Address does not automatically update if reading */ > + snd_soc_component_write(component, > + (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx), > + ((band_idx * BAND_MAX + coeff_idx) > + * sizeof(uint32_t)) & 0x7F); > + > + value |= snd_soc_component_read32(component, > + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)); > + > + snd_soc_component_write(component, > + (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx), > + ((band_idx * BAND_MAX + coeff_idx) > + * sizeof(uint32_t) + 1) & 0x7F); > + > + value |= (snd_soc_component_read32(component, > + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 8); > + > + snd_soc_component_write(component, > + (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx), > + ((band_idx * BAND_MAX + coeff_idx) > + * sizeof(uint32_t) + 2) & 0x7F); > + > + value |= (snd_soc_component_read32(component, > + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 16); > + > + snd_soc_component_write(component, > + (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx), > + ((band_idx * BAND_MAX + coeff_idx) > + * sizeof(uint32_t) + 3) & 0x7F); > + > + /* Mask bits top 2 bits since they are reserved */ > + value |= ((snd_soc_component_read32(component, > + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) & > 0x3f) << 24); > + return value; > + > +} > + > +static int msm8x16_wcd_get_iir_band_audio_mixer( > + struct snd_kcontrol > *kcontrol, > + struct snd_ctl_elem_value > *ucontrol) +{ > + > + struct snd_soc_component *component = > + snd_soc_kcontrol_component(kcontrol); > + struct wcd_iir_filter_ctl *ctl = > + (struct wcd_iir_filter_ctl > *)kcontrol->private_value; > + struct soc_bytes_ext *params = &ctl->bytes_ext; > + int iir_idx = ctl->iir_idx; > + int band_idx = ctl->band_idx; > + u32 coeff[BAND_MAX]; > + > + coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, > 0); > + coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, > 1); > + coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, > 2); > + coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, > 3); > + coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, > 4); + > + memcpy(ucontrol->value.bytes.data, &coeff[0], params->max); > + > + return 0; > +} > + > +static void set_iir_band_coeff(struct snd_soc_component *component, > + int iir_idx, int band_idx, > + uint32_t value) > +{ > + snd_soc_component_write(component, > + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx), > + (value & 0xFF)); > + > + snd_soc_component_write(component, > + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx), > + (value >> 8) & 0xFF); > + > + snd_soc_component_write(component, > + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx), > + (value >> 16) & 0xFF); > + > + /* Mask top 2 bits, 7-8 are reserved */ > + snd_soc_component_write(component, > + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx), > + (value >> 24) & 0x3F); > +} > + > +static int msm8x16_wcd_put_iir_band_audio_mixer( > + struct snd_kcontrol > *kcontrol, > + struct snd_ctl_elem_value > *ucontrol) +{ > + struct snd_soc_component *component = > + snd_soc_kcontrol_component(kcontrol); > + struct wcd_iir_filter_ctl *ctl = > + (struct wcd_iir_filter_ctl > *)kcontrol->private_value; > + struct soc_bytes_ext *params = &ctl->bytes_ext; > + int iir_idx = ctl->iir_idx; > + int band_idx = ctl->band_idx; > + u32 coeff[BAND_MAX]; > + > + memcpy(&coeff[0], ucontrol->value.bytes.data, params->max); > + > + /* Mask top bit it is reserved */ > + /* Updates addr automatically for each B2 write */ > + snd_soc_component_write(component, > + (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx), > + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); > + > + set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]); > + set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]); > + set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]); > + set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]); > + set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]); > + > + return 0; > +} > + > +static int wcd_iir_filter_info(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_info *ucontrol) > +{ > + struct wcd_iir_filter_ctl *ctl = > + (struct wcd_iir_filter_ctl *)kcontrol->private_value; > + struct soc_bytes_ext *params = &ctl->bytes_ext; > + > + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; > + ucontrol->count = params->max; > + > + return 0; > +} > + > static const struct snd_kcontrol_new > msm8916_wcd_digital_snd_controls[] = { SOC_SINGLE_S8_TLV("RX1 Digital > Volume", LPASS_CDC_RX1_VOL_CTL_B2_CTL, -128, 127, digital_gain), > @@ -322,6 +514,44 @@ static const struct snd_kcontrol_new > msm8916_wcd_digital_snd_controls[] = { SOC_SINGLE("RX1 Mute Switch", > LPASS_CDC_RX1_B6_CTL, 0, 1, 0), SOC_SINGLE("RX2 Mute Switch", > LPASS_CDC_RX2_B6_CTL, 0, 1, 0), SOC_SINGLE("RX3 Mute Switch", > LPASS_CDC_RX3_B6_CTL, 0, 1, 0), + > + SOC_SINGLE("IIR1 Band1 Switch", LPASS_CDC_IIR1_CTL, 0, 1, 0), > + SOC_SINGLE("IIR1 Band2 Switch", LPASS_CDC_IIR1_CTL, 1, 1, 0), > + SOC_SINGLE("IIR1 Band3 Switch", LPASS_CDC_IIR1_CTL, 2, 1, 0), > + SOC_SINGLE("IIR1 Band4 Switch", LPASS_CDC_IIR1_CTL, 3, 1, 0), > + SOC_SINGLE("IIR1 Band5 Switch", LPASS_CDC_IIR1_CTL, 4, 1, 0), > + SOC_SINGLE("IIR2 Band1 Switch", LPASS_CDC_IIR2_CTL, 0, 1, 0), > + SOC_SINGLE("IIR2 Band2 Switch", LPASS_CDC_IIR2_CTL, 1, 1, 0), > + SOC_SINGLE("IIR2 Band3 Switch", LPASS_CDC_IIR2_CTL, 2, 1, 0), > + SOC_SINGLE("IIR2 Band4 Switch", LPASS_CDC_IIR2_CTL, 3, 1, 0), > + SOC_SINGLE("IIR2 Band5 Switch", LPASS_CDC_IIR2_CTL, 4, 1, 0), > + WCD_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1), > + WCD_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2), > + WCD_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3), > + WCD_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4), > + WCD_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5), > + WCD_IIR_FILTER_CTL("IIR2 Band1", IIR2, BAND1), > + WCD_IIR_FILTER_CTL("IIR2 Band2", IIR2, BAND2), > + WCD_IIR_FILTER_CTL("IIR2 Band3", IIR2, BAND3), > + WCD_IIR_FILTER_CTL("IIR2 Band4", IIR2, BAND4), > + WCD_IIR_FILTER_CTL("IIR2 Band5", IIR2, BAND5), > + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", > LPASS_CDC_IIR1_GAIN_B1_CTL, > + 0, -84, 40, digital_gain), > + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", > LPASS_CDC_IIR1_GAIN_B2_CTL, > + 0, -84, 40, digital_gain), > + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", > LPASS_CDC_IIR1_GAIN_B3_CTL, > + 0, -84, 40, digital_gain), > + SOC_SINGLE_SX_TLV("IIR1 INP4 Volume", > LPASS_CDC_IIR1_GAIN_B4_CTL, > + 0, -84, 40, digital_gain), There seems to be some alignment issue in above line. And while I'm commenting this place, there is only 4 Volume controls, while there is 5 switches for IIR1 and IIR2, is this right? > + SOC_SINGLE_SX_TLV("IIR2 INP1 Volume", > LPASS_CDC_IIR2_GAIN_B1_CTL, > + 0, -84, 40, digital_gain), > + SOC_SINGLE_SX_TLV("IIR2 INP2 Volume", > LPASS_CDC_IIR2_GAIN_B2_CTL, > + 0, -84, 40, digital_gain), > + SOC_SINGLE_SX_TLV("IIR2 INP3 Volume", > LPASS_CDC_IIR2_GAIN_B3_CTL, > + 0, -84, 40, digital_gain), > + SOC_SINGLE_SX_TLV("IIR2 INP4 Volume", > LPASS_CDC_IIR2_GAIN_B4_CTL, > + 0, -84, 40, digital_gain), > + > }; > > static int msm8916_wcd_digital_enable_interpolator( > @@ -448,6 +678,24 @@ static int > msm8916_wcd_digital_enable_dmic(struct snd_soc_dapm_widget *w, return > 0; } > > +static const char * const iir_inp1_text[] = { > + "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3" > +}; > + > +static const struct soc_enum iir1_inp1_mux_enum = > + SOC_ENUM_SINGLE(LPASS_CDC_CONN_EQ1_B1_CTL, > + 0, 6, iir_inp1_text); > + > +static const struct soc_enum iir2_inp1_mux_enum = > + SOC_ENUM_SINGLE(LPASS_CDC_CONN_EQ2_B1_CTL, > + 0, 6, iir_inp1_text); > + > +static const struct snd_kcontrol_new iir1_inp1_mux = > + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); > + > +static const struct snd_kcontrol_new iir2_inp1_mux = > + SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum); > + > static const struct snd_soc_dapm_widget > msm8916_wcd_digital_dapm_widgets[] = { /*RX stuff */ > SND_SOC_DAPM_AIF_IN("I2S RX1", NULL, 0, SND_SOC_NOPM, 0, 0), > @@ -534,6 +782,15 @@ static const struct snd_soc_dapm_widget > msm8916_wcd_digital_dapm_widgets[] = { SND_SOC_DAPM_MIC("Digital > Mic1", NULL), SND_SOC_DAPM_MIC("Digital Mic2", NULL), > > + /* Sidetone */ > + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, > &iir1_inp1_mux), > + SND_SOC_DAPM_PGA_E("IIR1", LPASS_CDC_CLK_SD_CTL, 0, 0, NULL, > 0, > + msm8x16_wcd_codec_set_iir_gain, > SND_SOC_DAPM_POST_PMU), + > + SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, > &iir2_inp1_mux), > + SND_SOC_DAPM_PGA_E("IIR2", LPASS_CDC_CLK_SD_CTL, 1, 0, NULL, > 0, > + msm8x16_wcd_codec_set_iir_gain, > SND_SOC_DAPM_POST_PMU), + > }; > > static int msm8916_wcd_digital_get_clks(struct platform_device *pdev, > @@ -708,10 +965,14 @@ static const struct snd_soc_dapm_route > msm8916_wcd_digital_audio_map[] = { {"RX1 MIX1 INP1", "RX1", "I2S > RX1"}, {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, > {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, > + {"RX1 MIX1 INP1", "IIR1", "IIR1"}, > + {"RX1 MIX1 INP1", "IIR2", "IIR2"}, > > {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, > {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, > {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, > + {"RX1 MIX1 INP2", "IIR1", "IIR1"}, > + {"RX1 MIX1 INP2", "IIR2", "IIR2"}, > > {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, > {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, > @@ -728,10 +989,14 @@ static const struct snd_soc_dapm_route > msm8916_wcd_digital_audio_map[] = { {"RX2 MIX1 INP1", "RX1", "I2S > RX1"}, {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, > {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, > + {"RX2 MIX1 INP1", "IIR1", "IIR1"}, > + {"RX2 MIX1 INP1", "IIR2", "IIR2"}, > > {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, > {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, > {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, > + {"RX2 MIX1 INP1", "IIR1", "IIR1"}, > + {"RX2 MIX1 INP1", "IIR2", "IIR2"}, > > {"RX2 MIX1 INP3", "RX1", "I2S RX1"}, > {"RX2 MIX1 INP3", "RX2", "I2S RX2"}, > @@ -748,10 +1013,27 @@ static const struct snd_soc_dapm_route > msm8916_wcd_digital_audio_map[] = { {"RX3 MIX1 INP1", "RX1", "I2S > RX1"}, {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, > {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, > + {"RX3 MIX1 INP1", "IIR1", "IIR1"}, > + {"RX3 MIX1 INP1", "IIR2", "IIR2"}, > > {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, > {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, > {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, > + {"RX3 MIX1 INP2", "IIR1", "IIR1"}, > + {"RX3 MIX1 INP2", "IIR2", "IIR2"}, > + > + {"RX1 MIX2 INP1", "IIR1", "IIR1"}, > + {"RX2 MIX2 INP1", "IIR1", "IIR1"}, > + {"RX1 MIX2 INP1", "IIR2", "IIR2"}, > + {"RX2 MIX2 INP1", "IIR2", "IIR2"}, > + > + {"IIR1", NULL, "IIR1 INP1 MUX"}, > + {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"}, > + {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"}, > + > + {"IIR2", NULL, "IIR2 INP1 MUX"}, > + {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"}, > + {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"}, > > {"RX3 MIX1 INP3", "RX1", "I2S RX1"}, > {"RX3 MIX1 INP3", "RX2", "I2S RX2"}, _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx https://mailman.alsa-project.org/mailman/listinfo/alsa-devel