Skip to content

Commit 4ce3048

Browse files
digetxthierryreding
authored andcommitted
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 <[email protected]> Tested-by: Peter Geis <[email protected]> # Ouya T30 Tested-by: Paul Fertser <[email protected]> # PAZ00 T20 Tested-by: Nicolas Chauvet <[email protected]> # PAZ00 T20 and TK1 T124 Tested-by: Matt Merhar <[email protected]> # Ouya T30 Signed-off-by: Dmitry Osipenko <[email protected]> Signed-off-by: Thierry Reding <[email protected]>
1 parent a21115d commit 4ce3048

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

drivers/gpu/drm/tegra/dc.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111
#include <linux/interconnect.h>
1212
#include <linux/module.h>
1313
#include <linux/of_device.h>
14+
#include <linux/pm_domain.h>
15+
#include <linux/pm_opp.h>
1416
#include <linux/pm_runtime.h>
1517
#include <linux/reset.h>
1618

19+
#include <soc/tegra/common.h>
1720
#include <soc/tegra/pmc.h>
1821

1922
#include <drm/drm_atomic.h>
@@ -1834,6 +1837,52 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
18341837
return 0;
18351838
}
18361839

1840+
static void tegra_dc_update_voltage_state(struct tegra_dc *dc,
1841+
struct tegra_dc_state *state)
1842+
{
1843+
unsigned long rate, pstate;
1844+
struct dev_pm_opp *opp;
1845+
int err;
1846+
1847+
if (!dc->has_opp_table)
1848+
return;
1849+
1850+
/* calculate actual pixel clock rate which depends on internal divider */
1851+
rate = DIV_ROUND_UP(clk_get_rate(dc->clk) * 2, state->div + 2);
1852+
1853+
/* find suitable OPP for the rate */
1854+
opp = dev_pm_opp_find_freq_ceil(dc->dev, &rate);
1855+
1856+
/*
1857+
* Very high resolution modes may results in a clock rate that is
1858+
* above the characterized maximum. In this case it's okay to fall
1859+
* back to the characterized maximum.
1860+
*/
1861+
if (opp == ERR_PTR(-ERANGE))
1862+
opp = dev_pm_opp_find_freq_floor(dc->dev, &rate);
1863+
1864+
if (IS_ERR(opp)) {
1865+
dev_err(dc->dev, "failed to find OPP for %luHz: %pe\n",
1866+
rate, opp);
1867+
return;
1868+
}
1869+
1870+
pstate = dev_pm_opp_get_required_pstate(opp, 0);
1871+
dev_pm_opp_put(opp);
1872+
1873+
/*
1874+
* The minimum core voltage depends on the pixel clock rate (which
1875+
* depends on internal clock divider of the CRTC) and not on the
1876+
* rate of the display controller clock. This is why we're not using
1877+
* dev_pm_opp_set_rate() API and instead controlling the power domain
1878+
* directly.
1879+
*/
1880+
err = dev_pm_genpd_set_performance_state(dc->dev, pstate);
1881+
if (err)
1882+
dev_err(dc->dev, "failed to set power domain state to %lu: %d\n",
1883+
pstate, err);
1884+
}
1885+
18371886
static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
18381887
struct tegra_dc_state *state)
18391888
{
@@ -1867,6 +1916,8 @@ static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
18671916
DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
18681917
state->div);
18691918
DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
1919+
1920+
tegra_dc_update_voltage_state(dc, state);
18701921
}
18711922

18721923
static void tegra_dc_stop(struct tegra_dc *dc)
@@ -2057,6 +2108,13 @@ static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
20572108
err = host1x_client_suspend(&dc->client);
20582109
if (err < 0)
20592110
dev_err(dc->dev, "failed to suspend: %d\n", err);
2111+
2112+
if (dc->has_opp_table) {
2113+
err = dev_pm_genpd_set_performance_state(dc->dev, 0);
2114+
if (err)
2115+
dev_err(dc->dev,
2116+
"failed to clear power domain state: %d\n", err);
2117+
}
20602118
}
20612119

20622120
static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
@@ -3058,6 +3116,23 @@ static int tegra_dc_couple(struct tegra_dc *dc)
30583116
return 0;
30593117
}
30603118

3119+
static int tegra_dc_init_opp_table(struct tegra_dc *dc)
3120+
{
3121+
struct tegra_core_opp_params opp_params = {};
3122+
int err;
3123+
3124+
err = devm_tegra_core_dev_init_opp_table(dc->dev, &opp_params);
3125+
if (err && err != -ENODEV)
3126+
return err;
3127+
3128+
if (err)
3129+
dc->has_opp_table = false;
3130+
else
3131+
dc->has_opp_table = true;
3132+
3133+
return 0;
3134+
}
3135+
30613136
static int tegra_dc_probe(struct platform_device *pdev)
30623137
{
30633138
u64 dma_mask = dma_get_mask(pdev->dev.parent);
@@ -3123,6 +3198,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
31233198
tegra_powergate_power_off(dc->powergate);
31243199
}
31253200

3201+
err = tegra_dc_init_opp_table(dc);
3202+
if (err < 0)
3203+
return err;
3204+
31263205
dc->regs = devm_platform_ioremap_resource(pdev, 0);
31273206
if (IS_ERR(dc->regs))
31283207
return PTR_ERR(dc->regs);

drivers/gpu/drm/tegra/dc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ struct tegra_dc {
101101
struct drm_info_list *debugfs_files;
102102

103103
const struct tegra_dc_soc_info *soc;
104+
105+
bool has_opp_table;
104106
};
105107

106108
static inline struct tegra_dc *

0 commit comments

Comments
 (0)