Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/tegra: dc: Implement runtime PM

Use runtime PM to clock-gate, assert reset and powergate the display
controller. This ties in nicely with atomic DPMS in that a runtime PM
reference is taken before a pipe is enabled and dropped after it has
been shut down.

To make sure this works, make sure to only ever update planes on active
CRTCs, otherwise register accesses to a clock-gated and reset CRTC will
hang the CPU.

Signed-off-by: Thierry Reding <treding@nvidia.com>

+107 -71
+106 -70
drivers/gpu/drm/tegra/dc.c
··· 10 10 #include <linux/clk.h> 11 11 #include <linux/debugfs.h> 12 12 #include <linux/iommu.h> 13 + #include <linux/pm_runtime.h> 13 14 #include <linux/reset.h> 14 15 15 16 #include <soc/tegra/pmc.h> ··· 1217 1216 1218 1217 tegra_dc_stats_reset(&dc->stats); 1219 1218 drm_crtc_vblank_off(crtc); 1219 + 1220 + pm_runtime_put_sync(dc->dev); 1220 1221 } 1221 1222 1222 1223 static void tegra_crtc_enable(struct drm_crtc *crtc) ··· 1228 1225 struct tegra_dc *dc = to_tegra_dc(crtc); 1229 1226 u32 value; 1230 1227 1228 + pm_runtime_get_sync(dc->dev); 1229 + 1230 + /* initialize display controller */ 1231 + if (dc->syncpt) { 1232 + u32 syncpt = host1x_syncpt_id(dc->syncpt); 1233 + 1234 + value = SYNCPT_CNTRL_NO_STALL; 1235 + tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); 1236 + 1237 + value = SYNCPT_VSYNC_ENABLE | syncpt; 1238 + tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); 1239 + } 1240 + 1241 + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 1242 + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 1243 + tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); 1244 + 1245 + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 1246 + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 1247 + tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); 1248 + 1249 + /* initialize timer */ 1250 + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | 1251 + WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); 1252 + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); 1253 + 1254 + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | 1255 + WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); 1256 + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); 1257 + 1258 + value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 1259 + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 1260 + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 1261 + 1262 + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 1263 + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 1264 + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 1265 + 1266 + if (dc->soc->supports_border_color) 1267 + tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); 1268 + 1269 + /* apply PLL and pixel clock changes */ 1231 1270 tegra_dc_commit_state(dc, state); 1232 1271 1233 1272 /* program display mode */ ··· 1730 1685 struct tegra_drm *tegra = drm->dev_private; 1731 1686 struct drm_plane *primary = NULL; 1732 1687 struct drm_plane *cursor = NULL; 1733 - u32 value; 1734 1688 int err; 1735 1689 1736 1690 dc->syncpt = host1x_syncpt_request(dc->dev, flags); ··· 1798 1754 err); 1799 1755 goto cleanup; 1800 1756 } 1801 - 1802 - /* initialize display controller */ 1803 - if (dc->syncpt) { 1804 - u32 syncpt = host1x_syncpt_id(dc->syncpt); 1805 - 1806 - value = SYNCPT_CNTRL_NO_STALL; 1807 - tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); 1808 - 1809 - value = SYNCPT_VSYNC_ENABLE | syncpt; 1810 - tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); 1811 - } 1812 - 1813 - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 1814 - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 1815 - tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); 1816 - 1817 - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 1818 - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 1819 - tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); 1820 - 1821 - /* initialize timer */ 1822 - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | 1823 - WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); 1824 - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); 1825 - 1826 - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | 1827 - WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); 1828 - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); 1829 - 1830 - value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 1831 - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 1832 - tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 1833 - 1834 - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 1835 - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 1836 - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 1837 - 1838 - if (dc->soc->supports_border_color) 1839 - tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); 1840 - 1841 - tegra_dc_stats_reset(&dc->stats); 1842 1757 1843 1758 return 0; 1844 1759 ··· 1990 1987 return PTR_ERR(dc->rst); 1991 1988 } 1992 1989 1990 + reset_control_assert(dc->rst); 1991 + 1993 1992 if (dc->soc->has_powergate) { 1994 1993 if (dc->pipe == 0) 1995 1994 dc->powergate = TEGRA_POWERGATE_DIS; 1996 1995 else 1997 1996 dc->powergate = TEGRA_POWERGATE_DISB; 1998 1997 1999 - err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, 2000 - dc->rst); 2001 - if (err < 0) { 2002 - dev_err(&pdev->dev, "failed to power partition: %d\n", 2003 - err); 2004 - return err; 2005 - } 2006 - } else { 2007 - err = clk_prepare_enable(dc->clk); 2008 - if (err < 0) { 2009 - dev_err(&pdev->dev, "failed to enable clock: %d\n", 2010 - err); 2011 - return err; 2012 - } 2013 - 2014 - err = reset_control_deassert(dc->rst); 2015 - if (err < 0) { 2016 - dev_err(&pdev->dev, "failed to deassert reset: %d\n", 2017 - err); 2018 - return err; 2019 - } 1998 + tegra_powergate_power_off(dc->powergate); 2020 1999 } 2021 2000 2022 2001 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); ··· 2012 2027 return -ENXIO; 2013 2028 } 2014 2029 2015 - INIT_LIST_HEAD(&dc->client.list); 2016 - dc->client.ops = &dc_client_ops; 2017 - dc->client.dev = &pdev->dev; 2018 - 2019 2030 err = tegra_dc_rgb_probe(dc); 2020 2031 if (err < 0 && err != -ENODEV) { 2021 2032 dev_err(&pdev->dev, "failed to probe RGB output: %d\n", err); 2022 2033 return err; 2023 2034 } 2035 + 2036 + platform_set_drvdata(pdev, dc); 2037 + pm_runtime_enable(&pdev->dev); 2038 + 2039 + INIT_LIST_HEAD(&dc->client.list); 2040 + dc->client.ops = &dc_client_ops; 2041 + dc->client.dev = &pdev->dev; 2024 2042 2025 2043 err = host1x_client_register(&dc->client); 2026 2044 if (err < 0) { ··· 2031 2043 err); 2032 2044 return err; 2033 2045 } 2034 - 2035 - platform_set_drvdata(pdev, dc); 2036 2046 2037 2047 return 0; 2038 2048 } ··· 2053 2067 return err; 2054 2068 } 2055 2069 2056 - reset_control_assert(dc->rst); 2070 + pm_runtime_disable(&pdev->dev); 2071 + 2072 + return 0; 2073 + } 2074 + 2075 + #ifdef CONFIG_PM 2076 + static int tegra_dc_suspend(struct device *dev) 2077 + { 2078 + struct tegra_dc *dc = dev_get_drvdata(dev); 2079 + int err; 2080 + 2081 + err = reset_control_assert(dc->rst); 2082 + if (err < 0) { 2083 + dev_err(dev, "failed to assert reset: %d\n", err); 2084 + return err; 2085 + } 2057 2086 2058 2087 if (dc->soc->has_powergate) 2059 2088 tegra_powergate_power_off(dc->powergate); ··· 2078 2077 return 0; 2079 2078 } 2080 2079 2080 + static int tegra_dc_resume(struct device *dev) 2081 + { 2082 + struct tegra_dc *dc = dev_get_drvdata(dev); 2083 + int err; 2084 + 2085 + if (dc->soc->has_powergate) { 2086 + err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, 2087 + dc->rst); 2088 + if (err < 0) { 2089 + dev_err(dev, "failed to power partition: %d\n", err); 2090 + return err; 2091 + } 2092 + } else { 2093 + err = clk_prepare_enable(dc->clk); 2094 + if (err < 0) { 2095 + dev_err(dev, "failed to enable clock: %d\n", err); 2096 + return err; 2097 + } 2098 + 2099 + err = reset_control_deassert(dc->rst); 2100 + if (err < 0) { 2101 + dev_err(dev, "failed to deassert reset: %d\n", err); 2102 + return err; 2103 + } 2104 + } 2105 + 2106 + return 0; 2107 + } 2108 + #endif 2109 + 2110 + static const struct dev_pm_ops tegra_dc_pm_ops = { 2111 + SET_RUNTIME_PM_OPS(tegra_dc_suspend, tegra_dc_resume, NULL) 2112 + }; 2113 + 2081 2114 struct platform_driver tegra_dc_driver = { 2082 2115 .driver = { 2083 2116 .name = "tegra-dc", 2084 2117 .of_match_table = tegra_dc_of_match, 2118 + .pm = &tegra_dc_pm_ops, 2085 2119 }, 2086 2120 .probe = tegra_dc_probe, 2087 2121 .remove = tegra_dc_remove,
+1 -1
drivers/gpu/drm/tegra/drm.c
··· 56 56 */ 57 57 58 58 drm_atomic_helper_commit_modeset_disables(drm, state); 59 - drm_atomic_helper_commit_planes(drm, state, false); 60 59 drm_atomic_helper_commit_modeset_enables(drm, state); 60 + drm_atomic_helper_commit_planes(drm, state, true); 61 61 62 62 drm_atomic_helper_wait_for_vblanks(drm, state); 63 63