diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index ade5a6b44b66a..944450ba19b62 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -185,34 +185,9 @@ static int fsl_sai_set_dai_bclk_ratio(struct snd_soc_dai *dai,
 	return 0;
 }
 
-static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
-		int clk_id, unsigned int freq, bool tx)
+static int fsl_sai_set_dai_sysclk_tr(struct fsl_sai *sai, int clk_id, bool tx)
 {
-	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-	unsigned int ofs = sai->soc_data->reg_offset;
-	u32 val_cr2 = 0;
-
-	switch (clk_id) {
-	case FSL_SAI_CLK_BUS:
-		val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
-		break;
-	case FSL_SAI_CLK_MAST1:
-		val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
-		break;
-	case FSL_SAI_CLK_MAST2:
-		val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
-		break;
-	case FSL_SAI_CLK_MAST3:
-		val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
-			   FSL_SAI_CR2_MSEL_MASK, val_cr2);
-
-	return 0;
+	return clk_set_parent(sai->mclk_clk_out[tx], sai->mclk_clk[clk_id]);
 }
 
 static int fsl_sai_set_mclk_rate(struct snd_soc_dai *dai, int clk_id, unsigned int freq)
@@ -257,13 +232,13 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 		}
 	}
 
-	ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, true);
+	ret = fsl_sai_set_dai_sysclk_tr(sai, clk_id, true);
 	if (ret) {
 		dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret);
 		return ret;
 	}
 
-	ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, false);
+	ret = fsl_sai_set_dai_sysclk_tr(sai, clk_id, false);
 	if (ret)
 		dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret);
 
@@ -487,8 +462,7 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
 	else
 		return 0;
 
-	regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK,
-			   FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
+	fsl_sai_set_dai_sysclk_tr(sai, sai->mclk_id[tx], tx);
 
 	if (savediv == 1)
 		regmap_update_bits(sai->regmap, reg,
@@ -707,32 +681,41 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
 static void fsl_sai_reset(struct fsl_sai *sai, bool rx, bool tx)
 {
 	unsigned int ofs = sai->soc_data->reg_offset;
+	bool tx_keep_tere = false, rx_keep_tere = false;
+	u32 tcsr = 0, rcsr = 0;
+
+	if (sai->mclk_clk_out[0])
+		rx_keep_tere = clk_hw_is_enabled(&(sai->mclk_clk_hw[0]));
+	if (sai->mclk_clk_out[1])
+		tx_keep_tere = clk_hw_is_enabled(&(sai->mclk_clk_hw[1]));
+
+	if (rx_keep_tere) {
+		regmap_read(sai->regmap, FSL_SAI_RCSR(ofs), &rcsr);
+		rcsr &= FSL_SAI_CSR_TERE;
+	}
+
+	if (tx_keep_tere) {
+		regmap_read(sai->regmap, FSL_SAI_TCSR(ofs), &tcsr);
+		tcsr &= FSL_SAI_CSR_TERE;
+	}
 
 	if (tx)
-		regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR);
+		regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR | tcsr);
 	if (rx)
-		regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR);
+		regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR | rcsr);
 	usleep_range(1000, 2000);
 	if (tx)
-		regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0);
+		regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), tcsr);
 	if (rx)
-		regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0);
+		regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), rcsr);
 }
 
 static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
 {
 	unsigned int ofs = sai->soc_data->reg_offset;
 	bool tx = dir == TX;
-	u32 xcsr, count = 100;
 
-	regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
-			   FSL_SAI_CSR_TERE, 0);
-
-	/* TERE will remain set till the end of current frame */
-	do {
-		udelay(10);
-		regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
-	} while (--count && xcsr & FSL_SAI_CSR_TERE);
+	clk_disable_unprepare(sai->mclk_clk_out[tx]);
 
 	regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
 			   FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
@@ -780,8 +763,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
 		regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
 				   FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
 
-		regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
-				   FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+		clk_prepare_enable(sai->mclk_clk_out[tx]);
 		/*
 		 * Enable the opposite direction for synchronous mode
 		 * 1. Tx sync with Rx: only set RE for Rx; set TE & RE for Tx
@@ -794,8 +776,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
 		 * be safe to do, judging by years of testing results.
 		 */
 		if (fsl_sai_dir_is_synced(sai, adir))
-			regmap_update_bits(sai->regmap, FSL_SAI_xCSR((!tx), ofs),
-					   FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+			clk_prepare_enable(sai->mclk_clk_out[!tx]);
 
 		regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
 				   FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
@@ -1281,6 +1262,86 @@ static int fsl_sai_read_dlcfg(struct fsl_sai *sai)
 	return 0;
 }
 
