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

drm/tegra: dc: Support memory bandwidth management

Display controller (DC) performs isochronous memory transfers, and thus,
has a requirement for a minimum memory bandwidth that shall be fulfilled,
otherwise framebuffer data can't be fetched fast enough and this results
in a DC's data-FIFO underflow that follows by a visual corruption.

The Memory Controller drivers provide facility for memory bandwidth
management via interconnect API. Let's wire up the interconnect API
support to the DC driver in order to fix the distorted display output
on T30 Ouya, T124 TK1 and other Tegra devices.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
[treding@nvidia.com: unbreak Tegra186+ display support]
Signed-off-by: Thierry Reding <treding@nvidia.com>

authored by

Dmitry Osipenko and committed by
Thierry Reding
04d5d5df ef531d01

+507 -3
+1
drivers/gpu/drm/tegra/Kconfig
··· 9 9 select DRM_MIPI_DSI 10 10 select DRM_PANEL 11 11 select TEGRA_HOST1X 12 + select INTERCONNECT 12 13 select IOMMU_IOVA 13 14 select CEC_CORE if CEC_NOTIFIER 14 15 help
+346 -2
drivers/gpu/drm/tegra/dc.c
··· 8 8 #include <linux/debugfs.h> 9 9 #include <linux/delay.h> 10 10 #include <linux/iommu.h> 11 + #include <linux/interconnect.h> 11 12 #include <linux/module.h> 12 13 #include <linux/of_device.h> 13 14 #include <linux/pm_runtime.h> ··· 619 618 struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc); 620 619 int err; 621 620 621 + plane_state->peak_memory_bandwidth = 0; 622 + plane_state->avg_memory_bandwidth = 0; 623 + 622 624 /* no need for further checks if the plane is being disabled */ 623 - if (!new_plane_state->crtc) 625 + if (!new_plane_state->crtc) { 626 + plane_state->total_peak_memory_bandwidth = 0; 624 627 return 0; 628 + } 625 629 626 630 err = tegra_plane_format(new_plane_state->fb->format->format, 627 631 &plane_state->format, ··· 814 808 formats = dc->soc->primary_formats; 815 809 modifiers = dc->soc->modifiers; 816 810 811 + err = tegra_plane_interconnect_init(plane); 812 + if (err) { 813 + kfree(plane); 814 + return ERR_PTR(err); 815 + } 816 + 817 817 err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 818 818 &tegra_plane_funcs, formats, 819 819 num_formats, modifiers, type, NULL); ··· 857 845 { 858 846 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 859 847 plane); 848 + struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state); 860 849 struct tegra_plane *tegra = to_tegra_plane(plane); 861 850 int err; 862 851 852 + plane_state->peak_memory_bandwidth = 0; 853 + plane_state->avg_memory_bandwidth = 0; 854 + 863 855 /* no need for further checks if the plane is being disabled */ 864 - if (!new_plane_state->crtc) 856 + if (!new_plane_state->crtc) { 857 + plane_state->total_peak_memory_bandwidth = 0; 865 858 return 0; 859 + } 866 860 867 861 /* scaling not supported for cursor */ 868 862 if ((new_plane_state->src_w >> 16 != new_plane_state->crtc_w) || ··· 1048 1030 if (!dc->soc->has_nvdisplay) { 1049 1031 num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats); 1050 1032 formats = tegra_legacy_cursor_plane_formats; 1033 + 1034 + err = tegra_plane_interconnect_init(plane); 1035 + if (err) { 1036 + kfree(plane); 1037 + return ERR_PTR(err); 1038 + } 1051 1039 } else { 1052 1040 num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); 1053 1041 formats = tegra_cursor_plane_formats; ··· 1172 1148 1173 1149 num_formats = dc->soc->num_overlay_formats; 1174 1150 formats = dc->soc->overlay_formats; 1151 + 1152 + err = tegra_plane_interconnect_init(plane); 1153 + if (err) { 1154 + kfree(plane); 1155 + return ERR_PTR(err); 1156 + } 1175 1157 1176 1158 if (!cursor) 1177 1159 type = DRM_PLANE_TYPE_OVERLAY; ··· 1834 1804 return -ETIMEDOUT; 1835 1805 } 1836 1806 1807 + static void 1808 + tegra_crtc_update_memory_bandwidth(struct drm_crtc *crtc, 1809 + struct drm_atomic_state *state, 1810 + bool prepare_bandwidth_transition) 1811 + { 1812 + const struct tegra_plane_state *old_tegra_state, *new_tegra_state; 1813 + const struct tegra_dc_state *old_dc_state, *new_dc_state; 1814 + u32 i, new_avg_bw, old_avg_bw, new_peak_bw, old_peak_bw; 1815 + const struct drm_plane_state *old_plane_state; 1816 + const struct drm_crtc_state *old_crtc_state; 1817 + struct tegra_dc_window window, old_window; 1818 + struct tegra_dc *dc = to_tegra_dc(crtc); 1819 + struct tegra_plane *tegra; 1820 + struct drm_plane *plane; 1821 + 1822 + if (dc->soc->has_nvdisplay) 1823 + return; 1824 + 1825 + old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); 1826 + old_dc_state = to_const_dc_state(old_crtc_state); 1827 + new_dc_state = to_const_dc_state(crtc->state); 1828 + 1829 + if (!crtc->state->active) { 1830 + if (!old_crtc_state->active) 1831 + return; 1832 + 1833 + /* 1834 + * When CRTC is disabled on DPMS, the state of attached planes 1835 + * is kept unchanged. Hence we need to enforce removal of the 1836 + * bandwidths from the ICC paths. 1837 + */ 1838 + drm_atomic_crtc_for_each_plane(plane, crtc) { 1839 + tegra = to_tegra_plane(plane); 1840 + 1841 + icc_set_bw(tegra->icc_mem, 0, 0); 1842 + icc_set_bw(tegra->icc_mem_vfilter, 0, 0); 1843 + } 1844 + 1845 + return; 1846 + } 1847 + 1848 + for_each_old_plane_in_state(old_crtc_state->state, plane, 1849 + old_plane_state, i) { 1850 + old_tegra_state = to_const_tegra_plane_state(old_plane_state); 1851 + new_tegra_state = to_const_tegra_plane_state(plane->state); 1852 + tegra = to_tegra_plane(plane); 1853 + 1854 + /* 1855 + * We're iterating over the global atomic state and it contains 1856 + * planes from another CRTC, hence we need to filter out the 1857 + * planes unrelated to this CRTC. 1858 + */ 1859 + if (tegra->dc != dc) 1860 + continue; 1861 + 1862 + new_avg_bw = new_tegra_state->avg_memory_bandwidth; 1863 + old_avg_bw = old_tegra_state->avg_memory_bandwidth; 1864 + 1865 + new_peak_bw = new_tegra_state->total_peak_memory_bandwidth; 1866 + old_peak_bw = old_tegra_state->total_peak_memory_bandwidth; 1867 + 1868 + /* 1869 + * See the comment related to !crtc->state->active above, 1870 + * which explains why bandwidths need to be updated when 1871 + * CRTC is turning ON. 1872 + */ 1873 + if (new_avg_bw == old_avg_bw && new_peak_bw == old_peak_bw && 1874 + old_crtc_state->active) 1875 + continue; 1876 + 1877 + window.src.h = drm_rect_height(&plane->state->src) >> 16; 1878 + window.dst.h = drm_rect_height(&plane->state->dst); 1879 + 1880 + old_window.src.h = drm_rect_height(&old_plane_state->src) >> 16; 1881 + old_window.dst.h = drm_rect_height(&old_plane_state->dst); 1882 + 1883 + /* 1884 + * During the preparation phase (atomic_begin), the memory 1885 + * freq should go high before the DC changes are committed 1886 + * if bandwidth requirement goes up, otherwise memory freq 1887 + * should to stay high if BW requirement goes down. The 1888 + * opposite applies to the completion phase (post_commit). 1889 + */ 1890 + if (prepare_bandwidth_transition) { 1891 + new_avg_bw = max(old_avg_bw, new_avg_bw); 1892 + new_peak_bw = max(old_peak_bw, new_peak_bw); 1893 + 1894 + if (tegra_plane_use_vertical_filtering(tegra, &old_window)) 1895 + window = old_window; 1896 + } 1897 + 1898 + icc_set_bw(tegra->icc_mem, new_avg_bw, new_peak_bw); 1899 + 1900 + if (tegra_plane_use_vertical_filtering(tegra, &window)) 1901 + icc_set_bw(tegra->icc_mem_vfilter, new_avg_bw, new_peak_bw); 1902 + else 1903 + icc_set_bw(tegra->icc_mem_vfilter, 0, 0); 1904 + } 1905 + } 1906 + 1837 1907 static void tegra_crtc_atomic_disable(struct drm_crtc *crtc, 1838 1908 struct drm_atomic_state *state) 1839 1909 { ··· 2115 1985 { 2116 1986 unsigned long flags; 2117 1987 1988 + tegra_crtc_update_memory_bandwidth(crtc, state, true); 1989 + 2118 1990 if (crtc->state->event) { 2119 1991 spin_lock_irqsave(&crtc->dev->event_lock, flags); 2120 1992 ··· 2149 2017 value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 2150 2018 } 2151 2019 2020 + static bool tegra_plane_is_cursor(const struct drm_plane_state *state) 2021 + { 2022 + const struct tegra_dc_soc_info *soc = to_tegra_dc(state->crtc)->soc; 2023 + const struct drm_format_info *fmt = state->fb->format; 2024 + unsigned int src_w = drm_rect_width(&state->src) >> 16; 2025 + unsigned int dst_w = drm_rect_width(&state->dst); 2026 + 2027 + if (state->plane->type != DRM_PLANE_TYPE_CURSOR) 2028 + return false; 2029 + 2030 + if (soc->supports_cursor) 2031 + return true; 2032 + 2033 + if (src_w != dst_w || fmt->num_planes != 1 || src_w * fmt->cpp[0] > 256) 2034 + return false; 2035 + 2036 + return true; 2037 + } 2038 + 2039 + static unsigned long 2040 + tegra_plane_overlap_mask(struct drm_crtc_state *state, 2041 + const struct drm_plane_state *plane_state) 2042 + { 2043 + const struct drm_plane_state *other_state; 2044 + const struct tegra_plane *tegra; 2045 + unsigned long overlap_mask = 0; 2046 + struct drm_plane *plane; 2047 + struct drm_rect rect; 2048 + 2049 + if (!plane_state->visible || !plane_state->fb) 2050 + return 0; 2051 + 2052 + /* 2053 + * Data-prefetch FIFO will easily help to overcome temporal memory 2054 + * pressure if other plane overlaps with the cursor plane. 2055 + */ 2056 + if (tegra_plane_is_cursor(plane_state)) 2057 + return 0; 2058 + 2059 + drm_atomic_crtc_state_for_each_plane_state(plane, other_state, state) { 2060 + rect = plane_state->dst; 2061 + 2062 + tegra = to_tegra_plane(other_state->plane); 2063 + 2064 + if (!other_state->visible || !other_state->fb) 2065 + continue; 2066 + 2067 + /* 2068 + * Ignore cursor plane overlaps because it's not practical to 2069 + * assume that it contributes to the bandwidth in overlapping 2070 + * area if window width is small. 2071 + */ 2072 + if (tegra_plane_is_cursor(other_state)) 2073 + continue; 2074 + 2075 + if (drm_rect_intersect(&rect, &other_state->dst)) 2076 + overlap_mask |= BIT(tegra->index); 2077 + } 2078 + 2079 + return overlap_mask; 2080 + } 2081 + 2082 + static int tegra_crtc_calculate_memory_bandwidth(struct drm_crtc *crtc, 2083 + struct drm_atomic_state *state) 2084 + { 2085 + ulong overlap_mask[TEGRA_DC_LEGACY_PLANES_NUM] = {}, mask; 2086 + u32 plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM] = {}; 2087 + bool all_planes_overlap_simultaneously = true; 2088 + const struct tegra_plane_state *tegra_state; 2089 + const struct drm_plane_state *plane_state; 2090 + struct tegra_dc *dc = to_tegra_dc(crtc); 2091 + const struct drm_crtc_state *old_state; 2092 + struct drm_crtc_state *new_state; 2093 + struct tegra_plane *tegra; 2094 + struct drm_plane *plane; 2095 + 2096 + /* 2097 + * The nv-display uses shared planes. The algorithm below assumes 2098 + * maximum 3 planes per-CRTC, this assumption isn't applicable to 2099 + * the nv-display. Note that T124 support has additional windows, 2100 + * but currently they aren't supported by the driver. 2101 + */ 2102 + if (dc->soc->has_nvdisplay) 2103 + return 0; 2104 + 2105 + new_state = drm_atomic_get_new_crtc_state(state, crtc); 2106 + old_state = drm_atomic_get_old_crtc_state(state, crtc); 2107 + 2108 + /* 2109 + * For overlapping planes pixel's data is fetched for each plane at 2110 + * the same time, hence bandwidths are accumulated in this case. 2111 + * This needs to be taken into account for calculating total bandwidth 2112 + * consumed by all planes. 2113 + * 2114 + * Here we get the overlapping state of each plane, which is a 2115 + * bitmask of plane indices telling with what planes there is an 2116 + * overlap. Note that bitmask[plane] includes BIT(plane) in order 2117 + * to make further code nicer and simpler. 2118 + */ 2119 + drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) { 2120 + tegra_state = to_const_tegra_plane_state(plane_state); 2121 + tegra = to_tegra_plane(plane); 2122 + 2123 + if (WARN_ON_ONCE(tegra->index >= TEGRA_DC_LEGACY_PLANES_NUM)) 2124 + return -EINVAL; 2125 + 2126 + plane_peak_bw[tegra->index] = tegra_state->peak_memory_bandwidth; 2127 + mask = tegra_plane_overlap_mask(new_state, plane_state); 2128 + overlap_mask[tegra->index] = mask; 2129 + 2130 + if (hweight_long(mask) != 3) 2131 + all_planes_overlap_simultaneously = false; 2132 + } 2133 + 2134 + /* 2135 + * Then we calculate maximum bandwidth of each plane state. 2136 + * The bandwidth includes the plane BW + BW of the "simultaneously" 2137 + * overlapping planes, where "simultaneously" means areas where DC 2138 + * fetches from the planes simultaneously during of scan-out process. 2139 + * 2140 + * For example, if plane A overlaps with planes B and C, but B and C 2141 + * don't overlap, then the peak bandwidth will be either in area where 2142 + * A-and-B or A-and-C planes overlap. 2143 + * 2144 + * The plane_peak_bw[] contains peak memory bandwidth values of 2145 + * each plane, this information is needed by interconnect provider 2146 + * in order to set up latency allowance based on the peak BW, see 2147 + * tegra_crtc_update_memory_bandwidth(). 2148 + */ 2149 + drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) { 2150 + u32 i, old_peak_bw, new_peak_bw, overlap_bw = 0; 2151 + 2152 + /* 2153 + * Note that plane's atomic check doesn't touch the 2154 + * total_peak_memory_bandwidth of enabled plane, hence the 2155 + * current state contains the old bandwidth state from the 2156 + * previous CRTC commit. 2157 + */ 2158 + tegra_state = to_const_tegra_plane_state(plane_state); 2159 + tegra = to_tegra_plane(plane); 2160 + 2161 + for_each_set_bit(i, &overlap_mask[tegra->index], 3) { 2162 + if (i == tegra->index) 2163 + continue; 2164 + 2165 + if (all_planes_overlap_simultaneously) 2166 + overlap_bw += plane_peak_bw[i]; 2167 + else 2168 + overlap_bw = max(overlap_bw, plane_peak_bw[i]); 2169 + } 2170 + 2171 + new_peak_bw = plane_peak_bw[tegra->index] + overlap_bw; 2172 + old_peak_bw = tegra_state->total_peak_memory_bandwidth; 2173 + 2174 + /* 2175 + * If plane's peak bandwidth changed (for example plane isn't 2176 + * overlapped anymore) and plane isn't in the atomic state, 2177 + * then add plane to the state in order to have the bandwidth 2178 + * updated. 2179 + */ 2180 + if (old_peak_bw != new_peak_bw) { 2181 + struct tegra_plane_state *new_tegra_state; 2182 + struct drm_plane_state *new_plane_state; 2183 + 2184 + new_plane_state = drm_atomic_get_plane_state(state, plane); 2185 + if (IS_ERR(new_plane_state)) 2186 + return PTR_ERR(new_plane_state); 2187 + 2188 + new_tegra_state = to_tegra_plane_state(new_plane_state); 2189 + new_tegra_state->total_peak_memory_bandwidth = new_peak_bw; 2190 + } 2191 + } 2192 + 2193 + return 0; 2194 + } 2195 + 2196 + static int tegra_crtc_atomic_check(struct drm_crtc *crtc, 2197 + struct drm_atomic_state *state) 2198 + { 2199 + int err; 2200 + 2201 + err = tegra_crtc_calculate_memory_bandwidth(crtc, state); 2202 + if (err) 2203 + return err; 2204 + 2205 + return 0; 2206 + } 2207 + 2208 + void tegra_crtc_atomic_post_commit(struct drm_crtc *crtc, 2209 + struct drm_atomic_state *state) 2210 + { 2211 + /* 2212 + * Display bandwidth is allowed to go down only once hardware state 2213 + * is known to be armed, i.e. state was committed and VBLANK event 2214 + * received. 2215 + */ 2216 + tegra_crtc_update_memory_bandwidth(crtc, state, false); 2217 + } 2218 + 2152 2219 static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { 2220 + .atomic_check = tegra_crtc_atomic_check, 2153 2221 .atomic_begin = tegra_crtc_atomic_begin, 2154 2222 .atomic_flush = tegra_crtc_atomic_flush, 2155 2223 .atomic_enable = tegra_crtc_atomic_enable, ··· 2675 2343 .overlay_formats = tegra20_overlay_formats, 2676 2344 .modifiers = tegra20_modifiers, 2677 2345 .has_win_a_without_filters = true, 2346 + .has_win_b_vfilter_mem_client = true, 2678 2347 .has_win_c_without_vert_filter = true, 2348 + .plane_tiled_memory_bandwidth_x2 = false, 2679 2349 }; 2680 2350 2681 2351 static const struct tegra_dc_soc_info tegra30_dc_soc_info = { ··· 2697 2363 .overlay_formats = tegra20_overlay_formats, 2698 2364 .modifiers = tegra20_modifiers, 2699 2365 .has_win_a_without_filters = false, 2366 + .has_win_b_vfilter_mem_client = true, 2700 2367 .has_win_c_without_vert_filter = false, 2368 + .plane_tiled_memory_bandwidth_x2 = true, 2701 2369 }; 2702 2370 2703 2371 static const struct tegra_dc_soc_info tegra114_dc_soc_info = { ··· 2719 2383 .overlay_formats = tegra114_overlay_formats, 2720 2384 .modifiers = tegra20_modifiers, 2721 2385 .has_win_a_without_filters = false, 2386 + .has_win_b_vfilter_mem_client = false, 2722 2387 .has_win_c_without_vert_filter = false, 2388 + .plane_tiled_memory_bandwidth_x2 = true, 2723 2389 }; 2724 2390 2725 2391 static const struct tegra_dc_soc_info tegra124_dc_soc_info = { ··· 2741 2403 .overlay_formats = tegra124_overlay_formats, 2742 2404 .modifiers = tegra124_modifiers, 2743 2405 .has_win_a_without_filters = false, 2406 + .has_win_b_vfilter_mem_client = false, 2744 2407 .has_win_c_without_vert_filter = false, 2408 + .plane_tiled_memory_bandwidth_x2 = false, 2745 2409 }; 2746 2410 2747 2411 static const struct tegra_dc_soc_info tegra210_dc_soc_info = { ··· 2763 2423 .overlay_formats = tegra114_overlay_formats, 2764 2424 .modifiers = tegra124_modifiers, 2765 2425 .has_win_a_without_filters = false, 2426 + .has_win_b_vfilter_mem_client = false, 2766 2427 .has_win_c_without_vert_filter = false, 2428 + .plane_tiled_memory_bandwidth_x2 = false, 2767 2429 }; 2768 2430 2769 2431 static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { ··· 2815 2473 .has_nvdisplay = true, 2816 2474 .wgrps = tegra186_dc_wgrps, 2817 2475 .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), 2476 + .plane_tiled_memory_bandwidth_x2 = false, 2818 2477 }; 2819 2478 2820 2479 static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = { ··· 2865 2522 .has_nvdisplay = true, 2866 2523 .wgrps = tegra194_dc_wgrps, 2867 2524 .num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps), 2525 + .plane_tiled_memory_bandwidth_x2 = false, 2868 2526 }; 2869 2527 2870 2528 static const struct of_device_id tegra_dc_of_match[] = {
+12
drivers/gpu/drm/tegra/dc.h
··· 15 15 16 16 struct tegra_output; 17 17 18 + #define TEGRA_DC_LEGACY_PLANES_NUM 7 19 + 18 20 struct tegra_dc_state { 19 21 struct drm_crtc_state base; 20 22 ··· 33 31 return container_of(state, struct tegra_dc_state, base); 34 32 35 33 return NULL; 34 + } 35 + 36 + static inline const struct tegra_dc_state * 37 + to_const_dc_state(const struct drm_crtc_state *state) 38 + { 39 + return to_dc_state((struct drm_crtc_state *)state); 36 40 } 37 41 38 42 struct tegra_dc_stats { ··· 74 66 unsigned int num_overlay_formats; 75 67 const u64 *modifiers; 76 68 bool has_win_a_without_filters; 69 + bool has_win_b_vfilter_mem_client; 77 70 bool has_win_c_without_vert_filter; 71 + bool plane_tiled_memory_bandwidth_x2; 78 72 }; 79 73 80 74 struct tegra_dc { ··· 162 152 struct drm_crtc_state *crtc_state, 163 153 struct clk *clk, unsigned long pclk, 164 154 unsigned int div); 155 + void tegra_crtc_atomic_post_commit(struct drm_crtc *crtc, 156 + struct drm_atomic_state *state); 165 157 166 158 /* from rgb.c */ 167 159 int tegra_dc_rgb_probe(struct tegra_dc *dc);
+15 -1
drivers/gpu/drm/tegra/drm.c
··· 21 21 #include <drm/drm_prime.h> 22 22 #include <drm/drm_vblank.h> 23 23 24 - #include "uapi.h" 24 + #include "dc.h" 25 25 #include "drm.h" 26 26 #include "gem.h" 27 + #include "uapi.h" 27 28 28 29 #define DRIVER_NAME "tegra" 29 30 #define DRIVER_DESC "NVIDIA Tegra graphics" ··· 57 56 .atomic_commit = drm_atomic_helper_commit, 58 57 }; 59 58 59 + static void tegra_atomic_post_commit(struct drm_device *drm, 60 + struct drm_atomic_state *old_state) 61 + { 62 + struct drm_crtc_state *old_crtc_state __maybe_unused; 63 + struct drm_crtc *crtc; 64 + unsigned int i; 65 + 66 + for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) 67 + tegra_crtc_atomic_post_commit(crtc, old_state); 68 + } 69 + 60 70 static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state) 61 71 { 62 72 struct drm_device *drm = old_state->dev; ··· 87 75 } else { 88 76 drm_atomic_helper_commit_tail_rpm(old_state); 89 77 } 78 + 79 + tegra_atomic_post_commit(drm, old_state); 90 80 } 91 81 92 82 static const struct drm_mode_config_helper_funcs
+117
drivers/gpu/drm/tegra/plane.c
··· 4 4 */ 5 5 6 6 #include <linux/iommu.h> 7 + #include <linux/interconnect.h> 7 8 8 9 #include <drm/drm_atomic.h> 9 10 #include <drm/drm_atomic_helper.h> ··· 65 64 copy->reflect_x = state->reflect_x; 66 65 copy->reflect_y = state->reflect_y; 67 66 copy->opaque = state->opaque; 67 + copy->total_peak_memory_bandwidth = state->total_peak_memory_bandwidth; 68 + copy->peak_memory_bandwidth = state->peak_memory_bandwidth; 69 + copy->avg_memory_bandwidth = state->avg_memory_bandwidth; 68 70 69 71 for (i = 0; i < 2; i++) 70 72 copy->blending[i] = state->blending[i]; ··· 248 244 tegra_dc_unpin(dc, to_tegra_plane_state(state)); 249 245 } 250 246 247 + static int tegra_plane_calculate_memory_bandwidth(struct drm_plane_state *state) 248 + { 249 + struct tegra_plane_state *tegra_state = to_tegra_plane_state(state); 250 + unsigned int i, bpp, dst_w, dst_h, src_w, src_h, mul; 251 + const struct tegra_dc_soc_info *soc; 252 + const struct drm_format_info *fmt; 253 + struct drm_crtc_state *crtc_state; 254 + u64 avg_bandwidth, peak_bandwidth; 255 + 256 + if (!state->visible) 257 + return 0; 258 + 259 + crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc); 260 + if (!crtc_state) 261 + return -EINVAL; 262 + 263 + src_w = drm_rect_width(&state->src) >> 16; 264 + src_h = drm_rect_height(&state->src) >> 16; 265 + dst_w = drm_rect_width(&state->dst); 266 + dst_h = drm_rect_height(&state->dst); 267 + 268 + fmt = state->fb->format; 269 + soc = to_tegra_dc(state->crtc)->soc; 270 + 271 + /* 272 + * Note that real memory bandwidth vary depending on format and 273 + * memory layout, we are not taking that into account because small 274 + * estimation error isn't important since bandwidth is rounded up 275 + * anyway. 276 + */ 277 + for (i = 0, bpp = 0; i < fmt->num_planes; i++) { 278 + unsigned int bpp_plane = fmt->cpp[i] * 8; 279 + 280 + /* 281 + * Sub-sampling is relevant for chroma planes only and vertical 282 + * readouts are not cached, hence only horizontal sub-sampling 283 + * matters. 284 + */ 285 + if (i > 0) 286 + bpp_plane /= fmt->hsub; 287 + 288 + bpp += bpp_plane; 289 + } 290 + 291 + /* average bandwidth in kbytes/sec */ 292 + avg_bandwidth = min(src_w, dst_w) * min(src_h, dst_h); 293 + avg_bandwidth *= drm_mode_vrefresh(&crtc_state->adjusted_mode); 294 + avg_bandwidth = DIV_ROUND_UP(avg_bandwidth * bpp, 8) + 999; 295 + do_div(avg_bandwidth, 1000); 296 + 297 + /* mode.clock in kHz, peak bandwidth in kbytes/sec */ 298 + peak_bandwidth = DIV_ROUND_UP(crtc_state->adjusted_mode.clock * bpp, 8); 299 + 300 + /* 301 + * Tegra30/114 Memory Controller can't interleave DC memory requests 302 + * for the tiled windows because DC uses 16-bytes atom, while DDR3 303 + * uses 32-bytes atom. Hence there is x2 memory overfetch for tiled 304 + * framebuffer and DDR3 on these SoCs. 305 + */ 306 + if (soc->plane_tiled_memory_bandwidth_x2 && 307 + tegra_state->tiling.mode == TEGRA_BO_TILING_MODE_TILED) 308 + mul = 2; 309 + else 310 + mul = 1; 311 + 312 + /* ICC bandwidth in kbytes/sec */ 313 + tegra_state->peak_memory_bandwidth = kBps_to_icc(peak_bandwidth) * mul; 314 + tegra_state->avg_memory_bandwidth = kBps_to_icc(avg_bandwidth) * mul; 315 + 316 + return 0; 317 + } 318 + 251 319 int tegra_plane_state_add(struct tegra_plane *plane, 252 320 struct drm_plane_state *state) 253 321 { ··· 335 259 /* Check plane state for visibility and calculate clipping bounds */ 336 260 err = drm_atomic_helper_check_plane_state(state, crtc_state, 337 261 0, INT_MAX, true, true); 262 + if (err < 0) 263 + return err; 264 + 265 + err = tegra_plane_calculate_memory_bandwidth(state); 338 266 if (err < 0) 339 267 return err; 340 268 ··· 723 643 err = tegra_plane_setup_transparency(tegra, state); 724 644 if (err < 0) 725 645 return err; 646 + 647 + return 0; 648 + } 649 + 650 + static const char * const tegra_plane_icc_names[TEGRA_DC_LEGACY_PLANES_NUM] = { 651 + "wina", "winb", "winc", NULL, NULL, NULL, "cursor", 652 + }; 653 + 654 + int tegra_plane_interconnect_init(struct tegra_plane *plane) 655 + { 656 + const char *icc_name = tegra_plane_icc_names[plane->index]; 657 + struct device *dev = plane->dc->dev; 658 + struct tegra_dc *dc = plane->dc; 659 + int err; 660 + 661 + if (WARN_ON(plane->index >= TEGRA_DC_LEGACY_PLANES_NUM) || 662 + WARN_ON(!tegra_plane_icc_names[plane->index])) 663 + return -EINVAL; 664 + 665 + plane->icc_mem = devm_of_icc_get(dev, icc_name); 666 + err = PTR_ERR_OR_ZERO(plane->icc_mem); 667 + if (err) { 668 + dev_err_probe(dev, err, "failed to get %s interconnect\n", 669 + icc_name); 670 + return err; 671 + } 672 + 673 + /* plane B on T20/30 has a dedicated memory client for a 6-tap vertical filter */ 674 + if (plane->index == 1 && dc->soc->has_win_b_vfilter_mem_client) { 675 + plane->icc_mem_vfilter = devm_of_icc_get(dev, "winb-vfilter"); 676 + err = PTR_ERR_OR_ZERO(plane->icc_mem_vfilter); 677 + if (err) { 678 + dev_err_probe(dev, err, "failed to get %s interconnect\n", 679 + "winb-vfilter"); 680 + return err; 681 + } 682 + } 726 683 727 684 return 0; 728 685 }
+16
drivers/gpu/drm/tegra/plane.h
··· 8 8 9 9 #include <drm/drm_plane.h> 10 10 11 + struct icc_path; 11 12 struct tegra_bo; 12 13 struct tegra_dc; 13 14 ··· 17 16 struct tegra_dc *dc; 18 17 unsigned int offset; 19 18 unsigned int index; 19 + 20 + struct icc_path *icc_mem; 21 + struct icc_path *icc_mem_vfilter; 20 22 }; 21 23 22 24 struct tegra_cursor { ··· 56 52 /* used for legacy blending support only */ 57 53 struct tegra_plane_legacy_blending_state blending[2]; 58 54 bool opaque; 55 + 56 + /* bandwidths are in ICC units, i.e. kbytes/sec */ 57 + u32 total_peak_memory_bandwidth; 58 + u32 peak_memory_bandwidth; 59 + u32 avg_memory_bandwidth; 59 60 }; 60 61 61 62 static inline struct tegra_plane_state * ··· 70 61 return container_of(state, struct tegra_plane_state, base); 71 62 72 63 return NULL; 64 + } 65 + 66 + static inline const struct tegra_plane_state * 67 + to_const_tegra_plane_state(const struct drm_plane_state *state) 68 + { 69 + return to_tegra_plane_state((struct drm_plane_state *)state); 73 70 } 74 71 75 72 extern const struct drm_plane_funcs tegra_plane_funcs; ··· 93 78 bool tegra_plane_format_is_yuv(unsigned int format, bool *planar, unsigned int *bpc); 94 79 int tegra_plane_setup_legacy_state(struct tegra_plane *tegra, 95 80 struct tegra_plane_state *state); 81 + int tegra_plane_interconnect_init(struct tegra_plane *plane); 96 82 97 83 #endif /* TEGRA_PLANE_H */