ASoC: rt5670: add API to select ASRC clock source
authorMengdong Lin <mengdong.lin@intel.com>
Wed, 7 Jan 2015 02:19:12 +0000 (10:19 +0800)
committerMark Brown <broonie@kernel.org>
Wed, 7 Jan 2015 17:40:01 +0000 (17:40 +0000)
When codec is in slave mode, ASRC can suppress noise for asynchronous
MCLK and LRCLK or special I2S format. This patch defines an API to select
the clock source for specified filters.  And the codec driver will turn on ASRC
for these filters if ASRC is selected as their clock source.

Signed-off-by: Bard Liao <bardliao@realtek.com>
Signed-off-by: Mengdong Lin <mengdong.lin@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/rt5670.c
sound/soc/codecs/rt5670.h

index 0a027bc943991c4d30db3f575c7893dec2c3df39..0632b7458a532260dd4034ff648956743567bf53 100644 (file)
@@ -590,6 +590,89 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
        return 0;
 }
 
+
+/**
+ * rt5670_sel_asrc_clk_src - select ASRC clock source for a set of filters
+ * @codec: SoC audio codec device.
+ * @filter_mask: mask of filters.
+ * @clk_src: clock source
+ *
+ * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5670 can
+ * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
+ * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
+ * ASRC function will track i2s clock and generate a corresponding system clock
+ * for codec. This function provides an API to select the clock source for a
+ * set of filters specified by the mask. And the codec driver will turn on ASRC
+ * for these filters if ASRC is selected as their clock source.
+ */
+int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
+                           unsigned int filter_mask, unsigned int clk_src)
+{
+       unsigned int asrc2_mask = 0, asrc2_value = 0;
+       unsigned int asrc3_mask = 0, asrc3_value = 0;
+
+       if (clk_src > RT5670_CLK_SEL_SYS3)
+               return -EINVAL;
+
+       if (filter_mask & RT5670_DA_STEREO_FILTER) {
+               asrc2_mask |= RT5670_DA_STO_CLK_SEL_MASK;
+               asrc2_value = (asrc2_value & ~RT5670_DA_STO_CLK_SEL_MASK)
+                               | (clk_src <<  RT5670_DA_STO_CLK_SEL_SFT);
+       }
+
+       if (filter_mask & RT5670_DA_MONO_L_FILTER) {
+               asrc2_mask |= RT5670_DA_MONOL_CLK_SEL_MASK;
+               asrc2_value = (asrc2_value & ~RT5670_DA_MONOL_CLK_SEL_MASK)
+                               | (clk_src <<  RT5670_DA_MONOL_CLK_SEL_SFT);
+       }
+
+       if (filter_mask & RT5670_DA_MONO_R_FILTER) {
+               asrc2_mask |= RT5670_DA_MONOR_CLK_SEL_MASK;
+               asrc2_value = (asrc2_value & ~RT5670_DA_MONOR_CLK_SEL_MASK)
+                               | (clk_src <<  RT5670_DA_MONOR_CLK_SEL_SFT);
+       }
+
+       if (filter_mask & RT5670_AD_STEREO_FILTER) {
+               asrc2_mask |= RT5670_AD_STO1_CLK_SEL_MASK;
+               asrc2_value = (asrc2_value & ~RT5670_AD_STO1_CLK_SEL_MASK)
+                               | (clk_src <<  RT5670_AD_STO1_CLK_SEL_SFT);
+       }
+
+       if (filter_mask & RT5670_AD_MONO_L_FILTER) {
+               asrc3_mask |= RT5670_AD_MONOL_CLK_SEL_MASK;
+               asrc3_value = (asrc3_value & ~RT5670_AD_MONOL_CLK_SEL_MASK)
+                               | (clk_src <<  RT5670_AD_MONOL_CLK_SEL_SFT);
+       }
+
+       if (filter_mask & RT5670_AD_MONO_R_FILTER)  {
+               asrc3_mask |= RT5670_AD_MONOR_CLK_SEL_MASK;
+               asrc3_value = (asrc3_value & ~RT5670_AD_MONOR_CLK_SEL_MASK)
+                               | (clk_src <<  RT5670_AD_MONOR_CLK_SEL_SFT);
+       }
+
+       if (filter_mask & RT5670_UP_RATE_FILTER) {
+               asrc3_mask |= RT5670_UP_CLK_SEL_MASK;
+               asrc3_value = (asrc3_value & ~RT5670_UP_CLK_SEL_MASK)
+                               | (clk_src <<  RT5670_UP_CLK_SEL_SFT);
+       }
+
+       if (filter_mask & RT5670_DOWN_RATE_FILTER) {
+               asrc3_mask |= RT5670_DOWN_CLK_SEL_MASK;
+               asrc3_value = (asrc3_value & ~RT5670_DOWN_CLK_SEL_MASK)
+                               | (clk_src <<  RT5670_DOWN_CLK_SEL_SFT);
+       }
+
+       if (asrc2_mask)
+               snd_soc_update_bits(codec, RT5670_ASRC_2,
+                                   asrc2_mask, asrc2_value);
+
+       if (asrc3_mask)
+               snd_soc_update_bits(codec, RT5670_ASRC_3,
+                                   asrc3_mask, asrc3_value);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt5670_sel_asrc_clk_src);
+
 /* Digital Mixer */
 static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = {
        SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER,
index ec38d8f173e73c3db51a76acb6abf27f7219793f..21f8e18c13c48d606deb315b6abc82838029e14d 100644 (file)
@@ -1966,6 +1966,21 @@ enum {
        RT5670_DMIC_DATA_GPIO5,
 };
 
+/* filter mask */
+enum {
+       RT5670_DA_STEREO_FILTER = 0x1,
+       RT5670_DA_MONO_L_FILTER = (0x1 << 1),
+       RT5670_DA_MONO_R_FILTER = (0x1 << 2),
+       RT5670_AD_STEREO_FILTER = (0x1 << 3),
+       RT5670_AD_MONO_L_FILTER = (0x1 << 4),
+       RT5670_AD_MONO_R_FILTER = (0x1 << 5),
+       RT5670_UP_RATE_FILTER   = (0x1 << 6),
+       RT5670_DOWN_RATE_FILTER = (0x1 << 7),
+};
+
+int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
+                           unsigned int filter_mask, unsigned int clk_src);
+
 struct rt5670_priv {
        struct snd_soc_codec *codec;
        struct rt5670_platform_data pdata;