diff --git a/drivers/clk/mediatek/clk-mux.c b/drivers/clk/mediatek/clk-mux.c
index 60990296450b..8a2c89cb3cd5 100644
--- a/drivers/clk/mediatek/clk-mux.c
+++ b/drivers/clk/mediatek/clk-mux.c
@@ -15,11 +15,13 @@
 #include <linux/spinlock.h>
 #include <linux/slab.h>
 
+#include "clk-mtk.h"
 #include "clk-mux.h"
 
 struct mtk_clk_mux {
 	struct clk_hw hw;
 	struct regmap *regmap;
+	struct regmap *vote_regmap;
 	const struct mtk_mux *data;
 	spinlock_t *lock;
 	bool reparent;
@@ -30,6 +32,46 @@ static inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw)
 	return container_of(hw, struct mtk_clk_mux, hw);
 }
 
+static int mtk_clk_mux_fenc_enable_setclr(struct clk_hw *hw)
+{
+	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+	unsigned long flags = 0;
+	u32 val = 0;
+	int i = 0;
+	int ret = 0;
+
+	if (mux->lock)
+		spin_lock_irqsave(mux->lock, flags);
+	else
+		__acquire(mux->lock);
+
+	regmap_write(mux->regmap, mux->data->clr_ofs, BIT(mux->data->gate_shift));
+
+	while (1) {
+		regmap_read(mux->regmap, mux->data->fenc_sta_mon_ofs, &val);
+
+		if ((val & BIT(mux->data->fenc_shift)) != 0)
+			break;
+
+		if (i < MTK_WAIT_FENC_DONE_CNT) {
+			udelay(MTK_WAIT_FENC_DONE_US);
+		} else {
+			pr_err("%s wait fenc done timeout\n", clk_hw_get_name(hw));
+			ret = -EBUSY;
+			break;
+		}
+
+		i++;
+	}
+
+	if (mux->lock)
+		spin_unlock_irqrestore(mux->lock, flags);
+	else
+		__release(mux->lock);
+
+	return ret;
+}
+
 static int mtk_clk_mux_enable_setclr(struct clk_hw *hw)
 {
 	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -70,6 +112,16 @@ static void mtk_clk_mux_disable_setclr(struct clk_hw *hw)
 			BIT(mux->data->gate_shift));
 }
 
+static int mtk_clk_mux_fenc_is_enabled(struct clk_hw *hw)
+{
+	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+	u32 val = 0;
+
+	regmap_read(mux->regmap, mux->data->fenc_sta_mon_ofs, &val);
+
+	return (val & BIT(mux->data->fenc_shift)) != 0;
+}
+
 static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
 {
 	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -80,6 +132,106 @@ static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
 	return (val & BIT(mux->data->gate_shift)) == 0;
 }
 
