diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index e1503c2..3bf9680 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
@@ -18,6 +19,8 @@
 #include <linux/acpi.h>
 #include <linux/gpio.h>
 #include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -3527,6 +3530,9 @@ static int rt5659_set_component_pll(struct snd_soc_component *component, int pll
 	rt5659->pll_out = freq_out;
 	rt5659->pll_src = source;
 
+	dev_dbg(component->dev, "pll_in = %u Hz, pll_out = %u Hz, pll_src = %d\n",
+		freq_in, freq_out, source);
+
 	return 0;
 }
 
@@ -3843,6 +3849,237 @@ static int rt5659_parse_dt(struct rt5659_priv *rt5659, struct device *dev)
 	return 0;
 }
 
+static unsigned long rt5659_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct rt5659_priv *rt5659 =
+		container_of(hw, struct rt5659_priv, clk_pll_out);
+
+	return rt5659->pll_out;
+}
+
+static long rt5659_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			   unsigned long *parent_rate)
+{
+	return rate;
+}
+
+static int rt5659_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			   unsigned long parent_rate)
+{
+	struct rt5659_priv *rt5659 =
+		container_of(hw, struct rt5659_priv, clk_pll_out);
+
+	rt5659->pll_out = rate;
+
+	return 0;
+}
+
+static const struct clk_ops rt5659_pll_out_ops = {
+	.recalc_rate = &rt5659_pll_recalc_rate,
+	.round_rate = &rt5659_pll_round_rate,
+	.set_rate = &rt5659_pll_set_rate,
+};
+
+static int rt5659_setup_clk(struct snd_soc_dai *dai,
+			    struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_component *component = dai->component;
+	struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
+	int ret, sysclk_src;
+
+	/*
+	 * Update the clock rate if Codec is driving it. The consumers
+	 * can use clk_get_rate() function to get the rate.
+	 */
+	if (rt5659->master[dai->id] && rt5659->clk_bclk[dai->id]) {
+		unsigned int bclk_rate = params_rate(params) *
+					 params_width(params) *
+					 params_channels(params);
+
+		clk_set_rate(rt5659->clk_bclk[dai->id], bclk_rate);
+	}
+
+	if (rt5659->clk_sysclk_src) {
+		sysclk_src = clk_hw_get_parent_index(rt5659->clk_sysclk_src);
+
+		ret = rt5659_set_component_sysclk(component, sysclk_src, 0,
+						  rt5659->sysclk, 0);
+		if (ret)
+			return ret;
+	}
+
+	if (rt5659->clk_pll_src && (sysclk_src == RT5659_SCLK_S_PLL1)) {
+		unsigned int pll_src =
+			clk_hw_get_parent_index(rt5659->clk_pll_src);
+		unsigned int freq_in = clk_get_rate(rt5659->clk_pll_src->clk);
+
+		ret = rt5659_set_component_pll(component, 0, pll_src,
+					       freq_in, rt5659->sysclk);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int rt5659_register_clks(struct device *dev, struct rt5659_priv *rt5659)
+{
+	const struct clk_hw *sysclk_clk_hw[RT5659_NUM_SCLK_SRC_CLKS] = { NULL };
+	const char *pnames_sysclk[RT5659_NUM_SCLK_SRC_CLKS] = { NULL };
+	const char *pnames_pll[RT5659_NUM_PLL1_SRC_CLKS] = { NULL };
+	struct clk_init_data init = { };
+	static void __iomem *clk_base;
+	const char *clk_name;
+	int ret, i, count_pll_src = 0, count_sysclk_src = 0;
+
+	/* Check if MCLK provided */
+	rt5659->mclk = devm_clk_get(dev, "mclk");
+	if (IS_ERR(rt5659->mclk)) {
+		if (PTR_ERR(rt5659->mclk) != -ENOENT)
+			return PTR_ERR(rt5659->mclk);
+		/* Otherwise mark the mclk pointer to NULL */
+		rt5659->mclk = NULL;
+	}
+
+	if (!of_find_property(dev->of_node, "#clock-cells", NULL))
+		return 0;
+
+	/* Get PLL source */
+	rt5659->pll_ref = devm_clk_get(dev, "pll_ref");
+	if (IS_ERR(rt5659->pll_ref)) {
+		if (PTR_ERR(rt5659->pll_ref) != -ENOENT)
+			return PTR_ERR(rt5659->pll_ref);
+
+		rt5659->pll_ref = NULL;
+	}
+
+	/* Possible parents for PLL */
+	if (rt5659->mclk) {
+		pnames_pll[count_pll_src] = __clk_get_name(rt5659->mclk);
+		count_pll_src++;
+	}
+
+	for (i = 0; i < RT5659_AIFS; i++) {
+		char name[50];
+
+		memset(name, '\0', sizeof(name));
+		snprintf(name, sizeof(name), "%s%d", "bclk", i + 1);
+
+		rt5659->clk_bclk[i] = devm_clk_get(dev, name);
+		if (IS_ERR(rt5659->clk_bclk[i])) {
+			if (PTR_ERR(rt5659->clk_bclk[i]) != -ENOENT)
+				return PTR_ERR(rt5659->clk_bclk[i]);
+
+			rt5659->clk_bclk[i] = NULL;
+			continue;
+		}
+
+		pnames_pll[count_pll_src] = __clk_get_name(rt5659->clk_bclk[i]);
+		count_pll_src++;
+	}
+
+	clk_base = devm_kzalloc(dev, sizeof(char) * 4, GFP_KERNEL);
+
+	/* Register MUX for PLL source */
+	rt5659->clk_pll_src = clk_hw_register_mux(dev, "rt5659_pll_ref",
+						  pnames_pll, count_pll_src,
+						  CLK_SET_RATE_PARENT,
+						  clk_base, 0, 1, 0, NULL);
+
+	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get,
+				     rt5659->clk_pll_src);
+	if (ret) {
+		dev_err(dev, "failed to register clk hw\n");
+		return ret;
+	}
+
+	if (rt5659->pll_ref) {
+		ret = clk_set_parent(rt5659->clk_pll_src->clk, rt5659->pll_ref);
+		if (ret) {
+			dev_err(dev, "failaed to set parent for clk %s\n",
+				__clk_get_name(rt5659->clk_pll_src->clk));
+			return ret;
+		}
+	}
+
+	/* Register PLL out clock */
+	if (of_property_read_string(dev->of_node, "clock-output-names",
+	    (const char **) &clk_name))
+		clk_name = "rt5659_pll_out";
+
+	init.name = clk_name;
+	init.ops = &rt5659_pll_out_ops;
+	init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+	init.parent_hws = (const struct clk_hw **) &rt5659->clk_pll_src;
+	init.num_parents = 1;
+
+	rt5659->clk_pll_out.init = &init;
+
+	ret = devm_clk_hw_register(dev, &rt5659->clk_pll_out);
+	if (ret) {
+		dev_err(dev, "failed to register PLL clock HW\n");
+		return ret;
+	}
+
+	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+					  &rt5659->clk_pll_out);
+	if (ret) {
+		dev_err(dev, "failed to add PLL clock provider\n");
+		return ret;
+	}
+
+	/* Get sysclk source */
+	rt5659->sysclk_ref = devm_clk_get(dev, "sysclk");
+	if (IS_ERR(rt5659->sysclk_ref)) {
+		if (PTR_ERR(rt5659->sysclk_ref) != -ENOENT)
+			return PTR_ERR(rt5659->sysclk_ref);
+
+		rt5659->sysclk_ref = NULL;
+	}
+
+	/* Possible parents for Sysclk */
+	if (rt5659->mclk) {
+		/* For sysclk */
+		pnames_sysclk[count_sysclk_src] = __clk_get_name(rt5659->mclk);
+		sysclk_clk_hw[count_sysclk_src] = __clk_get_hw(rt5659->mclk);
+		count_sysclk_src++;
+	}
+
+	if (rt5659->clk_pll_out.clk) {
+		pnames_sysclk[count_sysclk_src] = __clk_get_name(rt5659->clk_pll_out.clk);
+		sysclk_clk_hw[count_sysclk_src] = __clk_get_hw(rt5659->clk_pll_out.clk);
+		count_sysclk_src++;
+	}
+
+	/* Register MUX for sysclk source */
+	rt5659->clk_sysclk_src = __clk_hw_register_mux(dev, dev->of_node,
+						       "rt5659_sysclk",
+						       count_sysclk_src,
+						       pnames_sysclk,
+						       sysclk_clk_hw, NULL,
+						       CLK_SET_RATE_PARENT,
+						       clk_base, 0, 1, 0,
+						       NULL, NULL);
+
+	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get,
+				     rt5659->clk_sysclk_src);
+	if (ret) {
+		dev_err(dev, "failed to register clk hw\n");
+		return ret;
+	}
+
+	if (rt5659->sysclk_ref) {
+		ret = clk_set_parent(rt5659->clk_sysclk_src->clk, rt5659->sysclk_ref);
+		if (ret) {
+			dev_err(dev, "failed to set parent for clk %s\n",
+				__clk_get_name(rt5659->clk_sysclk_src->clk));
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 static void rt5659_calibrate(struct rt5659_priv *rt5659)
 {
 	int value, count;
@@ -4142,14 +4379,9 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
 
 	regmap_write(rt5659->regmap, RT5659_RESET, 0);
 
-	/* Check if MCLK provided */
-	rt5659->mclk = devm_clk_get(&i2c->dev, "mclk");
-	if (IS_ERR(rt5659->mclk)) {
-		if (PTR_ERR(rt5659->mclk) != -ENOENT)
-			return PTR_ERR(rt5659->mclk);
-		/* Otherwise mark the mclk pointer to NULL */
-		rt5659->mclk = NULL;
-	}
+	ret = rt5659_register_clks(&i2c->dev, rt5659);
+	if (ret)
+		return ret;
 
 	rt5659_calibrate(rt5659);
 
diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h
index b49fd8b..d46d39f 100644
--- a/sound/soc/codecs/rt5659.h
+++ b/sound/soc/codecs/rt5659.h
@@ -1763,6 +1763,7 @@ enum {
 	RT5659_SCLK_S_MCLK,
 	RT5659_SCLK_S_PLL1,
 	RT5659_SCLK_S_RCCLK,
+	RT5659_NUM_SCLK_SRC_CLKS,
 };
 
 /* PLL1 Source */
@@ -1772,6 +1773,7 @@ enum {
 	RT5659_PLL1_S_BCLK2,
 	RT5659_PLL1_S_BCLK3,
 	RT5659_PLL1_S_BCLK4,
+	RT5659_NUM_PLL1_SRC_CLKS,
 };
 
 enum {
@@ -1797,6 +1799,13 @@ struct rt5659_priv {
 	struct gpio_desc *gpiod_reset;
 	struct snd_soc_jack *hs_jack;
 	struct delayed_work jack_detect_work;
+
+	struct clk_hw *clk_sysclk_src;
+	struct clk_hw *clk_pll_src;
+	struct clk_hw clk_pll_out;
+	struct clk *clk_bclk[RT5659_AIFS];
+	struct clk *sysclk_ref;
+	struct clk *pll_ref;
 	struct clk *mclk;
 
 	int sysclk;
