|
11 | 11 | #include <linux/interconnect.h>
|
12 | 12 | #include <linux/module.h>
|
13 | 13 | #include <linux/of_device.h>
|
| 14 | +#include <linux/pm_domain.h> |
| 15 | +#include <linux/pm_opp.h> |
14 | 16 | #include <linux/pm_runtime.h>
|
15 | 17 | #include <linux/reset.h>
|
16 | 18 |
|
| 19 | +#include <soc/tegra/common.h> |
17 | 20 | #include <soc/tegra/pmc.h>
|
18 | 21 |
|
19 | 22 | #include <drm/drm_atomic.h>
|
@@ -1834,6 +1837,52 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
|
1834 | 1837 | return 0;
|
1835 | 1838 | }
|
1836 | 1839 |
|
| 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 | + |
1837 | 1886 | static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
|
1838 | 1887 | struct tegra_dc_state *state)
|
1839 | 1888 | {
|
@@ -1867,6 +1916,8 @@ static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
|
1867 | 1916 | DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
|
1868 | 1917 | state->div);
|
1869 | 1918 | DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
|
| 1919 | + |
| 1920 | + tegra_dc_update_voltage_state(dc, state); |
1870 | 1921 | }
|
1871 | 1922 |
|
1872 | 1923 | static void tegra_dc_stop(struct tegra_dc *dc)
|
@@ -2057,6 +2108,13 @@ static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
|
2057 | 2108 | err = host1x_client_suspend(&dc->client);
|
2058 | 2109 | if (err < 0)
|
2059 | 2110 | 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 | + } |
2060 | 2118 | }
|
2061 | 2119 |
|
2062 | 2120 | static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
|
@@ -3058,6 +3116,23 @@ static int tegra_dc_couple(struct tegra_dc *dc)
|
3058 | 3116 | return 0;
|
3059 | 3117 | }
|
3060 | 3118 |
|
| 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 | + |
3061 | 3136 | static int tegra_dc_probe(struct platform_device *pdev)
|
3062 | 3137 | {
|
3063 | 3138 | u64 dma_mask = dma_get_mask(pdev->dev.parent);
|
@@ -3123,6 +3198,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
|
3123 | 3198 | tegra_powergate_power_off(dc->powergate);
|
3124 | 3199 | }
|
3125 | 3200 |
|
| 3201 | + err = tegra_dc_init_opp_table(dc); |
| 3202 | + if (err < 0) |
| 3203 | + return err; |
| 3204 | + |
3126 | 3205 | dc->regs = devm_platform_ioremap_resource(pdev, 0);
|
3127 | 3206 | if (IS_ERR(dc->regs))
|
3128 | 3207 | return PTR_ERR(dc->regs);
|
|
0 commit comments