mirror of
https://git.proxmox.com/git/mirror_ubuntu-kernels.git
synced 2026-01-27 11:55:10 +00:00
drm/tegra: dc: Support OPP and SoC core voltage scaling
Add OPP and SoC core voltage scaling support to the display controller driver. This is required for enabling system-wide DVFS on pre-Tegra186 SoCs. Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30 Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20 Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124 Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30 Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
parent
a21115dd38
commit
4ce3048c0a
@ -11,9 +11,12 @@
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <soc/tegra/common.h>
|
||||
#include <soc/tegra/pmc.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
@ -1834,6 +1837,52 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_dc_update_voltage_state(struct tegra_dc *dc,
|
||||
struct tegra_dc_state *state)
|
||||
{
|
||||
unsigned long rate, pstate;
|
||||
struct dev_pm_opp *opp;
|
||||
int err;
|
||||
|
||||
if (!dc->has_opp_table)
|
||||
return;
|
||||
|
||||
/* calculate actual pixel clock rate which depends on internal divider */
|
||||
rate = DIV_ROUND_UP(clk_get_rate(dc->clk) * 2, state->div + 2);
|
||||
|
||||
/* find suitable OPP for the rate */
|
||||
opp = dev_pm_opp_find_freq_ceil(dc->dev, &rate);
|
||||
|
||||
/*
|
||||
* Very high resolution modes may results in a clock rate that is
|
||||
* above the characterized maximum. In this case it's okay to fall
|
||||
* back to the characterized maximum.
|
||||
*/
|
||||
if (opp == ERR_PTR(-ERANGE))
|
||||
opp = dev_pm_opp_find_freq_floor(dc->dev, &rate);
|
||||
|
||||
if (IS_ERR(opp)) {
|
||||
dev_err(dc->dev, "failed to find OPP for %luHz: %pe\n",
|
||||
rate, opp);
|
||||
return;
|
||||
}
|
||||
|
||||
pstate = dev_pm_opp_get_required_pstate(opp, 0);
|
||||
dev_pm_opp_put(opp);
|
||||
|
||||
/*
|
||||
* The minimum core voltage depends on the pixel clock rate (which
|
||||
* depends on internal clock divider of the CRTC) and not on the
|
||||
* rate of the display controller clock. This is why we're not using
|
||||
* dev_pm_opp_set_rate() API and instead controlling the power domain
|
||||
* directly.
|
||||
*/
|
||||
err = dev_pm_genpd_set_performance_state(dc->dev, pstate);
|
||||
if (err)
|
||||
dev_err(dc->dev, "failed to set power domain state to %lu: %d\n",
|
||||
pstate, err);
|
||||
}
|
||||
|
||||
static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
|
||||
struct tegra_dc_state *state)
|
||||
{
|
||||
@ -1867,6 +1916,8 @@ static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
|
||||
DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
|
||||
state->div);
|
||||
DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
|
||||
|
||||
tegra_dc_update_voltage_state(dc, state);
|
||||
}
|
||||
|
||||
static void tegra_dc_stop(struct tegra_dc *dc)
|
||||
@ -2057,6 +2108,13 @@ static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
err = host1x_client_suspend(&dc->client);
|
||||
if (err < 0)
|
||||
dev_err(dc->dev, "failed to suspend: %d\n", err);
|
||||
|
||||
if (dc->has_opp_table) {
|
||||
err = dev_pm_genpd_set_performance_state(dc->dev, 0);
|
||||
if (err)
|
||||
dev_err(dc->dev,
|
||||
"failed to clear power domain state: %d\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
@ -3058,6 +3116,23 @@ static int tegra_dc_couple(struct tegra_dc *dc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_dc_init_opp_table(struct tegra_dc *dc)
|
||||
{
|
||||
struct tegra_core_opp_params opp_params = {};
|
||||
int err;
|
||||
|
||||
err = devm_tegra_core_dev_init_opp_table(dc->dev, &opp_params);
|
||||
if (err && err != -ENODEV)
|
||||
return err;
|
||||
|
||||
if (err)
|
||||
dc->has_opp_table = false;
|
||||
else
|
||||
dc->has_opp_table = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_dc_probe(struct platform_device *pdev)
|
||||
{
|
||||
u64 dma_mask = dma_get_mask(pdev->dev.parent);
|
||||
@ -3123,6 +3198,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
|
||||
tegra_powergate_power_off(dc->powergate);
|
||||
}
|
||||
|
||||
err = tegra_dc_init_opp_table(dc);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
dc->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(dc->regs))
|
||||
return PTR_ERR(dc->regs);
|
||||
|
||||
@ -101,6 +101,8 @@ struct tegra_dc {
|
||||
struct drm_info_list *debugfs_files;
|
||||
|
||||
const struct tegra_dc_soc_info *soc;
|
||||
|
||||
bool has_opp_table;
|
||||
};
|
||||
|
||||
static inline struct tegra_dc *
|
||||
|
||||
Loading…
Reference in New Issue
Block a user