+static int mtk_clk_vote_mux_is_enabled(struct clk_hw *hw)
+{
+	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+	u32 val = 0;
+
+	regmap_read(mux->vote_regmap, mux->data->vote_set_ofs, &val);
+
+	return (val & BIT(mux->data->gate_shift)) != 0;
+}
+
+static int mtk_clk_vote_mux_is_done(struct clk_hw *hw)
+{
+	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+	u32 val = 0;
+
+	regmap_read(mux->vote_regmap, mux->data->vote_sta_ofs, &val);
+
+	return (val & BIT(mux->data->gate_shift)) != 0;
+}
+
+static int mtk_clk_mux_vote_fenc_enable(struct clk_hw *hw)
+{
+	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+	u32 val = 0, val2 = 0;
+	bool is_done = false;
+	int i = 0;
+
+	regmap_write(mux->vote_regmap, mux->data->vote_set_ofs, BIT(mux->data->gate_shift));
+
+	while (!mtk_clk_vote_mux_is_enabled(hw)) {
+		if (i < MTK_WAIT_VOTE_PREPARE_CNT) {
+			udelay(MTK_WAIT_VOTE_PREPARE_US);
+		} else {
+			pr_err("%s mux prepare timeout(%x)\n", clk_hw_get_name(hw), val);
+			return -EBUSY;
+		}
+
+		i++;
+	}
+
+	i = 0;
+
+	while (1) {
+		if (!is_done)
+			regmap_read(mux->vote_regmap, mux->data->vote_sta_ofs, &val);
+
+		if (((val & BIT(mux->data->gate_shift)) != 0))
+			is_done = true;
+
+		if (is_done) {
+			regmap_read(mux->regmap, mux->data->fenc_sta_mon_ofs, &val2);
+			if ((val2 & BIT(mux->data->fenc_shift)) != 0)
+				break;
+		}
+
+		if (i < MTK_WAIT_VOTE_DONE_CNT) {
+			udelay(MTK_WAIT_VOTE_DONE_US);
+		} else {
+			pr_err("%s mux enable timeout(%x %x)\n", clk_hw_get_name(hw), val, val2);
+			return -EBUSY;
+		}
+
+		i++;
+	}
+
+	return 0;
+}
+
+static void mtk_clk_mux_vote_disable(struct clk_hw *hw)
+{
+	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+	int i = 0;
+
+	regmap_write(mux->vote_regmap, mux->data->vote_clr_ofs, BIT(mux->data->gate_shift));
+
+	while (mtk_clk_vote_mux_is_enabled(hw)) {
+		if (i < MTK_WAIT_VOTE_PREPARE_CNT) {
+			udelay(MTK_WAIT_VOTE_PREPARE_US);
+		} else {
+			pr_err("%s mux unprepare timeout\n", clk_hw_get_name(hw));
+			return;
+		}
+
+		i++;
+	}
+
+	i = 0;
+
+	while (!mtk_clk_vote_mux_is_done(hw)) {
+		if (i < MTK_WAIT_VOTE_DONE_CNT) {
+			udelay(MTK_WAIT_VOTE_DONE_US);
+		} else {
+			pr_err("%s mux disable timeout\n", clk_hw_get_name(hw));
+			return;
+		}
+
+		i++;
+	}
+}
+
 static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)
 {
 	struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -151,6 +303,12 @@ static int mtk_clk_mux_determine_rate(struct clk_hw *hw,
 	return clk_mux_determine_rate_flags(hw, req, mux->data->flags);
 }
 
+static void mtk_clk_mux_vote_fenc_disable_unused(struct clk_hw *hw)
+{
+	mtk_clk_mux_vote_fenc_enable(hw);
+	mtk_clk_mux_vote_disable(hw);
+}
+
 const struct clk_ops mtk_mux_clr_set_upd_ops = {
 	.get_parent = mtk_clk_mux_get_parent,
 	.set_parent = mtk_clk_mux_set_parent_setclr_lock,
@@ -168,9 +326,31 @@ const struct clk_ops mtk_mux_gate_clr_set_upd_ops  = {
 };
 EXPORT_SYMBOL_GPL(mtk_mux_gate_clr_set_upd_ops);
 
+const struct clk_ops mtk_mux_gate_fenc_clr_set_upd_ops = {
+	.enable = mtk_clk_mux_fenc_enable_setclr,
+	.disable = mtk_clk_mux_disable_setclr,
+	.is_enabled = mtk_clk_mux_fenc_is_enabled,
+	.get_parent = mtk_clk_mux_get_parent,
+	.set_parent = mtk_clk_mux_set_parent_setclr_lock,
+	.determine_rate = mtk_clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(mtk_mux_gate_fenc_clr_set_upd_ops);
+
+const struct clk_ops mtk_mux_vote_fenc_ops = {
+	.enable = mtk_clk_mux_vote_fenc_enable,
+	.disable = mtk_clk_mux_vote_disable,
+	.is_enabled = mtk_clk_mux_fenc_is_enabled,
+	.get_parent = mtk_clk_mux_get_parent,
+	.set_parent = mtk_clk_mux_set_parent_setclr_lock,
+	.determine_rate = mtk_clk_mux_determine_rate,
+	.disable_unused = mtk_clk_mux_vote_fenc_disable_unused,
+};
+EXPORT_SYMBOL_GPL(mtk_mux_vote_fenc_ops);
+
 static struct clk_hw *mtk_clk_register_mux(struct device *dev,
 					   const struct mtk_mux *mux,
 					   struct regmap *regmap,
+					   struct regmap *vote_regmap,
 					   spinlock_t *lock)
 {
 	struct mtk_clk_mux *clk_mux;
@@ -185,9 +365,17 @@ static struct clk_hw *mtk_clk_register_mux(struct device *dev,
 	init.flags = mux->flags;
 	init.parent_names = mux->parent_names;
 	init.num_parents = mux->num_parents;
-	init.ops = mux->ops;
+	if (mux->flags & CLK_USE_VOTE) {
+		if (vote_regmap)
+			init.ops = mux->ops;
+		else
+			init.ops = mux->dma_ops;
+	} else {
+		init.ops = mux->ops;
+	}
 
 	clk_mux->regmap = regmap;
+	clk_mux->vote_regmap = vote_regmap;
 	clk_mux->data = mux;
 	clk_mux->lock = lock;
 	clk_mux->hw.init = &init;
@@ -220,6 +408,7 @@ int mtk_clk_register_muxes(struct device *dev,
 			   struct clk_hw_onecell_data *clk_data)
 {
 	struct regmap *regmap;
+	struct regmap *vote_regmap = NULL;
 	struct clk_hw *hw;
 	int i;
 
@@ -238,8 +427,13 @@ int mtk_clk_register_muxes(struct device *dev,
 			continue;
 		}
 
-		hw = mtk_clk_register_mux(dev, mux, regmap, lock);
+		if (mux->vote_comp) {
+			vote_regmap = syscon_regmap_lookup_by_phandle(node, mux->vote_comp);
+			if (IS_ERR(vote_regmap))
+				vote_regmap = NULL;
+		}
 
+		hw = mtk_clk_register_mux(dev, mux, regmap, vote_regmap, lock);
 		if (IS_ERR(hw)) {
 			pr_err("Failed to register clk %s: %pe\n", mux->name,
 			       hw);
diff --git a/drivers/clk/mediatek/clk-mux.h b/drivers/clk/mediatek/clk-mux.h
index 943ad1d7ce4b..c202ad42be16 100644
--- a/drivers/clk/mediatek/clk-mux.h
+++ b/drivers/clk/mediatek/clk-mux.h
@@ -21,6 +21,7 @@ struct mtk_mux {
 	int id;
 	const char *name;
 	const char * const *parent_names;
+	const char *vote_comp;
 	const u8 *parent_index;
 	unsigned int flags;
 
@@ -28,13 +29,19 @@ struct mtk_mux {
 	u32 set_ofs;
 	u32 clr_ofs;
 	u32 upd_ofs;
+	u32 vote_set_ofs;
+	u32 vote_clr_ofs;
+	u32 vote_sta_ofs;
+	u32 fenc_sta_mon_ofs;
 
 	u8 mux_shift;
 	u8 mux_width;
 	u8 gate_shift;
 	s8 upd_shift;
+	u8 fenc_shift;
 
 	const struct clk_ops *ops;
+	const struct clk_ops *dma_ops;
 	signed char num_parents;
 };
 
@@ -77,6 +84,8 @@ struct mtk_mux {
 
 extern const struct clk_ops mtk_mux_clr_set_upd_ops;
 extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops;
+extern const struct clk_ops mtk_mux_gate_fenc_clr_set_upd_ops;
+extern const struct clk_ops mtk_mux_vote_fenc_ops;
 
 #define MUX_GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, _mux_ofs,	\
 			_mux_set_ofs, _mux_clr_ofs, _shift, _width,	\
@@ -118,6 +127,76 @@ extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops;
 			0, _upd_ofs, _upd, CLK_SET_RATE_PARENT,		\
 			mtk_mux_clr_set_upd_ops)
 
+#define MUX_MULT_VOTE_FENC_FLAGS(_id, _name, _parents,			\
+			 _mux_ofs, _mux_set_ofs, _mux_clr_ofs, _vote_comp,\
+			_vote_sta_ofs, _vote_set_ofs, _vote_clr_ofs,	\
+			_shift, _width, _gate, _upd_ofs, _upd,		\
+			_fenc_sta_mon_ofs, _fenc, _flags) {		\
+		.id = _id,						\
+		.name = _name,						\
+		.mux_ofs = _mux_ofs,					\
+		.set_ofs = _mux_set_ofs,				\
+		.clr_ofs = _mux_clr_ofs,				\
+		.vote_comp = _vote_comp,				\
+		.vote_sta_ofs = _vote_sta_ofs,				\
+		.vote_set_ofs = _vote_set_ofs,				\
+		.vote_clr_ofs = _vote_clr_ofs,				\
+		.upd_ofs = _upd_ofs,					\
+		.fenc_sta_mon_ofs = _fenc_sta_mon_ofs,			\
+		.mux_shift = _shift,					\
+		.mux_width = _width,					\
+		.gate_shift = _gate,					\
+		.upd_shift = _upd,					\
+		.fenc_shift = _fenc,					\
+		.parent_names = _parents,				\
+		.num_parents = ARRAY_SIZE(_parents),			\
+		.flags =  CLK_USE_VOTE | (_flags),			\
+		.ops = &mtk_mux_vote_fenc_ops,				\
+		.dma_ops = &mtk_mux_gate_fenc_clr_set_upd_ops,		\
+	}
+
+#define MUX_MULT_VOTE_FENC(_id, _name, _parents,				\
+			_mux_ofs, _mux_set_ofs, _mux_clr_ofs, _vote_comp,\
+			_vote_sta_ofs, _vote_set_ofs, _vote_clr_ofs,	\
+			_shift, _width, _gate, _upd_ofs, _upd,		\
+			_fenc_sta_mon_ofs, _fenc)			\
+		MUX_MULT_VOTE_FENC_FLAGS(_id, _name, _parents,		\
+			_mux_ofs, _mux_set_ofs, _mux_clr_ofs, _vote_comp,\
+			_vote_sta_ofs, _vote_set_ofs, _vote_clr_ofs,	\
+			_shift, _width, _gate, _upd_ofs, _upd,		\
+			_fenc_sta_mon_ofs, _fenc, 0)
+
+#define MUX_GATE_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents,		\
+			_mux_ofs, _mux_set_ofs, _mux_clr_ofs,		\
+			_shift, _width, _gate, _upd_ofs, _upd,		\
+			_fenc_sta_mon_ofs, _fenc, _flags) {		\
+		.id = _id,						\
+		.name = _name,						\
+		.mux_ofs = _mux_ofs,					\
+		.set_ofs = _mux_set_ofs,				\
+		.clr_ofs = _mux_clr_ofs,				\
+		.upd_ofs = _upd_ofs,					\
+		.fenc_sta_mon_ofs = _fenc_sta_mon_ofs,			\
+		.mux_shift = _shift,					\
+		.mux_width = _width,					\
+		.gate_shift = _gate,					\
+		.upd_shift = _upd,					\
+		.fenc_shift = _fenc,					\
+		.parent_names = _parents,				\
+		.num_parents = ARRAY_SIZE(_parents),			\
+		.flags = _flags,					\
+		.ops = &mtk_mux_gate_fenc_clr_set_upd_ops,		\
+	}
+
+#define MUX_GATE_FENC_CLR_SET_UPD(_id, _name, _parents,			\
+			_mux_ofs, _mux_set_ofs, _mux_clr_ofs,		\
+			_shift, _width, _gate, _upd_ofs, _upd,		\
+			_fenc_sta_mon_ofs, _fenc)			\
+		MUX_GATE_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents,	\
+			_mux_ofs, _mux_set_ofs, _mux_clr_ofs,		\
+			_shift, _width, _gate, _upd_ofs, _upd,		\
+			_fenc_sta_mon_ofs, _fenc, 0)
+
 int mtk_clk_register_muxes(struct device *dev,
 			   const struct mtk_mux *muxes,
 			   int num, struct device_node *node,
