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; + } } /* --