1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2dee8268fSThierry Reding /* 3dee8268fSThierry Reding * Copyright (C) 2012 Avionic Design GmbH 4dee8268fSThierry Reding * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 5dee8268fSThierry Reding */ 6dee8268fSThierry Reding 7dee8268fSThierry Reding #include <linux/clk.h> 8dee8268fSThierry Reding #include <linux/debugfs.h> 9eb1df694SSam Ravnborg #include <linux/delay.h> 10df06b759SThierry Reding #include <linux/iommu.h> 1104d5d5dfSDmitry Osipenko #include <linux/interconnect.h> 12eb1df694SSam Ravnborg #include <linux/module.h> 13b9ff7aeaSThierry Reding #include <linux/of_device.h> 1433a8eb8dSThierry Reding #include <linux/pm_runtime.h> 15ca48080aSStephen Warren #include <linux/reset.h> 16dee8268fSThierry Reding 179c012700SThierry Reding #include <soc/tegra/pmc.h> 189c012700SThierry Reding 19eb1df694SSam Ravnborg #include <drm/drm_atomic.h> 20eb1df694SSam Ravnborg #include <drm/drm_atomic_helper.h> 21eb1df694SSam Ravnborg #include <drm/drm_debugfs.h> 22eb1df694SSam Ravnborg #include <drm/drm_fourcc.h> 23eb1df694SSam Ravnborg #include <drm/drm_plane_helper.h> 24eb1df694SSam Ravnborg #include <drm/drm_vblank.h> 25eb1df694SSam Ravnborg 26dee8268fSThierry Reding #include "dc.h" 27dee8268fSThierry Reding #include "drm.h" 28dee8268fSThierry Reding #include "gem.h" 2947307954SThierry Reding #include "hub.h" 305acd3514SThierry Reding #include "plane.h" 31dee8268fSThierry Reding 32b7e0b04aSMaarten Lankhorst static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, 33b7e0b04aSMaarten Lankhorst struct drm_crtc_state *state); 34b7e0b04aSMaarten Lankhorst 35791ddb1eSThierry Reding static void tegra_dc_stats_reset(struct tegra_dc_stats *stats) 36791ddb1eSThierry Reding { 37791ddb1eSThierry Reding stats->frames = 0; 38791ddb1eSThierry Reding stats->vblank = 0; 39791ddb1eSThierry Reding stats->underflow = 0; 40791ddb1eSThierry Reding stats->overflow = 0; 41791ddb1eSThierry Reding } 42791ddb1eSThierry Reding 431087fac1SThierry Reding /* Reads the active copy of a register. */ 4486df256fSThierry Reding static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) 4586df256fSThierry Reding { 4686df256fSThierry Reding u32 value; 4786df256fSThierry Reding 4886df256fSThierry Reding tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); 4986df256fSThierry Reding value = tegra_dc_readl(dc, offset); 5086df256fSThierry Reding tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); 5186df256fSThierry Reding 5286df256fSThierry Reding return value; 5386df256fSThierry Reding } 5486df256fSThierry Reding 551087fac1SThierry Reding static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, 561087fac1SThierry Reding unsigned int offset) 571087fac1SThierry Reding { 581087fac1SThierry Reding if (offset >= 0x500 && offset <= 0x638) { 591087fac1SThierry Reding offset = 0x000 + (offset - 0x500); 601087fac1SThierry Reding return plane->offset + offset; 611087fac1SThierry Reding } 621087fac1SThierry Reding 631087fac1SThierry Reding if (offset >= 0x700 && offset <= 0x719) { 641087fac1SThierry Reding offset = 0x180 + (offset - 0x700); 651087fac1SThierry Reding return plane->offset + offset; 661087fac1SThierry Reding } 671087fac1SThierry Reding 681087fac1SThierry Reding if (offset >= 0x800 && offset <= 0x839) { 691087fac1SThierry Reding offset = 0x1c0 + (offset - 0x800); 701087fac1SThierry Reding return plane->offset + offset; 711087fac1SThierry Reding } 721087fac1SThierry Reding 731087fac1SThierry Reding dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); 741087fac1SThierry Reding 751087fac1SThierry Reding return plane->offset + offset; 761087fac1SThierry Reding } 771087fac1SThierry Reding 781087fac1SThierry Reding static inline u32 tegra_plane_readl(struct tegra_plane *plane, 791087fac1SThierry Reding unsigned int offset) 801087fac1SThierry Reding { 811087fac1SThierry Reding return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); 821087fac1SThierry Reding } 831087fac1SThierry Reding 841087fac1SThierry Reding static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value, 851087fac1SThierry Reding unsigned int offset) 861087fac1SThierry Reding { 871087fac1SThierry Reding tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); 881087fac1SThierry Reding } 891087fac1SThierry Reding 90c57997bcSThierry Reding bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) 91c57997bcSThierry Reding { 92c57997bcSThierry Reding struct device_node *np = dc->dev->of_node; 93c57997bcSThierry Reding struct of_phandle_iterator it; 94c57997bcSThierry Reding int err; 95c57997bcSThierry Reding 96c57997bcSThierry Reding of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) 97c57997bcSThierry Reding if (it.node == dev->of_node) 98c57997bcSThierry Reding return true; 99c57997bcSThierry Reding 100c57997bcSThierry Reding return false; 101c57997bcSThierry Reding } 102c57997bcSThierry Reding 10386df256fSThierry Reding /* 104d700ba7aSThierry Reding * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the 105d700ba7aSThierry Reding * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy. 106d700ba7aSThierry Reding * Latching happens mmediately if the display controller is in STOP mode or 107d700ba7aSThierry Reding * on the next frame boundary otherwise. 108d700ba7aSThierry Reding * 109d700ba7aSThierry Reding * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The 110d700ba7aSThierry Reding * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits 111d700ba7aSThierry Reding * are written. When the *_ACT_REQ bits are written, the ARM copy is latched 112d700ba7aSThierry Reding * into the ACTIVE copy, either immediately if the display controller is in 113d700ba7aSThierry Reding * STOP mode, or at the next frame boundary otherwise. 114d700ba7aSThierry Reding */ 11562b9e063SThierry Reding void tegra_dc_commit(struct tegra_dc *dc) 116205d48edSThierry Reding { 117205d48edSThierry Reding tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); 118205d48edSThierry Reding tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); 119205d48edSThierry Reding } 120205d48edSThierry Reding 12110288eeaSThierry Reding static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v, 12210288eeaSThierry Reding unsigned int bpp) 12310288eeaSThierry Reding { 12410288eeaSThierry Reding fixed20_12 outf = dfixed_init(out); 12510288eeaSThierry Reding fixed20_12 inf = dfixed_init(in); 12610288eeaSThierry Reding u32 dda_inc; 12710288eeaSThierry Reding int max; 12810288eeaSThierry Reding 12910288eeaSThierry Reding if (v) 13010288eeaSThierry Reding max = 15; 13110288eeaSThierry Reding else { 13210288eeaSThierry Reding switch (bpp) { 13310288eeaSThierry Reding case 2: 13410288eeaSThierry Reding max = 8; 13510288eeaSThierry Reding break; 13610288eeaSThierry Reding 13710288eeaSThierry Reding default: 13810288eeaSThierry Reding WARN_ON_ONCE(1); 139df561f66SGustavo A. R. Silva fallthrough; 14010288eeaSThierry Reding case 4: 14110288eeaSThierry Reding max = 4; 14210288eeaSThierry Reding break; 14310288eeaSThierry Reding } 14410288eeaSThierry Reding } 14510288eeaSThierry Reding 14610288eeaSThierry Reding outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1)); 14710288eeaSThierry Reding inf.full -= dfixed_const(1); 14810288eeaSThierry Reding 14910288eeaSThierry Reding dda_inc = dfixed_div(inf, outf); 15010288eeaSThierry Reding dda_inc = min_t(u32, dda_inc, dfixed_const(max)); 15110288eeaSThierry Reding 15210288eeaSThierry Reding return dda_inc; 15310288eeaSThierry Reding } 15410288eeaSThierry Reding 15510288eeaSThierry Reding static inline u32 compute_initial_dda(unsigned int in) 15610288eeaSThierry Reding { 15710288eeaSThierry Reding fixed20_12 inf = dfixed_init(in); 15810288eeaSThierry Reding return dfixed_frac(inf); 15910288eeaSThierry Reding } 16010288eeaSThierry Reding 161ab7d3f58SThierry Reding static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) 162ab7d3f58SThierry Reding { 163ebae8d07SThierry Reding u32 background[3] = { 164ebae8d07SThierry Reding BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, 165ebae8d07SThierry Reding BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, 166ebae8d07SThierry Reding BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, 167ebae8d07SThierry Reding }; 168ebae8d07SThierry Reding u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) | 169ebae8d07SThierry Reding BLEND_COLOR_KEY_NONE; 170ebae8d07SThierry Reding u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); 171ebae8d07SThierry Reding struct tegra_plane_state *state; 1723dae08bcSDmitry Osipenko u32 blending[2]; 173ebae8d07SThierry Reding unsigned int i; 174ebae8d07SThierry Reding 1753dae08bcSDmitry Osipenko /* disable blending for non-overlapping case */ 176ebae8d07SThierry Reding tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); 177ebae8d07SThierry Reding tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); 178ab7d3f58SThierry Reding 1793dae08bcSDmitry Osipenko state = to_tegra_plane_state(plane->base.state); 1803dae08bcSDmitry Osipenko 1813dae08bcSDmitry Osipenko if (state->opaque) { 1823dae08bcSDmitry Osipenko /* 1833dae08bcSDmitry Osipenko * Since custom fix-weight blending isn't utilized and weight 1843dae08bcSDmitry Osipenko * of top window is set to max, we can enforce dependent 1853dae08bcSDmitry Osipenko * blending which in this case results in transparent bottom 1863dae08bcSDmitry Osipenko * window if top window is opaque and if top window enables 1873dae08bcSDmitry Osipenko * alpha blending, then bottom window is getting alpha value 1883dae08bcSDmitry Osipenko * of 1 minus the sum of alpha components of the overlapping 1893dae08bcSDmitry Osipenko * plane. 1903dae08bcSDmitry Osipenko */ 1913dae08bcSDmitry Osipenko background[0] |= BLEND_CONTROL_DEPENDENT; 1923dae08bcSDmitry Osipenko background[1] |= BLEND_CONTROL_DEPENDENT; 1933dae08bcSDmitry Osipenko 1943dae08bcSDmitry Osipenko /* 1953dae08bcSDmitry Osipenko * The region where three windows overlap is the intersection 1963dae08bcSDmitry Osipenko * of the two regions where two windows overlap. It contributes 1973dae08bcSDmitry Osipenko * to the area if all of the windows on top of it have an alpha 1983dae08bcSDmitry Osipenko * component. 1993dae08bcSDmitry Osipenko */ 2003dae08bcSDmitry Osipenko switch (state->base.normalized_zpos) { 2013dae08bcSDmitry Osipenko case 0: 2023dae08bcSDmitry Osipenko if (state->blending[0].alpha && 2033dae08bcSDmitry Osipenko state->blending[1].alpha) 2043dae08bcSDmitry Osipenko background[2] |= BLEND_CONTROL_DEPENDENT; 2053dae08bcSDmitry Osipenko break; 2063dae08bcSDmitry Osipenko 2073dae08bcSDmitry Osipenko case 1: 2083dae08bcSDmitry Osipenko background[2] |= BLEND_CONTROL_DEPENDENT; 2093dae08bcSDmitry Osipenko break; 2103dae08bcSDmitry Osipenko } 2113dae08bcSDmitry Osipenko } else { 2123dae08bcSDmitry Osipenko /* 2133dae08bcSDmitry Osipenko * Enable alpha blending if pixel format has an alpha 2143dae08bcSDmitry Osipenko * component. 2153dae08bcSDmitry Osipenko */ 2163dae08bcSDmitry Osipenko foreground |= BLEND_CONTROL_ALPHA; 2173dae08bcSDmitry Osipenko 2183dae08bcSDmitry Osipenko /* 2193dae08bcSDmitry Osipenko * If any of the windows on top of this window is opaque, it 2203dae08bcSDmitry Osipenko * will completely conceal this window within that area. If 2213dae08bcSDmitry Osipenko * top window has an alpha component, it is blended over the 2223dae08bcSDmitry Osipenko * bottom window. 2233dae08bcSDmitry Osipenko */ 2243dae08bcSDmitry Osipenko for (i = 0; i < 2; i++) { 2253dae08bcSDmitry Osipenko if (state->blending[i].alpha && 2263dae08bcSDmitry Osipenko state->blending[i].top) 2273dae08bcSDmitry Osipenko background[i] |= BLEND_CONTROL_DEPENDENT; 2283dae08bcSDmitry Osipenko } 2293dae08bcSDmitry Osipenko 2303dae08bcSDmitry Osipenko switch (state->base.normalized_zpos) { 2313dae08bcSDmitry Osipenko case 0: 2323dae08bcSDmitry Osipenko if (state->blending[0].alpha && 2333dae08bcSDmitry Osipenko state->blending[1].alpha) 2343dae08bcSDmitry Osipenko background[2] |= BLEND_CONTROL_DEPENDENT; 2353dae08bcSDmitry Osipenko break; 2363dae08bcSDmitry Osipenko 2373dae08bcSDmitry Osipenko case 1: 2383dae08bcSDmitry Osipenko /* 2393dae08bcSDmitry Osipenko * When both middle and topmost windows have an alpha, 2403dae08bcSDmitry Osipenko * these windows a mixed together and then the result 2413dae08bcSDmitry Osipenko * is blended over the bottom window. 2423dae08bcSDmitry Osipenko */ 2433dae08bcSDmitry Osipenko if (state->blending[0].alpha && 2443dae08bcSDmitry Osipenko state->blending[0].top) 2453dae08bcSDmitry Osipenko background[2] |= BLEND_CONTROL_ALPHA; 2463dae08bcSDmitry Osipenko 2473dae08bcSDmitry Osipenko if (state->blending[1].alpha && 2483dae08bcSDmitry Osipenko state->blending[1].top) 2493dae08bcSDmitry Osipenko background[2] |= BLEND_CONTROL_ALPHA; 2503dae08bcSDmitry Osipenko break; 2513dae08bcSDmitry Osipenko } 2523dae08bcSDmitry Osipenko } 2533dae08bcSDmitry Osipenko 2543dae08bcSDmitry Osipenko switch (state->base.normalized_zpos) { 255ab7d3f58SThierry Reding case 0: 256ebae8d07SThierry Reding tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X); 257ebae8d07SThierry Reding tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); 258ebae8d07SThierry Reding tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); 259ab7d3f58SThierry Reding break; 260ab7d3f58SThierry Reding 261ab7d3f58SThierry Reding case 1: 2623dae08bcSDmitry Osipenko /* 2633dae08bcSDmitry Osipenko * If window B / C is topmost, then X / Y registers are 2643dae08bcSDmitry Osipenko * matching the order of blending[...] state indices, 2653dae08bcSDmitry Osipenko * otherwise a swap is required. 2663dae08bcSDmitry Osipenko */ 2673dae08bcSDmitry Osipenko if (!state->blending[0].top && state->blending[1].top) { 2683dae08bcSDmitry Osipenko blending[0] = foreground; 2693dae08bcSDmitry Osipenko blending[1] = background[1]; 2703dae08bcSDmitry Osipenko } else { 2713dae08bcSDmitry Osipenko blending[0] = background[0]; 2723dae08bcSDmitry Osipenko blending[1] = foreground; 2733dae08bcSDmitry Osipenko } 2743dae08bcSDmitry Osipenko 2753dae08bcSDmitry Osipenko tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X); 2763dae08bcSDmitry Osipenko tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y); 277ebae8d07SThierry Reding tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); 278ab7d3f58SThierry Reding break; 279ab7d3f58SThierry Reding 280ab7d3f58SThierry Reding case 2: 281ebae8d07SThierry Reding tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); 282ebae8d07SThierry Reding tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y); 283ebae8d07SThierry Reding tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY); 284ab7d3f58SThierry Reding break; 285ab7d3f58SThierry Reding } 286ab7d3f58SThierry Reding } 287ab7d3f58SThierry Reding 288ab7d3f58SThierry Reding static void tegra_plane_setup_blending(struct tegra_plane *plane, 289ab7d3f58SThierry Reding const struct tegra_dc_window *window) 290ab7d3f58SThierry Reding { 291ab7d3f58SThierry Reding u32 value; 292ab7d3f58SThierry Reding 293ab7d3f58SThierry Reding value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | 294ab7d3f58SThierry Reding BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | 295ab7d3f58SThierry Reding BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; 296ab7d3f58SThierry Reding tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT); 297ab7d3f58SThierry Reding 298ab7d3f58SThierry Reding value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | 299ab7d3f58SThierry Reding BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | 300ab7d3f58SThierry Reding BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; 301ab7d3f58SThierry Reding tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT); 302ab7d3f58SThierry Reding 303ab7d3f58SThierry Reding value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos); 304ab7d3f58SThierry Reding tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL); 305ab7d3f58SThierry Reding } 306ab7d3f58SThierry Reding 307acc6a3a9SDmitry Osipenko static bool 308acc6a3a9SDmitry Osipenko tegra_plane_use_horizontal_filtering(struct tegra_plane *plane, 309acc6a3a9SDmitry Osipenko const struct tegra_dc_window *window) 310acc6a3a9SDmitry Osipenko { 311acc6a3a9SDmitry Osipenko struct tegra_dc *dc = plane->dc; 312acc6a3a9SDmitry Osipenko 313acc6a3a9SDmitry Osipenko if (window->src.w == window->dst.w) 314acc6a3a9SDmitry Osipenko return false; 315acc6a3a9SDmitry Osipenko 316acc6a3a9SDmitry Osipenko if (plane->index == 0 && dc->soc->has_win_a_without_filters) 317acc6a3a9SDmitry Osipenko return false; 318acc6a3a9SDmitry Osipenko 319acc6a3a9SDmitry Osipenko return true; 320acc6a3a9SDmitry Osipenko } 321acc6a3a9SDmitry Osipenko 322acc6a3a9SDmitry Osipenko static bool 323acc6a3a9SDmitry Osipenko tegra_plane_use_vertical_filtering(struct tegra_plane *plane, 324acc6a3a9SDmitry Osipenko const struct tegra_dc_window *window) 325acc6a3a9SDmitry Osipenko { 326acc6a3a9SDmitry Osipenko struct tegra_dc *dc = plane->dc; 327acc6a3a9SDmitry Osipenko 328acc6a3a9SDmitry Osipenko if (window->src.h == window->dst.h) 329acc6a3a9SDmitry Osipenko return false; 330acc6a3a9SDmitry Osipenko 331acc6a3a9SDmitry Osipenko if (plane->index == 0 && dc->soc->has_win_a_without_filters) 332acc6a3a9SDmitry Osipenko return false; 333acc6a3a9SDmitry Osipenko 334acc6a3a9SDmitry Osipenko if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter) 335acc6a3a9SDmitry Osipenko return false; 336acc6a3a9SDmitry Osipenko 337acc6a3a9SDmitry Osipenko return true; 338acc6a3a9SDmitry Osipenko } 339acc6a3a9SDmitry Osipenko 3401087fac1SThierry Reding static void tegra_dc_setup_window(struct tegra_plane *plane, 34110288eeaSThierry Reding const struct tegra_dc_window *window) 34210288eeaSThierry Reding { 34310288eeaSThierry Reding unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; 3441087fac1SThierry Reding struct tegra_dc *dc = plane->dc; 34510288eeaSThierry Reding bool yuv, planar; 3461087fac1SThierry Reding u32 value; 34710288eeaSThierry Reding 34810288eeaSThierry Reding /* 34910288eeaSThierry Reding * For YUV planar modes, the number of bytes per pixel takes into 35010288eeaSThierry Reding * account only the luma component and therefore is 1. 35110288eeaSThierry Reding */ 352e16efff4SThierry Reding yuv = tegra_plane_format_is_yuv(window->format, &planar, NULL); 35310288eeaSThierry Reding if (!yuv) 35410288eeaSThierry Reding bpp = window->bits_per_pixel / 8; 35510288eeaSThierry Reding else 35610288eeaSThierry Reding bpp = planar ? 1 : 2; 35710288eeaSThierry Reding 3581087fac1SThierry Reding tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH); 3591087fac1SThierry Reding tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP); 36010288eeaSThierry Reding 36110288eeaSThierry Reding value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x); 3621087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_POSITION); 36310288eeaSThierry Reding 36410288eeaSThierry Reding value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w); 3651087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_SIZE); 36610288eeaSThierry Reding 36710288eeaSThierry Reding h_offset = window->src.x * bpp; 36810288eeaSThierry Reding v_offset = window->src.y; 36910288eeaSThierry Reding h_size = window->src.w * bpp; 37010288eeaSThierry Reding v_size = window->src.h; 37110288eeaSThierry Reding 372cd740777SDmitry Osipenko if (window->reflect_x) 373cd740777SDmitry Osipenko h_offset += (window->src.w - 1) * bpp; 374cd740777SDmitry Osipenko 375cd740777SDmitry Osipenko if (window->reflect_y) 376cd740777SDmitry Osipenko v_offset += window->src.h - 1; 377cd740777SDmitry Osipenko 37810288eeaSThierry Reding value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); 3791087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE); 38010288eeaSThierry Reding 38110288eeaSThierry Reding /* 38210288eeaSThierry Reding * For DDA computations the number of bytes per pixel for YUV planar 38310288eeaSThierry Reding * modes needs to take into account all Y, U and V components. 38410288eeaSThierry Reding */ 38510288eeaSThierry Reding if (yuv && planar) 38610288eeaSThierry Reding bpp = 2; 38710288eeaSThierry Reding 38810288eeaSThierry Reding h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp); 38910288eeaSThierry Reding v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp); 39010288eeaSThierry Reding 39110288eeaSThierry Reding value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); 3921087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_DDA_INC); 39310288eeaSThierry Reding 39410288eeaSThierry Reding h_dda = compute_initial_dda(window->src.x); 39510288eeaSThierry Reding v_dda = compute_initial_dda(window->src.y); 39610288eeaSThierry Reding 3971087fac1SThierry Reding tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA); 3981087fac1SThierry Reding tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA); 39910288eeaSThierry Reding 4001087fac1SThierry Reding tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE); 4011087fac1SThierry Reding tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE); 40210288eeaSThierry Reding 4031087fac1SThierry Reding tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR); 40410288eeaSThierry Reding 40510288eeaSThierry Reding if (yuv && planar) { 4061087fac1SThierry Reding tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U); 4071087fac1SThierry Reding tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V); 40810288eeaSThierry Reding value = window->stride[1] << 16 | window->stride[0]; 4091087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE); 41010288eeaSThierry Reding } else { 4111087fac1SThierry Reding tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE); 41210288eeaSThierry Reding } 41310288eeaSThierry Reding 4141087fac1SThierry Reding tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET); 4151087fac1SThierry Reding tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET); 41610288eeaSThierry Reding 417c134f019SThierry Reding if (dc->soc->supports_block_linear) { 418c134f019SThierry Reding unsigned long height = window->tiling.value; 419c134f019SThierry Reding 420c134f019SThierry Reding switch (window->tiling.mode) { 421c134f019SThierry Reding case TEGRA_BO_TILING_MODE_PITCH: 422c134f019SThierry Reding value = DC_WINBUF_SURFACE_KIND_PITCH; 423c134f019SThierry Reding break; 424c134f019SThierry Reding 425c134f019SThierry Reding case TEGRA_BO_TILING_MODE_TILED: 426c134f019SThierry Reding value = DC_WINBUF_SURFACE_KIND_TILED; 427c134f019SThierry Reding break; 428c134f019SThierry Reding 429c134f019SThierry Reding case TEGRA_BO_TILING_MODE_BLOCK: 430c134f019SThierry Reding value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) | 431c134f019SThierry Reding DC_WINBUF_SURFACE_KIND_BLOCK; 432c134f019SThierry Reding break; 433c134f019SThierry Reding } 434c134f019SThierry Reding 4351087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND); 43610288eeaSThierry Reding } else { 437c134f019SThierry Reding switch (window->tiling.mode) { 438c134f019SThierry Reding case TEGRA_BO_TILING_MODE_PITCH: 43910288eeaSThierry Reding value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV | 44010288eeaSThierry Reding DC_WIN_BUFFER_ADDR_MODE_LINEAR; 441c134f019SThierry Reding break; 442c134f019SThierry Reding 443c134f019SThierry Reding case TEGRA_BO_TILING_MODE_TILED: 444c134f019SThierry Reding value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | 445c134f019SThierry Reding DC_WIN_BUFFER_ADDR_MODE_TILE; 446c134f019SThierry Reding break; 447c134f019SThierry Reding 448c134f019SThierry Reding case TEGRA_BO_TILING_MODE_BLOCK: 4494aa3df71SThierry Reding /* 4504aa3df71SThierry Reding * No need to handle this here because ->atomic_check 4514aa3df71SThierry Reding * will already have filtered it out. 4524aa3df71SThierry Reding */ 4534aa3df71SThierry Reding break; 45410288eeaSThierry Reding } 45510288eeaSThierry Reding 4561087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE); 457c134f019SThierry Reding } 45810288eeaSThierry Reding 45910288eeaSThierry Reding value = WIN_ENABLE; 46010288eeaSThierry Reding 46110288eeaSThierry Reding if (yuv) { 46210288eeaSThierry Reding /* setup default colorspace conversion coefficients */ 4631087fac1SThierry Reding tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF); 4641087fac1SThierry Reding tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB); 4651087fac1SThierry Reding tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR); 4661087fac1SThierry Reding tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR); 4671087fac1SThierry Reding tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG); 4681087fac1SThierry Reding tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG); 4691087fac1SThierry Reding tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB); 4701087fac1SThierry Reding tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB); 47110288eeaSThierry Reding 47210288eeaSThierry Reding value |= CSC_ENABLE; 47310288eeaSThierry Reding } else if (window->bits_per_pixel < 24) { 47410288eeaSThierry Reding value |= COLOR_EXPAND; 47510288eeaSThierry Reding } 47610288eeaSThierry Reding 477cd740777SDmitry Osipenko if (window->reflect_x) 478cd740777SDmitry Osipenko value |= H_DIRECTION; 479cd740777SDmitry Osipenko 480e9e476f7SDmitry Osipenko if (window->reflect_y) 48110288eeaSThierry Reding value |= V_DIRECTION; 48210288eeaSThierry Reding 483acc6a3a9SDmitry Osipenko if (tegra_plane_use_horizontal_filtering(plane, window)) { 484acc6a3a9SDmitry Osipenko /* 485acc6a3a9SDmitry Osipenko * Enable horizontal 6-tap filter and set filtering 486acc6a3a9SDmitry Osipenko * coefficients to the default values defined in TRM. 487acc6a3a9SDmitry Osipenko */ 488acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0)); 489acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1)); 490acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2)); 491acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3)); 492acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4)); 493acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5)); 494acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6)); 495acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7)); 496acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8)); 497acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9)); 498acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10)); 499acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11)); 500acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12)); 501acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13)); 502acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14)); 503acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15)); 504acc6a3a9SDmitry Osipenko 505acc6a3a9SDmitry Osipenko value |= H_FILTER; 506acc6a3a9SDmitry Osipenko } 507acc6a3a9SDmitry Osipenko 508acc6a3a9SDmitry Osipenko if (tegra_plane_use_vertical_filtering(plane, window)) { 509acc6a3a9SDmitry Osipenko unsigned int i, k; 510acc6a3a9SDmitry Osipenko 511acc6a3a9SDmitry Osipenko /* 512acc6a3a9SDmitry Osipenko * Enable vertical 2-tap filter and set filtering 513acc6a3a9SDmitry Osipenko * coefficients to the default values defined in TRM. 514acc6a3a9SDmitry Osipenko */ 515acc6a3a9SDmitry Osipenko for (i = 0, k = 128; i < 16; i++, k -= 8) 516acc6a3a9SDmitry Osipenko tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i)); 517acc6a3a9SDmitry Osipenko 518acc6a3a9SDmitry Osipenko value |= V_FILTER; 519acc6a3a9SDmitry Osipenko } 520acc6a3a9SDmitry Osipenko 5211087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS); 52210288eeaSThierry Reding 523a43d0a00SDmitry Osipenko if (dc->soc->has_legacy_blending) 524ab7d3f58SThierry Reding tegra_plane_setup_blending_legacy(plane); 525a43d0a00SDmitry Osipenko else 526a43d0a00SDmitry Osipenko tegra_plane_setup_blending(plane, window); 527c7679306SThierry Reding } 528c7679306SThierry Reding 529511c7023SThierry Reding static const u32 tegra20_primary_formats[] = { 530511c7023SThierry Reding DRM_FORMAT_ARGB4444, 531511c7023SThierry Reding DRM_FORMAT_ARGB1555, 532c7679306SThierry Reding DRM_FORMAT_RGB565, 533511c7023SThierry Reding DRM_FORMAT_RGBA5551, 534511c7023SThierry Reding DRM_FORMAT_ABGR8888, 535511c7023SThierry Reding DRM_FORMAT_ARGB8888, 536ebae8d07SThierry Reding /* non-native formats */ 537ebae8d07SThierry Reding DRM_FORMAT_XRGB1555, 538ebae8d07SThierry Reding DRM_FORMAT_RGBX5551, 539ebae8d07SThierry Reding DRM_FORMAT_XBGR8888, 540ebae8d07SThierry Reding DRM_FORMAT_XRGB8888, 541511c7023SThierry Reding }; 542511c7023SThierry Reding 543e90124cbSThierry Reding static const u64 tegra20_modifiers[] = { 544e90124cbSThierry Reding DRM_FORMAT_MOD_LINEAR, 545e90124cbSThierry Reding DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED, 546e90124cbSThierry Reding DRM_FORMAT_MOD_INVALID 547e90124cbSThierry Reding }; 548e90124cbSThierry Reding 549511c7023SThierry Reding static const u32 tegra114_primary_formats[] = { 550511c7023SThierry Reding DRM_FORMAT_ARGB4444, 551511c7023SThierry Reding DRM_FORMAT_ARGB1555, 552511c7023SThierry Reding DRM_FORMAT_RGB565, 553511c7023SThierry Reding DRM_FORMAT_RGBA5551, 554511c7023SThierry Reding DRM_FORMAT_ABGR8888, 555511c7023SThierry Reding DRM_FORMAT_ARGB8888, 556511c7023SThierry Reding /* new on Tegra114 */ 557511c7023SThierry Reding DRM_FORMAT_ABGR4444, 558511c7023SThierry Reding DRM_FORMAT_ABGR1555, 559511c7023SThierry Reding DRM_FORMAT_BGRA5551, 560511c7023SThierry Reding DRM_FORMAT_XRGB1555, 561511c7023SThierry Reding DRM_FORMAT_RGBX5551, 562511c7023SThierry Reding DRM_FORMAT_XBGR1555, 563511c7023SThierry Reding DRM_FORMAT_BGRX5551, 564511c7023SThierry Reding DRM_FORMAT_BGR565, 565511c7023SThierry Reding DRM_FORMAT_BGRA8888, 566511c7023SThierry Reding DRM_FORMAT_RGBA8888, 567511c7023SThierry Reding DRM_FORMAT_XRGB8888, 568511c7023SThierry Reding DRM_FORMAT_XBGR8888, 569511c7023SThierry Reding }; 570511c7023SThierry Reding 571511c7023SThierry Reding static const u32 tegra124_primary_formats[] = { 572511c7023SThierry Reding DRM_FORMAT_ARGB4444, 573511c7023SThierry Reding DRM_FORMAT_ARGB1555, 574511c7023SThierry Reding DRM_FORMAT_RGB565, 575511c7023SThierry Reding DRM_FORMAT_RGBA5551, 576511c7023SThierry Reding DRM_FORMAT_ABGR8888, 577511c7023SThierry Reding DRM_FORMAT_ARGB8888, 578511c7023SThierry Reding /* new on Tegra114 */ 579511c7023SThierry Reding DRM_FORMAT_ABGR4444, 580511c7023SThierry Reding DRM_FORMAT_ABGR1555, 581511c7023SThierry Reding DRM_FORMAT_BGRA5551, 582511c7023SThierry Reding DRM_FORMAT_XRGB1555, 583511c7023SThierry Reding DRM_FORMAT_RGBX5551, 584511c7023SThierry Reding DRM_FORMAT_XBGR1555, 585511c7023SThierry Reding DRM_FORMAT_BGRX5551, 586511c7023SThierry Reding DRM_FORMAT_BGR565, 587511c7023SThierry Reding DRM_FORMAT_BGRA8888, 588511c7023SThierry Reding DRM_FORMAT_RGBA8888, 589511c7023SThierry Reding DRM_FORMAT_XRGB8888, 590511c7023SThierry Reding DRM_FORMAT_XBGR8888, 591511c7023SThierry Reding /* new on Tegra124 */ 592511c7023SThierry Reding DRM_FORMAT_RGBX8888, 593511c7023SThierry Reding DRM_FORMAT_BGRX8888, 594c7679306SThierry Reding }; 595c7679306SThierry Reding 596e90124cbSThierry Reding static const u64 tegra124_modifiers[] = { 597e90124cbSThierry Reding DRM_FORMAT_MOD_LINEAR, 598e90124cbSThierry Reding DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0), 599e90124cbSThierry Reding DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1), 600e90124cbSThierry Reding DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2), 601e90124cbSThierry Reding DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3), 602e90124cbSThierry Reding DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4), 603e90124cbSThierry Reding DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5), 604e90124cbSThierry Reding DRM_FORMAT_MOD_INVALID 605e90124cbSThierry Reding }; 606e90124cbSThierry Reding 6074aa3df71SThierry Reding static int tegra_plane_atomic_check(struct drm_plane *plane, 6087c11b99aSMaxime Ripard struct drm_atomic_state *state) 6094aa3df71SThierry Reding { 6107c11b99aSMaxime Ripard struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 6117c11b99aSMaxime Ripard plane); 612ba5c1649SMaxime Ripard struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state); 613cd740777SDmitry Osipenko unsigned int supported_rotation = DRM_MODE_ROTATE_0 | 614cd740777SDmitry Osipenko DRM_MODE_REFLECT_X | 615cd740777SDmitry Osipenko DRM_MODE_REFLECT_Y; 616ba5c1649SMaxime Ripard unsigned int rotation = new_plane_state->rotation; 6178f604f8cSThierry Reding struct tegra_bo_tiling *tiling = &plane_state->tiling; 61847802b09SThierry Reding struct tegra_plane *tegra = to_tegra_plane(plane); 619ba5c1649SMaxime Ripard struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc); 620c7679306SThierry Reding int err; 621c7679306SThierry Reding 62204d5d5dfSDmitry Osipenko plane_state->peak_memory_bandwidth = 0; 62304d5d5dfSDmitry Osipenko plane_state->avg_memory_bandwidth = 0; 62404d5d5dfSDmitry Osipenko 6254aa3df71SThierry Reding /* no need for further checks if the plane is being disabled */ 62604d5d5dfSDmitry Osipenko if (!new_plane_state->crtc) { 62704d5d5dfSDmitry Osipenko plane_state->total_peak_memory_bandwidth = 0; 6284aa3df71SThierry Reding return 0; 62904d5d5dfSDmitry Osipenko } 6304aa3df71SThierry Reding 631ba5c1649SMaxime Ripard err = tegra_plane_format(new_plane_state->fb->format->format, 6323dae08bcSDmitry Osipenko &plane_state->format, 6338f604f8cSThierry Reding &plane_state->swap); 6344aa3df71SThierry Reding if (err < 0) 6354aa3df71SThierry Reding return err; 6364aa3df71SThierry Reding 637ebae8d07SThierry Reding /* 638ebae8d07SThierry Reding * Tegra20 and Tegra30 are special cases here because they support 639ebae8d07SThierry Reding * only variants of specific formats with an alpha component, but not 640ebae8d07SThierry Reding * the corresponding opaque formats. However, the opaque formats can 641ebae8d07SThierry Reding * be emulated by disabling alpha blending for the plane. 642ebae8d07SThierry Reding */ 643a43d0a00SDmitry Osipenko if (dc->soc->has_legacy_blending) { 6443dae08bcSDmitry Osipenko err = tegra_plane_setup_legacy_state(tegra, plane_state); 645ebae8d07SThierry Reding if (err < 0) 646ebae8d07SThierry Reding return err; 647ebae8d07SThierry Reding } 648ebae8d07SThierry Reding 649ba5c1649SMaxime Ripard err = tegra_fb_get_tiling(new_plane_state->fb, tiling); 6508f604f8cSThierry Reding if (err < 0) 6518f604f8cSThierry Reding return err; 6528f604f8cSThierry Reding 6538f604f8cSThierry Reding if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK && 6544aa3df71SThierry Reding !dc->soc->supports_block_linear) { 6554aa3df71SThierry Reding DRM_ERROR("hardware doesn't support block linear mode\n"); 6564aa3df71SThierry Reding return -EINVAL; 6574aa3df71SThierry Reding } 6584aa3df71SThierry Reding 659cd740777SDmitry Osipenko /* 660cd740777SDmitry Osipenko * Older userspace used custom BO flag in order to specify the Y 661cd740777SDmitry Osipenko * reflection, while modern userspace uses the generic DRM rotation 662cd740777SDmitry Osipenko * property in order to achieve the same result. The legacy BO flag 663cd740777SDmitry Osipenko * duplicates the DRM rotation property when both are set. 664cd740777SDmitry Osipenko */ 665ba5c1649SMaxime Ripard if (tegra_fb_is_bottom_up(new_plane_state->fb)) 666cd740777SDmitry Osipenko rotation |= DRM_MODE_REFLECT_Y; 667cd740777SDmitry Osipenko 668cd740777SDmitry Osipenko rotation = drm_rotation_simplify(rotation, supported_rotation); 669cd740777SDmitry Osipenko 670cd740777SDmitry Osipenko if (rotation & DRM_MODE_REFLECT_X) 671cd740777SDmitry Osipenko plane_state->reflect_x = true; 672cd740777SDmitry Osipenko else 673cd740777SDmitry Osipenko plane_state->reflect_x = false; 674995c5a50SThierry Reding 675995c5a50SThierry Reding if (rotation & DRM_MODE_REFLECT_Y) 676e9e476f7SDmitry Osipenko plane_state->reflect_y = true; 677995c5a50SThierry Reding else 678e9e476f7SDmitry Osipenko plane_state->reflect_y = false; 679995c5a50SThierry Reding 6804aa3df71SThierry Reding /* 6814aa3df71SThierry Reding * Tegra doesn't support different strides for U and V planes so we 6824aa3df71SThierry Reding * error out if the user tries to display a framebuffer with such a 6834aa3df71SThierry Reding * configuration. 6844aa3df71SThierry Reding */ 685ba5c1649SMaxime Ripard if (new_plane_state->fb->format->num_planes > 2) { 686ba5c1649SMaxime Ripard if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) { 6874aa3df71SThierry Reding DRM_ERROR("unsupported UV-plane configuration\n"); 6884aa3df71SThierry Reding return -EINVAL; 6894aa3df71SThierry Reding } 6904aa3df71SThierry Reding } 6914aa3df71SThierry Reding 692ba5c1649SMaxime Ripard err = tegra_plane_state_add(tegra, new_plane_state); 69347802b09SThierry Reding if (err < 0) 69447802b09SThierry Reding return err; 69547802b09SThierry Reding 6964aa3df71SThierry Reding return 0; 6974aa3df71SThierry Reding } 6984aa3df71SThierry Reding 699a4bfa096SThierry Reding static void tegra_plane_atomic_disable(struct drm_plane *plane, 700977697e2SMaxime Ripard struct drm_atomic_state *state) 70180d3eef1SDmitry Osipenko { 702977697e2SMaxime Ripard struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 703977697e2SMaxime Ripard plane); 704a4bfa096SThierry Reding struct tegra_plane *p = to_tegra_plane(plane); 70580d3eef1SDmitry Osipenko u32 value; 70680d3eef1SDmitry Osipenko 707a4bfa096SThierry Reding /* rien ne va plus */ 708a4bfa096SThierry Reding if (!old_state || !old_state->crtc) 709a4bfa096SThierry Reding return; 710a4bfa096SThierry Reding 7111087fac1SThierry Reding value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS); 71280d3eef1SDmitry Osipenko value &= ~WIN_ENABLE; 7131087fac1SThierry Reding tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); 71480d3eef1SDmitry Osipenko } 71580d3eef1SDmitry Osipenko 7164aa3df71SThierry Reding static void tegra_plane_atomic_update(struct drm_plane *plane, 717977697e2SMaxime Ripard struct drm_atomic_state *state) 7184aa3df71SThierry Reding { 71937418bf1SMaxime Ripard struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 72037418bf1SMaxime Ripard plane); 72141016fe1SMaxime Ripard struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state); 722e05162c0SMaxime Ripard struct drm_framebuffer *fb = new_state->fb; 7234aa3df71SThierry Reding struct tegra_plane *p = to_tegra_plane(plane); 7244aa3df71SThierry Reding struct tegra_dc_window window; 7254aa3df71SThierry Reding unsigned int i; 7264aa3df71SThierry Reding 7274aa3df71SThierry Reding /* rien ne va plus */ 728e05162c0SMaxime Ripard if (!new_state->crtc || !new_state->fb) 7294aa3df71SThierry Reding return; 7304aa3df71SThierry Reding 731e05162c0SMaxime Ripard if (!new_state->visible) 732977697e2SMaxime Ripard return tegra_plane_atomic_disable(plane, state); 73380d3eef1SDmitry Osipenko 734c7679306SThierry Reding memset(&window, 0, sizeof(window)); 735e05162c0SMaxime Ripard window.src.x = new_state->src.x1 >> 16; 736e05162c0SMaxime Ripard window.src.y = new_state->src.y1 >> 16; 737e05162c0SMaxime Ripard window.src.w = drm_rect_width(&new_state->src) >> 16; 738e05162c0SMaxime Ripard window.src.h = drm_rect_height(&new_state->src) >> 16; 739e05162c0SMaxime Ripard window.dst.x = new_state->dst.x1; 740e05162c0SMaxime Ripard window.dst.y = new_state->dst.y1; 741e05162c0SMaxime Ripard window.dst.w = drm_rect_width(&new_state->dst); 742e05162c0SMaxime Ripard window.dst.h = drm_rect_height(&new_state->dst); 743272725c7SVille Syrjälä window.bits_per_pixel = fb->format->cpp[0] * 8; 74441016fe1SMaxime Ripard window.reflect_x = tegra_plane_state->reflect_x; 74541016fe1SMaxime Ripard window.reflect_y = tegra_plane_state->reflect_y; 746c7679306SThierry Reding 7478f604f8cSThierry Reding /* copy from state */ 748e05162c0SMaxime Ripard window.zpos = new_state->normalized_zpos; 74941016fe1SMaxime Ripard window.tiling = tegra_plane_state->tiling; 75041016fe1SMaxime Ripard window.format = tegra_plane_state->format; 75141016fe1SMaxime Ripard window.swap = tegra_plane_state->swap; 752c7679306SThierry Reding 753bcb0b461SVille Syrjälä for (i = 0; i < fb->format->num_planes; i++) { 75441016fe1SMaxime Ripard window.base[i] = tegra_plane_state->iova[i] + fb->offsets[i]; 75508ee0178SDmitry Osipenko 75608ee0178SDmitry Osipenko /* 75708ee0178SDmitry Osipenko * Tegra uses a shared stride for UV planes. Framebuffers are 75808ee0178SDmitry Osipenko * already checked for this in the tegra_plane_atomic_check() 75908ee0178SDmitry Osipenko * function, so it's safe to ignore the V-plane pitch here. 76008ee0178SDmitry Osipenko */ 76108ee0178SDmitry Osipenko if (i < 2) 7624aa3df71SThierry Reding window.stride[i] = fb->pitches[i]; 763c7679306SThierry Reding } 764c7679306SThierry Reding 7651087fac1SThierry Reding tegra_dc_setup_window(p, &window); 7664aa3df71SThierry Reding } 7674aa3df71SThierry Reding 768a4bfa096SThierry Reding static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { 7692e8d8749SThierry Reding .prepare_fb = tegra_plane_prepare_fb, 7702e8d8749SThierry Reding .cleanup_fb = tegra_plane_cleanup_fb, 7714aa3df71SThierry Reding .atomic_check = tegra_plane_atomic_check, 7724aa3df71SThierry Reding .atomic_disable = tegra_plane_atomic_disable, 773a4bfa096SThierry Reding .atomic_update = tegra_plane_atomic_update, 774c7679306SThierry Reding }; 775c7679306SThierry Reding 77689f65018SThierry Reding static unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm) 777c7679306SThierry Reding { 778518e6227SThierry Reding /* 779518e6227SThierry Reding * Ideally this would use drm_crtc_mask(), but that would require the 780518e6227SThierry Reding * CRTC to already be in the mode_config's list of CRTCs. However, it 781518e6227SThierry Reding * will only be added to that list in the drm_crtc_init_with_planes() 782518e6227SThierry Reding * (in tegra_dc_init()), which in turn requires registration of these 783518e6227SThierry Reding * planes. So we have ourselves a nice little chicken and egg problem 784518e6227SThierry Reding * here. 785518e6227SThierry Reding * 786518e6227SThierry Reding * We work around this by manually creating the mask from the number 787518e6227SThierry Reding * of CRTCs that have been registered, and should therefore always be 788518e6227SThierry Reding * the same as drm_crtc_index() after registration. 789518e6227SThierry Reding */ 79089f65018SThierry Reding return 1 << drm->mode_config.num_crtc; 79189f65018SThierry Reding } 79289f65018SThierry Reding 79389f65018SThierry Reding static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, 79489f65018SThierry Reding struct tegra_dc *dc) 79589f65018SThierry Reding { 79689f65018SThierry Reding unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); 79747307954SThierry Reding enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY; 798c7679306SThierry Reding struct tegra_plane *plane; 799c7679306SThierry Reding unsigned int num_formats; 800e90124cbSThierry Reding const u64 *modifiers; 801c7679306SThierry Reding const u32 *formats; 802c7679306SThierry Reding int err; 803c7679306SThierry Reding 804c7679306SThierry Reding plane = kzalloc(sizeof(*plane), GFP_KERNEL); 805c7679306SThierry Reding if (!plane) 806c7679306SThierry Reding return ERR_PTR(-ENOMEM); 807c7679306SThierry Reding 8081087fac1SThierry Reding /* Always use window A as primary window */ 8091087fac1SThierry Reding plane->offset = 0xa00; 810c4755fb9SThierry Reding plane->index = 0; 8111087fac1SThierry Reding plane->dc = dc; 8121087fac1SThierry Reding 8131087fac1SThierry Reding num_formats = dc->soc->num_primary_formats; 8141087fac1SThierry Reding formats = dc->soc->primary_formats; 815e90124cbSThierry Reding modifiers = dc->soc->modifiers; 816c4755fb9SThierry Reding 81704d5d5dfSDmitry Osipenko err = tegra_plane_interconnect_init(plane); 81804d5d5dfSDmitry Osipenko if (err) { 81904d5d5dfSDmitry Osipenko kfree(plane); 82004d5d5dfSDmitry Osipenko return ERR_PTR(err); 82104d5d5dfSDmitry Osipenko } 82204d5d5dfSDmitry Osipenko 823518e6227SThierry Reding err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 824c1cb4b61SThierry Reding &tegra_plane_funcs, formats, 825e90124cbSThierry Reding num_formats, modifiers, type, NULL); 826c7679306SThierry Reding if (err < 0) { 827c7679306SThierry Reding kfree(plane); 828c7679306SThierry Reding return ERR_PTR(err); 829c7679306SThierry Reding } 830c7679306SThierry Reding 831a4bfa096SThierry Reding drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); 8323dae08bcSDmitry Osipenko drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255); 833ab7d3f58SThierry Reding 834995c5a50SThierry Reding err = drm_plane_create_rotation_property(&plane->base, 835995c5a50SThierry Reding DRM_MODE_ROTATE_0, 836995c5a50SThierry Reding DRM_MODE_ROTATE_0 | 8374fba6d22SDmitry Osipenko DRM_MODE_ROTATE_180 | 838cd740777SDmitry Osipenko DRM_MODE_REFLECT_X | 839995c5a50SThierry Reding DRM_MODE_REFLECT_Y); 840995c5a50SThierry Reding if (err < 0) 841995c5a50SThierry Reding dev_err(dc->dev, "failed to create rotation property: %d\n", 842995c5a50SThierry Reding err); 843995c5a50SThierry Reding 844c7679306SThierry Reding return &plane->base; 845c7679306SThierry Reding } 846c7679306SThierry Reding 847d5ec699dSThierry Reding static const u32 tegra_legacy_cursor_plane_formats[] = { 848c7679306SThierry Reding DRM_FORMAT_RGBA8888, 849c7679306SThierry Reding }; 850c7679306SThierry Reding 851d5ec699dSThierry Reding static const u32 tegra_cursor_plane_formats[] = { 852d5ec699dSThierry Reding DRM_FORMAT_ARGB8888, 853d5ec699dSThierry Reding }; 854d5ec699dSThierry Reding 8554aa3df71SThierry Reding static int tegra_cursor_atomic_check(struct drm_plane *plane, 8567c11b99aSMaxime Ripard struct drm_atomic_state *state) 857c7679306SThierry Reding { 8587c11b99aSMaxime Ripard struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 8597c11b99aSMaxime Ripard plane); 86004d5d5dfSDmitry Osipenko struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state); 86147802b09SThierry Reding struct tegra_plane *tegra = to_tegra_plane(plane); 86247802b09SThierry Reding int err; 86347802b09SThierry Reding 86404d5d5dfSDmitry Osipenko plane_state->peak_memory_bandwidth = 0; 86504d5d5dfSDmitry Osipenko plane_state->avg_memory_bandwidth = 0; 86604d5d5dfSDmitry Osipenko 8674aa3df71SThierry Reding /* no need for further checks if the plane is being disabled */ 86804d5d5dfSDmitry Osipenko if (!new_plane_state->crtc) { 86904d5d5dfSDmitry Osipenko plane_state->total_peak_memory_bandwidth = 0; 8704aa3df71SThierry Reding return 0; 87104d5d5dfSDmitry Osipenko } 872c7679306SThierry Reding 873c7679306SThierry Reding /* scaling not supported for cursor */ 874ba5c1649SMaxime Ripard if ((new_plane_state->src_w >> 16 != new_plane_state->crtc_w) || 875ba5c1649SMaxime Ripard (new_plane_state->src_h >> 16 != new_plane_state->crtc_h)) 876c7679306SThierry Reding return -EINVAL; 877c7679306SThierry Reding 878c7679306SThierry Reding /* only square cursors supported */ 879ba5c1649SMaxime Ripard if (new_plane_state->src_w != new_plane_state->src_h) 880c7679306SThierry Reding return -EINVAL; 881c7679306SThierry Reding 882ba5c1649SMaxime Ripard if (new_plane_state->crtc_w != 32 && new_plane_state->crtc_w != 64 && 883ba5c1649SMaxime Ripard new_plane_state->crtc_w != 128 && new_plane_state->crtc_w != 256) 8844aa3df71SThierry Reding return -EINVAL; 8854aa3df71SThierry Reding 886ba5c1649SMaxime Ripard err = tegra_plane_state_add(tegra, new_plane_state); 88747802b09SThierry Reding if (err < 0) 88847802b09SThierry Reding return err; 88947802b09SThierry Reding 8904aa3df71SThierry Reding return 0; 8914aa3df71SThierry Reding } 8924aa3df71SThierry Reding 893cae7472eSThierry Reding static void __tegra_cursor_atomic_update(struct drm_plane *plane, 894cae7472eSThierry Reding struct drm_plane_state *new_state) 8954aa3df71SThierry Reding { 89641016fe1SMaxime Ripard struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state); 897e05162c0SMaxime Ripard struct tegra_dc *dc = to_tegra_dc(new_state->crtc); 898d5ec699dSThierry Reding struct tegra_drm *tegra = plane->dev->dev_private; 899d5ec699dSThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 900d5ec699dSThierry Reding u64 dma_mask = *dc->dev->dma_mask; 901d5ec699dSThierry Reding #endif 902d5ec699dSThierry Reding unsigned int x, y; 903d5ec699dSThierry Reding u32 value = 0; 9044aa3df71SThierry Reding 9054aa3df71SThierry Reding /* rien ne va plus */ 906e05162c0SMaxime Ripard if (!new_state->crtc || !new_state->fb) 9074aa3df71SThierry Reding return; 9084aa3df71SThierry Reding 909d5ec699dSThierry Reding /* 910d5ec699dSThierry Reding * Legacy display supports hardware clipping of the cursor, but 911d5ec699dSThierry Reding * nvdisplay relies on software to clip the cursor to the screen. 912d5ec699dSThierry Reding */ 913d5ec699dSThierry Reding if (!dc->soc->has_nvdisplay) 914d5ec699dSThierry Reding value |= CURSOR_CLIP_DISPLAY; 915d5ec699dSThierry Reding 916e05162c0SMaxime Ripard switch (new_state->crtc_w) { 917c7679306SThierry Reding case 32: 918c7679306SThierry Reding value |= CURSOR_SIZE_32x32; 919c7679306SThierry Reding break; 920c7679306SThierry Reding 921c7679306SThierry Reding case 64: 922c7679306SThierry Reding value |= CURSOR_SIZE_64x64; 923c7679306SThierry Reding break; 924c7679306SThierry Reding 925c7679306SThierry Reding case 128: 926c7679306SThierry Reding value |= CURSOR_SIZE_128x128; 927c7679306SThierry Reding break; 928c7679306SThierry Reding 929c7679306SThierry Reding case 256: 930c7679306SThierry Reding value |= CURSOR_SIZE_256x256; 931c7679306SThierry Reding break; 932c7679306SThierry Reding 933c7679306SThierry Reding default: 934c52e167bSThierry Reding WARN(1, "cursor size %ux%u not supported\n", 935e05162c0SMaxime Ripard new_state->crtc_w, new_state->crtc_h); 9364aa3df71SThierry Reding return; 937c7679306SThierry Reding } 938c7679306SThierry Reding 93941016fe1SMaxime Ripard value |= (tegra_plane_state->iova[0] >> 10) & 0x3fffff; 940c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR); 941c7679306SThierry Reding 942c7679306SThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 943d5ec699dSThierry Reding value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32); 944c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI); 945c7679306SThierry Reding #endif 946c7679306SThierry Reding 947c7679306SThierry Reding /* enable cursor and set blend mode */ 948c7679306SThierry Reding value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 949c7679306SThierry Reding value |= CURSOR_ENABLE; 950c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 951c7679306SThierry Reding 952c7679306SThierry Reding value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL); 953c7679306SThierry Reding value &= ~CURSOR_DST_BLEND_MASK; 954c7679306SThierry Reding value &= ~CURSOR_SRC_BLEND_MASK; 955d5ec699dSThierry Reding 956d5ec699dSThierry Reding if (dc->soc->has_nvdisplay) 957d5ec699dSThierry Reding value &= ~CURSOR_COMPOSITION_MODE_XOR; 958d5ec699dSThierry Reding else 959c7679306SThierry Reding value |= CURSOR_MODE_NORMAL; 960d5ec699dSThierry Reding 961c7679306SThierry Reding value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC; 962c7679306SThierry Reding value |= CURSOR_SRC_BLEND_K1_TIMES_SRC; 963c7679306SThierry Reding value |= CURSOR_ALPHA; 964c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); 965c7679306SThierry Reding 966d5ec699dSThierry Reding /* nvdisplay relies on software for clipping */ 967d5ec699dSThierry Reding if (dc->soc->has_nvdisplay) { 968d5ec699dSThierry Reding struct drm_rect src; 969d5ec699dSThierry Reding 970d5ec699dSThierry Reding x = new_state->dst.x1; 971d5ec699dSThierry Reding y = new_state->dst.y1; 972d5ec699dSThierry Reding 973d5ec699dSThierry Reding drm_rect_fp_to_int(&src, &new_state->src); 974d5ec699dSThierry Reding 975d5ec699dSThierry Reding value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask); 976d5ec699dSThierry Reding tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR); 977d5ec699dSThierry Reding 978d5ec699dSThierry Reding value = (drm_rect_height(&src) & tegra->vmask) << 16 | 979d5ec699dSThierry Reding (drm_rect_width(&src) & tegra->hmask); 980d5ec699dSThierry Reding tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR); 981d5ec699dSThierry Reding } else { 982d5ec699dSThierry Reding x = new_state->crtc_x; 983d5ec699dSThierry Reding y = new_state->crtc_y; 984d5ec699dSThierry Reding } 985d5ec699dSThierry Reding 986c7679306SThierry Reding /* position the cursor */ 987d5ec699dSThierry Reding value = ((y & tegra->vmask) << 16) | (x & tegra->hmask); 988c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); 989c7679306SThierry Reding } 990c7679306SThierry Reding 991cae7472eSThierry Reding static void tegra_cursor_atomic_update(struct drm_plane *plane, 992cae7472eSThierry Reding struct drm_atomic_state *state) 993cae7472eSThierry Reding { 994cae7472eSThierry Reding struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); 995cae7472eSThierry Reding 996cae7472eSThierry Reding __tegra_cursor_atomic_update(plane, new_state); 997cae7472eSThierry Reding } 998cae7472eSThierry Reding 9994aa3df71SThierry Reding static void tegra_cursor_atomic_disable(struct drm_plane *plane, 1000977697e2SMaxime Ripard struct drm_atomic_state *state) 1001c7679306SThierry Reding { 1002977697e2SMaxime Ripard struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 1003977697e2SMaxime Ripard plane); 10044aa3df71SThierry Reding struct tegra_dc *dc; 1005c7679306SThierry Reding u32 value; 1006c7679306SThierry Reding 10074aa3df71SThierry Reding /* rien ne va plus */ 10084aa3df71SThierry Reding if (!old_state || !old_state->crtc) 10094aa3df71SThierry Reding return; 10104aa3df71SThierry Reding 10114aa3df71SThierry Reding dc = to_tegra_dc(old_state->crtc); 1012c7679306SThierry Reding 1013c7679306SThierry Reding value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 1014c7679306SThierry Reding value &= ~CURSOR_ENABLE; 1015c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 1016c7679306SThierry Reding } 1017c7679306SThierry Reding 1018cae7472eSThierry Reding static int tegra_cursor_atomic_async_check(struct drm_plane *plane, struct drm_atomic_state *state) 1019cae7472eSThierry Reding { 1020cae7472eSThierry Reding struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); 1021cae7472eSThierry Reding struct drm_crtc_state *crtc_state; 1022cae7472eSThierry Reding int min_scale, max_scale; 1023cae7472eSThierry Reding int err; 1024cae7472eSThierry Reding 1025cae7472eSThierry Reding crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc); 1026cae7472eSThierry Reding if (WARN_ON(!crtc_state)) 1027cae7472eSThierry Reding return -EINVAL; 1028cae7472eSThierry Reding 1029cae7472eSThierry Reding if (!crtc_state->active) 1030cae7472eSThierry Reding return -EINVAL; 1031cae7472eSThierry Reding 1032cae7472eSThierry Reding if (plane->state->crtc != new_state->crtc || 1033cae7472eSThierry Reding plane->state->src_w != new_state->src_w || 1034cae7472eSThierry Reding plane->state->src_h != new_state->src_h || 1035cae7472eSThierry Reding plane->state->crtc_w != new_state->crtc_w || 1036cae7472eSThierry Reding plane->state->crtc_h != new_state->crtc_h || 1037cae7472eSThierry Reding plane->state->fb != new_state->fb || 1038cae7472eSThierry Reding plane->state->fb == NULL) 1039cae7472eSThierry Reding return -EINVAL; 1040cae7472eSThierry Reding 1041cae7472eSThierry Reding min_scale = (1 << 16) / 8; 1042cae7472eSThierry Reding max_scale = (8 << 16) / 1; 1043cae7472eSThierry Reding 1044cae7472eSThierry Reding err = drm_atomic_helper_check_plane_state(new_state, crtc_state, min_scale, max_scale, 1045cae7472eSThierry Reding true, true); 1046cae7472eSThierry Reding if (err < 0) 1047cae7472eSThierry Reding return err; 1048cae7472eSThierry Reding 1049cae7472eSThierry Reding if (new_state->visible != plane->state->visible) 1050cae7472eSThierry Reding return -EINVAL; 1051cae7472eSThierry Reding 1052cae7472eSThierry Reding return 0; 1053cae7472eSThierry Reding } 1054cae7472eSThierry Reding 1055cae7472eSThierry Reding static void tegra_cursor_atomic_async_update(struct drm_plane *plane, 1056cae7472eSThierry Reding struct drm_atomic_state *state) 1057cae7472eSThierry Reding { 1058cae7472eSThierry Reding struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); 1059cae7472eSThierry Reding struct tegra_dc *dc = to_tegra_dc(new_state->crtc); 1060cae7472eSThierry Reding 1061cae7472eSThierry Reding plane->state->src_x = new_state->src_x; 1062cae7472eSThierry Reding plane->state->src_y = new_state->src_y; 1063cae7472eSThierry Reding plane->state->crtc_x = new_state->crtc_x; 1064cae7472eSThierry Reding plane->state->crtc_y = new_state->crtc_y; 1065cae7472eSThierry Reding 1066cae7472eSThierry Reding if (new_state->visible) { 1067cae7472eSThierry Reding struct tegra_plane *p = to_tegra_plane(plane); 1068cae7472eSThierry Reding u32 value; 1069cae7472eSThierry Reding 1070cae7472eSThierry Reding __tegra_cursor_atomic_update(plane, new_state); 1071cae7472eSThierry Reding 1072cae7472eSThierry Reding value = (WIN_A_ACT_REQ << p->index) << 8 | GENERAL_UPDATE; 1073cae7472eSThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 1074cae7472eSThierry Reding (void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 1075cae7472eSThierry Reding 1076cae7472eSThierry Reding value = (WIN_A_ACT_REQ << p->index) | GENERAL_ACT_REQ; 1077cae7472eSThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 1078cae7472eSThierry Reding (void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 1079cae7472eSThierry Reding } 1080cae7472eSThierry Reding } 1081cae7472eSThierry Reding 10824aa3df71SThierry Reding static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { 10832e8d8749SThierry Reding .prepare_fb = tegra_plane_prepare_fb, 10842e8d8749SThierry Reding .cleanup_fb = tegra_plane_cleanup_fb, 10854aa3df71SThierry Reding .atomic_check = tegra_cursor_atomic_check, 10864aa3df71SThierry Reding .atomic_update = tegra_cursor_atomic_update, 10874aa3df71SThierry Reding .atomic_disable = tegra_cursor_atomic_disable, 1088cae7472eSThierry Reding .atomic_async_check = tegra_cursor_atomic_async_check, 1089cae7472eSThierry Reding .atomic_async_update = tegra_cursor_atomic_async_update, 1090c7679306SThierry Reding }; 1091c7679306SThierry Reding 1092be4306adSDaniel Vetter static const uint64_t linear_modifiers[] = { 1093be4306adSDaniel Vetter DRM_FORMAT_MOD_LINEAR, 1094be4306adSDaniel Vetter DRM_FORMAT_MOD_INVALID 1095be4306adSDaniel Vetter }; 1096be4306adSDaniel Vetter 1097c7679306SThierry Reding static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, 1098c7679306SThierry Reding struct tegra_dc *dc) 1099c7679306SThierry Reding { 110089f65018SThierry Reding unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); 1101c7679306SThierry Reding struct tegra_plane *plane; 1102c7679306SThierry Reding unsigned int num_formats; 1103c7679306SThierry Reding const u32 *formats; 1104c7679306SThierry Reding int err; 1105c7679306SThierry Reding 1106c7679306SThierry Reding plane = kzalloc(sizeof(*plane), GFP_KERNEL); 1107c7679306SThierry Reding if (!plane) 1108c7679306SThierry Reding return ERR_PTR(-ENOMEM); 1109c7679306SThierry Reding 111047802b09SThierry Reding /* 1111a1df3b24SThierry Reding * This index is kind of fake. The cursor isn't a regular plane, but 1112a1df3b24SThierry Reding * its update and activation request bits in DC_CMD_STATE_CONTROL do 1113a1df3b24SThierry Reding * use the same programming. Setting this fake index here allows the 1114a1df3b24SThierry Reding * code in tegra_add_plane_state() to do the right thing without the 1115a1df3b24SThierry Reding * need to special-casing the cursor plane. 111647802b09SThierry Reding */ 111747802b09SThierry Reding plane->index = 6; 11181087fac1SThierry Reding plane->dc = dc; 111947802b09SThierry Reding 1120d5ec699dSThierry Reding if (!dc->soc->has_nvdisplay) { 1121d5ec699dSThierry Reding num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats); 1122d5ec699dSThierry Reding formats = tegra_legacy_cursor_plane_formats; 112304d5d5dfSDmitry Osipenko 112404d5d5dfSDmitry Osipenko err = tegra_plane_interconnect_init(plane); 112504d5d5dfSDmitry Osipenko if (err) { 112604d5d5dfSDmitry Osipenko kfree(plane); 112704d5d5dfSDmitry Osipenko return ERR_PTR(err); 112804d5d5dfSDmitry Osipenko } 1129d5ec699dSThierry Reding } else { 1130c7679306SThierry Reding num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); 1131c7679306SThierry Reding formats = tegra_cursor_plane_formats; 1132d5ec699dSThierry Reding } 1133c7679306SThierry Reding 113489f65018SThierry Reding err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 1135c1cb4b61SThierry Reding &tegra_plane_funcs, formats, 1136be4306adSDaniel Vetter num_formats, linear_modifiers, 1137e6fc3b68SBen Widawsky DRM_PLANE_TYPE_CURSOR, NULL); 1138c7679306SThierry Reding if (err < 0) { 1139c7679306SThierry Reding kfree(plane); 1140c7679306SThierry Reding return ERR_PTR(err); 1141c7679306SThierry Reding } 1142c7679306SThierry Reding 11434aa3df71SThierry Reding drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs); 1144fce3a51dSThierry Reding drm_plane_create_zpos_immutable_property(&plane->base, 255); 11454aa3df71SThierry Reding 1146c7679306SThierry Reding return &plane->base; 1147c7679306SThierry Reding } 1148c7679306SThierry Reding 1149511c7023SThierry Reding static const u32 tegra20_overlay_formats[] = { 1150511c7023SThierry Reding DRM_FORMAT_ARGB4444, 1151511c7023SThierry Reding DRM_FORMAT_ARGB1555, 1152dee8268fSThierry Reding DRM_FORMAT_RGB565, 1153511c7023SThierry Reding DRM_FORMAT_RGBA5551, 1154511c7023SThierry Reding DRM_FORMAT_ABGR8888, 1155511c7023SThierry Reding DRM_FORMAT_ARGB8888, 1156ebae8d07SThierry Reding /* non-native formats */ 1157ebae8d07SThierry Reding DRM_FORMAT_XRGB1555, 1158ebae8d07SThierry Reding DRM_FORMAT_RGBX5551, 1159ebae8d07SThierry Reding DRM_FORMAT_XBGR8888, 1160ebae8d07SThierry Reding DRM_FORMAT_XRGB8888, 1161511c7023SThierry Reding /* planar formats */ 1162511c7023SThierry Reding DRM_FORMAT_UYVY, 1163511c7023SThierry Reding DRM_FORMAT_YUYV, 1164511c7023SThierry Reding DRM_FORMAT_YUV420, 1165511c7023SThierry Reding DRM_FORMAT_YUV422, 1166511c7023SThierry Reding }; 1167511c7023SThierry Reding 1168511c7023SThierry Reding static const u32 tegra114_overlay_formats[] = { 1169511c7023SThierry Reding DRM_FORMAT_ARGB4444, 1170511c7023SThierry Reding DRM_FORMAT_ARGB1555, 1171511c7023SThierry Reding DRM_FORMAT_RGB565, 1172511c7023SThierry Reding DRM_FORMAT_RGBA5551, 1173511c7023SThierry Reding DRM_FORMAT_ABGR8888, 1174511c7023SThierry Reding DRM_FORMAT_ARGB8888, 1175511c7023SThierry Reding /* new on Tegra114 */ 1176511c7023SThierry Reding DRM_FORMAT_ABGR4444, 1177511c7023SThierry Reding DRM_FORMAT_ABGR1555, 1178511c7023SThierry Reding DRM_FORMAT_BGRA5551, 1179511c7023SThierry Reding DRM_FORMAT_XRGB1555, 1180511c7023SThierry Reding DRM_FORMAT_RGBX5551, 1181511c7023SThierry Reding DRM_FORMAT_XBGR1555, 1182511c7023SThierry Reding DRM_FORMAT_BGRX5551, 1183511c7023SThierry Reding DRM_FORMAT_BGR565, 1184511c7023SThierry Reding DRM_FORMAT_BGRA8888, 1185511c7023SThierry Reding DRM_FORMAT_RGBA8888, 1186511c7023SThierry Reding DRM_FORMAT_XRGB8888, 1187511c7023SThierry Reding DRM_FORMAT_XBGR8888, 1188511c7023SThierry Reding /* planar formats */ 1189511c7023SThierry Reding DRM_FORMAT_UYVY, 1190511c7023SThierry Reding DRM_FORMAT_YUYV, 1191511c7023SThierry Reding DRM_FORMAT_YUV420, 1192511c7023SThierry Reding DRM_FORMAT_YUV422, 1193511c7023SThierry Reding }; 1194511c7023SThierry Reding 1195511c7023SThierry Reding static const u32 tegra124_overlay_formats[] = { 1196511c7023SThierry Reding DRM_FORMAT_ARGB4444, 1197511c7023SThierry Reding DRM_FORMAT_ARGB1555, 1198511c7023SThierry Reding DRM_FORMAT_RGB565, 1199511c7023SThierry Reding DRM_FORMAT_RGBA5551, 1200511c7023SThierry Reding DRM_FORMAT_ABGR8888, 1201511c7023SThierry Reding DRM_FORMAT_ARGB8888, 1202511c7023SThierry Reding /* new on Tegra114 */ 1203511c7023SThierry Reding DRM_FORMAT_ABGR4444, 1204511c7023SThierry Reding DRM_FORMAT_ABGR1555, 1205511c7023SThierry Reding DRM_FORMAT_BGRA5551, 1206511c7023SThierry Reding DRM_FORMAT_XRGB1555, 1207511c7023SThierry Reding DRM_FORMAT_RGBX5551, 1208511c7023SThierry Reding DRM_FORMAT_XBGR1555, 1209511c7023SThierry Reding DRM_FORMAT_BGRX5551, 1210511c7023SThierry Reding DRM_FORMAT_BGR565, 1211511c7023SThierry Reding DRM_FORMAT_BGRA8888, 1212511c7023SThierry Reding DRM_FORMAT_RGBA8888, 1213511c7023SThierry Reding DRM_FORMAT_XRGB8888, 1214511c7023SThierry Reding DRM_FORMAT_XBGR8888, 1215511c7023SThierry Reding /* new on Tegra124 */ 1216511c7023SThierry Reding DRM_FORMAT_RGBX8888, 1217511c7023SThierry Reding DRM_FORMAT_BGRX8888, 1218511c7023SThierry Reding /* planar formats */ 1219dee8268fSThierry Reding DRM_FORMAT_UYVY, 1220f925390eSThierry Reding DRM_FORMAT_YUYV, 1221dee8268fSThierry Reding DRM_FORMAT_YUV420, 1222dee8268fSThierry Reding DRM_FORMAT_YUV422, 1223dee8268fSThierry Reding }; 1224dee8268fSThierry Reding 1225c7679306SThierry Reding static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, 1226c7679306SThierry Reding struct tegra_dc *dc, 12279f446d83SDmitry Osipenko unsigned int index, 12289f446d83SDmitry Osipenko bool cursor) 1229dee8268fSThierry Reding { 123089f65018SThierry Reding unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); 1231dee8268fSThierry Reding struct tegra_plane *plane; 1232c7679306SThierry Reding unsigned int num_formats; 12339f446d83SDmitry Osipenko enum drm_plane_type type; 1234c7679306SThierry Reding const u32 *formats; 1235c7679306SThierry Reding int err; 1236dee8268fSThierry Reding 1237f002abc1SThierry Reding plane = kzalloc(sizeof(*plane), GFP_KERNEL); 1238dee8268fSThierry Reding if (!plane) 1239c7679306SThierry Reding return ERR_PTR(-ENOMEM); 1240dee8268fSThierry Reding 12411087fac1SThierry Reding plane->offset = 0xa00 + 0x200 * index; 1242c7679306SThierry Reding plane->index = index; 12431087fac1SThierry Reding plane->dc = dc; 1244dee8268fSThierry Reding 1245511c7023SThierry Reding num_formats = dc->soc->num_overlay_formats; 1246511c7023SThierry Reding formats = dc->soc->overlay_formats; 1247c7679306SThierry Reding 124804d5d5dfSDmitry Osipenko err = tegra_plane_interconnect_init(plane); 124904d5d5dfSDmitry Osipenko if (err) { 125004d5d5dfSDmitry Osipenko kfree(plane); 125104d5d5dfSDmitry Osipenko return ERR_PTR(err); 125204d5d5dfSDmitry Osipenko } 125304d5d5dfSDmitry Osipenko 12549f446d83SDmitry Osipenko if (!cursor) 12559f446d83SDmitry Osipenko type = DRM_PLANE_TYPE_OVERLAY; 12569f446d83SDmitry Osipenko else 12579f446d83SDmitry Osipenko type = DRM_PLANE_TYPE_CURSOR; 12589f446d83SDmitry Osipenko 125989f65018SThierry Reding err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 1260301e0ddbSThierry Reding &tegra_plane_funcs, formats, 1261be4306adSDaniel Vetter num_formats, linear_modifiers, 1262be4306adSDaniel Vetter type, NULL); 1263f002abc1SThierry Reding if (err < 0) { 1264f002abc1SThierry Reding kfree(plane); 1265c7679306SThierry Reding return ERR_PTR(err); 1266dee8268fSThierry Reding } 1267c7679306SThierry Reding 1268a4bfa096SThierry Reding drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); 12693dae08bcSDmitry Osipenko drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255); 1270ab7d3f58SThierry Reding 1271995c5a50SThierry Reding err = drm_plane_create_rotation_property(&plane->base, 1272995c5a50SThierry Reding DRM_MODE_ROTATE_0, 1273995c5a50SThierry Reding DRM_MODE_ROTATE_0 | 12744fba6d22SDmitry Osipenko DRM_MODE_ROTATE_180 | 1275cd740777SDmitry Osipenko DRM_MODE_REFLECT_X | 1276995c5a50SThierry Reding DRM_MODE_REFLECT_Y); 1277995c5a50SThierry Reding if (err < 0) 1278995c5a50SThierry Reding dev_err(dc->dev, "failed to create rotation property: %d\n", 1279995c5a50SThierry Reding err); 1280995c5a50SThierry Reding 1281c7679306SThierry Reding return &plane->base; 1282c7679306SThierry Reding } 1283c7679306SThierry Reding 128447307954SThierry Reding static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm, 128547307954SThierry Reding struct tegra_dc *dc) 1286c7679306SThierry Reding { 128747307954SThierry Reding struct drm_plane *plane, *primary = NULL; 128847307954SThierry Reding unsigned int i, j; 128947307954SThierry Reding 129047307954SThierry Reding for (i = 0; i < dc->soc->num_wgrps; i++) { 129147307954SThierry Reding const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; 129247307954SThierry Reding 129347307954SThierry Reding if (wgrp->dc == dc->pipe) { 129447307954SThierry Reding for (j = 0; j < wgrp->num_windows; j++) { 129547307954SThierry Reding unsigned int index = wgrp->windows[j]; 129647307954SThierry Reding 129747307954SThierry Reding plane = tegra_shared_plane_create(drm, dc, 129847307954SThierry Reding wgrp->index, 129947307954SThierry Reding index); 130047307954SThierry Reding if (IS_ERR(plane)) 130147307954SThierry Reding return plane; 130247307954SThierry Reding 130347307954SThierry Reding /* 130447307954SThierry Reding * Choose the first shared plane owned by this 130547307954SThierry Reding * head as the primary plane. 130647307954SThierry Reding */ 130747307954SThierry Reding if (!primary) { 130847307954SThierry Reding plane->type = DRM_PLANE_TYPE_PRIMARY; 130947307954SThierry Reding primary = plane; 131047307954SThierry Reding } 131147307954SThierry Reding } 131247307954SThierry Reding } 131347307954SThierry Reding } 131447307954SThierry Reding 131547307954SThierry Reding return primary; 131647307954SThierry Reding } 131747307954SThierry Reding 131847307954SThierry Reding static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, 131947307954SThierry Reding struct tegra_dc *dc) 132047307954SThierry Reding { 13218f62142eSThierry Reding struct drm_plane *planes[2], *primary; 13229f446d83SDmitry Osipenko unsigned int planes_num; 1323c7679306SThierry Reding unsigned int i; 13248f62142eSThierry Reding int err; 1325c7679306SThierry Reding 132647307954SThierry Reding primary = tegra_primary_plane_create(drm, dc); 132747307954SThierry Reding if (IS_ERR(primary)) 132847307954SThierry Reding return primary; 132947307954SThierry Reding 13309f446d83SDmitry Osipenko if (dc->soc->supports_cursor) 13319f446d83SDmitry Osipenko planes_num = 2; 13329f446d83SDmitry Osipenko else 13339f446d83SDmitry Osipenko planes_num = 1; 13349f446d83SDmitry Osipenko 13359f446d83SDmitry Osipenko for (i = 0; i < planes_num; i++) { 13369f446d83SDmitry Osipenko planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i, 13379f446d83SDmitry Osipenko false); 13388f62142eSThierry Reding if (IS_ERR(planes[i])) { 13398f62142eSThierry Reding err = PTR_ERR(planes[i]); 13408f62142eSThierry Reding 13418f62142eSThierry Reding while (i--) 134240dc962dSThierry Reding planes[i]->funcs->destroy(planes[i]); 13438f62142eSThierry Reding 134440dc962dSThierry Reding primary->funcs->destroy(primary); 13458f62142eSThierry Reding return ERR_PTR(err); 134647307954SThierry Reding } 1347f002abc1SThierry Reding } 1348dee8268fSThierry Reding 134947307954SThierry Reding return primary; 1350dee8268fSThierry Reding } 1351dee8268fSThierry Reding 1352f002abc1SThierry Reding static void tegra_dc_destroy(struct drm_crtc *crtc) 1353f002abc1SThierry Reding { 1354f002abc1SThierry Reding drm_crtc_cleanup(crtc); 1355f002abc1SThierry Reding } 1356f002abc1SThierry Reding 1357ca915b10SThierry Reding static void tegra_crtc_reset(struct drm_crtc *crtc) 1358ca915b10SThierry Reding { 1359b7e0b04aSMaarten Lankhorst struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL); 1360ca915b10SThierry Reding 13613b59b7acSThierry Reding if (crtc->state) 1362b7e0b04aSMaarten Lankhorst tegra_crtc_atomic_destroy_state(crtc, crtc->state); 13633b59b7acSThierry Reding 1364b7e0b04aSMaarten Lankhorst __drm_atomic_helper_crtc_reset(crtc, &state->base); 1365ca915b10SThierry Reding } 1366ca915b10SThierry Reding 1367ca915b10SThierry Reding static struct drm_crtc_state * 1368ca915b10SThierry Reding tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) 1369ca915b10SThierry Reding { 1370ca915b10SThierry Reding struct tegra_dc_state *state = to_dc_state(crtc->state); 1371ca915b10SThierry Reding struct tegra_dc_state *copy; 1372ca915b10SThierry Reding 13733b59b7acSThierry Reding copy = kmalloc(sizeof(*copy), GFP_KERNEL); 1374ca915b10SThierry Reding if (!copy) 1375ca915b10SThierry Reding return NULL; 1376ca915b10SThierry Reding 13773b59b7acSThierry Reding __drm_atomic_helper_crtc_duplicate_state(crtc, ©->base); 13783b59b7acSThierry Reding copy->clk = state->clk; 13793b59b7acSThierry Reding copy->pclk = state->pclk; 13803b59b7acSThierry Reding copy->div = state->div; 13813b59b7acSThierry Reding copy->planes = state->planes; 1382ca915b10SThierry Reding 1383ca915b10SThierry Reding return ©->base; 1384ca915b10SThierry Reding } 1385ca915b10SThierry Reding 1386ca915b10SThierry Reding static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, 1387ca915b10SThierry Reding struct drm_crtc_state *state) 1388ca915b10SThierry Reding { 1389ec2dc6a0SDaniel Vetter __drm_atomic_helper_crtc_destroy_state(state); 1390ca915b10SThierry Reding kfree(state); 1391ca915b10SThierry Reding } 1392ca915b10SThierry Reding 1393b95800eeSThierry Reding #define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } 1394b95800eeSThierry Reding 1395b95800eeSThierry Reding static const struct debugfs_reg32 tegra_dc_regs[] = { 1396b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT), 1397b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL), 1398b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR), 1399b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT), 1400b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL), 1401b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR), 1402b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT), 1403b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL), 1404b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR), 1405b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT), 1406b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL), 1407b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR), 1408b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC), 1409b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0), 1410b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND), 1411b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE), 1412b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL), 1413b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_INT_STATUS), 1414b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_INT_MASK), 1415b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_INT_ENABLE), 1416b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_INT_TYPE), 1417b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_INT_POLARITY), 1418b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1), 1419b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2), 1420b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3), 1421b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_STATE_ACCESS), 1422b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_STATE_CONTROL), 1423b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER), 1424b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL), 1425b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_CRC_CONTROL), 1426b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_CRC_CHECKSUM), 1427b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)), 1428b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)), 1429b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)), 1430b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)), 1431b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)), 1432b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)), 1433b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)), 1434b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)), 1435b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)), 1436b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)), 1437b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)), 1438b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)), 1439b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)), 1440b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)), 1441b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)), 1442b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)), 1443b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)), 1444b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)), 1445b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)), 1446b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)), 1447b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)), 1448b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)), 1449b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)), 1450b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)), 1451b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)), 1452b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL), 1453b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL), 1454b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE), 1455b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL), 1456b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE), 1457b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_SPI_CONTROL), 1458b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_SPI_START_BYTE), 1459b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB), 1460b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD), 1461b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_HSPI_CS_DC), 1462b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A), 1463b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B), 1464b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_GPIO_CTRL), 1465b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER), 1466b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED), 1467b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0), 1468b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1), 1469b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS), 1470b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY), 1471b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER), 1472b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS), 1473b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_REF_TO_SYNC), 1474b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SYNC_WIDTH), 1475b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_BACK_PORCH), 1476b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_ACTIVE), 1477b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_FRONT_PORCH), 1478b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL), 1479b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A), 1480b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B), 1481b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C), 1482b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D), 1483b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL), 1484b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A), 1485b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B), 1486b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C), 1487b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D), 1488b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL), 1489b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A), 1490b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B), 1491b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C), 1492b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D), 1493b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL), 1494b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A), 1495b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B), 1496b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C), 1497b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL), 1498b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A), 1499b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B), 1500b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C), 1501b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL), 1502b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A), 1503b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL), 1504b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A), 1505b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_M0_CONTROL), 1506b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_M1_CONTROL), 1507b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DI_CONTROL), 1508b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_PP_CONTROL), 1509b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_PP_SELECT_A), 1510b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_PP_SELECT_B), 1511b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_PP_SELECT_C), 1512b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_PP_SELECT_D), 1513b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL), 1514b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL), 1515b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL), 1516b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS), 1517b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS), 1518b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS), 1519b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS), 1520b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_BORDER_COLOR), 1521b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER), 1522b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER), 1523b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER), 1524b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER), 1525b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND), 1526b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND), 1527b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR), 1528b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS), 1529b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_POSITION), 1530b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS), 1531b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL), 1532b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A), 1533b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B), 1534b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C), 1535b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D), 1536b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL), 1537b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST), 1538b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST), 1539b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST), 1540b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST), 1541b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL), 1542b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL), 1543b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_CONTROL), 1544b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF), 1545b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(0)), 1546b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(1)), 1547b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(2)), 1548b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(3)), 1549b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(4)), 1550b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(5)), 1551b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(6)), 1552b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(7)), 1553b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(8)), 1554b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL), 1555b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT), 1556b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)), 1557b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)), 1558b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)), 1559b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)), 1560b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)), 1561b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)), 1562b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)), 1563b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)), 1564b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)), 1565b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)), 1566b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)), 1567b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)), 1568b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL), 1569b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES), 1570b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES), 1571b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI), 1572b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL), 1573b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_WIN_OPTIONS), 1574b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BYTE_SWAP), 1575b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL), 1576b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_COLOR_DEPTH), 1577b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_POSITION), 1578b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_SIZE), 1579b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE), 1580b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA), 1581b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA), 1582b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_DDA_INC), 1583b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_LINE_STRIDE), 1584b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BUF_STRIDE), 1585b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE), 1586b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE), 1587b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_DV_CONTROL), 1588b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BLEND_NOKEY), 1589b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BLEND_1WIN), 1590b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X), 1591b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y), 1592b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY), 1593b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL), 1594b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR), 1595b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS), 1596b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR_U), 1597b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS), 1598b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR_V), 1599b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS), 1600b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET), 1601b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS), 1602b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET), 1603b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS), 1604b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS), 1605b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS), 1606b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS), 1607b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS), 1608b95800eeSThierry Reding }; 1609b95800eeSThierry Reding 1610b95800eeSThierry Reding static int tegra_dc_show_regs(struct seq_file *s, void *data) 1611b95800eeSThierry Reding { 1612b95800eeSThierry Reding struct drm_info_node *node = s->private; 1613b95800eeSThierry Reding struct tegra_dc *dc = node->info_ent->data; 1614b95800eeSThierry Reding unsigned int i; 1615b95800eeSThierry Reding int err = 0; 1616b95800eeSThierry Reding 1617b95800eeSThierry Reding drm_modeset_lock(&dc->base.mutex, NULL); 1618b95800eeSThierry Reding 1619b95800eeSThierry Reding if (!dc->base.state->active) { 1620b95800eeSThierry Reding err = -EBUSY; 1621b95800eeSThierry Reding goto unlock; 1622b95800eeSThierry Reding } 1623b95800eeSThierry Reding 1624b95800eeSThierry Reding for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { 1625b95800eeSThierry Reding unsigned int offset = tegra_dc_regs[i].offset; 1626b95800eeSThierry Reding 1627b95800eeSThierry Reding seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name, 1628b95800eeSThierry Reding offset, tegra_dc_readl(dc, offset)); 1629b95800eeSThierry Reding } 1630b95800eeSThierry Reding 1631b95800eeSThierry Reding unlock: 1632b95800eeSThierry Reding drm_modeset_unlock(&dc->base.mutex); 1633b95800eeSThierry Reding return err; 1634b95800eeSThierry Reding } 1635b95800eeSThierry Reding 1636b95800eeSThierry Reding static int tegra_dc_show_crc(struct seq_file *s, void *data) 1637b95800eeSThierry Reding { 1638b95800eeSThierry Reding struct drm_info_node *node = s->private; 1639b95800eeSThierry Reding struct tegra_dc *dc = node->info_ent->data; 1640b95800eeSThierry Reding int err = 0; 1641b95800eeSThierry Reding u32 value; 1642b95800eeSThierry Reding 1643b95800eeSThierry Reding drm_modeset_lock(&dc->base.mutex, NULL); 1644b95800eeSThierry Reding 1645b95800eeSThierry Reding if (!dc->base.state->active) { 1646b95800eeSThierry Reding err = -EBUSY; 1647b95800eeSThierry Reding goto unlock; 1648b95800eeSThierry Reding } 1649b95800eeSThierry Reding 1650b95800eeSThierry Reding value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE; 1651b95800eeSThierry Reding tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL); 1652b95800eeSThierry Reding tegra_dc_commit(dc); 1653b95800eeSThierry Reding 1654b95800eeSThierry Reding drm_crtc_wait_one_vblank(&dc->base); 1655b95800eeSThierry Reding drm_crtc_wait_one_vblank(&dc->base); 1656b95800eeSThierry Reding 1657b95800eeSThierry Reding value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); 1658b95800eeSThierry Reding seq_printf(s, "%08x\n", value); 1659b95800eeSThierry Reding 1660b95800eeSThierry Reding tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); 1661b95800eeSThierry Reding 1662b95800eeSThierry Reding unlock: 1663b95800eeSThierry Reding drm_modeset_unlock(&dc->base.mutex); 1664b95800eeSThierry Reding return err; 1665b95800eeSThierry Reding } 1666b95800eeSThierry Reding 1667b95800eeSThierry Reding static int tegra_dc_show_stats(struct seq_file *s, void *data) 1668b95800eeSThierry Reding { 1669b95800eeSThierry Reding struct drm_info_node *node = s->private; 1670b95800eeSThierry Reding struct tegra_dc *dc = node->info_ent->data; 1671b95800eeSThierry Reding 1672b95800eeSThierry Reding seq_printf(s, "frames: %lu\n", dc->stats.frames); 1673b95800eeSThierry Reding seq_printf(s, "vblank: %lu\n", dc->stats.vblank); 1674b95800eeSThierry Reding seq_printf(s, "underflow: %lu\n", dc->stats.underflow); 1675b95800eeSThierry Reding seq_printf(s, "overflow: %lu\n", dc->stats.overflow); 1676b95800eeSThierry Reding 1677ad85b084SDmitry Osipenko seq_printf(s, "frames total: %lu\n", dc->stats.frames_total); 1678ad85b084SDmitry Osipenko seq_printf(s, "vblank total: %lu\n", dc->stats.vblank_total); 1679ad85b084SDmitry Osipenko seq_printf(s, "underflow total: %lu\n", dc->stats.underflow_total); 1680ad85b084SDmitry Osipenko seq_printf(s, "overflow total: %lu\n", dc->stats.overflow_total); 1681ad85b084SDmitry Osipenko 1682b95800eeSThierry Reding return 0; 1683b95800eeSThierry Reding } 1684b95800eeSThierry Reding 1685b95800eeSThierry Reding static struct drm_info_list debugfs_files[] = { 1686b95800eeSThierry Reding { "regs", tegra_dc_show_regs, 0, NULL }, 1687b95800eeSThierry Reding { "crc", tegra_dc_show_crc, 0, NULL }, 1688b95800eeSThierry Reding { "stats", tegra_dc_show_stats, 0, NULL }, 1689b95800eeSThierry Reding }; 1690b95800eeSThierry Reding 1691b95800eeSThierry Reding static int tegra_dc_late_register(struct drm_crtc *crtc) 1692b95800eeSThierry Reding { 1693b95800eeSThierry Reding unsigned int i, count = ARRAY_SIZE(debugfs_files); 1694b95800eeSThierry Reding struct drm_minor *minor = crtc->dev->primary; 169539f55c61SArnd Bergmann struct dentry *root; 1696b95800eeSThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1697b95800eeSThierry Reding 169839f55c61SArnd Bergmann #ifdef CONFIG_DEBUG_FS 169939f55c61SArnd Bergmann root = crtc->debugfs_entry; 170039f55c61SArnd Bergmann #else 170139f55c61SArnd Bergmann root = NULL; 170239f55c61SArnd Bergmann #endif 170339f55c61SArnd Bergmann 1704b95800eeSThierry Reding dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), 1705b95800eeSThierry Reding GFP_KERNEL); 1706b95800eeSThierry Reding if (!dc->debugfs_files) 1707b95800eeSThierry Reding return -ENOMEM; 1708b95800eeSThierry Reding 1709b95800eeSThierry Reding for (i = 0; i < count; i++) 1710b95800eeSThierry Reding dc->debugfs_files[i].data = dc; 1711b95800eeSThierry Reding 1712ad6d94f2SWambui Karuga drm_debugfs_create_files(dc->debugfs_files, count, root, minor); 1713b95800eeSThierry Reding 1714b95800eeSThierry Reding return 0; 1715b95800eeSThierry Reding } 1716b95800eeSThierry Reding 1717b95800eeSThierry Reding static void tegra_dc_early_unregister(struct drm_crtc *crtc) 1718b95800eeSThierry Reding { 1719b95800eeSThierry Reding unsigned int count = ARRAY_SIZE(debugfs_files); 1720b95800eeSThierry Reding struct drm_minor *minor = crtc->dev->primary; 1721b95800eeSThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1722b95800eeSThierry Reding 1723b95800eeSThierry Reding drm_debugfs_remove_files(dc->debugfs_files, count, minor); 1724b95800eeSThierry Reding kfree(dc->debugfs_files); 1725b95800eeSThierry Reding dc->debugfs_files = NULL; 1726b95800eeSThierry Reding } 1727b95800eeSThierry Reding 1728c49c81e2SThierry Reding static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) 1729c49c81e2SThierry Reding { 1730c49c81e2SThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1731c49c81e2SThierry Reding 173247307954SThierry Reding /* XXX vblank syncpoints don't work with nvdisplay yet */ 173347307954SThierry Reding if (dc->syncpt && !dc->soc->has_nvdisplay) 1734c49c81e2SThierry Reding return host1x_syncpt_read(dc->syncpt); 1735c49c81e2SThierry Reding 1736c49c81e2SThierry Reding /* fallback to software emulated VBLANK counter */ 17373abe2413SDhinakaran Pandiyan return (u32)drm_crtc_vblank_count(&dc->base); 1738c49c81e2SThierry Reding } 1739c49c81e2SThierry Reding 1740c49c81e2SThierry Reding static int tegra_dc_enable_vblank(struct drm_crtc *crtc) 1741c49c81e2SThierry Reding { 1742c49c81e2SThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1743363541e8SThierry Reding u32 value; 1744c49c81e2SThierry Reding 1745c49c81e2SThierry Reding value = tegra_dc_readl(dc, DC_CMD_INT_MASK); 1746c49c81e2SThierry Reding value |= VBLANK_INT; 1747c49c81e2SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 1748c49c81e2SThierry Reding 1749c49c81e2SThierry Reding return 0; 1750c49c81e2SThierry Reding } 1751c49c81e2SThierry Reding 1752c49c81e2SThierry Reding static void tegra_dc_disable_vblank(struct drm_crtc *crtc) 1753c49c81e2SThierry Reding { 1754c49c81e2SThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1755363541e8SThierry Reding u32 value; 1756c49c81e2SThierry Reding 1757c49c81e2SThierry Reding value = tegra_dc_readl(dc, DC_CMD_INT_MASK); 1758c49c81e2SThierry Reding value &= ~VBLANK_INT; 1759c49c81e2SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 1760c49c81e2SThierry Reding } 1761c49c81e2SThierry Reding 1762dee8268fSThierry Reding static const struct drm_crtc_funcs tegra_crtc_funcs = { 17631503ca47SThierry Reding .page_flip = drm_atomic_helper_page_flip, 176474f48791SThierry Reding .set_config = drm_atomic_helper_set_config, 1765f002abc1SThierry Reding .destroy = tegra_dc_destroy, 1766ca915b10SThierry Reding .reset = tegra_crtc_reset, 1767ca915b10SThierry Reding .atomic_duplicate_state = tegra_crtc_atomic_duplicate_state, 1768ca915b10SThierry Reding .atomic_destroy_state = tegra_crtc_atomic_destroy_state, 1769b95800eeSThierry Reding .late_register = tegra_dc_late_register, 1770b95800eeSThierry Reding .early_unregister = tegra_dc_early_unregister, 177110437d9bSShawn Guo .get_vblank_counter = tegra_dc_get_vblank_counter, 177210437d9bSShawn Guo .enable_vblank = tegra_dc_enable_vblank, 177310437d9bSShawn Guo .disable_vblank = tegra_dc_disable_vblank, 1774dee8268fSThierry Reding }; 1775dee8268fSThierry Reding 1776dee8268fSThierry Reding static int tegra_dc_set_timings(struct tegra_dc *dc, 1777dee8268fSThierry Reding struct drm_display_mode *mode) 1778dee8268fSThierry Reding { 17790444c0ffSThierry Reding unsigned int h_ref_to_sync = 1; 17800444c0ffSThierry Reding unsigned int v_ref_to_sync = 1; 1781dee8268fSThierry Reding unsigned long value; 1782dee8268fSThierry Reding 178347307954SThierry Reding if (!dc->soc->has_nvdisplay) { 1784dee8268fSThierry Reding tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); 1785dee8268fSThierry Reding 1786dee8268fSThierry Reding value = (v_ref_to_sync << 16) | h_ref_to_sync; 1787dee8268fSThierry Reding tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC); 178847307954SThierry Reding } 1789dee8268fSThierry Reding 1790dee8268fSThierry Reding value = ((mode->vsync_end - mode->vsync_start) << 16) | 1791dee8268fSThierry Reding ((mode->hsync_end - mode->hsync_start) << 0); 1792dee8268fSThierry Reding tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH); 1793dee8268fSThierry Reding 1794dee8268fSThierry Reding value = ((mode->vtotal - mode->vsync_end) << 16) | 1795dee8268fSThierry Reding ((mode->htotal - mode->hsync_end) << 0); 1796dee8268fSThierry Reding tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH); 1797dee8268fSThierry Reding 1798dee8268fSThierry Reding value = ((mode->vsync_start - mode->vdisplay) << 16) | 1799dee8268fSThierry Reding ((mode->hsync_start - mode->hdisplay) << 0); 1800dee8268fSThierry Reding tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH); 1801dee8268fSThierry Reding 1802dee8268fSThierry Reding value = (mode->vdisplay << 16) | mode->hdisplay; 1803dee8268fSThierry Reding tegra_dc_writel(dc, value, DC_DISP_ACTIVE); 1804dee8268fSThierry Reding 1805dee8268fSThierry Reding return 0; 1806dee8268fSThierry Reding } 1807dee8268fSThierry Reding 18089d910b60SThierry Reding /** 18099d910b60SThierry Reding * tegra_dc_state_setup_clock - check clock settings and store them in atomic 18109d910b60SThierry Reding * state 18119d910b60SThierry Reding * @dc: display controller 18129d910b60SThierry Reding * @crtc_state: CRTC atomic state 18139d910b60SThierry Reding * @clk: parent clock for display controller 18149d910b60SThierry Reding * @pclk: pixel clock 18159d910b60SThierry Reding * @div: shift clock divider 18169d910b60SThierry Reding * 18179d910b60SThierry Reding * Returns: 18189d910b60SThierry Reding * 0 on success or a negative error-code on failure. 18199d910b60SThierry Reding */ 1820ca915b10SThierry Reding int tegra_dc_state_setup_clock(struct tegra_dc *dc, 1821ca915b10SThierry Reding struct drm_crtc_state *crtc_state, 1822ca915b10SThierry Reding struct clk *clk, unsigned long pclk, 1823ca915b10SThierry Reding unsigned int div) 1824ca915b10SThierry Reding { 1825ca915b10SThierry Reding struct tegra_dc_state *state = to_dc_state(crtc_state); 1826ca915b10SThierry Reding 1827d2982748SThierry Reding if (!clk_has_parent(dc->clk, clk)) 1828d2982748SThierry Reding return -EINVAL; 1829d2982748SThierry Reding 1830ca915b10SThierry Reding state->clk = clk; 1831ca915b10SThierry Reding state->pclk = pclk; 1832ca915b10SThierry Reding state->div = div; 1833ca915b10SThierry Reding 1834ca915b10SThierry Reding return 0; 1835ca915b10SThierry Reding } 1836ca915b10SThierry Reding 183776d59ed0SThierry Reding static void tegra_dc_commit_state(struct tegra_dc *dc, 183876d59ed0SThierry Reding struct tegra_dc_state *state) 183976d59ed0SThierry Reding { 184076d59ed0SThierry Reding u32 value; 184176d59ed0SThierry Reding int err; 184276d59ed0SThierry Reding 184376d59ed0SThierry Reding err = clk_set_parent(dc->clk, state->clk); 184476d59ed0SThierry Reding if (err < 0) 184576d59ed0SThierry Reding dev_err(dc->dev, "failed to set parent clock: %d\n", err); 184676d59ed0SThierry Reding 184776d59ed0SThierry Reding /* 184876d59ed0SThierry Reding * Outputs may not want to change the parent clock rate. This is only 184976d59ed0SThierry Reding * relevant to Tegra20 where only a single display PLL is available. 185076d59ed0SThierry Reding * Since that PLL would typically be used for HDMI, an internal LVDS 185176d59ed0SThierry Reding * panel would need to be driven by some other clock such as PLL_P 185276d59ed0SThierry Reding * which is shared with other peripherals. Changing the clock rate 185376d59ed0SThierry Reding * should therefore be avoided. 185476d59ed0SThierry Reding */ 185576d59ed0SThierry Reding if (state->pclk > 0) { 185676d59ed0SThierry Reding err = clk_set_rate(state->clk, state->pclk); 185776d59ed0SThierry Reding if (err < 0) 185876d59ed0SThierry Reding dev_err(dc->dev, 185976d59ed0SThierry Reding "failed to set clock rate to %lu Hz\n", 186076d59ed0SThierry Reding state->pclk); 1861f8fb97c9SDmitry Osipenko 1862f8fb97c9SDmitry Osipenko err = clk_set_rate(dc->clk, state->pclk); 1863f8fb97c9SDmitry Osipenko if (err < 0) 1864f8fb97c9SDmitry Osipenko dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n", 1865f8fb97c9SDmitry Osipenko dc->clk, state->pclk, err); 186676d59ed0SThierry Reding } 186776d59ed0SThierry Reding 186876d59ed0SThierry Reding DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), 186976d59ed0SThierry Reding state->div); 187076d59ed0SThierry Reding DRM_DEBUG_KMS("pclk: %lu\n", state->pclk); 187176d59ed0SThierry Reding 187247307954SThierry Reding if (!dc->soc->has_nvdisplay) { 187376d59ed0SThierry Reding value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1; 187476d59ed0SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); 187547307954SThierry Reding } 187676d59ed0SThierry Reding } 187776d59ed0SThierry Reding 1878003fc848SThierry Reding static void tegra_dc_stop(struct tegra_dc *dc) 1879003fc848SThierry Reding { 1880003fc848SThierry Reding u32 value; 1881003fc848SThierry Reding 1882003fc848SThierry Reding /* stop the display controller */ 1883003fc848SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); 1884003fc848SThierry Reding value &= ~DISP_CTRL_MODE_MASK; 1885003fc848SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); 1886003fc848SThierry Reding 1887003fc848SThierry Reding tegra_dc_commit(dc); 1888003fc848SThierry Reding } 1889003fc848SThierry Reding 1890003fc848SThierry Reding static bool tegra_dc_idle(struct tegra_dc *dc) 1891003fc848SThierry Reding { 1892003fc848SThierry Reding u32 value; 1893003fc848SThierry Reding 1894003fc848SThierry Reding value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND); 1895003fc848SThierry Reding 1896003fc848SThierry Reding return (value & DISP_CTRL_MODE_MASK) == 0; 1897003fc848SThierry Reding } 1898003fc848SThierry Reding 1899003fc848SThierry Reding static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout) 1900003fc848SThierry Reding { 1901003fc848SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 1902003fc848SThierry Reding 1903003fc848SThierry Reding while (time_before(jiffies, timeout)) { 1904003fc848SThierry Reding if (tegra_dc_idle(dc)) 1905003fc848SThierry Reding return 0; 1906003fc848SThierry Reding 1907003fc848SThierry Reding usleep_range(1000, 2000); 1908003fc848SThierry Reding } 1909003fc848SThierry Reding 1910003fc848SThierry Reding dev_dbg(dc->dev, "timeout waiting for DC to become idle\n"); 1911003fc848SThierry Reding return -ETIMEDOUT; 1912003fc848SThierry Reding } 1913003fc848SThierry Reding 191404d5d5dfSDmitry Osipenko static void 191504d5d5dfSDmitry Osipenko tegra_crtc_update_memory_bandwidth(struct drm_crtc *crtc, 191604d5d5dfSDmitry Osipenko struct drm_atomic_state *state, 191704d5d5dfSDmitry Osipenko bool prepare_bandwidth_transition) 191804d5d5dfSDmitry Osipenko { 191904d5d5dfSDmitry Osipenko const struct tegra_plane_state *old_tegra_state, *new_tegra_state; 192004d5d5dfSDmitry Osipenko u32 i, new_avg_bw, old_avg_bw, new_peak_bw, old_peak_bw; 192104d5d5dfSDmitry Osipenko const struct drm_plane_state *old_plane_state; 192204d5d5dfSDmitry Osipenko const struct drm_crtc_state *old_crtc_state; 192304d5d5dfSDmitry Osipenko struct tegra_dc_window window, old_window; 192404d5d5dfSDmitry Osipenko struct tegra_dc *dc = to_tegra_dc(crtc); 192504d5d5dfSDmitry Osipenko struct tegra_plane *tegra; 192604d5d5dfSDmitry Osipenko struct drm_plane *plane; 192704d5d5dfSDmitry Osipenko 192804d5d5dfSDmitry Osipenko if (dc->soc->has_nvdisplay) 192904d5d5dfSDmitry Osipenko return; 193004d5d5dfSDmitry Osipenko 193104d5d5dfSDmitry Osipenko old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); 193204d5d5dfSDmitry Osipenko 193304d5d5dfSDmitry Osipenko if (!crtc->state->active) { 193404d5d5dfSDmitry Osipenko if (!old_crtc_state->active) 193504d5d5dfSDmitry Osipenko return; 193604d5d5dfSDmitry Osipenko 193704d5d5dfSDmitry Osipenko /* 193804d5d5dfSDmitry Osipenko * When CRTC is disabled on DPMS, the state of attached planes 193904d5d5dfSDmitry Osipenko * is kept unchanged. Hence we need to enforce removal of the 194004d5d5dfSDmitry Osipenko * bandwidths from the ICC paths. 194104d5d5dfSDmitry Osipenko */ 194204d5d5dfSDmitry Osipenko drm_atomic_crtc_for_each_plane(plane, crtc) { 194304d5d5dfSDmitry Osipenko tegra = to_tegra_plane(plane); 194404d5d5dfSDmitry Osipenko 194504d5d5dfSDmitry Osipenko icc_set_bw(tegra->icc_mem, 0, 0); 194604d5d5dfSDmitry Osipenko icc_set_bw(tegra->icc_mem_vfilter, 0, 0); 194704d5d5dfSDmitry Osipenko } 194804d5d5dfSDmitry Osipenko 194904d5d5dfSDmitry Osipenko return; 195004d5d5dfSDmitry Osipenko } 195104d5d5dfSDmitry Osipenko 195204d5d5dfSDmitry Osipenko for_each_old_plane_in_state(old_crtc_state->state, plane, 195304d5d5dfSDmitry Osipenko old_plane_state, i) { 195404d5d5dfSDmitry Osipenko old_tegra_state = to_const_tegra_plane_state(old_plane_state); 195504d5d5dfSDmitry Osipenko new_tegra_state = to_const_tegra_plane_state(plane->state); 195604d5d5dfSDmitry Osipenko tegra = to_tegra_plane(plane); 195704d5d5dfSDmitry Osipenko 195804d5d5dfSDmitry Osipenko /* 195904d5d5dfSDmitry Osipenko * We're iterating over the global atomic state and it contains 196004d5d5dfSDmitry Osipenko * planes from another CRTC, hence we need to filter out the 196104d5d5dfSDmitry Osipenko * planes unrelated to this CRTC. 196204d5d5dfSDmitry Osipenko */ 196304d5d5dfSDmitry Osipenko if (tegra->dc != dc) 196404d5d5dfSDmitry Osipenko continue; 196504d5d5dfSDmitry Osipenko 196604d5d5dfSDmitry Osipenko new_avg_bw = new_tegra_state->avg_memory_bandwidth; 196704d5d5dfSDmitry Osipenko old_avg_bw = old_tegra_state->avg_memory_bandwidth; 196804d5d5dfSDmitry Osipenko 196904d5d5dfSDmitry Osipenko new_peak_bw = new_tegra_state->total_peak_memory_bandwidth; 197004d5d5dfSDmitry Osipenko old_peak_bw = old_tegra_state->total_peak_memory_bandwidth; 197104d5d5dfSDmitry Osipenko 197204d5d5dfSDmitry Osipenko /* 197304d5d5dfSDmitry Osipenko * See the comment related to !crtc->state->active above, 197404d5d5dfSDmitry Osipenko * which explains why bandwidths need to be updated when 197504d5d5dfSDmitry Osipenko * CRTC is turning ON. 197604d5d5dfSDmitry Osipenko */ 197704d5d5dfSDmitry Osipenko if (new_avg_bw == old_avg_bw && new_peak_bw == old_peak_bw && 197804d5d5dfSDmitry Osipenko old_crtc_state->active) 197904d5d5dfSDmitry Osipenko continue; 198004d5d5dfSDmitry Osipenko 198104d5d5dfSDmitry Osipenko window.src.h = drm_rect_height(&plane->state->src) >> 16; 198204d5d5dfSDmitry Osipenko window.dst.h = drm_rect_height(&plane->state->dst); 198304d5d5dfSDmitry Osipenko 198404d5d5dfSDmitry Osipenko old_window.src.h = drm_rect_height(&old_plane_state->src) >> 16; 198504d5d5dfSDmitry Osipenko old_window.dst.h = drm_rect_height(&old_plane_state->dst); 198604d5d5dfSDmitry Osipenko 198704d5d5dfSDmitry Osipenko /* 198804d5d5dfSDmitry Osipenko * During the preparation phase (atomic_begin), the memory 198904d5d5dfSDmitry Osipenko * freq should go high before the DC changes are committed 199004d5d5dfSDmitry Osipenko * if bandwidth requirement goes up, otherwise memory freq 199104d5d5dfSDmitry Osipenko * should to stay high if BW requirement goes down. The 199204d5d5dfSDmitry Osipenko * opposite applies to the completion phase (post_commit). 199304d5d5dfSDmitry Osipenko */ 199404d5d5dfSDmitry Osipenko if (prepare_bandwidth_transition) { 199504d5d5dfSDmitry Osipenko new_avg_bw = max(old_avg_bw, new_avg_bw); 199604d5d5dfSDmitry Osipenko new_peak_bw = max(old_peak_bw, new_peak_bw); 199704d5d5dfSDmitry Osipenko 199804d5d5dfSDmitry Osipenko if (tegra_plane_use_vertical_filtering(tegra, &old_window)) 199904d5d5dfSDmitry Osipenko window = old_window; 200004d5d5dfSDmitry Osipenko } 200104d5d5dfSDmitry Osipenko 200204d5d5dfSDmitry Osipenko icc_set_bw(tegra->icc_mem, new_avg_bw, new_peak_bw); 200304d5d5dfSDmitry Osipenko 200404d5d5dfSDmitry Osipenko if (tegra_plane_use_vertical_filtering(tegra, &window)) 200504d5d5dfSDmitry Osipenko icc_set_bw(tegra->icc_mem_vfilter, new_avg_bw, new_peak_bw); 200604d5d5dfSDmitry Osipenko else 200704d5d5dfSDmitry Osipenko icc_set_bw(tegra->icc_mem_vfilter, 0, 0); 200804d5d5dfSDmitry Osipenko } 200904d5d5dfSDmitry Osipenko } 201004d5d5dfSDmitry Osipenko 201164581714SLaurent Pinchart static void tegra_crtc_atomic_disable(struct drm_crtc *crtc, 2012351f950dSMaxime Ripard struct drm_atomic_state *state) 2013003fc848SThierry Reding { 2014003fc848SThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 2015003fc848SThierry Reding u32 value; 2016fd67e9c6SThierry Reding int err; 2017003fc848SThierry Reding 2018003fc848SThierry Reding if (!tegra_dc_idle(dc)) { 2019003fc848SThierry Reding tegra_dc_stop(dc); 2020003fc848SThierry Reding 2021003fc848SThierry Reding /* 2022003fc848SThierry Reding * Ignore the return value, there isn't anything useful to do 2023003fc848SThierry Reding * in case this fails. 2024003fc848SThierry Reding */ 2025003fc848SThierry Reding tegra_dc_wait_idle(dc, 100); 2026003fc848SThierry Reding } 2027003fc848SThierry Reding 2028003fc848SThierry Reding /* 2029003fc848SThierry Reding * This should really be part of the RGB encoder driver, but clearing 2030003fc848SThierry Reding * these bits has the side-effect of stopping the display controller. 2031003fc848SThierry Reding * When that happens no VBLANK interrupts will be raised. At the same 2032003fc848SThierry Reding * time the encoder is disabled before the display controller, so the 2033003fc848SThierry Reding * above code is always going to timeout waiting for the controller 2034003fc848SThierry Reding * to go idle. 2035003fc848SThierry Reding * 2036003fc848SThierry Reding * Given the close coupling between the RGB encoder and the display 2037003fc848SThierry Reding * controller doing it here is still kind of okay. None of the other 2038003fc848SThierry Reding * encoder drivers require these bits to be cleared. 2039003fc848SThierry Reding * 2040003fc848SThierry Reding * XXX: Perhaps given that the display controller is switched off at 2041003fc848SThierry Reding * this point anyway maybe clearing these bits isn't even useful for 2042003fc848SThierry Reding * the RGB encoder? 2043003fc848SThierry Reding */ 2044003fc848SThierry Reding if (dc->rgb) { 2045003fc848SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 2046003fc848SThierry Reding value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 2047003fc848SThierry Reding PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); 2048003fc848SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 2049003fc848SThierry Reding } 2050003fc848SThierry Reding 2051003fc848SThierry Reding tegra_dc_stats_reset(&dc->stats); 2052003fc848SThierry Reding drm_crtc_vblank_off(crtc); 205333a8eb8dSThierry Reding 20549d99ab6eSThierry Reding spin_lock_irq(&crtc->dev->event_lock); 20559d99ab6eSThierry Reding 20569d99ab6eSThierry Reding if (crtc->state->event) { 20579d99ab6eSThierry Reding drm_crtc_send_vblank_event(crtc, crtc->state->event); 20589d99ab6eSThierry Reding crtc->state->event = NULL; 20599d99ab6eSThierry Reding } 20609d99ab6eSThierry Reding 20619d99ab6eSThierry Reding spin_unlock_irq(&crtc->dev->event_lock); 20629d99ab6eSThierry Reding 2063fd67e9c6SThierry Reding err = host1x_client_suspend(&dc->client); 2064fd67e9c6SThierry Reding if (err < 0) 2065fd67e9c6SThierry Reding dev_err(dc->dev, "failed to suspend: %d\n", err); 2066003fc848SThierry Reding } 2067003fc848SThierry Reding 20680b20a0f8SLaurent Pinchart static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, 2069351f950dSMaxime Ripard struct drm_atomic_state *state) 2070dee8268fSThierry Reding { 20714aa3df71SThierry Reding struct drm_display_mode *mode = &crtc->state->adjusted_mode; 2072351f950dSMaxime Ripard struct tegra_dc_state *crtc_state = to_dc_state(crtc->state); 2073dee8268fSThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 2074dbb3f2f7SThierry Reding u32 value; 2075fd67e9c6SThierry Reding int err; 2076dee8268fSThierry Reding 2077fd67e9c6SThierry Reding err = host1x_client_resume(&dc->client); 2078fd67e9c6SThierry Reding if (err < 0) { 2079fd67e9c6SThierry Reding dev_err(dc->dev, "failed to resume: %d\n", err); 2080fd67e9c6SThierry Reding return; 2081fd67e9c6SThierry Reding } 208233a8eb8dSThierry Reding 208333a8eb8dSThierry Reding /* initialize display controller */ 208433a8eb8dSThierry Reding if (dc->syncpt) { 208547307954SThierry Reding u32 syncpt = host1x_syncpt_id(dc->syncpt), enable; 208647307954SThierry Reding 208747307954SThierry Reding if (dc->soc->has_nvdisplay) 208847307954SThierry Reding enable = 1 << 31; 208947307954SThierry Reding else 209047307954SThierry Reding enable = 1 << 8; 209133a8eb8dSThierry Reding 209233a8eb8dSThierry Reding value = SYNCPT_CNTRL_NO_STALL; 209333a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); 209433a8eb8dSThierry Reding 209547307954SThierry Reding value = enable | syncpt; 209633a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); 209733a8eb8dSThierry Reding } 209833a8eb8dSThierry Reding 209947307954SThierry Reding if (dc->soc->has_nvdisplay) { 210047307954SThierry Reding value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | 210147307954SThierry Reding DSC_OBUF_UF_INT; 210247307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); 210347307954SThierry Reding 210447307954SThierry Reding value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | 210547307954SThierry Reding DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT | 210647307954SThierry Reding HEAD_UF_INT | MSF_INT | REG_TMOUT_INT | 210747307954SThierry Reding REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT | 210847307954SThierry Reding VBLANK_INT | FRAME_END_INT; 210947307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); 211047307954SThierry Reding 211147307954SThierry Reding value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT | 211247307954SThierry Reding FRAME_END_INT; 211347307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 211447307954SThierry Reding 211547307954SThierry Reding value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT; 211647307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 211747307954SThierry Reding 211847307954SThierry Reding tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); 211947307954SThierry Reding } else { 212033a8eb8dSThierry Reding value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 212133a8eb8dSThierry Reding WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 212233a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); 212333a8eb8dSThierry Reding 212433a8eb8dSThierry Reding value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 212533a8eb8dSThierry Reding WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 212633a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); 212733a8eb8dSThierry Reding 212833a8eb8dSThierry Reding /* initialize timer */ 212933a8eb8dSThierry Reding value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | 213033a8eb8dSThierry Reding WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); 213133a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); 213233a8eb8dSThierry Reding 213333a8eb8dSThierry Reding value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | 213433a8eb8dSThierry Reding WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); 213533a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); 213633a8eb8dSThierry Reding 213733a8eb8dSThierry Reding value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 213833a8eb8dSThierry Reding WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 213933a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 214033a8eb8dSThierry Reding 214133a8eb8dSThierry Reding value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 214233a8eb8dSThierry Reding WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 214333a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 214447307954SThierry Reding } 214533a8eb8dSThierry Reding 21467116e9a8SThierry Reding if (dc->soc->supports_background_color) 21477116e9a8SThierry Reding tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR); 21487116e9a8SThierry Reding else 214933a8eb8dSThierry Reding tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); 215033a8eb8dSThierry Reding 215133a8eb8dSThierry Reding /* apply PLL and pixel clock changes */ 2152351f950dSMaxime Ripard tegra_dc_commit_state(dc, crtc_state); 215376d59ed0SThierry Reding 2154dee8268fSThierry Reding /* program display mode */ 2155dee8268fSThierry Reding tegra_dc_set_timings(dc, mode); 2156dee8268fSThierry Reding 21578620fc62SThierry Reding /* interlacing isn't supported yet, so disable it */ 21588620fc62SThierry Reding if (dc->soc->supports_interlacing) { 21598620fc62SThierry Reding value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); 21608620fc62SThierry Reding value &= ~INTERLACE_ENABLE; 21618620fc62SThierry Reding tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); 21628620fc62SThierry Reding } 2163666cb873SThierry Reding 2164666cb873SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); 2165666cb873SThierry Reding value &= ~DISP_CTRL_MODE_MASK; 2166666cb873SThierry Reding value |= DISP_CTRL_MODE_C_DISPLAY; 2167666cb873SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); 2168666cb873SThierry Reding 216947307954SThierry Reding if (!dc->soc->has_nvdisplay) { 2170666cb873SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 2171666cb873SThierry Reding value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 2172666cb873SThierry Reding PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; 2173666cb873SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 217447307954SThierry Reding } 217547307954SThierry Reding 217647307954SThierry Reding /* enable underflow reporting and display red for missing pixels */ 217747307954SThierry Reding if (dc->soc->has_nvdisplay) { 217847307954SThierry Reding value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE; 217947307954SThierry Reding tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW); 218047307954SThierry Reding } 2181666cb873SThierry Reding 2182*f7d6c6aeSDmitry Osipenko if (dc->rgb) { 2183*f7d6c6aeSDmitry Osipenko /* XXX: parameterize? */ 2184*f7d6c6aeSDmitry Osipenko value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE; 2185*f7d6c6aeSDmitry Osipenko tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS); 2186*f7d6c6aeSDmitry Osipenko } 2187*f7d6c6aeSDmitry Osipenko 2188666cb873SThierry Reding tegra_dc_commit(dc); 2189dee8268fSThierry Reding 21908ff64c17SThierry Reding drm_crtc_vblank_on(crtc); 2191dee8268fSThierry Reding } 2192dee8268fSThierry Reding 2193613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_begin(struct drm_crtc *crtc, 2194f6ebe9f9SMaxime Ripard struct drm_atomic_state *state) 21954aa3df71SThierry Reding { 21969d99ab6eSThierry Reding unsigned long flags; 21971503ca47SThierry Reding 219804d5d5dfSDmitry Osipenko tegra_crtc_update_memory_bandwidth(crtc, state, true); 219904d5d5dfSDmitry Osipenko 22001503ca47SThierry Reding if (crtc->state->event) { 22019d99ab6eSThierry Reding spin_lock_irqsave(&crtc->dev->event_lock, flags); 22021503ca47SThierry Reding 22039d99ab6eSThierry Reding if (drm_crtc_vblank_get(crtc) != 0) 22049d99ab6eSThierry Reding drm_crtc_send_vblank_event(crtc, crtc->state->event); 22059d99ab6eSThierry Reding else 22069d99ab6eSThierry Reding drm_crtc_arm_vblank_event(crtc, crtc->state->event); 22071503ca47SThierry Reding 22089d99ab6eSThierry Reding spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 22099d99ab6eSThierry Reding 22101503ca47SThierry Reding crtc->state->event = NULL; 22111503ca47SThierry Reding } 22124aa3df71SThierry Reding } 22134aa3df71SThierry Reding 2214613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, 2215f6ebe9f9SMaxime Ripard struct drm_atomic_state *state) 22164aa3df71SThierry Reding { 2217253f28b6SMaxime Ripard struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 2218253f28b6SMaxime Ripard crtc); 2219253f28b6SMaxime Ripard struct tegra_dc_state *dc_state = to_dc_state(crtc_state); 222047802b09SThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 222147307954SThierry Reding u32 value; 222247802b09SThierry Reding 2223253f28b6SMaxime Ripard value = dc_state->planes << 8 | GENERAL_UPDATE; 222447307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 222547307954SThierry Reding value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 222647307954SThierry Reding 2227253f28b6SMaxime Ripard value = dc_state->planes | GENERAL_ACT_REQ; 222847307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 222947307954SThierry Reding value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 22304aa3df71SThierry Reding } 22314aa3df71SThierry Reding 223204d5d5dfSDmitry Osipenko static bool tegra_plane_is_cursor(const struct drm_plane_state *state) 223304d5d5dfSDmitry Osipenko { 223404d5d5dfSDmitry Osipenko const struct tegra_dc_soc_info *soc = to_tegra_dc(state->crtc)->soc; 223504d5d5dfSDmitry Osipenko const struct drm_format_info *fmt = state->fb->format; 223604d5d5dfSDmitry Osipenko unsigned int src_w = drm_rect_width(&state->src) >> 16; 223704d5d5dfSDmitry Osipenko unsigned int dst_w = drm_rect_width(&state->dst); 223804d5d5dfSDmitry Osipenko 223904d5d5dfSDmitry Osipenko if (state->plane->type != DRM_PLANE_TYPE_CURSOR) 224004d5d5dfSDmitry Osipenko return false; 224104d5d5dfSDmitry Osipenko 224204d5d5dfSDmitry Osipenko if (soc->supports_cursor) 224304d5d5dfSDmitry Osipenko return true; 224404d5d5dfSDmitry Osipenko 224504d5d5dfSDmitry Osipenko if (src_w != dst_w || fmt->num_planes != 1 || src_w * fmt->cpp[0] > 256) 224604d5d5dfSDmitry Osipenko return false; 224704d5d5dfSDmitry Osipenko 224804d5d5dfSDmitry Osipenko return true; 224904d5d5dfSDmitry Osipenko } 225004d5d5dfSDmitry Osipenko 225104d5d5dfSDmitry Osipenko static unsigned long 225204d5d5dfSDmitry Osipenko tegra_plane_overlap_mask(struct drm_crtc_state *state, 225304d5d5dfSDmitry Osipenko const struct drm_plane_state *plane_state) 225404d5d5dfSDmitry Osipenko { 225504d5d5dfSDmitry Osipenko const struct drm_plane_state *other_state; 225604d5d5dfSDmitry Osipenko const struct tegra_plane *tegra; 225704d5d5dfSDmitry Osipenko unsigned long overlap_mask = 0; 225804d5d5dfSDmitry Osipenko struct drm_plane *plane; 225904d5d5dfSDmitry Osipenko struct drm_rect rect; 226004d5d5dfSDmitry Osipenko 226104d5d5dfSDmitry Osipenko if (!plane_state->visible || !plane_state->fb) 226204d5d5dfSDmitry Osipenko return 0; 226304d5d5dfSDmitry Osipenko 226404d5d5dfSDmitry Osipenko /* 226504d5d5dfSDmitry Osipenko * Data-prefetch FIFO will easily help to overcome temporal memory 226604d5d5dfSDmitry Osipenko * pressure if other plane overlaps with the cursor plane. 226704d5d5dfSDmitry Osipenko */ 226804d5d5dfSDmitry Osipenko if (tegra_plane_is_cursor(plane_state)) 226904d5d5dfSDmitry Osipenko return 0; 227004d5d5dfSDmitry Osipenko 227104d5d5dfSDmitry Osipenko drm_atomic_crtc_state_for_each_plane_state(plane, other_state, state) { 227204d5d5dfSDmitry Osipenko rect = plane_state->dst; 227304d5d5dfSDmitry Osipenko 227404d5d5dfSDmitry Osipenko tegra = to_tegra_plane(other_state->plane); 227504d5d5dfSDmitry Osipenko 227604d5d5dfSDmitry Osipenko if (!other_state->visible || !other_state->fb) 227704d5d5dfSDmitry Osipenko continue; 227804d5d5dfSDmitry Osipenko 227904d5d5dfSDmitry Osipenko /* 228004d5d5dfSDmitry Osipenko * Ignore cursor plane overlaps because it's not practical to 228104d5d5dfSDmitry Osipenko * assume that it contributes to the bandwidth in overlapping 228204d5d5dfSDmitry Osipenko * area if window width is small. 228304d5d5dfSDmitry Osipenko */ 228404d5d5dfSDmitry Osipenko if (tegra_plane_is_cursor(other_state)) 228504d5d5dfSDmitry Osipenko continue; 228604d5d5dfSDmitry Osipenko 228704d5d5dfSDmitry Osipenko if (drm_rect_intersect(&rect, &other_state->dst)) 228804d5d5dfSDmitry Osipenko overlap_mask |= BIT(tegra->index); 228904d5d5dfSDmitry Osipenko } 229004d5d5dfSDmitry Osipenko 229104d5d5dfSDmitry Osipenko return overlap_mask; 229204d5d5dfSDmitry Osipenko } 229304d5d5dfSDmitry Osipenko 229404d5d5dfSDmitry Osipenko static int tegra_crtc_calculate_memory_bandwidth(struct drm_crtc *crtc, 229504d5d5dfSDmitry Osipenko struct drm_atomic_state *state) 229604d5d5dfSDmitry Osipenko { 229704d5d5dfSDmitry Osipenko ulong overlap_mask[TEGRA_DC_LEGACY_PLANES_NUM] = {}, mask; 229804d5d5dfSDmitry Osipenko u32 plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM] = {}; 229904d5d5dfSDmitry Osipenko bool all_planes_overlap_simultaneously = true; 230004d5d5dfSDmitry Osipenko const struct tegra_plane_state *tegra_state; 230104d5d5dfSDmitry Osipenko const struct drm_plane_state *plane_state; 230204d5d5dfSDmitry Osipenko struct tegra_dc *dc = to_tegra_dc(crtc); 230304d5d5dfSDmitry Osipenko const struct drm_crtc_state *old_state; 230404d5d5dfSDmitry Osipenko struct drm_crtc_state *new_state; 230504d5d5dfSDmitry Osipenko struct tegra_plane *tegra; 230604d5d5dfSDmitry Osipenko struct drm_plane *plane; 230704d5d5dfSDmitry Osipenko 230804d5d5dfSDmitry Osipenko /* 230904d5d5dfSDmitry Osipenko * The nv-display uses shared planes. The algorithm below assumes 231004d5d5dfSDmitry Osipenko * maximum 3 planes per-CRTC, this assumption isn't applicable to 231104d5d5dfSDmitry Osipenko * the nv-display. Note that T124 support has additional windows, 231204d5d5dfSDmitry Osipenko * but currently they aren't supported by the driver. 231304d5d5dfSDmitry Osipenko */ 231404d5d5dfSDmitry Osipenko if (dc->soc->has_nvdisplay) 231504d5d5dfSDmitry Osipenko return 0; 231604d5d5dfSDmitry Osipenko 231704d5d5dfSDmitry Osipenko new_state = drm_atomic_get_new_crtc_state(state, crtc); 231804d5d5dfSDmitry Osipenko old_state = drm_atomic_get_old_crtc_state(state, crtc); 231904d5d5dfSDmitry Osipenko 232004d5d5dfSDmitry Osipenko /* 232104d5d5dfSDmitry Osipenko * For overlapping planes pixel's data is fetched for each plane at 232204d5d5dfSDmitry Osipenko * the same time, hence bandwidths are accumulated in this case. 232304d5d5dfSDmitry Osipenko * This needs to be taken into account for calculating total bandwidth 232404d5d5dfSDmitry Osipenko * consumed by all planes. 232504d5d5dfSDmitry Osipenko * 232604d5d5dfSDmitry Osipenko * Here we get the overlapping state of each plane, which is a 232704d5d5dfSDmitry Osipenko * bitmask of plane indices telling with what planes there is an 232804d5d5dfSDmitry Osipenko * overlap. Note that bitmask[plane] includes BIT(plane) in order 232904d5d5dfSDmitry Osipenko * to make further code nicer and simpler. 233004d5d5dfSDmitry Osipenko */ 233104d5d5dfSDmitry Osipenko drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) { 233204d5d5dfSDmitry Osipenko tegra_state = to_const_tegra_plane_state(plane_state); 233304d5d5dfSDmitry Osipenko tegra = to_tegra_plane(plane); 233404d5d5dfSDmitry Osipenko 233504d5d5dfSDmitry Osipenko if (WARN_ON_ONCE(tegra->index >= TEGRA_DC_LEGACY_PLANES_NUM)) 233604d5d5dfSDmitry Osipenko return -EINVAL; 233704d5d5dfSDmitry Osipenko 233804d5d5dfSDmitry Osipenko plane_peak_bw[tegra->index] = tegra_state->peak_memory_bandwidth; 233904d5d5dfSDmitry Osipenko mask = tegra_plane_overlap_mask(new_state, plane_state); 234004d5d5dfSDmitry Osipenko overlap_mask[tegra->index] = mask; 234104d5d5dfSDmitry Osipenko 234204d5d5dfSDmitry Osipenko if (hweight_long(mask) != 3) 234304d5d5dfSDmitry Osipenko all_planes_overlap_simultaneously = false; 234404d5d5dfSDmitry Osipenko } 234504d5d5dfSDmitry Osipenko 234604d5d5dfSDmitry Osipenko /* 234704d5d5dfSDmitry Osipenko * Then we calculate maximum bandwidth of each plane state. 234804d5d5dfSDmitry Osipenko * The bandwidth includes the plane BW + BW of the "simultaneously" 234904d5d5dfSDmitry Osipenko * overlapping planes, where "simultaneously" means areas where DC 235004d5d5dfSDmitry Osipenko * fetches from the planes simultaneously during of scan-out process. 235104d5d5dfSDmitry Osipenko * 235204d5d5dfSDmitry Osipenko * For example, if plane A overlaps with planes B and C, but B and C 235304d5d5dfSDmitry Osipenko * don't overlap, then the peak bandwidth will be either in area where 235404d5d5dfSDmitry Osipenko * A-and-B or A-and-C planes overlap. 235504d5d5dfSDmitry Osipenko * 235604d5d5dfSDmitry Osipenko * The plane_peak_bw[] contains peak memory bandwidth values of 235704d5d5dfSDmitry Osipenko * each plane, this information is needed by interconnect provider 235804d5d5dfSDmitry Osipenko * in order to set up latency allowance based on the peak BW, see 235904d5d5dfSDmitry Osipenko * tegra_crtc_update_memory_bandwidth(). 236004d5d5dfSDmitry Osipenko */ 236104d5d5dfSDmitry Osipenko drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) { 236204d5d5dfSDmitry Osipenko u32 i, old_peak_bw, new_peak_bw, overlap_bw = 0; 236304d5d5dfSDmitry Osipenko 236404d5d5dfSDmitry Osipenko /* 236504d5d5dfSDmitry Osipenko * Note that plane's atomic check doesn't touch the 236604d5d5dfSDmitry Osipenko * total_peak_memory_bandwidth of enabled plane, hence the 236704d5d5dfSDmitry Osipenko * current state contains the old bandwidth state from the 236804d5d5dfSDmitry Osipenko * previous CRTC commit. 236904d5d5dfSDmitry Osipenko */ 237004d5d5dfSDmitry Osipenko tegra_state = to_const_tegra_plane_state(plane_state); 237104d5d5dfSDmitry Osipenko tegra = to_tegra_plane(plane); 237204d5d5dfSDmitry Osipenko 237304d5d5dfSDmitry Osipenko for_each_set_bit(i, &overlap_mask[tegra->index], 3) { 237404d5d5dfSDmitry Osipenko if (i == tegra->index) 237504d5d5dfSDmitry Osipenko continue; 237604d5d5dfSDmitry Osipenko 237704d5d5dfSDmitry Osipenko if (all_planes_overlap_simultaneously) 237804d5d5dfSDmitry Osipenko overlap_bw += plane_peak_bw[i]; 237904d5d5dfSDmitry Osipenko else 238004d5d5dfSDmitry Osipenko overlap_bw = max(overlap_bw, plane_peak_bw[i]); 238104d5d5dfSDmitry Osipenko } 238204d5d5dfSDmitry Osipenko 238304d5d5dfSDmitry Osipenko new_peak_bw = plane_peak_bw[tegra->index] + overlap_bw; 238404d5d5dfSDmitry Osipenko old_peak_bw = tegra_state->total_peak_memory_bandwidth; 238504d5d5dfSDmitry Osipenko 238604d5d5dfSDmitry Osipenko /* 238704d5d5dfSDmitry Osipenko * If plane's peak bandwidth changed (for example plane isn't 238804d5d5dfSDmitry Osipenko * overlapped anymore) and plane isn't in the atomic state, 238904d5d5dfSDmitry Osipenko * then add plane to the state in order to have the bandwidth 239004d5d5dfSDmitry Osipenko * updated. 239104d5d5dfSDmitry Osipenko */ 239204d5d5dfSDmitry Osipenko if (old_peak_bw != new_peak_bw) { 239304d5d5dfSDmitry Osipenko struct tegra_plane_state *new_tegra_state; 239404d5d5dfSDmitry Osipenko struct drm_plane_state *new_plane_state; 239504d5d5dfSDmitry Osipenko 239604d5d5dfSDmitry Osipenko new_plane_state = drm_atomic_get_plane_state(state, plane); 239704d5d5dfSDmitry Osipenko if (IS_ERR(new_plane_state)) 239804d5d5dfSDmitry Osipenko return PTR_ERR(new_plane_state); 239904d5d5dfSDmitry Osipenko 240004d5d5dfSDmitry Osipenko new_tegra_state = to_tegra_plane_state(new_plane_state); 240104d5d5dfSDmitry Osipenko new_tegra_state->total_peak_memory_bandwidth = new_peak_bw; 240204d5d5dfSDmitry Osipenko } 240304d5d5dfSDmitry Osipenko } 240404d5d5dfSDmitry Osipenko 240504d5d5dfSDmitry Osipenko return 0; 240604d5d5dfSDmitry Osipenko } 240704d5d5dfSDmitry Osipenko 240804d5d5dfSDmitry Osipenko static int tegra_crtc_atomic_check(struct drm_crtc *crtc, 240904d5d5dfSDmitry Osipenko struct drm_atomic_state *state) 241004d5d5dfSDmitry Osipenko { 241104d5d5dfSDmitry Osipenko int err; 241204d5d5dfSDmitry Osipenko 241304d5d5dfSDmitry Osipenko err = tegra_crtc_calculate_memory_bandwidth(crtc, state); 241404d5d5dfSDmitry Osipenko if (err) 241504d5d5dfSDmitry Osipenko return err; 241604d5d5dfSDmitry Osipenko 241704d5d5dfSDmitry Osipenko return 0; 241804d5d5dfSDmitry Osipenko } 241904d5d5dfSDmitry Osipenko 242004d5d5dfSDmitry Osipenko void tegra_crtc_atomic_post_commit(struct drm_crtc *crtc, 242104d5d5dfSDmitry Osipenko struct drm_atomic_state *state) 242204d5d5dfSDmitry Osipenko { 242304d5d5dfSDmitry Osipenko /* 242404d5d5dfSDmitry Osipenko * Display bandwidth is allowed to go down only once hardware state 242504d5d5dfSDmitry Osipenko * is known to be armed, i.e. state was committed and VBLANK event 242604d5d5dfSDmitry Osipenko * received. 242704d5d5dfSDmitry Osipenko */ 242804d5d5dfSDmitry Osipenko tegra_crtc_update_memory_bandwidth(crtc, state, false); 242904d5d5dfSDmitry Osipenko } 243004d5d5dfSDmitry Osipenko 2431dee8268fSThierry Reding static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { 243204d5d5dfSDmitry Osipenko .atomic_check = tegra_crtc_atomic_check, 24334aa3df71SThierry Reding .atomic_begin = tegra_crtc_atomic_begin, 24344aa3df71SThierry Reding .atomic_flush = tegra_crtc_atomic_flush, 24350b20a0f8SLaurent Pinchart .atomic_enable = tegra_crtc_atomic_enable, 243664581714SLaurent Pinchart .atomic_disable = tegra_crtc_atomic_disable, 2437dee8268fSThierry Reding }; 2438dee8268fSThierry Reding 2439dee8268fSThierry Reding static irqreturn_t tegra_dc_irq(int irq, void *data) 2440dee8268fSThierry Reding { 2441dee8268fSThierry Reding struct tegra_dc *dc = data; 2442dee8268fSThierry Reding unsigned long status; 2443dee8268fSThierry Reding 2444dee8268fSThierry Reding status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); 2445dee8268fSThierry Reding tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); 2446dee8268fSThierry Reding 2447dee8268fSThierry Reding if (status & FRAME_END_INT) { 2448dee8268fSThierry Reding /* 2449dee8268fSThierry Reding dev_dbg(dc->dev, "%s(): frame end\n", __func__); 2450dee8268fSThierry Reding */ 2451ad85b084SDmitry Osipenko dc->stats.frames_total++; 2452791ddb1eSThierry Reding dc->stats.frames++; 2453dee8268fSThierry Reding } 2454dee8268fSThierry Reding 2455dee8268fSThierry Reding if (status & VBLANK_INT) { 2456dee8268fSThierry Reding /* 2457dee8268fSThierry Reding dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); 2458dee8268fSThierry Reding */ 2459ed7dae58SThierry Reding drm_crtc_handle_vblank(&dc->base); 2460ad85b084SDmitry Osipenko dc->stats.vblank_total++; 2461791ddb1eSThierry Reding dc->stats.vblank++; 2462dee8268fSThierry Reding } 2463dee8268fSThierry Reding 2464dee8268fSThierry Reding if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { 2465dee8268fSThierry Reding /* 2466dee8268fSThierry Reding dev_dbg(dc->dev, "%s(): underflow\n", __func__); 2467dee8268fSThierry Reding */ 2468ad85b084SDmitry Osipenko dc->stats.underflow_total++; 2469791ddb1eSThierry Reding dc->stats.underflow++; 2470791ddb1eSThierry Reding } 2471791ddb1eSThierry Reding 2472791ddb1eSThierry Reding if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) { 2473791ddb1eSThierry Reding /* 2474791ddb1eSThierry Reding dev_dbg(dc->dev, "%s(): overflow\n", __func__); 2475791ddb1eSThierry Reding */ 2476ad85b084SDmitry Osipenko dc->stats.overflow_total++; 2477791ddb1eSThierry Reding dc->stats.overflow++; 2478dee8268fSThierry Reding } 2479dee8268fSThierry Reding 248047307954SThierry Reding if (status & HEAD_UF_INT) { 248147307954SThierry Reding dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__); 2482ad85b084SDmitry Osipenko dc->stats.underflow_total++; 248347307954SThierry Reding dc->stats.underflow++; 248447307954SThierry Reding } 248547307954SThierry Reding 2486dee8268fSThierry Reding return IRQ_HANDLED; 2487dee8268fSThierry Reding } 2488dee8268fSThierry Reding 2489e75d0477SThierry Reding static bool tegra_dc_has_window_groups(struct tegra_dc *dc) 2490e75d0477SThierry Reding { 2491e75d0477SThierry Reding unsigned int i; 2492e75d0477SThierry Reding 2493e75d0477SThierry Reding if (!dc->soc->wgrps) 2494e75d0477SThierry Reding return true; 2495e75d0477SThierry Reding 2496e75d0477SThierry Reding for (i = 0; i < dc->soc->num_wgrps; i++) { 2497e75d0477SThierry Reding const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; 2498e75d0477SThierry Reding 2499e75d0477SThierry Reding if (wgrp->dc == dc->pipe && wgrp->num_windows > 0) 2500e75d0477SThierry Reding return true; 2501e75d0477SThierry Reding } 2502e75d0477SThierry Reding 2503e75d0477SThierry Reding return false; 2504e75d0477SThierry Reding } 2505e75d0477SThierry Reding 250605d1adfeSThierry Reding static int tegra_dc_early_init(struct host1x_client *client) 250705d1adfeSThierry Reding { 250805d1adfeSThierry Reding struct drm_device *drm = dev_get_drvdata(client->host); 250905d1adfeSThierry Reding struct tegra_drm *tegra = drm->dev_private; 251005d1adfeSThierry Reding 251105d1adfeSThierry Reding tegra->num_crtcs++; 251205d1adfeSThierry Reding 251305d1adfeSThierry Reding return 0; 251405d1adfeSThierry Reding } 251505d1adfeSThierry Reding 2516dee8268fSThierry Reding static int tegra_dc_init(struct host1x_client *client) 2517dee8268fSThierry Reding { 2518608f43adSThierry Reding struct drm_device *drm = dev_get_drvdata(client->host); 25192bcdcbfaSThierry Reding unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; 2520dee8268fSThierry Reding struct tegra_dc *dc = host1x_client_to_dc(client); 2521d1f3e1e0SThierry Reding struct tegra_drm *tegra = drm->dev_private; 2522c7679306SThierry Reding struct drm_plane *primary = NULL; 2523c7679306SThierry Reding struct drm_plane *cursor = NULL; 2524dee8268fSThierry Reding int err; 2525dee8268fSThierry Reding 2526759d706fSThierry Reding /* 2527f5ba33fbSMikko Perttunen * DC has been reset by now, so VBLANK syncpoint can be released 2528f5ba33fbSMikko Perttunen * for general use. 2529f5ba33fbSMikko Perttunen */ 2530f5ba33fbSMikko Perttunen host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe); 2531f5ba33fbSMikko Perttunen 2532f5ba33fbSMikko Perttunen /* 2533759d706fSThierry Reding * XXX do not register DCs with no window groups because we cannot 2534759d706fSThierry Reding * assign a primary plane to them, which in turn will cause KMS to 2535759d706fSThierry Reding * crash. 2536759d706fSThierry Reding */ 2537e75d0477SThierry Reding if (!tegra_dc_has_window_groups(dc)) 2538759d706fSThierry Reding return 0; 2539759d706fSThierry Reding 2540fd67e9c6SThierry Reding /* 2541fd67e9c6SThierry Reding * Set the display hub as the host1x client parent for the display 2542fd67e9c6SThierry Reding * controller. This is needed for the runtime reference counting that 2543fd67e9c6SThierry Reding * ensures the display hub is always powered when any of the display 2544fd67e9c6SThierry Reding * controllers are. 2545fd67e9c6SThierry Reding */ 2546fd67e9c6SThierry Reding if (dc->soc->has_nvdisplay) 2547fd67e9c6SThierry Reding client->parent = &tegra->hub->client; 2548fd67e9c6SThierry Reding 2549617dd7ccSThierry Reding dc->syncpt = host1x_syncpt_request(client, flags); 25502bcdcbfaSThierry Reding if (!dc->syncpt) 25512bcdcbfaSThierry Reding dev_warn(dc->dev, "failed to allocate syncpoint\n"); 25522bcdcbfaSThierry Reding 25537edd7961SThierry Reding err = host1x_client_iommu_attach(client); 2554a8817489SThierry Reding if (err < 0 && err != -ENODEV) { 25550c407de5SThierry Reding dev_err(client->dev, "failed to attach to domain: %d\n", err); 2556df06b759SThierry Reding return err; 2557df06b759SThierry Reding } 2558df06b759SThierry Reding 255947307954SThierry Reding if (dc->soc->wgrps) 256047307954SThierry Reding primary = tegra_dc_add_shared_planes(drm, dc); 256147307954SThierry Reding else 256247307954SThierry Reding primary = tegra_dc_add_planes(drm, dc); 256347307954SThierry Reding 2564c7679306SThierry Reding if (IS_ERR(primary)) { 2565c7679306SThierry Reding err = PTR_ERR(primary); 2566c7679306SThierry Reding goto cleanup; 2567c7679306SThierry Reding } 2568c7679306SThierry Reding 2569c7679306SThierry Reding if (dc->soc->supports_cursor) { 2570c7679306SThierry Reding cursor = tegra_dc_cursor_plane_create(drm, dc); 2571c7679306SThierry Reding if (IS_ERR(cursor)) { 2572c7679306SThierry Reding err = PTR_ERR(cursor); 2573c7679306SThierry Reding goto cleanup; 2574c7679306SThierry Reding } 25759f446d83SDmitry Osipenko } else { 25769f446d83SDmitry Osipenko /* dedicate one overlay to mouse cursor */ 25779f446d83SDmitry Osipenko cursor = tegra_dc_overlay_plane_create(drm, dc, 2, true); 25789f446d83SDmitry Osipenko if (IS_ERR(cursor)) { 25799f446d83SDmitry Osipenko err = PTR_ERR(cursor); 25809f446d83SDmitry Osipenko goto cleanup; 25819f446d83SDmitry Osipenko } 2582c7679306SThierry Reding } 2583c7679306SThierry Reding 2584c7679306SThierry Reding err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor, 2585f9882876SVille Syrjälä &tegra_crtc_funcs, NULL); 2586c7679306SThierry Reding if (err < 0) 2587c7679306SThierry Reding goto cleanup; 2588c7679306SThierry Reding 2589dee8268fSThierry Reding drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); 2590dee8268fSThierry Reding 2591d1f3e1e0SThierry Reding /* 2592d1f3e1e0SThierry Reding * Keep track of the minimum pitch alignment across all display 2593d1f3e1e0SThierry Reding * controllers. 2594d1f3e1e0SThierry Reding */ 2595d1f3e1e0SThierry Reding if (dc->soc->pitch_align > tegra->pitch_align) 2596d1f3e1e0SThierry Reding tegra->pitch_align = dc->soc->pitch_align; 2597d1f3e1e0SThierry Reding 2598042c0bd7SThierry Reding /* track maximum resolution */ 2599042c0bd7SThierry Reding if (dc->soc->has_nvdisplay) 2600042c0bd7SThierry Reding drm->mode_config.max_width = drm->mode_config.max_height = 16384; 2601042c0bd7SThierry Reding else 2602042c0bd7SThierry Reding drm->mode_config.max_width = drm->mode_config.max_height = 4096; 2603042c0bd7SThierry Reding 26049910f5c4SThierry Reding err = tegra_dc_rgb_init(drm, dc); 2605dee8268fSThierry Reding if (err < 0 && err != -ENODEV) { 2606dee8268fSThierry Reding dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); 2607c7679306SThierry Reding goto cleanup; 2608dee8268fSThierry Reding } 2609dee8268fSThierry Reding 2610dee8268fSThierry Reding err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0, 2611dee8268fSThierry Reding dev_name(dc->dev), dc); 2612dee8268fSThierry Reding if (err < 0) { 2613dee8268fSThierry Reding dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, 2614dee8268fSThierry Reding err); 2615c7679306SThierry Reding goto cleanup; 2616dee8268fSThierry Reding } 2617dee8268fSThierry Reding 261847b15779SThierry Reding /* 261947b15779SThierry Reding * Inherit the DMA parameters (such as maximum segment size) from the 2620608f43adSThierry Reding * parent host1x device. 262147b15779SThierry Reding */ 2622608f43adSThierry Reding client->dev->dma_parms = client->host->dma_parms; 262347b15779SThierry Reding 2624dee8268fSThierry Reding return 0; 2625c7679306SThierry Reding 2626c7679306SThierry Reding cleanup: 262747307954SThierry Reding if (!IS_ERR_OR_NULL(cursor)) 2628c7679306SThierry Reding drm_plane_cleanup(cursor); 2629c7679306SThierry Reding 263047307954SThierry Reding if (!IS_ERR(primary)) 2631c7679306SThierry Reding drm_plane_cleanup(primary); 2632c7679306SThierry Reding 2633aacdf198SThierry Reding host1x_client_iommu_detach(client); 26342aed4f5aSMikko Perttunen host1x_syncpt_put(dc->syncpt); 2635fd5ec0dcSThierry Reding 2636c7679306SThierry Reding return err; 2637dee8268fSThierry Reding } 2638dee8268fSThierry Reding 2639dee8268fSThierry Reding static int tegra_dc_exit(struct host1x_client *client) 2640dee8268fSThierry Reding { 2641dee8268fSThierry Reding struct tegra_dc *dc = host1x_client_to_dc(client); 2642dee8268fSThierry Reding int err; 2643dee8268fSThierry Reding 2644e75d0477SThierry Reding if (!tegra_dc_has_window_groups(dc)) 2645e75d0477SThierry Reding return 0; 2646e75d0477SThierry Reding 264747b15779SThierry Reding /* avoid a dangling pointer just in case this disappears */ 264847b15779SThierry Reding client->dev->dma_parms = NULL; 264947b15779SThierry Reding 2650dee8268fSThierry Reding devm_free_irq(dc->dev, dc->irq, dc); 2651dee8268fSThierry Reding 2652dee8268fSThierry Reding err = tegra_dc_rgb_exit(dc); 2653dee8268fSThierry Reding if (err) { 2654dee8268fSThierry Reding dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err); 2655dee8268fSThierry Reding return err; 2656dee8268fSThierry Reding } 2657dee8268fSThierry Reding 2658aacdf198SThierry Reding host1x_client_iommu_detach(client); 26592aed4f5aSMikko Perttunen host1x_syncpt_put(dc->syncpt); 26602bcdcbfaSThierry Reding 2661dee8268fSThierry Reding return 0; 2662dee8268fSThierry Reding } 2663dee8268fSThierry Reding 266405d1adfeSThierry Reding static int tegra_dc_late_exit(struct host1x_client *client) 266505d1adfeSThierry Reding { 266605d1adfeSThierry Reding struct drm_device *drm = dev_get_drvdata(client->host); 266705d1adfeSThierry Reding struct tegra_drm *tegra = drm->dev_private; 266805d1adfeSThierry Reding 266905d1adfeSThierry Reding tegra->num_crtcs--; 2670dee8268fSThierry Reding 2671dee8268fSThierry Reding return 0; 2672dee8268fSThierry Reding } 2673dee8268fSThierry Reding 2674fd67e9c6SThierry Reding static int tegra_dc_runtime_suspend(struct host1x_client *client) 2675fd67e9c6SThierry Reding { 2676fd67e9c6SThierry Reding struct tegra_dc *dc = host1x_client_to_dc(client); 2677fd67e9c6SThierry Reding struct device *dev = client->dev; 2678fd67e9c6SThierry Reding int err; 2679fd67e9c6SThierry Reding 2680fd67e9c6SThierry Reding err = reset_control_assert(dc->rst); 2681fd67e9c6SThierry Reding if (err < 0) { 2682fd67e9c6SThierry Reding dev_err(dev, "failed to assert reset: %d\n", err); 2683fd67e9c6SThierry Reding return err; 2684fd67e9c6SThierry Reding } 2685fd67e9c6SThierry Reding 2686fd67e9c6SThierry Reding if (dc->soc->has_powergate) 2687fd67e9c6SThierry Reding tegra_powergate_power_off(dc->powergate); 2688fd67e9c6SThierry Reding 2689fd67e9c6SThierry Reding clk_disable_unprepare(dc->clk); 2690fd67e9c6SThierry Reding pm_runtime_put_sync(dev); 2691fd67e9c6SThierry Reding 2692fd67e9c6SThierry Reding return 0; 2693fd67e9c6SThierry Reding } 2694fd67e9c6SThierry Reding 2695fd67e9c6SThierry Reding static int tegra_dc_runtime_resume(struct host1x_client *client) 2696fd67e9c6SThierry Reding { 2697fd67e9c6SThierry Reding struct tegra_dc *dc = host1x_client_to_dc(client); 2698fd67e9c6SThierry Reding struct device *dev = client->dev; 2699fd67e9c6SThierry Reding int err; 2700fd67e9c6SThierry Reding 2701dcdfe271SQinglang Miao err = pm_runtime_resume_and_get(dev); 2702fd67e9c6SThierry Reding if (err < 0) { 2703fd67e9c6SThierry Reding dev_err(dev, "failed to get runtime PM: %d\n", err); 2704fd67e9c6SThierry Reding return err; 2705fd67e9c6SThierry Reding } 2706fd67e9c6SThierry Reding 2707fd67e9c6SThierry Reding if (dc->soc->has_powergate) { 2708fd67e9c6SThierry Reding err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, 2709fd67e9c6SThierry Reding dc->rst); 2710fd67e9c6SThierry Reding if (err < 0) { 2711fd67e9c6SThierry Reding dev_err(dev, "failed to power partition: %d\n", err); 2712fd67e9c6SThierry Reding goto put_rpm; 2713fd67e9c6SThierry Reding } 2714fd67e9c6SThierry Reding } else { 2715fd67e9c6SThierry Reding err = clk_prepare_enable(dc->clk); 2716fd67e9c6SThierry Reding if (err < 0) { 2717fd67e9c6SThierry Reding dev_err(dev, "failed to enable clock: %d\n", err); 2718fd67e9c6SThierry Reding goto put_rpm; 2719fd67e9c6SThierry Reding } 2720fd67e9c6SThierry Reding 2721fd67e9c6SThierry Reding err = reset_control_deassert(dc->rst); 2722fd67e9c6SThierry Reding if (err < 0) { 2723fd67e9c6SThierry Reding dev_err(dev, "failed to deassert reset: %d\n", err); 2724fd67e9c6SThierry Reding goto disable_clk; 2725fd67e9c6SThierry Reding } 2726fd67e9c6SThierry Reding } 2727fd67e9c6SThierry Reding 2728fd67e9c6SThierry Reding return 0; 2729fd67e9c6SThierry Reding 2730fd67e9c6SThierry Reding disable_clk: 2731fd67e9c6SThierry Reding clk_disable_unprepare(dc->clk); 2732fd67e9c6SThierry Reding put_rpm: 2733fd67e9c6SThierry Reding pm_runtime_put_sync(dev); 2734fd67e9c6SThierry Reding return err; 2735fd67e9c6SThierry Reding } 2736fd67e9c6SThierry Reding 2737dee8268fSThierry Reding static const struct host1x_client_ops dc_client_ops = { 273805d1adfeSThierry Reding .early_init = tegra_dc_early_init, 2739dee8268fSThierry Reding .init = tegra_dc_init, 2740dee8268fSThierry Reding .exit = tegra_dc_exit, 274105d1adfeSThierry Reding .late_exit = tegra_dc_late_exit, 2742fd67e9c6SThierry Reding .suspend = tegra_dc_runtime_suspend, 2743fd67e9c6SThierry Reding .resume = tegra_dc_runtime_resume, 2744dee8268fSThierry Reding }; 2745dee8268fSThierry Reding 27468620fc62SThierry Reding static const struct tegra_dc_soc_info tegra20_dc_soc_info = { 27477116e9a8SThierry Reding .supports_background_color = false, 27488620fc62SThierry Reding .supports_interlacing = false, 2749e687651bSThierry Reding .supports_cursor = false, 2750c134f019SThierry Reding .supports_block_linear = false, 27517b6f8467SThierry Reding .supports_sector_layout = false, 2752a43d0a00SDmitry Osipenko .has_legacy_blending = true, 2753d1f3e1e0SThierry Reding .pitch_align = 8, 27549c012700SThierry Reding .has_powergate = false, 2755f68ba691SDmitry Osipenko .coupled_pm = true, 275647307954SThierry Reding .has_nvdisplay = false, 2757511c7023SThierry Reding .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), 2758511c7023SThierry Reding .primary_formats = tegra20_primary_formats, 2759511c7023SThierry Reding .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), 2760511c7023SThierry Reding .overlay_formats = tegra20_overlay_formats, 2761e90124cbSThierry Reding .modifiers = tegra20_modifiers, 2762acc6a3a9SDmitry Osipenko .has_win_a_without_filters = true, 276304d5d5dfSDmitry Osipenko .has_win_b_vfilter_mem_client = true, 2764acc6a3a9SDmitry Osipenko .has_win_c_without_vert_filter = true, 276504d5d5dfSDmitry Osipenko .plane_tiled_memory_bandwidth_x2 = false, 27668620fc62SThierry Reding }; 27678620fc62SThierry Reding 27688620fc62SThierry Reding static const struct tegra_dc_soc_info tegra30_dc_soc_info = { 27697116e9a8SThierry Reding .supports_background_color = false, 27708620fc62SThierry Reding .supports_interlacing = false, 2771e687651bSThierry Reding .supports_cursor = false, 2772c134f019SThierry Reding .supports_block_linear = false, 27737b6f8467SThierry Reding .supports_sector_layout = false, 2774a43d0a00SDmitry Osipenko .has_legacy_blending = true, 2775d1f3e1e0SThierry Reding .pitch_align = 8, 27769c012700SThierry Reding .has_powergate = false, 2777f68ba691SDmitry Osipenko .coupled_pm = false, 277847307954SThierry Reding .has_nvdisplay = false, 2779511c7023SThierry Reding .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), 2780511c7023SThierry Reding .primary_formats = tegra20_primary_formats, 2781511c7023SThierry Reding .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), 2782511c7023SThierry Reding .overlay_formats = tegra20_overlay_formats, 2783e90124cbSThierry Reding .modifiers = tegra20_modifiers, 2784acc6a3a9SDmitry Osipenko .has_win_a_without_filters = false, 278504d5d5dfSDmitry Osipenko .has_win_b_vfilter_mem_client = true, 2786acc6a3a9SDmitry Osipenko .has_win_c_without_vert_filter = false, 278704d5d5dfSDmitry Osipenko .plane_tiled_memory_bandwidth_x2 = true, 2788d1f3e1e0SThierry Reding }; 2789d1f3e1e0SThierry Reding 2790d1f3e1e0SThierry Reding static const struct tegra_dc_soc_info tegra114_dc_soc_info = { 27917116e9a8SThierry Reding .supports_background_color = false, 2792d1f3e1e0SThierry Reding .supports_interlacing = false, 2793d1f3e1e0SThierry Reding .supports_cursor = false, 2794d1f3e1e0SThierry Reding .supports_block_linear = false, 27957b6f8467SThierry Reding .supports_sector_layout = false, 2796a43d0a00SDmitry Osipenko .has_legacy_blending = true, 2797d1f3e1e0SThierry Reding .pitch_align = 64, 27989c012700SThierry Reding .has_powergate = true, 2799f68ba691SDmitry Osipenko .coupled_pm = false, 280047307954SThierry Reding .has_nvdisplay = false, 2801511c7023SThierry Reding .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), 2802511c7023SThierry Reding .primary_formats = tegra114_primary_formats, 2803511c7023SThierry Reding .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), 2804511c7023SThierry Reding .overlay_formats = tegra114_overlay_formats, 2805e90124cbSThierry Reding .modifiers = tegra20_modifiers, 2806acc6a3a9SDmitry Osipenko .has_win_a_without_filters = false, 280704d5d5dfSDmitry Osipenko .has_win_b_vfilter_mem_client = false, 2808acc6a3a9SDmitry Osipenko .has_win_c_without_vert_filter = false, 280904d5d5dfSDmitry Osipenko .plane_tiled_memory_bandwidth_x2 = true, 28108620fc62SThierry Reding }; 28118620fc62SThierry Reding 28128620fc62SThierry Reding static const struct tegra_dc_soc_info tegra124_dc_soc_info = { 28137116e9a8SThierry Reding .supports_background_color = true, 28148620fc62SThierry Reding .supports_interlacing = true, 2815e687651bSThierry Reding .supports_cursor = true, 2816c134f019SThierry Reding .supports_block_linear = true, 28177b6f8467SThierry Reding .supports_sector_layout = false, 2818a43d0a00SDmitry Osipenko .has_legacy_blending = false, 2819d1f3e1e0SThierry Reding .pitch_align = 64, 28209c012700SThierry Reding .has_powergate = true, 2821f68ba691SDmitry Osipenko .coupled_pm = false, 282247307954SThierry Reding .has_nvdisplay = false, 2823511c7023SThierry Reding .num_primary_formats = ARRAY_SIZE(tegra124_primary_formats), 28249a02d3afSStefan Agner .primary_formats = tegra124_primary_formats, 2825511c7023SThierry Reding .num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats), 28269a02d3afSStefan Agner .overlay_formats = tegra124_overlay_formats, 2827e90124cbSThierry Reding .modifiers = tegra124_modifiers, 2828acc6a3a9SDmitry Osipenko .has_win_a_without_filters = false, 282904d5d5dfSDmitry Osipenko .has_win_b_vfilter_mem_client = false, 2830acc6a3a9SDmitry Osipenko .has_win_c_without_vert_filter = false, 283104d5d5dfSDmitry Osipenko .plane_tiled_memory_bandwidth_x2 = false, 28328620fc62SThierry Reding }; 28338620fc62SThierry Reding 28345b4f516fSThierry Reding static const struct tegra_dc_soc_info tegra210_dc_soc_info = { 28357116e9a8SThierry Reding .supports_background_color = true, 28365b4f516fSThierry Reding .supports_interlacing = true, 28375b4f516fSThierry Reding .supports_cursor = true, 28385b4f516fSThierry Reding .supports_block_linear = true, 28397b6f8467SThierry Reding .supports_sector_layout = false, 2840a43d0a00SDmitry Osipenko .has_legacy_blending = false, 28415b4f516fSThierry Reding .pitch_align = 64, 28425b4f516fSThierry Reding .has_powergate = true, 2843f68ba691SDmitry Osipenko .coupled_pm = false, 284447307954SThierry Reding .has_nvdisplay = false, 2845511c7023SThierry Reding .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), 2846511c7023SThierry Reding .primary_formats = tegra114_primary_formats, 2847511c7023SThierry Reding .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), 2848511c7023SThierry Reding .overlay_formats = tegra114_overlay_formats, 2849e90124cbSThierry Reding .modifiers = tegra124_modifiers, 2850acc6a3a9SDmitry Osipenko .has_win_a_without_filters = false, 285104d5d5dfSDmitry Osipenko .has_win_b_vfilter_mem_client = false, 2852acc6a3a9SDmitry Osipenko .has_win_c_without_vert_filter = false, 285304d5d5dfSDmitry Osipenko .plane_tiled_memory_bandwidth_x2 = false, 285447307954SThierry Reding }; 285547307954SThierry Reding 285647307954SThierry Reding static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { 285747307954SThierry Reding { 285847307954SThierry Reding .index = 0, 285947307954SThierry Reding .dc = 0, 286047307954SThierry Reding .windows = (const unsigned int[]) { 0 }, 286147307954SThierry Reding .num_windows = 1, 286247307954SThierry Reding }, { 286347307954SThierry Reding .index = 1, 286447307954SThierry Reding .dc = 1, 286547307954SThierry Reding .windows = (const unsigned int[]) { 1 }, 286647307954SThierry Reding .num_windows = 1, 286747307954SThierry Reding }, { 286847307954SThierry Reding .index = 2, 286947307954SThierry Reding .dc = 1, 287047307954SThierry Reding .windows = (const unsigned int[]) { 2 }, 287147307954SThierry Reding .num_windows = 1, 287247307954SThierry Reding }, { 287347307954SThierry Reding .index = 3, 287447307954SThierry Reding .dc = 2, 287547307954SThierry Reding .windows = (const unsigned int[]) { 3 }, 287647307954SThierry Reding .num_windows = 1, 287747307954SThierry Reding }, { 287847307954SThierry Reding .index = 4, 287947307954SThierry Reding .dc = 2, 288047307954SThierry Reding .windows = (const unsigned int[]) { 4 }, 288147307954SThierry Reding .num_windows = 1, 288247307954SThierry Reding }, { 288347307954SThierry Reding .index = 5, 288447307954SThierry Reding .dc = 2, 288547307954SThierry Reding .windows = (const unsigned int[]) { 5 }, 288647307954SThierry Reding .num_windows = 1, 288747307954SThierry Reding }, 288847307954SThierry Reding }; 288947307954SThierry Reding 289047307954SThierry Reding static const struct tegra_dc_soc_info tegra186_dc_soc_info = { 289147307954SThierry Reding .supports_background_color = true, 289247307954SThierry Reding .supports_interlacing = true, 289347307954SThierry Reding .supports_cursor = true, 289447307954SThierry Reding .supports_block_linear = true, 28957b6f8467SThierry Reding .supports_sector_layout = false, 2896a43d0a00SDmitry Osipenko .has_legacy_blending = false, 289747307954SThierry Reding .pitch_align = 64, 289847307954SThierry Reding .has_powergate = false, 2899f68ba691SDmitry Osipenko .coupled_pm = false, 290047307954SThierry Reding .has_nvdisplay = true, 290147307954SThierry Reding .wgrps = tegra186_dc_wgrps, 290247307954SThierry Reding .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), 290304d5d5dfSDmitry Osipenko .plane_tiled_memory_bandwidth_x2 = false, 29045b4f516fSThierry Reding }; 29055b4f516fSThierry Reding 290647443196SThierry Reding static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = { 290747443196SThierry Reding { 290847443196SThierry Reding .index = 0, 290947443196SThierry Reding .dc = 0, 291047443196SThierry Reding .windows = (const unsigned int[]) { 0 }, 291147443196SThierry Reding .num_windows = 1, 291247443196SThierry Reding }, { 291347443196SThierry Reding .index = 1, 291447443196SThierry Reding .dc = 1, 291547443196SThierry Reding .windows = (const unsigned int[]) { 1 }, 291647443196SThierry Reding .num_windows = 1, 291747443196SThierry Reding }, { 291847443196SThierry Reding .index = 2, 291947443196SThierry Reding .dc = 1, 292047443196SThierry Reding .windows = (const unsigned int[]) { 2 }, 292147443196SThierry Reding .num_windows = 1, 292247443196SThierry Reding }, { 292347443196SThierry Reding .index = 3, 292447443196SThierry Reding .dc = 2, 292547443196SThierry Reding .windows = (const unsigned int[]) { 3 }, 292647443196SThierry Reding .num_windows = 1, 292747443196SThierry Reding }, { 292847443196SThierry Reding .index = 4, 292947443196SThierry Reding .dc = 2, 293047443196SThierry Reding .windows = (const unsigned int[]) { 4 }, 293147443196SThierry Reding .num_windows = 1, 293247443196SThierry Reding }, { 293347443196SThierry Reding .index = 5, 293447443196SThierry Reding .dc = 2, 293547443196SThierry Reding .windows = (const unsigned int[]) { 5 }, 293647443196SThierry Reding .num_windows = 1, 293747443196SThierry Reding }, 293847443196SThierry Reding }; 293947443196SThierry Reding 294047443196SThierry Reding static const struct tegra_dc_soc_info tegra194_dc_soc_info = { 294147443196SThierry Reding .supports_background_color = true, 294247443196SThierry Reding .supports_interlacing = true, 294347443196SThierry Reding .supports_cursor = true, 294447443196SThierry Reding .supports_block_linear = true, 29457b6f8467SThierry Reding .supports_sector_layout = true, 294647443196SThierry Reding .has_legacy_blending = false, 294747443196SThierry Reding .pitch_align = 64, 294847443196SThierry Reding .has_powergate = false, 294947443196SThierry Reding .coupled_pm = false, 295047443196SThierry Reding .has_nvdisplay = true, 295147443196SThierry Reding .wgrps = tegra194_dc_wgrps, 295247443196SThierry Reding .num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps), 295304d5d5dfSDmitry Osipenko .plane_tiled_memory_bandwidth_x2 = false, 295447443196SThierry Reding }; 295547443196SThierry Reding 29568620fc62SThierry Reding static const struct of_device_id tegra_dc_of_match[] = { 29578620fc62SThierry Reding { 295847443196SThierry Reding .compatible = "nvidia,tegra194-dc", 295947443196SThierry Reding .data = &tegra194_dc_soc_info, 296047443196SThierry Reding }, { 296147307954SThierry Reding .compatible = "nvidia,tegra186-dc", 296247307954SThierry Reding .data = &tegra186_dc_soc_info, 296347307954SThierry Reding }, { 29645b4f516fSThierry Reding .compatible = "nvidia,tegra210-dc", 29655b4f516fSThierry Reding .data = &tegra210_dc_soc_info, 29665b4f516fSThierry Reding }, { 29678620fc62SThierry Reding .compatible = "nvidia,tegra124-dc", 29688620fc62SThierry Reding .data = &tegra124_dc_soc_info, 29698620fc62SThierry Reding }, { 29709c012700SThierry Reding .compatible = "nvidia,tegra114-dc", 29719c012700SThierry Reding .data = &tegra114_dc_soc_info, 29729c012700SThierry Reding }, { 29738620fc62SThierry Reding .compatible = "nvidia,tegra30-dc", 29748620fc62SThierry Reding .data = &tegra30_dc_soc_info, 29758620fc62SThierry Reding }, { 29768620fc62SThierry Reding .compatible = "nvidia,tegra20-dc", 29778620fc62SThierry Reding .data = &tegra20_dc_soc_info, 29788620fc62SThierry Reding }, { 29798620fc62SThierry Reding /* sentinel */ 29808620fc62SThierry Reding } 29818620fc62SThierry Reding }; 2982ef70728cSStephen Warren MODULE_DEVICE_TABLE(of, tegra_dc_of_match); 29838620fc62SThierry Reding 298413411dddSThierry Reding static int tegra_dc_parse_dt(struct tegra_dc *dc) 298513411dddSThierry Reding { 298613411dddSThierry Reding struct device_node *np; 298713411dddSThierry Reding u32 value = 0; 298813411dddSThierry Reding int err; 298913411dddSThierry Reding 299013411dddSThierry Reding err = of_property_read_u32(dc->dev->of_node, "nvidia,head", &value); 299113411dddSThierry Reding if (err < 0) { 299213411dddSThierry Reding dev_err(dc->dev, "missing \"nvidia,head\" property\n"); 299313411dddSThierry Reding 299413411dddSThierry Reding /* 299513411dddSThierry Reding * If the nvidia,head property isn't present, try to find the 299613411dddSThierry Reding * correct head number by looking up the position of this 299713411dddSThierry Reding * display controller's node within the device tree. Assuming 299813411dddSThierry Reding * that the nodes are ordered properly in the DTS file and 299913411dddSThierry Reding * that the translation into a flattened device tree blob 300013411dddSThierry Reding * preserves that ordering this will actually yield the right 300113411dddSThierry Reding * head number. 300213411dddSThierry Reding * 300313411dddSThierry Reding * If those assumptions don't hold, this will still work for 300413411dddSThierry Reding * cases where only a single display controller is used. 300513411dddSThierry Reding */ 300613411dddSThierry Reding for_each_matching_node(np, tegra_dc_of_match) { 3007cf6b1744SJulia Lawall if (np == dc->dev->of_node) { 3008cf6b1744SJulia Lawall of_node_put(np); 300913411dddSThierry Reding break; 3010cf6b1744SJulia Lawall } 301113411dddSThierry Reding 301213411dddSThierry Reding value++; 301313411dddSThierry Reding } 301413411dddSThierry Reding } 301513411dddSThierry Reding 301613411dddSThierry Reding dc->pipe = value; 301713411dddSThierry Reding 301813411dddSThierry Reding return 0; 301913411dddSThierry Reding } 302013411dddSThierry Reding 302192ce7e83SSuzuki K Poulose static int tegra_dc_match_by_pipe(struct device *dev, const void *data) 3022f68ba691SDmitry Osipenko { 3023f68ba691SDmitry Osipenko struct tegra_dc *dc = dev_get_drvdata(dev); 302492ce7e83SSuzuki K Poulose unsigned int pipe = (unsigned long)(void *)data; 3025f68ba691SDmitry Osipenko 3026f68ba691SDmitry Osipenko return dc->pipe == pipe; 3027f68ba691SDmitry Osipenko } 3028f68ba691SDmitry Osipenko 3029f68ba691SDmitry Osipenko static int tegra_dc_couple(struct tegra_dc *dc) 3030f68ba691SDmitry Osipenko { 3031f68ba691SDmitry Osipenko /* 3032f68ba691SDmitry Osipenko * On Tegra20, DC1 requires DC0 to be taken out of reset in order to 3033f68ba691SDmitry Osipenko * be enabled, otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND / 3034f68ba691SDmitry Osipenko * POWER_CONTROL registers during CRTC enabling. 3035f68ba691SDmitry Osipenko */ 3036f68ba691SDmitry Osipenko if (dc->soc->coupled_pm && dc->pipe == 1) { 3037a31500feSThierry Reding struct device *companion; 3038a31500feSThierry Reding struct tegra_dc *parent; 3039f68ba691SDmitry Osipenko 3040a31500feSThierry Reding companion = driver_find_device(dc->dev->driver, NULL, (const void *)0, 3041f68ba691SDmitry Osipenko tegra_dc_match_by_pipe); 3042a31500feSThierry Reding if (!companion) 3043f68ba691SDmitry Osipenko return -EPROBE_DEFER; 3044f68ba691SDmitry Osipenko 3045a31500feSThierry Reding parent = dev_get_drvdata(companion); 3046a31500feSThierry Reding dc->client.parent = &parent->client; 3047f68ba691SDmitry Osipenko 3048a31500feSThierry Reding dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion)); 3049f68ba691SDmitry Osipenko } 3050f68ba691SDmitry Osipenko 3051f68ba691SDmitry Osipenko return 0; 3052f68ba691SDmitry Osipenko } 3053f68ba691SDmitry Osipenko 3054dee8268fSThierry Reding static int tegra_dc_probe(struct platform_device *pdev) 3055dee8268fSThierry Reding { 305686044e74SThierry Reding u64 dma_mask = dma_get_mask(pdev->dev.parent); 3057dee8268fSThierry Reding struct tegra_dc *dc; 3058dee8268fSThierry Reding int err; 3059dee8268fSThierry Reding 306086044e74SThierry Reding err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask); 306186044e74SThierry Reding if (err < 0) { 306286044e74SThierry Reding dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); 306386044e74SThierry Reding return err; 306486044e74SThierry Reding } 306586044e74SThierry Reding 3066dee8268fSThierry Reding dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL); 3067dee8268fSThierry Reding if (!dc) 3068dee8268fSThierry Reding return -ENOMEM; 3069dee8268fSThierry Reding 3070b9ff7aeaSThierry Reding dc->soc = of_device_get_match_data(&pdev->dev); 30718620fc62SThierry Reding 3072dee8268fSThierry Reding INIT_LIST_HEAD(&dc->list); 3073dee8268fSThierry Reding dc->dev = &pdev->dev; 3074dee8268fSThierry Reding 307513411dddSThierry Reding err = tegra_dc_parse_dt(dc); 307613411dddSThierry Reding if (err < 0) 307713411dddSThierry Reding return err; 307813411dddSThierry Reding 3079f68ba691SDmitry Osipenko err = tegra_dc_couple(dc); 3080f68ba691SDmitry Osipenko if (err < 0) 3081f68ba691SDmitry Osipenko return err; 3082f68ba691SDmitry Osipenko 3083dee8268fSThierry Reding dc->clk = devm_clk_get(&pdev->dev, NULL); 3084dee8268fSThierry Reding if (IS_ERR(dc->clk)) { 3085dee8268fSThierry Reding dev_err(&pdev->dev, "failed to get clock\n"); 3086dee8268fSThierry Reding return PTR_ERR(dc->clk); 3087dee8268fSThierry Reding } 3088dee8268fSThierry Reding 3089ca48080aSStephen Warren dc->rst = devm_reset_control_get(&pdev->dev, "dc"); 3090ca48080aSStephen Warren if (IS_ERR(dc->rst)) { 3091ca48080aSStephen Warren dev_err(&pdev->dev, "failed to get reset\n"); 3092ca48080aSStephen Warren return PTR_ERR(dc->rst); 3093ca48080aSStephen Warren } 3094ca48080aSStephen Warren 3095a2f2f740SThierry Reding /* assert reset and disable clock */ 3096a2f2f740SThierry Reding err = clk_prepare_enable(dc->clk); 3097a2f2f740SThierry Reding if (err < 0) 3098a2f2f740SThierry Reding return err; 3099a2f2f740SThierry Reding 3100a2f2f740SThierry Reding usleep_range(2000, 4000); 3101a2f2f740SThierry Reding 3102a2f2f740SThierry Reding err = reset_control_assert(dc->rst); 3103a2f2f740SThierry Reding if (err < 0) 3104a2f2f740SThierry Reding return err; 3105a2f2f740SThierry Reding 3106a2f2f740SThierry Reding usleep_range(2000, 4000); 3107a2f2f740SThierry Reding 3108a2f2f740SThierry Reding clk_disable_unprepare(dc->clk); 310933a8eb8dSThierry Reding 31109c012700SThierry Reding if (dc->soc->has_powergate) { 31119c012700SThierry Reding if (dc->pipe == 0) 31129c012700SThierry Reding dc->powergate = TEGRA_POWERGATE_DIS; 31139c012700SThierry Reding else 31149c012700SThierry Reding dc->powergate = TEGRA_POWERGATE_DISB; 31159c012700SThierry Reding 311633a8eb8dSThierry Reding tegra_powergate_power_off(dc->powergate); 31179c012700SThierry Reding } 3118dee8268fSThierry Reding 3119a858ac8fSDmitry Osipenko dc->regs = devm_platform_ioremap_resource(pdev, 0); 3120dee8268fSThierry Reding if (IS_ERR(dc->regs)) 3121dee8268fSThierry Reding return PTR_ERR(dc->regs); 3122dee8268fSThierry Reding 3123dee8268fSThierry Reding dc->irq = platform_get_irq(pdev, 0); 31245f1df70fSTang Bin if (dc->irq < 0) 3125dee8268fSThierry Reding return -ENXIO; 3126dee8268fSThierry Reding 3127dee8268fSThierry Reding err = tegra_dc_rgb_probe(dc); 3128dee8268fSThierry Reding if (err < 0 && err != -ENODEV) { 31298f839fb6SDmitry Osipenko const char *level = KERN_ERR; 31308f839fb6SDmitry Osipenko 31318f839fb6SDmitry Osipenko if (err == -EPROBE_DEFER) 31328f839fb6SDmitry Osipenko level = KERN_DEBUG; 31338f839fb6SDmitry Osipenko 31348f839fb6SDmitry Osipenko dev_printk(level, dc->dev, "failed to probe RGB output: %d\n", 31358f839fb6SDmitry Osipenko err); 3136dee8268fSThierry Reding return err; 3137dee8268fSThierry Reding } 3138dee8268fSThierry Reding 313933a8eb8dSThierry Reding platform_set_drvdata(pdev, dc); 314033a8eb8dSThierry Reding pm_runtime_enable(&pdev->dev); 314133a8eb8dSThierry Reding 314233a8eb8dSThierry Reding INIT_LIST_HEAD(&dc->client.list); 314333a8eb8dSThierry Reding dc->client.ops = &dc_client_ops; 314433a8eb8dSThierry Reding dc->client.dev = &pdev->dev; 314533a8eb8dSThierry Reding 3146dee8268fSThierry Reding err = host1x_client_register(&dc->client); 3147dee8268fSThierry Reding if (err < 0) { 3148dee8268fSThierry Reding dev_err(&pdev->dev, "failed to register host1x client: %d\n", 3149dee8268fSThierry Reding err); 31500411ea89SDmitry Osipenko goto disable_pm; 3151dee8268fSThierry Reding } 3152dee8268fSThierry Reding 3153dee8268fSThierry Reding return 0; 31540411ea89SDmitry Osipenko 31550411ea89SDmitry Osipenko disable_pm: 31560411ea89SDmitry Osipenko pm_runtime_disable(&pdev->dev); 31570411ea89SDmitry Osipenko tegra_dc_rgb_remove(dc); 31580411ea89SDmitry Osipenko 31590411ea89SDmitry Osipenko return err; 3160dee8268fSThierry Reding } 3161dee8268fSThierry Reding 3162dee8268fSThierry Reding static int tegra_dc_remove(struct platform_device *pdev) 3163dee8268fSThierry Reding { 3164dee8268fSThierry Reding struct tegra_dc *dc = platform_get_drvdata(pdev); 3165dee8268fSThierry Reding int err; 3166dee8268fSThierry Reding 3167dee8268fSThierry Reding err = host1x_client_unregister(&dc->client); 3168dee8268fSThierry Reding if (err < 0) { 3169dee8268fSThierry Reding dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", 3170dee8268fSThierry Reding err); 3171dee8268fSThierry Reding return err; 3172dee8268fSThierry Reding } 3173dee8268fSThierry Reding 317459d29c0eSThierry Reding err = tegra_dc_rgb_remove(dc); 317559d29c0eSThierry Reding if (err < 0) { 317659d29c0eSThierry Reding dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err); 317759d29c0eSThierry Reding return err; 317859d29c0eSThierry Reding } 317959d29c0eSThierry Reding 318033a8eb8dSThierry Reding pm_runtime_disable(&pdev->dev); 318133a8eb8dSThierry Reding 318233a8eb8dSThierry Reding return 0; 318333a8eb8dSThierry Reding } 318433a8eb8dSThierry Reding 3185dee8268fSThierry Reding struct platform_driver tegra_dc_driver = { 3186dee8268fSThierry Reding .driver = { 3187dee8268fSThierry Reding .name = "tegra-dc", 3188dee8268fSThierry Reding .of_match_table = tegra_dc_of_match, 3189dee8268fSThierry Reding }, 3190dee8268fSThierry Reding .probe = tegra_dc_probe, 3191dee8268fSThierry Reding .remove = tegra_dc_remove, 3192dee8268fSThierry Reding }; 3193