ASoC: rt5640: add ASRC support
authorJack Yu <jack.yu@realtek.com>
Mon, 4 Jan 2016 23:20:26 +0000 (17:20 -0600)
committerMark Brown <broonie@kernel.org>
Tue, 5 Jan 2016 12:32:22 +0000 (12:32 +0000)
Signed-off-by: Jack Yu <jack.yu@realtek.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/rt5640.c
sound/soc/codecs/rt5640.h

index f2beb1aa5763dfe99dc6de821f4c9367446773a7..18f2d3bd3c80b245bcb23fbf997963d1ee698bfd 100644 (file)
@@ -488,6 +488,18 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
                return 0;
 }
 
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+       struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
+       if (!rt5640->asrc_en)
+               return 0;
+
+       return 1;
+}
+
 /* Digital Mixer */
 static const struct snd_kcontrol_new rt5640_sto_adc_l_mix[] = {
        SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
@@ -1059,6 +1071,20 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
 static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
        SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
                        RT5640_PWR_PLL_BIT, 0, NULL, 0),
+
+       /* ASRC */
+       SND_SOC_DAPM_SUPPLY_S("Stereo Filter ASRC", 1, RT5640_ASRC_1,
+                        15, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("I2S2 Filter ASRC", 1, RT5640_ASRC_1,
+                        12, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5640_ASRC_1,
+                        11, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC1 ASRC", 1, RT5640_ASRC_1,
+                        9, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC2 ASRC", 1, RT5640_ASRC_1,
+                        8, 0, NULL, 0),
+
+
        /* Input Side */
        /* micbias */
        SND_SOC_DAPM_SUPPLY("LDO2", RT5640_PWR_ANLG1,
@@ -1319,6 +1345,12 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
+       { "I2S1", NULL, "Stereo Filter ASRC", is_using_asrc },
+       { "I2S2", NULL, "I2S2 ASRC", is_using_asrc },
+       { "I2S2", NULL, "I2S2 Filter ASRC", is_using_asrc },
+       { "DMIC1", NULL, "DMIC1 ASRC", is_using_asrc },
+       { "DMIC2", NULL, "DMIC2 ASRC", is_using_asrc },
+
        {"IN1P", NULL, "LDO2"},
        {"IN2P", NULL, "LDO2"},
        {"IN3P", NULL, "LDO2"},
@@ -1981,6 +2013,76 @@ int rt5640_dmic_enable(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
 
+int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
+               unsigned int filter_mask, unsigned int clk_src)
+{
+       struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+       unsigned int asrc2_mask = 0;
+       unsigned int asrc2_value = 0;
+
+       switch (clk_src) {
+       case RT5640_CLK_SEL_SYS:
+       case RT5640_CLK_SEL_ASRC:
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       if (!filter_mask)
+               return -EINVAL;
+
+       if (filter_mask & RT5640_DA_STEREO_FILTER) {
+               asrc2_mask |= RT5640_STO_DAC_M_MASK;
+               asrc2_value = (asrc2_value & ~RT5640_STO_DAC_M_MASK)
+                       | (clk_src << RT5640_STO_DAC_M_SFT);
+       }
+
+       if (filter_mask & RT5640_DA_MONO_L_FILTER) {
+               asrc2_mask |= RT5640_MDA_L_M_MASK;
+               asrc2_value = (asrc2_value & ~RT5640_MDA_L_M_MASK)
+                       | (clk_src << RT5640_MDA_L_M_SFT);
+       }
+
+       if (filter_mask & RT5640_DA_MONO_R_FILTER) {
+               asrc2_mask |= RT5640_MDA_R_M_MASK;
+               asrc2_value = (asrc2_value & ~RT5640_MDA_R_M_MASK)
+                       | (clk_src << RT5640_MDA_R_M_SFT);
+       }
+
+       if (filter_mask & RT5640_AD_STEREO_FILTER) {
+               asrc2_mask |= RT5640_ADC_M_MASK;
+               asrc2_value = (asrc2_value & ~RT5640_ADC_M_MASK)
+                       | (clk_src << RT5640_ADC_M_SFT);
+       }
+
+       if (filter_mask & RT5640_AD_MONO_L_FILTER) {
+               asrc2_mask |= RT5640_MAD_L_M_MASK;
+               asrc2_value = (asrc2_value & ~RT5640_MAD_L_M_MASK)
+                       | (clk_src << RT5640_MAD_L_M_SFT);
+       }
+
+       if (filter_mask & RT5640_AD_MONO_R_FILTER)  {
+               asrc2_mask |= RT5640_MAD_R_M_MASK;
+               asrc2_value = (asrc2_value & ~RT5640_MAD_R_M_MASK)
+                       | (clk_src << RT5640_MAD_R_M_SFT);
+       }
+
+       snd_soc_update_bits(codec, RT5640_ASRC_2,
+               asrc2_mask, asrc2_value);
+
+       if (snd_soc_read(codec, RT5640_ASRC_2)) {
+               rt5640->asrc_en = true;
+               snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x3);
+       } else {
+               rt5640->asrc_en = false;
+               snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x0);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src);
+
 static int rt5640_probe(struct snd_soc_codec *codec)
 {
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
index 3deb8babeabb691d37a314bb3ab19c84d5c9cd1f..83a7150ddc24eb637de10d1670eb8614ef0063c2 100644 (file)
 #define RT5640_DMIC_2_M_NOR                    (0x0 << 8)
 #define RT5640_DMIC_2_M_ASYN                   (0x1 << 8)
 
+/* ASRC clock source selection (0x84) */
+#define RT5640_CLK_SEL_SYS                     (0x0)
+#define RT5640_CLK_SEL_ASRC                    (0x1)
+
 /* ASRC Control 2 (0x84) */
 #define RT5640_MDA_L_M_MASK                    (0x1 << 15)
 #define RT5640_MDA_L_M_SFT                     15
@@ -2079,6 +2083,16 @@ enum {
        RT5640_DMIC2,
 };
 
+/* filter mask */
+enum {
+       RT5640_DA_STEREO_FILTER = 0x1,
+       RT5640_DA_MONO_L_FILTER = (0x1 << 1),
+       RT5640_DA_MONO_R_FILTER = (0x1 << 2),
+       RT5640_AD_STEREO_FILTER = (0x1 << 3),
+       RT5640_AD_MONO_L_FILTER = (0x1 << 4),
+       RT5640_AD_MONO_R_FILTER = (0x1 << 5),
+};
+
 struct rt5640_priv {
        struct snd_soc_codec *codec;
        struct rt5640_platform_data pdata;
@@ -2095,9 +2109,12 @@ struct rt5640_priv {
        int pll_out;
 
        bool hp_mute;
+       bool asrc_en;
 };
 
 int rt5640_dmic_enable(struct snd_soc_codec *codec,
                       bool dmic1_data_pin, bool dmic2_data_pin);
+int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
+               unsigned int filter_mask, unsigned int clk_src);
 
 #endif