+static struct fsl_sai *mclk_get_clk_hw_to_sai(struct clk_hw *hw, bool *tx)
+{
+	*tx = !strcmp(clk_hw_get_name(hw), "mclk_tx");
+
+	return container_of(hw, struct fsl_sai, mclk_clk_hw[*tx]);
+}
+
+static int mclk_gate_enable(struct clk_hw *hw)
+{
+	bool tx;
+	struct fsl_sai *sai = mclk_get_clk_hw_to_sai(hw, &tx);
+	unsigned int ofs = sai->soc_data->reg_offset;
+
+	pm_runtime_get_sync(&sai->pdev->dev);
+
+	/* Transmitter must be enabled to generate MCLK on pad */
+	regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
+			   FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+
+	return 0;
+}
+
+static void mclk_gate_disable(struct clk_hw *hw)
+{
+	bool tx;
+	struct fsl_sai *sai = mclk_get_clk_hw_to_sai(hw, &tx);
+	unsigned int ofs = sai->soc_data->reg_offset;
+	u32 xcsr, count = 100;
+
+	regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_TERE, 0);
+
+	/* TERE will remain set till the end of current frame */
+	do {
+		udelay(10);
+		regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
+	} while (--count && xcsr & FSL_SAI_CSR_TERE);
+
+	pm_runtime_put_sync(&sai->pdev->dev);
+}
+
+static int mclk_gate_is_enabled(struct clk_hw *hw)
+{
+	bool tx;
+	struct fsl_sai *sai = mclk_get_clk_hw_to_sai(hw, &tx);
+	unsigned int ofs = sai->soc_data->reg_offset;
+	u32 xcsr;
+
+	regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
+
+	return !!(xcsr & FSL_SAI_CSR_TERE);
+}
+
+static u8 mclk_gate_get_parent(struct clk_hw *hw)
+{
+	bool tx;
+	struct fsl_sai *sai = mclk_get_clk_hw_to_sai(hw, &tx);
+
+	return sai->mclk_id[tx];
+}
+
+static int mclk_gate_set_parent(struct clk_hw *hw, u8 index)
+{
+	bool tx;
+	struct fsl_sai *sai = mclk_get_clk_hw_to_sai(hw, &tx);
+	unsigned int ofs = sai->soc_data->reg_offset;
+
+	regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
+			   FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(index));
+
+	return 0;
+}
+
+const struct clk_ops mclk_gate_ops = {
+	.enable		= mclk_gate_enable,
+	.disable	= mclk_gate_disable,
+	.is_enabled	= mclk_gate_is_enabled,
+	.get_parent	= mclk_gate_get_parent,
+	.set_parent	= mclk_gate_set_parent,
+};
+
 static int fsl_sai_runtime_suspend(struct device *dev);
 static int fsl_sai_runtime_resume(struct device *dev);
 
@@ -1295,6 +1356,13 @@ static int fsl_sai_probe(struct platform_device *pdev)
 	int irq, ret, i;
 	int index;
 	u32 dmas[4];
+	const char *mclk_parent_names[FSL_SAI_MCLK_MAX];
+	const char *mclk_rxtx_names[2] = { "mclk_rx", "mclk_tx" };
+	struct clk_init_data mclk_init = {
+		.ops		= &mclk_gate_ops,
+		.parent_names	= mclk_parent_names,
+		.num_parents	= FSL_SAI_MCLK_MAX,
+	};
 
 	sai = devm_kzalloc(dev, sizeof(*sai), GFP_KERNEL);
 	if (!sai)
@@ -1341,12 +1409,14 @@ static int fsl_sai_probe(struct platform_device *pdev)
 					i, PTR_ERR(sai->mclk_clk[i]));
 			sai->mclk_clk[i] = NULL;
 		}
+		mclk_parent_names[i] = __clk_get_name(sai->mclk_clk[i]);
 	}
 
 	if (sai->soc_data->mclk0_is_mclk1)
 		sai->mclk_clk[0] = sai->mclk_clk[1];
 	else
 		sai->mclk_clk[0] = sai->bus_clk;
+	mclk_parent_names[0] = __clk_get_name(sai->mclk_clk[0]);
 
 	fsl_asoc_get_pll_clocks(&pdev->dev, &sai->pll8k_clk,
 				&sai->pll11k_clk);
@@ -1443,8 +1513,34 @@ static int fsl_sai_probe(struct platform_device *pdev)
 
 	/* Get sai version */
 	ret = fsl_sai_check_version(dev);
