ASoC: rt5651: add alc5651 ASRC switch for HDMIIn
[firefly-linux-kernel-4.4.55.git] / sound / soc / codecs / rt5651.c
index 1d4031818966638c7ad994ffd18bf294cc7c79bd..7de7850879588495e7696944dfc39d5bb9788c7d 100644 (file)
@@ -26,6 +26,7 @@
 #include <sound/initval.h>
 #include <sound/tlv.h>
 
+#include <linux/clk.h>
 #include "rl6231.h"
 #include "rt5651.h"
 
@@ -285,6 +286,39 @@ static bool rt5651_readable_register(struct device *dev, unsigned int reg)
        }
 }
 
+static int rt5651_asrc_get(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = rt5651->asrc_en;
+
+       return 0;
+}
+
+static int rt5651_asrc_put(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+       rt5651->asrc_en = ucontrol->value.integer.value[0];
+       if (rt5651->asrc_en) {
+               snd_soc_write(codec, 0x80, 0x4000);
+               snd_soc_write(codec, 0x81, 0x0302);
+               snd_soc_write(codec, 0x82, 0x0800);
+               snd_soc_write(codec, 0x73, 0x1004);
+               snd_soc_write(codec, 0x83, 0x1000);
+               snd_soc_write(codec, 0x84, 0x7000);
+               snd_soc_update_bits(codec, 0x64, 0x0200, 0x0200);
+       } else {
+               snd_soc_write(codec, 0x83, 0x0);
+               snd_soc_write(codec, 0x84, 0x0);
+       }
+       return 0;
+}
+
 static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
 static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
 static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
@@ -312,6 +346,11 @@ static SOC_ENUM_SINGLE_DECL(rt5651_if2_dac_enum, RT5651_DIG_INF_DATA,
 static SOC_ENUM_SINGLE_DECL(rt5651_if2_adc_enum, RT5651_DIG_INF_DATA,
                                RT5651_IF2_ADC_SEL_SFT, rt5651_data_select);
 
+static const char * const rt5651_asrc_mode[] = {"Disable", "Enable"};
+
+static const SOC_ENUM_SINGLE_DECL(rt5651_asrc_enum, 0, 0,
+               rt5651_asrc_mode);
+
 static const struct snd_kcontrol_new rt5651_snd_controls[] = {
        /* Headphone Output Volume */
        SOC_DOUBLE_TLV("HP Playback Volume", RT5651_HP_VOL,
@@ -352,6 +391,9 @@ static const struct snd_kcontrol_new rt5651_snd_controls[] = {
                        RT5651_ADC_L_BST_SFT, RT5651_ADC_R_BST_SFT,
                        3, 0, adc_bst_tlv),
 
+       /* RT5651 ASRC Switch */
+       SOC_ENUM_EXT("RT5651 ASRC Switch", rt5651_asrc_enum,
+                    rt5651_asrc_get, rt5651_asrc_put),
        /* ASRC */
        SOC_SINGLE("IF1 ASRC Switch", RT5651_PLL_MODE_1,
                RT5651_STO1_T_SFT, 1, 0),
@@ -920,8 +962,8 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
        /* micbias */
        SND_SOC_DAPM_SUPPLY("LDO", RT5651_PWR_ANLG1,
                        RT5651_PWR_LDO_BIT, 0, NULL, 0),
-       SND_SOC_DAPM_MICBIAS("micbias1", RT5651_PWR_ANLG2,
-                       RT5651_PWR_MB1_BIT, 0),
+       SND_SOC_DAPM_SUPPLY("micbias1", RT5651_PWR_ANLG2,
+                       RT5651_PWR_MB1_BIT, 0, NULL, 0),
        /* Input Lines */
        SND_SOC_DAPM_INPUT("MIC1"),
        SND_SOC_DAPM_INPUT("MIC2"),
@@ -1144,6 +1186,9 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
        {"IN1P", NULL, "LDO"},
        {"IN2P", NULL, "LDO"},
        {"IN3P", NULL, "LDO"},
+       {"BST1", NULL, "micbias1"},
+       {"BST2", NULL, "micbias1"},
+       {"BST3", NULL, "micbias1"},
 
        {"IN1P", NULL, "MIC1"},
        {"IN2P", NULL, "MIC2"},
@@ -1569,9 +1614,13 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 static int rt5651_set_bias_level(struct snd_soc_codec *codec,
                        enum snd_soc_bias_level level)
 {
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
        switch (level) {
        case SND_SOC_BIAS_PREPARE:
                if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
+                       if (!IS_ERR(rt5651->mclk))
+                               clk_prepare_enable(rt5651->mclk);
                        snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
                                RT5651_PWR_VREF1 | RT5651_PWR_MB |
                                RT5651_PWR_BG | RT5651_PWR_VREF2,
@@ -1599,6 +1648,10 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec,
                snd_soc_write(codec, RT5651_PWR_MIXER, 0x0000);
                snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000);
                snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000);
+               if (SND_SOC_BIAS_PREPARE ==
+                               snd_soc_codec_get_bias_level(codec))
+                       if (!IS_ERR(rt5651->mclk))
+                               clk_disable_unprepare(rt5651->mclk);
                break;
 
        default:
@@ -1613,6 +1666,9 @@ static int rt5651_probe(struct snd_soc_codec *codec)
        struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
 
        rt5651->codec = codec;
+       rt5651->mclk = devm_clk_get(codec->dev, "mclk");
+       if (PTR_ERR(rt5651->mclk) == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
 
        snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
                RT5651_PWR_VREF1 | RT5651_PWR_MB |
@@ -1803,9 +1859,15 @@ static int rt5651_i2c_remove(struct i2c_client *i2c)
        return 0;
 }
 
+static const struct of_device_id rt5651_of_match[] = {
+       { .compatible = "realtek,rt5651", },
+       { }
+};
+
 static struct i2c_driver rt5651_i2c_driver = {
        .driver = {
                .name = "rt5651",
+               .of_match_table = rt5651_of_match,
        },
        .probe = rt5651_i2c_probe,
        .remove   = rt5651_i2c_remove,