Merge branch 'asoc-5.2' into asoc-next
[sfrench/cifs-2.6.git] / sound / soc / codecs / da7219.c
index 121a8190f93edce3395ea22b5e85218112b06a1e..cf2948c00262abfbb4aeaca0d67ebabfc0988041 100644 (file)
@@ -797,6 +797,7 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
 {
        struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
        struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+       struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
        u8 pll_ctrl, pll_status;
        int i = 0, ret;
        bool srm_lock = false;
@@ -805,11 +806,11 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
        case SND_SOC_DAPM_PRE_PMU:
                if (da7219->master) {
                        /* Enable DAI clks for master mode */
-                       if (da7219->dai_clks) {
-                               ret = clk_prepare_enable(da7219->dai_clks);
+                       if (bclk) {
+                               ret = clk_prepare_enable(bclk);
                                if (ret) {
                                        dev_err(component->dev,
-                                               "Failed to enable dai_clks\n");
+                                               "Failed to enable DAI clks\n");
                                        return ret;
                                }
                        } else {
@@ -852,8 +853,8 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
 
                /* Disable DAI clks if in master mode */
                if (da7219->master) {
-                       if (da7219->dai_clks)
-                               clk_disable_unprepare(da7219->dai_clks);
+                       if (bclk)
+                               clk_disable_unprepare(bclk);
                        else
                                snd_soc_component_update_bits(component,
                                                              DA7219_DAI_CLK_MODE,
@@ -1385,17 +1386,50 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
        return 0;
 }
 
+static int da7219_set_bclks_per_wclk(struct snd_soc_component *component,
+                                    unsigned long factor)
+{
+       u8 bclks_per_wclk;
+
+       switch (factor) {
+       case 32:
+               bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+               break;
+       case 64:
+               bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+               break;
+       case 128:
+               bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
+               break;
+       case 256:
+               bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+                                     DA7219_DAI_BCLKS_PER_WCLK_MASK,
+                                     bclks_per_wclk);
+
+       return 0;
+}
+
 static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
                                   unsigned int tx_mask, unsigned int rx_mask,
                                   int slots, int slot_width)
 {
        struct snd_soc_component *component = dai->component;
        struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+       struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
+       struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
        unsigned int ch_mask;
-       u8 dai_bclks_per_wclk, slot_offset;
+       unsigned long sr, bclk_rate;
+       u8 slot_offset;
        u16 offset;
        __le16 dai_offset;
        u32 frame_size;
+       int ret;
 
        /* No channels enabled so disable TDM */
        if (!tx_mask) {
@@ -1432,28 +1466,26 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
         */
        if (da7219->master) {
                frame_size = slots * slot_width;
-               switch (frame_size) {
-               case 32:
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
-                       break;
-               case 64:
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
-                       break;
-               case 128:
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
-                       break;
-               case 256:
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
-                       break;
-               default:
-                       dev_err(component->dev, "Invalid frame size %d\n",
-                               frame_size);
-                       return -EINVAL;
-               }
 
-               snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
-                               DA7219_DAI_BCLKS_PER_WCLK_MASK,
-                               dai_bclks_per_wclk);
+               if (bclk) {
+                       sr = clk_get_rate(wclk);
+                       bclk_rate = sr * frame_size;
+                       ret = clk_set_rate(bclk, bclk_rate);
+                       if (ret) {
+                               dev_err(component->dev,
+                                       "Failed to set TDM BCLK rate %lu: %d\n",
+                                       bclk_rate, ret);
+                               return ret;
+                       }
+               } else {
+                       ret = da7219_set_bclks_per_wclk(component, frame_size);
+                       if (ret) {
+                               dev_err(component->dev,
+                                       "Failed to set TDM BCLKs per WCLK %d: %d\n",
+                                       frame_size, ret);
+                               return ret;
+                       }
+               }
        }
 
        dai_offset = cpu_to_le16(offset);
@@ -1471,44 +1503,12 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
        return 0;
 }
 
-static int da7219_hw_params(struct snd_pcm_substream *substream,
-                           struct snd_pcm_hw_params *params,
-                           struct snd_soc_dai *dai)
+static int da7219_set_sr(struct snd_soc_component *component,
+                        unsigned long rate)
 {
-       struct snd_soc_component *component = dai->component;
-       struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
-       u8 dai_ctrl = 0, dai_bclks_per_wclk = 0, fs;
-       unsigned int channels;
-       int word_len = params_width(params);
-       int frame_size;
-
-       switch (word_len) {
-       case 16:
-               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
-               break;
-       case 20:
-               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
-               break;
-       case 24:
-               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
-               break;
-       case 32:
-               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       channels = params_channels(params);
-       if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
-               dev_err(component->dev,
-                       "Invalid number of channels, only 1 to %d supported\n",
-                       DA7219_DAI_CH_NUM_MAX);
-               return -EINVAL;
-       }
-       dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+       u8 fs;
 
-       switch (params_rate(params)) {
+       switch (rate) {
        case 8000:
                fs = DA7219_SR_8000;
                break;
@@ -1546,28 +1546,118 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
+       snd_soc_component_write(component, DA7219_SR, fs);
+
+       return 0;
+}
+
+static int da7219_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_component *component = dai->component;
+       struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+       struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
+       struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
+       u8 dai_ctrl = 0;
+       unsigned int channels;
+       unsigned long sr, bclk_rate;
+       int word_len = params_width(params);
+       int frame_size, ret;
+
+       switch (word_len) {
+       case 16:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
+               break;
+       case 20:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
+               break;
+       case 24:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
+               break;
+       case 32:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       channels = params_channels(params);
+       if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
+               dev_err(component->dev,
+                       "Invalid number of channels, only 1 to %d supported\n",
+                       DA7219_DAI_CH_NUM_MAX);
+               return -EINVAL;
+       }
+       dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+
+       sr = params_rate(params);
+       if (da7219->master && wclk) {
+               ret = clk_set_rate(wclk, sr);
+               if (ret) {
+                       dev_err(component->dev,
+                               "Failed to set WCLK SR %lu: %d\n", sr, ret);
+                       return ret;
+               }
+       } else {
+               ret = da7219_set_sr(component, sr);
+               if (ret) {
+                       dev_err(component->dev,
+                               "Failed to set SR %lu: %d\n", sr, ret);
+                       return ret;
+               }
+       }
+
        /*
         * If we're master, then we have a limited set of BCLK rates we
         * support. For slave mode this isn't the case and the codec can detect
         * the BCLK rate automatically.
         */
        if (da7219->master && !da7219->tdm_en) {
-               frame_size = word_len * 2;
-               if (frame_size <= 32)
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+               if ((word_len * DA7219_DAI_CH_NUM_MAX) <= 32)
+                       frame_size = 32;
                else
-                       dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+                       frame_size = 64;
+
+               if (bclk) {
+                       bclk_rate = frame_size * sr;
+                       /*
+                        * Rounding the rate here avoids failure trying to set a
+                        * new rate on an already enabled bclk. In that
+                        * instance this will just set the same rate as is
+                        * currently in use, and so should continue without
+                        * problem, as long as the BCLK rate is suitable for the
+                        * desired frame size.
+                        */
+                       bclk_rate = clk_round_rate(bclk, bclk_rate);
+                       if ((bclk_rate / sr) < frame_size) {
+                               dev_err(component->dev,
+                                       "BCLK rate mismatch against frame size");
+                               return -EINVAL;
+                       }
 
-               snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
-                                             DA7219_DAI_BCLKS_PER_WCLK_MASK,
-                                             dai_bclks_per_wclk);
+                       ret = clk_set_rate(bclk, bclk_rate);
+                       if (ret) {
+                               dev_err(component->dev,
+                                       "Failed to set BCLK rate %lu: %d\n",
+                                       bclk_rate, ret);
+                               return ret;
+                       }
+               } else {
+                       ret = da7219_set_bclks_per_wclk(component, frame_size);
+                       if (ret) {
+                               dev_err(component->dev,
+                                       "Failed to set BCLKs per WCLK %d: %d\n",
+                                       frame_size, ret);
+                               return ret;
+                       }
+               }
        }
 
        snd_soc_component_update_bits(component, DA7219_DAI_CTRL,
                            DA7219_DAI_WORD_LENGTH_MASK |
                            DA7219_DAI_CH_NUM_MASK,
                            dai_ctrl);
-       snd_soc_component_write(component, DA7219_SR, fs);
 
        return 0;
 }
@@ -1583,20 +1673,26 @@ static const struct snd_soc_dai_ops da7219_dai_ops = {
 #define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
+#define DA7219_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+                     SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+                     SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+                     SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+                     SNDRV_PCM_RATE_96000)
+
 static struct snd_soc_dai_driver da7219_dai = {
        .name = "da7219-hifi",
        .playback = {
                .stream_name = "Playback",
                .channels_min = 1,
                .channels_max = DA7219_DAI_CH_NUM_MAX,
-               .rates = SNDRV_PCM_RATE_8000_96000,
+               .rates = DA7219_RATES,
                .formats = DA7219_FORMATS,
        },
        .capture = {
                .stream_name = "Capture",
                .channels_min = 1,
                .channels_max = DA7219_DAI_CH_NUM_MAX,
-               .rates = SNDRV_PCM_RATE_8000_96000,
+               .rates = DA7219_RATES,
                .formats = DA7219_FORMATS,
        },
        .ops = &da7219_dai_ops,
@@ -1672,11 +1768,14 @@ static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_component *compone
 
        pdata->wakeup_source = device_property_read_bool(dev, "wakeup-source");
 
-       pdata->dai_clks_name = "da7219-dai-clks";
-       if (device_property_read_string(dev, "clock-output-names",
-                                       &pdata->dai_clks_name))
-               dev_warn(dev, "Using default clk name: %s\n",
-                        pdata->dai_clks_name);
+       pdata->dai_clk_names[DA7219_DAI_WCLK_IDX] = "da7219-dai-wclk";
+       pdata->dai_clk_names[DA7219_DAI_BCLK_IDX] = "da7219-dai-bclk";
+       if (device_property_read_string_array(dev, "clock-output-names",
+                                             pdata->dai_clk_names,
+                                             DA7219_DAI_NUM_CLKS) < 0)
+               dev_warn(dev, "Using default DAI clk names: %s, %s\n",
+                        pdata->dai_clk_names[DA7219_DAI_WCLK_IDX],
+                        pdata->dai_clk_names[DA7219_DAI_BCLK_IDX]);
 
        if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
                pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
@@ -1793,12 +1892,16 @@ static int da7219_handle_supplies(struct snd_soc_component *component)
 }
 
 #ifdef CONFIG_COMMON_CLK
-static int da7219_dai_clks_prepare(struct clk_hw *hw)
+static int da7219_wclk_prepare(struct clk_hw *hw)
 {
        struct da7219_priv *da7219 =
-               container_of(hw, struct da7219_priv, dai_clks_hw);
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
        struct snd_soc_component *component = da7219->component;
 
+       if (!da7219->master)
+               return -EINVAL;
+
        snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
                                      DA7219_DAI_CLK_EN_MASK,
                                      DA7219_DAI_CLK_EN_MASK);
@@ -1806,33 +1909,42 @@ static int da7219_dai_clks_prepare(struct clk_hw *hw)
        return 0;
 }
 
-static void da7219_dai_clks_unprepare(struct clk_hw *hw)
+static void da7219_wclk_unprepare(struct clk_hw *hw)
 {
        struct da7219_priv *da7219 =
-               container_of(hw, struct da7219_priv, dai_clks_hw);
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
        struct snd_soc_component *component = da7219->component;
 
+       if (!da7219->master)
+               return;
+
        snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
                                      DA7219_DAI_CLK_EN_MASK, 0);
 }
 
-static int da7219_dai_clks_is_prepared(struct clk_hw *hw)
+static int da7219_wclk_is_prepared(struct clk_hw *hw)
 {
        struct da7219_priv *da7219 =
-               container_of(hw, struct da7219_priv, dai_clks_hw);
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
        struct snd_soc_component *component = da7219->component;
        u8 clk_reg;
 
+       if (!da7219->master)
+               return -EINVAL;
+
        clk_reg = snd_soc_component_read32(component, DA7219_DAI_CLK_MODE);
 
        return !!(clk_reg & DA7219_DAI_CLK_EN_MASK);
 }
 
-static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
-                                                unsigned long parent_rate)
+static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw,
+                                            unsigned long parent_rate)
 {
        struct da7219_priv *da7219 =
-               container_of(hw, struct da7219_priv, dai_clks_hw);
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
        struct snd_soc_component *component = da7219->component;
        u8 fs = snd_soc_component_read32(component, DA7219_SR);
 
@@ -1864,11 +1976,148 @@ static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
        }
 }
 
