Merge branch 'asoc-5.4' into asoc-5.5
[sfrench/cifs-2.6.git] / sound / soc / codecs / wm8994.c
index d5fb7f5dd551cb834ad77ae9b99ca453d7264008..15ce64a48a87438672678f128be57f05f91eaa6d 100644 (file)
@@ -167,12 +167,12 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif)
 
        switch (wm8994->sysclk[aif]) {
        case WM8994_SYSCLK_MCLK1:
-               rate = wm8994->mclk[0];
+               rate = wm8994->mclk_rate[0];
                break;
 
        case WM8994_SYSCLK_MCLK2:
                reg1 |= 0x8;
-               rate = wm8994->mclk[1];
+               rate = wm8994->mclk_rate[1];
                break;
 
        case WM8994_SYSCLK_FLL1:
@@ -1038,6 +1038,45 @@ static bool wm8994_check_class_w_digital(struct snd_soc_component *component)
        return true;
 }
 
+static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enable)
+{
+       struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
+       unsigned int offset, val, clk_idx;
+       int ret;
+
+       if (aif)
+               offset = 4;
+       else
+               offset = 0;
+
+       val = snd_soc_component_read32(component, WM8994_AIF1_CLOCKING_1 + offset);
+       val &= WM8994_AIF1CLK_SRC_MASK;
+
+       switch (val) {
+       case 0:
+               clk_idx = WM8994_MCLK1;
+               break;
+       case 1:
+               clk_idx = WM8994_MCLK2;
+               break;
+       default:
+               return 0;
+       }
+
+       if (enable) {
+               ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk);
+               if (ret < 0) {
+                       dev_err(component->dev, "Failed to enable MCLK%d\n",
+                               clk_idx);
+                       return ret;
+               }
+       } else {
+               clk_disable_unprepare(wm8994->mclk[clk_idx].clk);
+       }
+
+       return 0;
+}
+
 static int aif1clk_ev(struct snd_soc_dapm_widget *w,
                      struct snd_kcontrol *kcontrol, int event)
 {
@@ -1045,7 +1084,7 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
        struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
        struct wm8994 *control = wm8994->wm8994;
        int mask = WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA;
-       int i;
+       int ret, i;
        int dac;
        int adc;
        int val;
@@ -1061,6 +1100,10 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
+               ret = aif_mclk_set(component, 0, true);
+               if (ret < 0)
+                       return ret;
+
                /* Don't enable timeslot 2 if not in use */
                if (wm8994->channels[0] <= 2)
                        mask &= ~(WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
@@ -1133,6 +1176,12 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
                break;
        }
 
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMD:
+               aif_mclk_set(component, 0, false);
+               break;
+       }
+
        return 0;
 }
 
@@ -1140,13 +1189,17 @@ static int aif2clk_ev(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 i;
+       int ret, i;
        int dac;
        int adc;
        int val;
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
+               ret = aif_mclk_set(component, 1, true);
+               if (ret < 0)
+                       return ret;
+
                val = snd_soc_component_read32(component, WM8994_AIF2_CONTROL_1);
                if ((val & WM8994_AIF2ADCL_SRC) &&
                    (val & WM8994_AIF2ADCR_SRC))
@@ -1218,6 +1271,12 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
                break;
        }
 
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMD:
+               aif_mclk_set(component, 1, false);
+               break;
+       }
+
        return 0;
 }
 
@@ -1623,10 +1682,10 @@ SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)
 static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = {
 SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, aif1clk_ev,
                    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                   SND_SOC_DAPM_PRE_PMD),
+                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
 SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, aif2clk_ev,
                    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                   SND_SOC_DAPM_PRE_PMD),
+                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
 SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
 SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
                   left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
@@ -2141,6 +2200,7 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
        u16 reg, clk1, aif_reg, aif_src;
        unsigned long timeout;
        bool was_enabled;
+       struct clk *mclk;
 
        switch (id) {
        case WM8994_FLL1:
@@ -2216,6 +2276,27 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
        snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset,
                            WM8994_FLL1_ENA, 0);
 
