this patch is based on:

https://pastebin.com/kwNHAdhu
via: https://oftc.irclog.whitequark.org/panfrost/2022-05-09#30915485

as it is supposed to be an improved version of the older and known to
be broken gpu freq scaling code from cadmium

diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
index acce9d02dba0..9641769c64d1 100644
--- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c
+++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
@@ -29,13 +29,34 @@ static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
 				   u32 flags)
 {
 	struct dev_pm_opp *opp;
+	struct panfrost_device *pfdev = dev_get_drvdata(dev);
+	int ret, err;
+	char needs_reparenting = pfdev->mtk_clk_mux != NULL;
 
 	opp = devfreq_recommended_opp(dev, freq, flags);
 	if (IS_ERR(opp))
 		return PTR_ERR(opp);
 	dev_pm_opp_put(opp);
 
-	return dev_pm_opp_set_rate(dev, *freq);
+	if (needs_reparenting) {
+		err = clk_set_parent(pfdev->mtk_clk_mux, pfdev->mtk_clk_sub);
+		if (err) {
+			DRM_DEV_ERROR(dev, "Failed to select sub clock source\n");
+			return err;
+		}
+	}
+
+	ret = dev_pm_opp_set_rate(dev, *freq);
+
+	if (needs_reparenting) {
+		err = clk_set_parent(pfdev->mtk_clk_mux, pfdev->mtk_clk_parent);
+		if (err) {
+			DRM_DEV_ERROR(dev, "Failed to select main clock source\n");
+			return err;
+		}
+	}
+
+	return ret;
 }
 
 static void panfrost_devfreq_reset(struct panfrost_devfreq *pfdevfreq)
@@ -91,16 +112,7 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
 	struct devfreq *devfreq;
 	struct opp_table *opp_table;
 	struct thermal_cooling_device *cooling;
-	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
-
-	if (pfdev->comp->num_supplies > 1) {
-		/*
-		 * GPUs with more than 1 supply require platform-specific handling:
-		 * continue without devfreq
-		 */
-		DRM_DEV_INFO(dev, "More than 1 supply is not supported yet\n");
-		return 0;
-	}
+	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;	
 
 	opp_table = dev_pm_opp_set_regulators(dev, pfdev->comp->supply_names,
 					      pfdev->comp->num_supplies);
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index a2a09c51eed7..b77d738c7fb8 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -67,6 +67,32 @@ static int panfrost_clk_init(struct panfrost_device *pfdev)
 			goto disable_clock;
 	}
 
+	pfdev->mtk_clk_mux = devm_clk_get_optional(pfdev->dev, "clk_mux");
+	if (pfdev->mtk_clk_mux) {
+		if (IS_ERR(pfdev->mtk_clk_mux)) {
+			dev_err(pfdev->dev, "get clk_mux failed %ld\n",
+				PTR_ERR(pfdev->mtk_clk_mux));
+			err = PTR_ERR(pfdev->mtk_clk_mux);
+			goto disable_clock;
+		}
+
+		pfdev->mtk_clk_parent = devm_clk_get(pfdev->dev, "clk_main_parent");
+		if (IS_ERR(pfdev->mtk_clk_parent)) {
+			dev_err(pfdev->dev, "get clk_main_parent failed %ld\n",
+			PTR_ERR(pfdev->mtk_clk_parent));
+			err = PTR_ERR(pfdev->mtk_clk_parent);
+			goto disable_clock;
+		}
+
+		pfdev->mtk_clk_sub = devm_clk_get(pfdev->dev, "clk_sub_parent");
+		if (IS_ERR(pfdev->mtk_clk_sub)) {
+			dev_err(pfdev->dev, "get clk_sub_parent failed %ld\n",
+			PTR_ERR(pfdev->mtk_clk_sub));
+			err = PTR_ERR(pfdev->mtk_clk_sub);
+			goto disable_clock;
+		}
+	}
+
 	return 0;
 
 disable_clock:
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index 8b2cdb8c701d..410b6099fea9 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -84,6 +84,9 @@ struct panfrost_device {
 	void __iomem *iomem;
 	struct clk *clock;
 	struct clk *bus_clock;
+	struct clk *mtk_clk_mux;
+	struct clk *mtk_clk_parent;
+	struct clk *mtk_clk_sub;
 	struct regulator_bulk_data *regulators;
 	struct reset_control *rstc;
 	/* pm_domains for devices with more than one. */
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 65df3a106cf5..a3534dd83a00 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -784,18 +784,15 @@ static int _generic_set_opp_regulator(struct opp_table *opp_table,
 	struct regulator *reg = opp_table->regulators[0];
 	struct dev_pm_opp *old_opp = opp_table->current_opp;
 	int ret;
-
-	/* This function only supports single regulator per device */
-	if (WARN_ON(opp_table->regulator_count > 1)) {
-		dev_err(dev, "multiple regulators are not supported\n");
-		return -EINVAL;
-	}
+	unsigned i;
 
 	/* Scaling up? Scale voltage before frequency */
 	if (!scaling_down) {
-		ret = _set_opp_voltage(dev, reg, opp->supplies);
-		if (ret)
-			goto restore_voltage;
+		for (i = 0; i < opp_table->regulator_count; i++) {
+			ret = _set_opp_voltage(dev, opp_table->regulators[i], &opp->supplies[i]);
+			if (ret)
+				goto restore_voltage;
+		}
 	}
 
 	/* Change frequency */
@@ -805,9 +802,11 @@ static int _generic_set_opp_regulator(struct opp_table *opp_table,
 
 	/* Scaling down? Scale voltage after frequency */
 	if (scaling_down) {
-		ret = _set_opp_voltage(dev, reg, opp->supplies);
-		if (ret)
-			goto restore_freq;
+		for (i = 0; i < opp_table->regulator_count; i++) {
+			ret = _set_opp_voltage(dev, opp_table->regulators[i], &opp->supplies[i]);
+			if (ret)
+				goto restore_freq;
+		}
 	}
 
 	/*
-- 