-static const struct clk_ops da7219_dai_clks_ops = {
-       .prepare = da7219_dai_clks_prepare,
-       .unprepare = da7219_dai_clks_unprepare,
-       .is_prepared = da7219_dai_clks_is_prepared,
-       .recalc_rate = da7219_dai_clks_recalc_rate,
+static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                  unsigned long *parent_rate)
+{
+       struct da7219_priv *da7219 =
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+
+       if (!da7219->master)
+               return -EINVAL;
+
+       if (rate < 11025)
+               return 8000;
+       else if (rate < 12000)
+               return 11025;
+       else if (rate < 16000)
+               return 12000;
+       else if (rate < 22050)
+               return 16000;
+       else if (rate < 24000)
+               return 22050;
+       else if (rate < 32000)
+               return 24000;
+       else if (rate < 44100)
+               return 32000;
+       else if (rate < 48000)
+               return 44100;
+       else if (rate < 88200)
+               return 48000;
+       else if (rate < 96000)
+               return 88200;
+       else
+               return 96000;
+}
+
+static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate)
+{
+       struct da7219_priv *da7219 =
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+       struct snd_soc_component *component = da7219->component;
+
+       if (!da7219->master)
+               return -EINVAL;
+
+       return da7219_set_sr(component, rate);
+}
+
+static unsigned long da7219_bclk_recalc_rate(struct clk_hw *hw,
+                                            unsigned long parent_rate)
+{
+       struct da7219_priv *da7219 =
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+       struct snd_soc_component *component = da7219->component;
+       u8 bclks_per_wclk = snd_soc_component_read32(component,
+                                                    DA7219_DAI_CLK_MODE);
+
+       switch (bclks_per_wclk & DA7219_DAI_BCLKS_PER_WCLK_MASK) {
+       case DA7219_DAI_BCLKS_PER_WCLK_32:
+               return parent_rate * 32;
+       case DA7219_DAI_BCLKS_PER_WCLK_64:
+               return parent_rate * 64;
+       case DA7219_DAI_BCLKS_PER_WCLK_128:
+               return parent_rate * 128;
+       case DA7219_DAI_BCLKS_PER_WCLK_256:
+               return parent_rate * 256;
+       default:
+               return 0;
+       }
+}
+
+static unsigned long da7219_bclk_get_factor(unsigned long rate,
+                                           unsigned long parent_rate)
+{
+       unsigned long factor;
+
+       factor = rate / parent_rate;
+       if (factor < 64)
+               return 32;
+       else if (factor < 128)
+               return 64;
+       else if (factor < 256)
+               return 128;
+       else
+               return 256;
+}
+
+static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                  unsigned long *parent_rate)
+{
+       struct da7219_priv *da7219 =
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+       unsigned long factor;
+
+       if (!*parent_rate || !da7219->master)
+               return -EINVAL;
+
+       /*
+        * We don't allow changing the parent rate as some BCLK rates can be
+        * derived from multiple parent WCLK rates (BCLK rates are set as a
+        * multiplier of WCLK in HW). We just do some rounding down based on the
+        * parent WCLK rate set and find the appropriate multiplier of BCLK to
+        * get the rounded down BCLK value.
+        */
+       factor = da7219_bclk_get_factor(rate, *parent_rate);
+
+       return *parent_rate * factor;
+}
+
+static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate)
+{
+       struct da7219_priv *da7219 =
+               container_of(hw, struct da7219_priv,
+                            dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+       struct snd_soc_component *component = da7219->component;
+       unsigned long factor;
+
+       if (!da7219->master)
+               return -EINVAL;
+
+       factor = da7219_bclk_get_factor(rate, parent_rate);
+
+       return da7219_set_bclks_per_wclk(component, factor);
+}
+
+static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = {
+       [DA7219_DAI_WCLK_IDX] = {
+               .prepare = da7219_wclk_prepare,
+               .unprepare = da7219_wclk_unprepare,
+               .is_prepared = da7219_wclk_is_prepared,
+               .recalc_rate = da7219_wclk_recalc_rate,
+               .round_rate = da7219_wclk_round_rate,
+               .set_rate = da7219_wclk_set_rate,
+       },
+       [DA7219_DAI_BCLK_IDX] = {
+               .recalc_rate = da7219_bclk_recalc_rate,
+               .round_rate = da7219_bclk_round_rate,
+               .set_rate = da7219_bclk_set_rate,
+       },
 };
 
 static int da7219_register_dai_clks(struct snd_soc_component *component)
@@ -1876,47 +2125,81 @@ static int da7219_register_dai_clks(struct snd_soc_component *component)
        struct device *dev = component->dev;
        struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
        struct da7219_pdata *pdata = da7219->pdata;
-       struct clk_init_data init = {};
-       struct clk *dai_clks;
-       struct clk_lookup *dai_clks_lookup;
        const char *parent_name;
+       int i, ret;
 
-       if (da7219->mclk) {
-               parent_name = __clk_get_name(da7219->mclk);
-               init.parent_names = &parent_name;
-               init.num_parents = 1;
-       } else {
-               init.parent_names = NULL;
-               init.num_parents = 0;
-       }
+       for (i = 0; i < DA7219_DAI_NUM_CLKS; ++i) {
+               struct clk_init_data init = {};
+               struct clk *dai_clk;
+               struct clk_lookup *dai_clk_lookup;
+               struct clk_hw *dai_clk_hw = &da7219->dai_clks_hw[i];
 
-       init.name = pdata->dai_clks_name;
-       init.ops = &da7219_dai_clks_ops;
-       init.flags = CLK_GET_RATE_NOCACHE;
-       da7219->dai_clks_hw.init = &init;
+               switch (i) {
+               case DA7219_DAI_WCLK_IDX:
+                       /*
+                        * If we can, make MCLK the parent of WCLK to ensure
+                        * it's enabled as required.
+                        */
+                       if (da7219->mclk) {
+                               parent_name = __clk_get_name(da7219->mclk);
+                               init.parent_names = &parent_name;
+                               init.num_parents = 1;
+                       } else {
+                               init.parent_names = NULL;
+                               init.num_parents = 0;
+                       }
+                       break;
+               case DA7219_DAI_BCLK_IDX:
+                       /* Make WCLK the parent of BCLK */
+                       parent_name = __clk_get_name(da7219->dai_clks[DA7219_DAI_WCLK_IDX]);
+                       init.parent_names = &parent_name;
+                       init.num_parents = 1;
+                       break;
+               default:
+                       dev_err(dev, "Invalid clock index\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
 
-       dai_clks = devm_clk_register(dev, &da7219->dai_clks_hw);
-       if (IS_ERR(dai_clks)) {
-               dev_warn(dev, "Failed to register DAI clocks: %ld\n",
-                        PTR_ERR(dai_clks));
-               return PTR_ERR(dai_clks);
-       }
-       da7219->dai_clks = dai_clks;
+               init.name = pdata->dai_clk_names[i];
+               init.ops = &da7219_dai_clk_ops[i];
+               init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+               dai_clk_hw->init = &init;
+
+               dai_clk = devm_clk_register(dev, dai_clk_hw);
+               if (IS_ERR(dai_clk)) {
+                       dev_warn(dev, "Failed to register %s: %ld\n",
+                                init.name, PTR_ERR(dai_clk));
+                       ret = PTR_ERR(dai_clk);
+                       goto err;
+               }
+               da7219->dai_clks[i] = dai_clk;
 
-       /* If we're using DT, then register as provider accordingly */
-       if (dev->of_node) {
-               devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
-                                           &da7219->dai_clks_hw);
-       } else {
-               dai_clks_lookup = clkdev_create(dai_clks, pdata->dai_clks_name,
-                                               "%s", dev_name(dev));
-               if (!dai_clks_lookup)
-                       return -ENOMEM;
-               else
-                       da7219->dai_clks_lookup = dai_clks_lookup;
+               /* If we're using DT, then register as provider accordingly */
+               if (dev->of_node) {
+                       devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+                                                   dai_clk_hw);
+               } else {
+                       dai_clk_lookup = clkdev_create(dai_clk, init.name,
+                                                      "%s", dev_name(dev));
+                       if (!dai_clk_lookup) {
+                               ret = -ENOMEM;
+                               goto err;
+                       } else {
+                               da7219->dai_clks_lookup[i] = dai_clk_lookup;
+                       }
+               }
        }
 
        return 0;
+
+err:
+       do {
+               if (da7219->dai_clks_lookup[i])
+                       clkdev_drop(da7219->dai_clks_lookup[i]);
+       } while (i-- > 0);
+
+       return ret;
 }
 #else
 static inline int da7219_register_dai_clks(struct snd_soc_component *component)
@@ -2080,12 +2363,15 @@ err_disable_reg:
 static void da7219_remove(struct snd_soc_component *component)
 {
        struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+       int i;
 
        da7219_aad_exit(component);
 
 #ifdef CONFIG_COMMON_CLK
-       if (da7219->dai_clks_lookup)
-               clkdev_drop(da7219->dai_clks_lookup);
+       for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) {
+               if (da7219->dai_clks_lookup[i])
+                       clkdev_drop(da7219->dai_clks_lookup[i]);
+       }
 #endif
 
        /* Supplies */