-	if (ret < 0)
+	if (ret < 0) {
 		dev_warn(dev, "Error reading SAI version: %d\n", ret);
+		goto err_pm_get_sync;
+	}
+
+	mclk_init.name = mclk_rxtx_names[0];
+	sai->mclk_clk_hw[0].init = &mclk_init;
+	ret = of_clk_hw_register(np, &sai->mclk_clk_hw[0]);
+	if (ret)
+		goto err_pm_get_sync;
+
+	mclk_init.name = mclk_rxtx_names[1];
+	sai->mclk_clk_hw[1].init = &mclk_init;
+	ret = of_clk_hw_register(np, &sai->mclk_clk_hw[1]);
+	if (ret)
+		goto err_clk_unregister_rx;
+
+	sai->mclk_clk_out[0] = clk_hw_get_clk(&sai->mclk_clk_hw[0], NULL);
+	if (IS_ERR(sai->mclk_clk_out[0])) {
+		ret = PTR_ERR(sai->mclk_clk_out[0]);
+		goto err_clk_unregister_tx;
+	}
+
+	sai->mclk_clk_out[1] = clk_hw_get_clk(&sai->mclk_clk_hw[1], NULL);
+	if (IS_ERR(sai->mclk_clk_out[1])) {
+		ret = PTR_ERR(sai->mclk_clk_out[1]);
+		goto err_clk_put_rx;
+	}
 
 	/* Select MCLK direction */
 	if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
@@ -1455,7 +1551,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
 
 	ret = pm_runtime_put_sync(dev);
 	if (ret < 0 && ret != -ENOSYS)
-		goto err_pm_get_sync;
+		goto err_clk_put_tx;
 
 	/*
 	 * Register platform component before registering cpu dai for there
@@ -1466,21 +1562,39 @@ static int fsl_sai_probe(struct platform_device *pdev)
 		if (ret) {
 			if (!IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA))
 				dev_err(dev, "Error: You must enable the imx-pcm-dma support!\n");
-			goto err_pm_get_sync;
+			goto err_clk_put_tx;
 		}
 	} else {
 		ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
 		if (ret)
-			goto err_pm_get_sync;
+			goto err_clk_put_tx;
 	}
 
 	ret = devm_snd_soc_register_component(dev, &fsl_component,
 					      &sai->cpu_dai_drv, 1);
 	if (ret)
-		goto err_pm_get_sync;
+		goto err_clk_put_tx;
+
+	/* Expose only the TX clock */
+	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+					  &sai->mclk_clk_hw[1]);
+	if (ret)
+		goto err_clk_put_tx;
 
 	return ret;
 
+err_clk_put_tx:
+	clk_put(sai->mclk_clk_out[1]);
+
+err_clk_put_rx:
+	clk_put(sai->mclk_clk_out[0]);
+
+err_clk_unregister_tx:
+	clk_hw_unregister(&sai->mclk_clk_hw[1]);
+
+err_clk_unregister_rx:
+	clk_hw_unregister(&sai->mclk_clk_hw[0]);
+
 err_pm_get_sync:
 	if (!pm_runtime_status_suspended(dev))
 		fsl_sai_runtime_suspend(dev);
@@ -1492,6 +1606,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
 
 static int fsl_sai_remove(struct platform_device *pdev)
 {
+	struct fsl_sai *sai = dev_get_drvdata(&pdev->dev);
+
+	clk_put(sai->mclk_clk_out[1]);
+	clk_put(sai->mclk_clk_out[0]);
+	clk_hw_unregister(&sai->mclk_clk_hw[1]);
+	clk_hw_unregister(&sai->mclk_clk_hw[0]);
 	pm_runtime_disable(&pdev->dev);
 	if (!pm_runtime_status_suspended(&pdev->dev))
 		fsl_sai_runtime_suspend(&pdev->dev);
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 197748a888d5f..f2abf2d6de218 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -6,6 +6,7 @@
 #ifndef __FSL_SAI_H
 #define __FSL_SAI_H
 
+#include <linux/clk-provider.h>
 #include <linux/dma/imx-dma.h>
 #include <sound/dmaengine_pcm.h>
 
@@ -277,6 +278,8 @@ struct fsl_sai {
 	struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
 	struct clk *pll8k_clk;
 	struct clk *pll11k_clk;
+	struct clk_hw mclk_clk_hw[2];	/* MCLK provider */
+	struct clk *mclk_clk_out[2];	/* MCLK output */
 	struct resource *res;
 
 	bool is_consumer_mode;