+       /* Disable MCLK if needed before we possibly change to new clock parent */
+       if (was_enabled) {
+               reg = snd_soc_component_read32(component, WM8994_FLL1_CONTROL_5
+                                                       + reg_offset);
+               reg = ((reg & WM8994_FLL1_REFCLK_SRC_MASK)
+                       >> WM8994_FLL1_REFCLK_SRC_SHIFT) + 1;
+
+               switch (reg) {
+               case WM8994_FLL_SRC_MCLK1:
+                       mclk = wm8994->mclk[WM8994_MCLK1].clk;
+                       break;
+               case WM8994_FLL_SRC_MCLK2:
+                       mclk = wm8994->mclk[WM8994_MCLK2].clk;
+                       break;
+               default:
+                       mclk = NULL;
+               }
+
+               clk_disable_unprepare(mclk);
+       }
+
        if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK &&
            freq_in == freq_out && freq_out) {
                dev_dbg(component->dev, "Bypassing FLL%d\n", id + 1);
@@ -2260,10 +2341,29 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
        /* Clear any pending completion from a previous failure */
        try_wait_for_completion(&wm8994->fll_locked[id]);
 
+       switch (src) {
+       case WM8994_FLL_SRC_MCLK1:
+               mclk = wm8994->mclk[WM8994_MCLK1].clk;
+               break;
+       case WM8994_FLL_SRC_MCLK2:
+               mclk = wm8994->mclk[WM8994_MCLK2].clk;
+               break;
+       default:
+               mclk = NULL;
+       }
+
        /* Enable (with fractional mode if required) */
        if (freq_out) {
+               ret = clk_prepare_enable(mclk);
+               if (ret < 0) {
+                       dev_err(component->dev, "Failed to enable MCLK for FLL%d\n",
+                               id + 1);
+                       return ret;
+               }
+
                /* Enable VMID if we need it */
                if (!was_enabled) {
+
                        active_reference(component);
 
                        switch (control->type) {
@@ -2372,12 +2472,29 @@ static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src,
        return _wm8994_set_fll(dai->component, id, src, freq_in, freq_out);
 }
 
+static int wm8994_set_mclk_rate(struct wm8994_priv *wm8994, unsigned int id,
+                               unsigned int *freq)
+{
+       int ret;
+
+       if (!wm8994->mclk[id].clk || *freq == wm8994->mclk_rate[id])
+               return 0;
+
+       ret = clk_set_rate(wm8994->mclk[id].clk, *freq);
+       if (ret < 0)
+               return ret;
+
+       *freq = clk_get_rate(wm8994->mclk[id].clk);
+
+       return 0;
+}
+
 static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
                int clk_id, unsigned int freq, int dir)
 {
        struct snd_soc_component *component = dai->component;
        struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
-       int i;
+       int ret, i;
 
        switch (dai->id) {
        case 1:
@@ -2392,7 +2509,12 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
        switch (clk_id) {
        case WM8994_SYSCLK_MCLK1:
                wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1;
-               wm8994->mclk[0] = freq;
+
+               ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq);
+               if (ret < 0)
+                       return ret;
+
+               wm8994->mclk_rate[0] = freq;
                dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n",
                        dai->id, freq);
                break;
@@ -2400,7 +2522,12 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
        case WM8994_SYSCLK_MCLK2:
                /* TODO: Set GPIO AF */
                wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2;
-               wm8994->mclk[1] = freq;
+
+               ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq);
+               if (ret < 0)
+                       return ret;
+
+               wm8994->mclk_rate[1] = freq;
                dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n",
                        dai->id, freq);
                break;
@@ -4456,6 +4583,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8994 = {
 static int wm8994_probe(struct platform_device *pdev)
 {
        struct wm8994_priv *wm8994;
+       int ret;
 
        wm8994 = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_priv),
                              GFP_KERNEL);
@@ -4467,6 +4595,16 @@ static int wm8994_probe(struct platform_device *pdev)
 
        wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
 
+       wm8994->mclk[WM8994_MCLK1].id = "MCLK1";
+       wm8994->mclk[WM8994_MCLK2].id = "MCLK2";
+
+       ret = devm_clk_bulk_get_optional(pdev->dev.parent, ARRAY_SIZE(wm8994->mclk),
+                                        wm8994->mclk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to get clocks: %d\n", ret);
+               return ret;
+       }
+
        pm_runtime_enable(&pdev->dev);
        pm_runtime_idle(&pdev->dev);