18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012 Avionic Design GmbH 48c2ecf20Sopenharmony_ci * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/iommu.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 148c2ecf20Sopenharmony_ci#include <linux/reset.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <soc/tegra/pmc.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_debugfs.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "dc.h" 268c2ecf20Sopenharmony_ci#include "drm.h" 278c2ecf20Sopenharmony_ci#include "gem.h" 288c2ecf20Sopenharmony_ci#include "hub.h" 298c2ecf20Sopenharmony_ci#include "plane.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, 328c2ecf20Sopenharmony_ci struct drm_crtc_state *state); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void tegra_dc_stats_reset(struct tegra_dc_stats *stats) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci stats->frames = 0; 378c2ecf20Sopenharmony_ci stats->vblank = 0; 388c2ecf20Sopenharmony_ci stats->underflow = 0; 398c2ecf20Sopenharmony_ci stats->overflow = 0; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Reads the active copy of a register. */ 438c2ecf20Sopenharmony_cistatic u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci u32 value; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); 488c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, offset); 498c2ecf20Sopenharmony_ci tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return value; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic inline unsigned int tegra_plane_offset(struct tegra_plane *plane, 558c2ecf20Sopenharmony_ci unsigned int offset) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci if (offset >= 0x500 && offset <= 0x638) { 588c2ecf20Sopenharmony_ci offset = 0x000 + (offset - 0x500); 598c2ecf20Sopenharmony_ci return plane->offset + offset; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (offset >= 0x700 && offset <= 0x719) { 638c2ecf20Sopenharmony_ci offset = 0x180 + (offset - 0x700); 648c2ecf20Sopenharmony_ci return plane->offset + offset; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (offset >= 0x800 && offset <= 0x839) { 688c2ecf20Sopenharmony_ci offset = 0x1c0 + (offset - 0x800); 698c2ecf20Sopenharmony_ci return plane->offset + offset; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return plane->offset + offset; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic inline u32 tegra_plane_readl(struct tegra_plane *plane, 788c2ecf20Sopenharmony_ci unsigned int offset) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic inline void tegra_plane_writel(struct tegra_plane *plane, u32 value, 848c2ecf20Sopenharmony_ci unsigned int offset) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cibool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct device_node *np = dc->dev->of_node; 928c2ecf20Sopenharmony_ci struct of_phandle_iterator it; 938c2ecf20Sopenharmony_ci int err; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) 968c2ecf20Sopenharmony_ci if (it.node == dev->of_node) 978c2ecf20Sopenharmony_ci return true; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return false; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* 1038c2ecf20Sopenharmony_ci * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the 1048c2ecf20Sopenharmony_ci * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy. 1058c2ecf20Sopenharmony_ci * Latching happens mmediately if the display controller is in STOP mode or 1068c2ecf20Sopenharmony_ci * on the next frame boundary otherwise. 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The 1098c2ecf20Sopenharmony_ci * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits 1108c2ecf20Sopenharmony_ci * are written. When the *_ACT_REQ bits are written, the ARM copy is latched 1118c2ecf20Sopenharmony_ci * into the ACTIVE copy, either immediately if the display controller is in 1128c2ecf20Sopenharmony_ci * STOP mode, or at the next frame boundary otherwise. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_civoid tegra_dc_commit(struct tegra_dc *dc) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); 1178c2ecf20Sopenharmony_ci tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v, 1218c2ecf20Sopenharmony_ci unsigned int bpp) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci fixed20_12 outf = dfixed_init(out); 1248c2ecf20Sopenharmony_ci fixed20_12 inf = dfixed_init(in); 1258c2ecf20Sopenharmony_ci u32 dda_inc; 1268c2ecf20Sopenharmony_ci int max; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (v) 1298c2ecf20Sopenharmony_ci max = 15; 1308c2ecf20Sopenharmony_ci else { 1318c2ecf20Sopenharmony_ci switch (bpp) { 1328c2ecf20Sopenharmony_ci case 2: 1338c2ecf20Sopenharmony_ci max = 8; 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci default: 1378c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 1388c2ecf20Sopenharmony_ci fallthrough; 1398c2ecf20Sopenharmony_ci case 4: 1408c2ecf20Sopenharmony_ci max = 4; 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1)); 1468c2ecf20Sopenharmony_ci inf.full -= dfixed_const(1); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci dda_inc = dfixed_div(inf, outf); 1498c2ecf20Sopenharmony_ci dda_inc = min_t(u32, dda_inc, dfixed_const(max)); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return dda_inc; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic inline u32 compute_initial_dda(unsigned int in) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci fixed20_12 inf = dfixed_init(in); 1578c2ecf20Sopenharmony_ci return dfixed_frac(inf); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci u32 background[3] = { 1638c2ecf20Sopenharmony_ci BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, 1648c2ecf20Sopenharmony_ci BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, 1658c2ecf20Sopenharmony_ci BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, 1668c2ecf20Sopenharmony_ci }; 1678c2ecf20Sopenharmony_ci u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) | 1688c2ecf20Sopenharmony_ci BLEND_COLOR_KEY_NONE; 1698c2ecf20Sopenharmony_ci u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); 1708c2ecf20Sopenharmony_ci struct tegra_plane_state *state; 1718c2ecf20Sopenharmony_ci u32 blending[2]; 1728c2ecf20Sopenharmony_ci unsigned int i; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* disable blending for non-overlapping case */ 1758c2ecf20Sopenharmony_ci tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); 1768c2ecf20Sopenharmony_ci tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci state = to_tegra_plane_state(plane->base.state); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (state->opaque) { 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * Since custom fix-weight blending isn't utilized and weight 1838c2ecf20Sopenharmony_ci * of top window is set to max, we can enforce dependent 1848c2ecf20Sopenharmony_ci * blending which in this case results in transparent bottom 1858c2ecf20Sopenharmony_ci * window if top window is opaque and if top window enables 1868c2ecf20Sopenharmony_ci * alpha blending, then bottom window is getting alpha value 1878c2ecf20Sopenharmony_ci * of 1 minus the sum of alpha components of the overlapping 1888c2ecf20Sopenharmony_ci * plane. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci background[0] |= BLEND_CONTROL_DEPENDENT; 1918c2ecf20Sopenharmony_ci background[1] |= BLEND_CONTROL_DEPENDENT; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* 1948c2ecf20Sopenharmony_ci * The region where three windows overlap is the intersection 1958c2ecf20Sopenharmony_ci * of the two regions where two windows overlap. It contributes 1968c2ecf20Sopenharmony_ci * to the area if all of the windows on top of it have an alpha 1978c2ecf20Sopenharmony_ci * component. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci switch (state->base.normalized_zpos) { 2008c2ecf20Sopenharmony_ci case 0: 2018c2ecf20Sopenharmony_ci if (state->blending[0].alpha && 2028c2ecf20Sopenharmony_ci state->blending[1].alpha) 2038c2ecf20Sopenharmony_ci background[2] |= BLEND_CONTROL_DEPENDENT; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci case 1: 2078c2ecf20Sopenharmony_ci background[2] |= BLEND_CONTROL_DEPENDENT; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } else { 2118c2ecf20Sopenharmony_ci /* 2128c2ecf20Sopenharmony_ci * Enable alpha blending if pixel format has an alpha 2138c2ecf20Sopenharmony_ci * component. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_ci foreground |= BLEND_CONTROL_ALPHA; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* 2188c2ecf20Sopenharmony_ci * If any of the windows on top of this window is opaque, it 2198c2ecf20Sopenharmony_ci * will completely conceal this window within that area. If 2208c2ecf20Sopenharmony_ci * top window has an alpha component, it is blended over the 2218c2ecf20Sopenharmony_ci * bottom window. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 2248c2ecf20Sopenharmony_ci if (state->blending[i].alpha && 2258c2ecf20Sopenharmony_ci state->blending[i].top) 2268c2ecf20Sopenharmony_ci background[i] |= BLEND_CONTROL_DEPENDENT; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci switch (state->base.normalized_zpos) { 2308c2ecf20Sopenharmony_ci case 0: 2318c2ecf20Sopenharmony_ci if (state->blending[0].alpha && 2328c2ecf20Sopenharmony_ci state->blending[1].alpha) 2338c2ecf20Sopenharmony_ci background[2] |= BLEND_CONTROL_DEPENDENT; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci case 1: 2378c2ecf20Sopenharmony_ci /* 2388c2ecf20Sopenharmony_ci * When both middle and topmost windows have an alpha, 2398c2ecf20Sopenharmony_ci * these windows a mixed together and then the result 2408c2ecf20Sopenharmony_ci * is blended over the bottom window. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci if (state->blending[0].alpha && 2438c2ecf20Sopenharmony_ci state->blending[0].top) 2448c2ecf20Sopenharmony_ci background[2] |= BLEND_CONTROL_ALPHA; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (state->blending[1].alpha && 2478c2ecf20Sopenharmony_ci state->blending[1].top) 2488c2ecf20Sopenharmony_ci background[2] |= BLEND_CONTROL_ALPHA; 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci switch (state->base.normalized_zpos) { 2548c2ecf20Sopenharmony_ci case 0: 2558c2ecf20Sopenharmony_ci tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X); 2568c2ecf20Sopenharmony_ci tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); 2578c2ecf20Sopenharmony_ci tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci case 1: 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * If window B / C is topmost, then X / Y registers are 2638c2ecf20Sopenharmony_ci * matching the order of blending[...] state indices, 2648c2ecf20Sopenharmony_ci * otherwise a swap is required. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci if (!state->blending[0].top && state->blending[1].top) { 2678c2ecf20Sopenharmony_ci blending[0] = foreground; 2688c2ecf20Sopenharmony_ci blending[1] = background[1]; 2698c2ecf20Sopenharmony_ci } else { 2708c2ecf20Sopenharmony_ci blending[0] = background[0]; 2718c2ecf20Sopenharmony_ci blending[1] = foreground; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X); 2758c2ecf20Sopenharmony_ci tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y); 2768c2ecf20Sopenharmony_ci tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci case 2: 2808c2ecf20Sopenharmony_ci tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); 2818c2ecf20Sopenharmony_ci tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y); 2828c2ecf20Sopenharmony_ci tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic void tegra_plane_setup_blending(struct tegra_plane *plane, 2888c2ecf20Sopenharmony_ci const struct tegra_dc_window *window) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci u32 value; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | 2938c2ecf20Sopenharmony_ci BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | 2948c2ecf20Sopenharmony_ci BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; 2958c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | 2988c2ecf20Sopenharmony_ci BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | 2998c2ecf20Sopenharmony_ci BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; 3008c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos); 3038c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic bool 3078c2ecf20Sopenharmony_citegra_plane_use_horizontal_filtering(struct tegra_plane *plane, 3088c2ecf20Sopenharmony_ci const struct tegra_dc_window *window) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct tegra_dc *dc = plane->dc; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (window->src.w == window->dst.w) 3138c2ecf20Sopenharmony_ci return false; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (plane->index == 0 && dc->soc->has_win_a_without_filters) 3168c2ecf20Sopenharmony_ci return false; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return true; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic bool 3228c2ecf20Sopenharmony_citegra_plane_use_vertical_filtering(struct tegra_plane *plane, 3238c2ecf20Sopenharmony_ci const struct tegra_dc_window *window) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct tegra_dc *dc = plane->dc; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (window->src.h == window->dst.h) 3288c2ecf20Sopenharmony_ci return false; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (plane->index == 0 && dc->soc->has_win_a_without_filters) 3318c2ecf20Sopenharmony_ci return false; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter) 3348c2ecf20Sopenharmony_ci return false; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return true; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic void tegra_dc_setup_window(struct tegra_plane *plane, 3408c2ecf20Sopenharmony_ci const struct tegra_dc_window *window) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; 3438c2ecf20Sopenharmony_ci struct tegra_dc *dc = plane->dc; 3448c2ecf20Sopenharmony_ci bool yuv, planar; 3458c2ecf20Sopenharmony_ci u32 value; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* 3488c2ecf20Sopenharmony_ci * For YUV planar modes, the number of bytes per pixel takes into 3498c2ecf20Sopenharmony_ci * account only the luma component and therefore is 1. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_ci yuv = tegra_plane_format_is_yuv(window->format, &planar); 3528c2ecf20Sopenharmony_ci if (!yuv) 3538c2ecf20Sopenharmony_ci bpp = window->bits_per_pixel / 8; 3548c2ecf20Sopenharmony_ci else 3558c2ecf20Sopenharmony_ci bpp = planar ? 1 : 2; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH); 3588c2ecf20Sopenharmony_ci tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x); 3618c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_POSITION); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w); 3648c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_SIZE); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci h_offset = window->src.x * bpp; 3678c2ecf20Sopenharmony_ci v_offset = window->src.y; 3688c2ecf20Sopenharmony_ci h_size = window->src.w * bpp; 3698c2ecf20Sopenharmony_ci v_size = window->src.h; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (window->reflect_x) 3728c2ecf20Sopenharmony_ci h_offset += (window->src.w - 1) * bpp; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (window->reflect_y) 3758c2ecf20Sopenharmony_ci v_offset += window->src.h - 1; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); 3788c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* 3818c2ecf20Sopenharmony_ci * For DDA computations the number of bytes per pixel for YUV planar 3828c2ecf20Sopenharmony_ci * modes needs to take into account all Y, U and V components. 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci if (yuv && planar) 3858c2ecf20Sopenharmony_ci bpp = 2; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp); 3888c2ecf20Sopenharmony_ci v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); 3918c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_DDA_INC); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci h_dda = compute_initial_dda(window->src.x); 3948c2ecf20Sopenharmony_ci v_dda = compute_initial_dda(window->src.y); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA); 3978c2ecf20Sopenharmony_ci tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE); 4008c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (yuv && planar) { 4058c2ecf20Sopenharmony_ci tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U); 4068c2ecf20Sopenharmony_ci tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V); 4078c2ecf20Sopenharmony_ci value = window->stride[1] << 16 | window->stride[0]; 4088c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE); 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET); 4148c2ecf20Sopenharmony_ci tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (dc->soc->supports_block_linear) { 4178c2ecf20Sopenharmony_ci unsigned long height = window->tiling.value; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci switch (window->tiling.mode) { 4208c2ecf20Sopenharmony_ci case TEGRA_BO_TILING_MODE_PITCH: 4218c2ecf20Sopenharmony_ci value = DC_WINBUF_SURFACE_KIND_PITCH; 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci case TEGRA_BO_TILING_MODE_TILED: 4258c2ecf20Sopenharmony_ci value = DC_WINBUF_SURFACE_KIND_TILED; 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci case TEGRA_BO_TILING_MODE_BLOCK: 4298c2ecf20Sopenharmony_ci value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) | 4308c2ecf20Sopenharmony_ci DC_WINBUF_SURFACE_KIND_BLOCK; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND); 4358c2ecf20Sopenharmony_ci } else { 4368c2ecf20Sopenharmony_ci switch (window->tiling.mode) { 4378c2ecf20Sopenharmony_ci case TEGRA_BO_TILING_MODE_PITCH: 4388c2ecf20Sopenharmony_ci value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV | 4398c2ecf20Sopenharmony_ci DC_WIN_BUFFER_ADDR_MODE_LINEAR; 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci case TEGRA_BO_TILING_MODE_TILED: 4438c2ecf20Sopenharmony_ci value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | 4448c2ecf20Sopenharmony_ci DC_WIN_BUFFER_ADDR_MODE_TILE; 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci case TEGRA_BO_TILING_MODE_BLOCK: 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * No need to handle this here because ->atomic_check 4508c2ecf20Sopenharmony_ci * will already have filtered it out. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci value = WIN_ENABLE; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (yuv) { 4618c2ecf20Sopenharmony_ci /* setup default colorspace conversion coefficients */ 4628c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF); 4638c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB); 4648c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR); 4658c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR); 4668c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG); 4678c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG); 4688c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB); 4698c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci value |= CSC_ENABLE; 4728c2ecf20Sopenharmony_ci } else if (window->bits_per_pixel < 24) { 4738c2ecf20Sopenharmony_ci value |= COLOR_EXPAND; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (window->reflect_x) 4778c2ecf20Sopenharmony_ci value |= H_DIRECTION; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (window->reflect_y) 4808c2ecf20Sopenharmony_ci value |= V_DIRECTION; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (tegra_plane_use_horizontal_filtering(plane, window)) { 4838c2ecf20Sopenharmony_ci /* 4848c2ecf20Sopenharmony_ci * Enable horizontal 6-tap filter and set filtering 4858c2ecf20Sopenharmony_ci * coefficients to the default values defined in TRM. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0)); 4888c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1)); 4898c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2)); 4908c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3)); 4918c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4)); 4928c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5)); 4938c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6)); 4948c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7)); 4958c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8)); 4968c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9)); 4978c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10)); 4988c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11)); 4998c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12)); 5008c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13)); 5018c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14)); 5028c2ecf20Sopenharmony_ci tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15)); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci value |= H_FILTER; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (tegra_plane_use_vertical_filtering(plane, window)) { 5088c2ecf20Sopenharmony_ci unsigned int i, k; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* 5118c2ecf20Sopenharmony_ci * Enable vertical 2-tap filter and set filtering 5128c2ecf20Sopenharmony_ci * coefficients to the default values defined in TRM. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_ci for (i = 0, k = 128; i < 16; i++, k -= 8) 5158c2ecf20Sopenharmony_ci tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i)); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci value |= V_FILTER; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (dc->soc->has_legacy_blending) 5238c2ecf20Sopenharmony_ci tegra_plane_setup_blending_legacy(plane); 5248c2ecf20Sopenharmony_ci else 5258c2ecf20Sopenharmony_ci tegra_plane_setup_blending(plane, window); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic const u32 tegra20_primary_formats[] = { 5298c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB4444, 5308c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB1555, 5318c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 5328c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA5551, 5338c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR8888, 5348c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 5358c2ecf20Sopenharmony_ci /* non-native formats */ 5368c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 5378c2ecf20Sopenharmony_ci DRM_FORMAT_RGBX5551, 5388c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR8888, 5398c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 5408c2ecf20Sopenharmony_ci}; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic const u64 tegra20_modifiers[] = { 5438c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 5448c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED, 5458c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_INVALID 5468c2ecf20Sopenharmony_ci}; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic const u32 tegra114_primary_formats[] = { 5498c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB4444, 5508c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB1555, 5518c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 5528c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA5551, 5538c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR8888, 5548c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 5558c2ecf20Sopenharmony_ci /* new on Tegra114 */ 5568c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR4444, 5578c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR1555, 5588c2ecf20Sopenharmony_ci DRM_FORMAT_BGRA5551, 5598c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 5608c2ecf20Sopenharmony_ci DRM_FORMAT_RGBX5551, 5618c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR1555, 5628c2ecf20Sopenharmony_ci DRM_FORMAT_BGRX5551, 5638c2ecf20Sopenharmony_ci DRM_FORMAT_BGR565, 5648c2ecf20Sopenharmony_ci DRM_FORMAT_BGRA8888, 5658c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA8888, 5668c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 5678c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR8888, 5688c2ecf20Sopenharmony_ci}; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic const u32 tegra124_primary_formats[] = { 5718c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB4444, 5728c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB1555, 5738c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 5748c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA5551, 5758c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR8888, 5768c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 5778c2ecf20Sopenharmony_ci /* new on Tegra114 */ 5788c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR4444, 5798c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR1555, 5808c2ecf20Sopenharmony_ci DRM_FORMAT_BGRA5551, 5818c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 5828c2ecf20Sopenharmony_ci DRM_FORMAT_RGBX5551, 5838c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR1555, 5848c2ecf20Sopenharmony_ci DRM_FORMAT_BGRX5551, 5858c2ecf20Sopenharmony_ci DRM_FORMAT_BGR565, 5868c2ecf20Sopenharmony_ci DRM_FORMAT_BGRA8888, 5878c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA8888, 5888c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 5898c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR8888, 5908c2ecf20Sopenharmony_ci /* new on Tegra124 */ 5918c2ecf20Sopenharmony_ci DRM_FORMAT_RGBX8888, 5928c2ecf20Sopenharmony_ci DRM_FORMAT_BGRX8888, 5938c2ecf20Sopenharmony_ci}; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic const u64 tegra124_modifiers[] = { 5968c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 5978c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0), 5988c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1), 5998c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2), 6008c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3), 6018c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4), 6028c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5), 6038c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_INVALID 6048c2ecf20Sopenharmony_ci}; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic int tegra_plane_atomic_check(struct drm_plane *plane, 6078c2ecf20Sopenharmony_ci struct drm_plane_state *state) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct tegra_plane_state *plane_state = to_tegra_plane_state(state); 6108c2ecf20Sopenharmony_ci unsigned int supported_rotation = DRM_MODE_ROTATE_0 | 6118c2ecf20Sopenharmony_ci DRM_MODE_REFLECT_X | 6128c2ecf20Sopenharmony_ci DRM_MODE_REFLECT_Y; 6138c2ecf20Sopenharmony_ci unsigned int rotation = state->rotation; 6148c2ecf20Sopenharmony_ci struct tegra_bo_tiling *tiling = &plane_state->tiling; 6158c2ecf20Sopenharmony_ci struct tegra_plane *tegra = to_tegra_plane(plane); 6168c2ecf20Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(state->crtc); 6178c2ecf20Sopenharmony_ci int err; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* no need for further checks if the plane is being disabled */ 6208c2ecf20Sopenharmony_ci if (!state->crtc) 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci err = tegra_plane_format(state->fb->format->format, 6248c2ecf20Sopenharmony_ci &plane_state->format, 6258c2ecf20Sopenharmony_ci &plane_state->swap); 6268c2ecf20Sopenharmony_ci if (err < 0) 6278c2ecf20Sopenharmony_ci return err; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* 6308c2ecf20Sopenharmony_ci * Tegra20 and Tegra30 are special cases here because they support 6318c2ecf20Sopenharmony_ci * only variants of specific formats with an alpha component, but not 6328c2ecf20Sopenharmony_ci * the corresponding opaque formats. However, the opaque formats can 6338c2ecf20Sopenharmony_ci * be emulated by disabling alpha blending for the plane. 6348c2ecf20Sopenharmony_ci */ 6358c2ecf20Sopenharmony_ci if (dc->soc->has_legacy_blending) { 6368c2ecf20Sopenharmony_ci err = tegra_plane_setup_legacy_state(tegra, plane_state); 6378c2ecf20Sopenharmony_ci if (err < 0) 6388c2ecf20Sopenharmony_ci return err; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci err = tegra_fb_get_tiling(state->fb, tiling); 6428c2ecf20Sopenharmony_ci if (err < 0) 6438c2ecf20Sopenharmony_ci return err; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK && 6468c2ecf20Sopenharmony_ci !dc->soc->supports_block_linear) { 6478c2ecf20Sopenharmony_ci DRM_ERROR("hardware doesn't support block linear mode\n"); 6488c2ecf20Sopenharmony_ci return -EINVAL; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* 6528c2ecf20Sopenharmony_ci * Older userspace used custom BO flag in order to specify the Y 6538c2ecf20Sopenharmony_ci * reflection, while modern userspace uses the generic DRM rotation 6548c2ecf20Sopenharmony_ci * property in order to achieve the same result. The legacy BO flag 6558c2ecf20Sopenharmony_ci * duplicates the DRM rotation property when both are set. 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_ci if (tegra_fb_is_bottom_up(state->fb)) 6588c2ecf20Sopenharmony_ci rotation |= DRM_MODE_REFLECT_Y; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci rotation = drm_rotation_simplify(rotation, supported_rotation); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (rotation & DRM_MODE_REFLECT_X) 6638c2ecf20Sopenharmony_ci plane_state->reflect_x = true; 6648c2ecf20Sopenharmony_ci else 6658c2ecf20Sopenharmony_ci plane_state->reflect_x = false; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (rotation & DRM_MODE_REFLECT_Y) 6688c2ecf20Sopenharmony_ci plane_state->reflect_y = true; 6698c2ecf20Sopenharmony_ci else 6708c2ecf20Sopenharmony_ci plane_state->reflect_y = false; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* 6738c2ecf20Sopenharmony_ci * Tegra doesn't support different strides for U and V planes so we 6748c2ecf20Sopenharmony_ci * error out if the user tries to display a framebuffer with such a 6758c2ecf20Sopenharmony_ci * configuration. 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_ci if (state->fb->format->num_planes > 2) { 6788c2ecf20Sopenharmony_ci if (state->fb->pitches[2] != state->fb->pitches[1]) { 6798c2ecf20Sopenharmony_ci DRM_ERROR("unsupported UV-plane configuration\n"); 6808c2ecf20Sopenharmony_ci return -EINVAL; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci err = tegra_plane_state_add(tegra, state); 6858c2ecf20Sopenharmony_ci if (err < 0) 6868c2ecf20Sopenharmony_ci return err; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci return 0; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic void tegra_plane_atomic_disable(struct drm_plane *plane, 6928c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci struct tegra_plane *p = to_tegra_plane(plane); 6958c2ecf20Sopenharmony_ci u32 value; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* rien ne va plus */ 6988c2ecf20Sopenharmony_ci if (!old_state || !old_state->crtc) 6998c2ecf20Sopenharmony_ci return; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS); 7028c2ecf20Sopenharmony_ci value &= ~WIN_ENABLE; 7038c2ecf20Sopenharmony_ci tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic void tegra_plane_atomic_update(struct drm_plane *plane, 7078c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci struct tegra_plane_state *state = to_tegra_plane_state(plane->state); 7108c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = plane->state->fb; 7118c2ecf20Sopenharmony_ci struct tegra_plane *p = to_tegra_plane(plane); 7128c2ecf20Sopenharmony_ci struct tegra_dc_window window; 7138c2ecf20Sopenharmony_ci unsigned int i; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* rien ne va plus */ 7168c2ecf20Sopenharmony_ci if (!plane->state->crtc || !plane->state->fb) 7178c2ecf20Sopenharmony_ci return; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (!plane->state->visible) 7208c2ecf20Sopenharmony_ci return tegra_plane_atomic_disable(plane, old_state); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci memset(&window, 0, sizeof(window)); 7238c2ecf20Sopenharmony_ci window.src.x = plane->state->src.x1 >> 16; 7248c2ecf20Sopenharmony_ci window.src.y = plane->state->src.y1 >> 16; 7258c2ecf20Sopenharmony_ci window.src.w = drm_rect_width(&plane->state->src) >> 16; 7268c2ecf20Sopenharmony_ci window.src.h = drm_rect_height(&plane->state->src) >> 16; 7278c2ecf20Sopenharmony_ci window.dst.x = plane->state->dst.x1; 7288c2ecf20Sopenharmony_ci window.dst.y = plane->state->dst.y1; 7298c2ecf20Sopenharmony_ci window.dst.w = drm_rect_width(&plane->state->dst); 7308c2ecf20Sopenharmony_ci window.dst.h = drm_rect_height(&plane->state->dst); 7318c2ecf20Sopenharmony_ci window.bits_per_pixel = fb->format->cpp[0] * 8; 7328c2ecf20Sopenharmony_ci window.reflect_x = state->reflect_x; 7338c2ecf20Sopenharmony_ci window.reflect_y = state->reflect_y; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* copy from state */ 7368c2ecf20Sopenharmony_ci window.zpos = plane->state->normalized_zpos; 7378c2ecf20Sopenharmony_ci window.tiling = state->tiling; 7388c2ecf20Sopenharmony_ci window.format = state->format; 7398c2ecf20Sopenharmony_ci window.swap = state->swap; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci for (i = 0; i < fb->format->num_planes; i++) { 7428c2ecf20Sopenharmony_ci window.base[i] = state->iova[i] + fb->offsets[i]; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* 7458c2ecf20Sopenharmony_ci * Tegra uses a shared stride for UV planes. Framebuffers are 7468c2ecf20Sopenharmony_ci * already checked for this in the tegra_plane_atomic_check() 7478c2ecf20Sopenharmony_ci * function, so it's safe to ignore the V-plane pitch here. 7488c2ecf20Sopenharmony_ci */ 7498c2ecf20Sopenharmony_ci if (i < 2) 7508c2ecf20Sopenharmony_ci window.stride[i] = fb->pitches[i]; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci tegra_dc_setup_window(p, &window); 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { 7578c2ecf20Sopenharmony_ci .prepare_fb = tegra_plane_prepare_fb, 7588c2ecf20Sopenharmony_ci .cleanup_fb = tegra_plane_cleanup_fb, 7598c2ecf20Sopenharmony_ci .atomic_check = tegra_plane_atomic_check, 7608c2ecf20Sopenharmony_ci .atomic_disable = tegra_plane_atomic_disable, 7618c2ecf20Sopenharmony_ci .atomic_update = tegra_plane_atomic_update, 7628c2ecf20Sopenharmony_ci}; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci /* 7678c2ecf20Sopenharmony_ci * Ideally this would use drm_crtc_mask(), but that would require the 7688c2ecf20Sopenharmony_ci * CRTC to already be in the mode_config's list of CRTCs. However, it 7698c2ecf20Sopenharmony_ci * will only be added to that list in the drm_crtc_init_with_planes() 7708c2ecf20Sopenharmony_ci * (in tegra_dc_init()), which in turn requires registration of these 7718c2ecf20Sopenharmony_ci * planes. So we have ourselves a nice little chicken and egg problem 7728c2ecf20Sopenharmony_ci * here. 7738c2ecf20Sopenharmony_ci * 7748c2ecf20Sopenharmony_ci * We work around this by manually creating the mask from the number 7758c2ecf20Sopenharmony_ci * of CRTCs that have been registered, and should therefore always be 7768c2ecf20Sopenharmony_ci * the same as drm_crtc_index() after registration. 7778c2ecf20Sopenharmony_ci */ 7788c2ecf20Sopenharmony_ci return 1 << drm->mode_config.num_crtc; 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, 7828c2ecf20Sopenharmony_ci struct tegra_dc *dc) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); 7858c2ecf20Sopenharmony_ci enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY; 7868c2ecf20Sopenharmony_ci struct tegra_plane *plane; 7878c2ecf20Sopenharmony_ci unsigned int num_formats; 7888c2ecf20Sopenharmony_ci const u64 *modifiers; 7898c2ecf20Sopenharmony_ci const u32 *formats; 7908c2ecf20Sopenharmony_ci int err; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci plane = kzalloc(sizeof(*plane), GFP_KERNEL); 7938c2ecf20Sopenharmony_ci if (!plane) 7948c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* Always use window A as primary window */ 7978c2ecf20Sopenharmony_ci plane->offset = 0xa00; 7988c2ecf20Sopenharmony_ci plane->index = 0; 7998c2ecf20Sopenharmony_ci plane->dc = dc; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci num_formats = dc->soc->num_primary_formats; 8028c2ecf20Sopenharmony_ci formats = dc->soc->primary_formats; 8038c2ecf20Sopenharmony_ci modifiers = dc->soc->modifiers; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 8068c2ecf20Sopenharmony_ci &tegra_plane_funcs, formats, 8078c2ecf20Sopenharmony_ci num_formats, modifiers, type, NULL); 8088c2ecf20Sopenharmony_ci if (err < 0) { 8098c2ecf20Sopenharmony_ci kfree(plane); 8108c2ecf20Sopenharmony_ci return ERR_PTR(err); 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); 8148c2ecf20Sopenharmony_ci drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci err = drm_plane_create_rotation_property(&plane->base, 8178c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_0, 8188c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_0 | 8198c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_180 | 8208c2ecf20Sopenharmony_ci DRM_MODE_REFLECT_X | 8218c2ecf20Sopenharmony_ci DRM_MODE_REFLECT_Y); 8228c2ecf20Sopenharmony_ci if (err < 0) 8238c2ecf20Sopenharmony_ci dev_err(dc->dev, "failed to create rotation property: %d\n", 8248c2ecf20Sopenharmony_ci err); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci return &plane->base; 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic const u32 tegra_cursor_plane_formats[] = { 8308c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA8888, 8318c2ecf20Sopenharmony_ci}; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic int tegra_cursor_atomic_check(struct drm_plane *plane, 8348c2ecf20Sopenharmony_ci struct drm_plane_state *state) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci struct tegra_plane *tegra = to_tegra_plane(plane); 8378c2ecf20Sopenharmony_ci int err; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* no need for further checks if the plane is being disabled */ 8408c2ecf20Sopenharmony_ci if (!state->crtc) 8418c2ecf20Sopenharmony_ci return 0; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci /* scaling not supported for cursor */ 8448c2ecf20Sopenharmony_ci if ((state->src_w >> 16 != state->crtc_w) || 8458c2ecf20Sopenharmony_ci (state->src_h >> 16 != state->crtc_h)) 8468c2ecf20Sopenharmony_ci return -EINVAL; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* only square cursors supported */ 8498c2ecf20Sopenharmony_ci if (state->src_w != state->src_h) 8508c2ecf20Sopenharmony_ci return -EINVAL; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (state->crtc_w != 32 && state->crtc_w != 64 && 8538c2ecf20Sopenharmony_ci state->crtc_w != 128 && state->crtc_w != 256) 8548c2ecf20Sopenharmony_ci return -EINVAL; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci err = tegra_plane_state_add(tegra, state); 8578c2ecf20Sopenharmony_ci if (err < 0) 8588c2ecf20Sopenharmony_ci return err; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return 0; 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic void tegra_cursor_atomic_update(struct drm_plane *plane, 8648c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct tegra_plane_state *state = to_tegra_plane_state(plane->state); 8678c2ecf20Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); 8688c2ecf20Sopenharmony_ci u32 value = CURSOR_CLIP_DISPLAY; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci /* rien ne va plus */ 8718c2ecf20Sopenharmony_ci if (!plane->state->crtc || !plane->state->fb) 8728c2ecf20Sopenharmony_ci return; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci switch (plane->state->crtc_w) { 8758c2ecf20Sopenharmony_ci case 32: 8768c2ecf20Sopenharmony_ci value |= CURSOR_SIZE_32x32; 8778c2ecf20Sopenharmony_ci break; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci case 64: 8808c2ecf20Sopenharmony_ci value |= CURSOR_SIZE_64x64; 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci case 128: 8848c2ecf20Sopenharmony_ci value |= CURSOR_SIZE_128x128; 8858c2ecf20Sopenharmony_ci break; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci case 256: 8888c2ecf20Sopenharmony_ci value |= CURSOR_SIZE_256x256; 8898c2ecf20Sopenharmony_ci break; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci default: 8928c2ecf20Sopenharmony_ci WARN(1, "cursor size %ux%u not supported\n", 8938c2ecf20Sopenharmony_ci plane->state->crtc_w, plane->state->crtc_h); 8948c2ecf20Sopenharmony_ci return; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci value |= (state->iova[0] >> 10) & 0x3fffff; 8988c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 9018c2ecf20Sopenharmony_ci value = (state->iova[0] >> 32) & 0x3; 9028c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI); 9038c2ecf20Sopenharmony_ci#endif 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci /* enable cursor and set blend mode */ 9068c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 9078c2ecf20Sopenharmony_ci value |= CURSOR_ENABLE; 9088c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL); 9118c2ecf20Sopenharmony_ci value &= ~CURSOR_DST_BLEND_MASK; 9128c2ecf20Sopenharmony_ci value &= ~CURSOR_SRC_BLEND_MASK; 9138c2ecf20Sopenharmony_ci value |= CURSOR_MODE_NORMAL; 9148c2ecf20Sopenharmony_ci value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC; 9158c2ecf20Sopenharmony_ci value |= CURSOR_SRC_BLEND_K1_TIMES_SRC; 9168c2ecf20Sopenharmony_ci value |= CURSOR_ALPHA; 9178c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* position the cursor */ 9208c2ecf20Sopenharmony_ci value = (plane->state->crtc_y & 0x3fff) << 16 | 9218c2ecf20Sopenharmony_ci (plane->state->crtc_x & 0x3fff); 9228c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cistatic void tegra_cursor_atomic_disable(struct drm_plane *plane, 9268c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct tegra_dc *dc; 9298c2ecf20Sopenharmony_ci u32 value; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* rien ne va plus */ 9328c2ecf20Sopenharmony_ci if (!old_state || !old_state->crtc) 9338c2ecf20Sopenharmony_ci return; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci dc = to_tegra_dc(old_state->crtc); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 9388c2ecf20Sopenharmony_ci value &= ~CURSOR_ENABLE; 9398c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { 9438c2ecf20Sopenharmony_ci .prepare_fb = tegra_plane_prepare_fb, 9448c2ecf20Sopenharmony_ci .cleanup_fb = tegra_plane_cleanup_fb, 9458c2ecf20Sopenharmony_ci .atomic_check = tegra_cursor_atomic_check, 9468c2ecf20Sopenharmony_ci .atomic_update = tegra_cursor_atomic_update, 9478c2ecf20Sopenharmony_ci .atomic_disable = tegra_cursor_atomic_disable, 9488c2ecf20Sopenharmony_ci}; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic const uint64_t linear_modifiers[] = { 9518c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 9528c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_INVALID 9538c2ecf20Sopenharmony_ci}; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, 9568c2ecf20Sopenharmony_ci struct tegra_dc *dc) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); 9598c2ecf20Sopenharmony_ci struct tegra_plane *plane; 9608c2ecf20Sopenharmony_ci unsigned int num_formats; 9618c2ecf20Sopenharmony_ci const u32 *formats; 9628c2ecf20Sopenharmony_ci int err; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci plane = kzalloc(sizeof(*plane), GFP_KERNEL); 9658c2ecf20Sopenharmony_ci if (!plane) 9668c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* 9698c2ecf20Sopenharmony_ci * This index is kind of fake. The cursor isn't a regular plane, but 9708c2ecf20Sopenharmony_ci * its update and activation request bits in DC_CMD_STATE_CONTROL do 9718c2ecf20Sopenharmony_ci * use the same programming. Setting this fake index here allows the 9728c2ecf20Sopenharmony_ci * code in tegra_add_plane_state() to do the right thing without the 9738c2ecf20Sopenharmony_ci * need to special-casing the cursor plane. 9748c2ecf20Sopenharmony_ci */ 9758c2ecf20Sopenharmony_ci plane->index = 6; 9768c2ecf20Sopenharmony_ci plane->dc = dc; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); 9798c2ecf20Sopenharmony_ci formats = tegra_cursor_plane_formats; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 9828c2ecf20Sopenharmony_ci &tegra_plane_funcs, formats, 9838c2ecf20Sopenharmony_ci num_formats, linear_modifiers, 9848c2ecf20Sopenharmony_ci DRM_PLANE_TYPE_CURSOR, NULL); 9858c2ecf20Sopenharmony_ci if (err < 0) { 9868c2ecf20Sopenharmony_ci kfree(plane); 9878c2ecf20Sopenharmony_ci return ERR_PTR(err); 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs); 9918c2ecf20Sopenharmony_ci drm_plane_create_zpos_immutable_property(&plane->base, 255); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return &plane->base; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic const u32 tegra20_overlay_formats[] = { 9978c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB4444, 9988c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB1555, 9998c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 10008c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA5551, 10018c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR8888, 10028c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 10038c2ecf20Sopenharmony_ci /* non-native formats */ 10048c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 10058c2ecf20Sopenharmony_ci DRM_FORMAT_RGBX5551, 10068c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR8888, 10078c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 10088c2ecf20Sopenharmony_ci /* planar formats */ 10098c2ecf20Sopenharmony_ci DRM_FORMAT_UYVY, 10108c2ecf20Sopenharmony_ci DRM_FORMAT_YUYV, 10118c2ecf20Sopenharmony_ci DRM_FORMAT_YUV420, 10128c2ecf20Sopenharmony_ci DRM_FORMAT_YUV422, 10138c2ecf20Sopenharmony_ci}; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cistatic const u32 tegra114_overlay_formats[] = { 10168c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB4444, 10178c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB1555, 10188c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 10198c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA5551, 10208c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR8888, 10218c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 10228c2ecf20Sopenharmony_ci /* new on Tegra114 */ 10238c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR4444, 10248c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR1555, 10258c2ecf20Sopenharmony_ci DRM_FORMAT_BGRA5551, 10268c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 10278c2ecf20Sopenharmony_ci DRM_FORMAT_RGBX5551, 10288c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR1555, 10298c2ecf20Sopenharmony_ci DRM_FORMAT_BGRX5551, 10308c2ecf20Sopenharmony_ci DRM_FORMAT_BGR565, 10318c2ecf20Sopenharmony_ci DRM_FORMAT_BGRA8888, 10328c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA8888, 10338c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 10348c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR8888, 10358c2ecf20Sopenharmony_ci /* planar formats */ 10368c2ecf20Sopenharmony_ci DRM_FORMAT_UYVY, 10378c2ecf20Sopenharmony_ci DRM_FORMAT_YUYV, 10388c2ecf20Sopenharmony_ci DRM_FORMAT_YUV420, 10398c2ecf20Sopenharmony_ci DRM_FORMAT_YUV422, 10408c2ecf20Sopenharmony_ci}; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_cistatic const u32 tegra124_overlay_formats[] = { 10438c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB4444, 10448c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB1555, 10458c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 10468c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA5551, 10478c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR8888, 10488c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 10498c2ecf20Sopenharmony_ci /* new on Tegra114 */ 10508c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR4444, 10518c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR1555, 10528c2ecf20Sopenharmony_ci DRM_FORMAT_BGRA5551, 10538c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 10548c2ecf20Sopenharmony_ci DRM_FORMAT_RGBX5551, 10558c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR1555, 10568c2ecf20Sopenharmony_ci DRM_FORMAT_BGRX5551, 10578c2ecf20Sopenharmony_ci DRM_FORMAT_BGR565, 10588c2ecf20Sopenharmony_ci DRM_FORMAT_BGRA8888, 10598c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA8888, 10608c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 10618c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR8888, 10628c2ecf20Sopenharmony_ci /* new on Tegra124 */ 10638c2ecf20Sopenharmony_ci DRM_FORMAT_RGBX8888, 10648c2ecf20Sopenharmony_ci DRM_FORMAT_BGRX8888, 10658c2ecf20Sopenharmony_ci /* planar formats */ 10668c2ecf20Sopenharmony_ci DRM_FORMAT_UYVY, 10678c2ecf20Sopenharmony_ci DRM_FORMAT_YUYV, 10688c2ecf20Sopenharmony_ci DRM_FORMAT_YUV420, 10698c2ecf20Sopenharmony_ci DRM_FORMAT_YUV422, 10708c2ecf20Sopenharmony_ci}; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, 10738c2ecf20Sopenharmony_ci struct tegra_dc *dc, 10748c2ecf20Sopenharmony_ci unsigned int index, 10758c2ecf20Sopenharmony_ci bool cursor) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); 10788c2ecf20Sopenharmony_ci struct tegra_plane *plane; 10798c2ecf20Sopenharmony_ci unsigned int num_formats; 10808c2ecf20Sopenharmony_ci enum drm_plane_type type; 10818c2ecf20Sopenharmony_ci const u32 *formats; 10828c2ecf20Sopenharmony_ci int err; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci plane = kzalloc(sizeof(*plane), GFP_KERNEL); 10858c2ecf20Sopenharmony_ci if (!plane) 10868c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci plane->offset = 0xa00 + 0x200 * index; 10898c2ecf20Sopenharmony_ci plane->index = index; 10908c2ecf20Sopenharmony_ci plane->dc = dc; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci num_formats = dc->soc->num_overlay_formats; 10938c2ecf20Sopenharmony_ci formats = dc->soc->overlay_formats; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (!cursor) 10968c2ecf20Sopenharmony_ci type = DRM_PLANE_TYPE_OVERLAY; 10978c2ecf20Sopenharmony_ci else 10988c2ecf20Sopenharmony_ci type = DRM_PLANE_TYPE_CURSOR; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 11018c2ecf20Sopenharmony_ci &tegra_plane_funcs, formats, 11028c2ecf20Sopenharmony_ci num_formats, linear_modifiers, 11038c2ecf20Sopenharmony_ci type, NULL); 11048c2ecf20Sopenharmony_ci if (err < 0) { 11058c2ecf20Sopenharmony_ci kfree(plane); 11068c2ecf20Sopenharmony_ci return ERR_PTR(err); 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); 11108c2ecf20Sopenharmony_ci drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci err = drm_plane_create_rotation_property(&plane->base, 11138c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_0, 11148c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_0 | 11158c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_180 | 11168c2ecf20Sopenharmony_ci DRM_MODE_REFLECT_X | 11178c2ecf20Sopenharmony_ci DRM_MODE_REFLECT_Y); 11188c2ecf20Sopenharmony_ci if (err < 0) 11198c2ecf20Sopenharmony_ci dev_err(dc->dev, "failed to create rotation property: %d\n", 11208c2ecf20Sopenharmony_ci err); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci return &plane->base; 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm, 11268c2ecf20Sopenharmony_ci struct tegra_dc *dc) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci struct drm_plane *plane, *primary = NULL; 11298c2ecf20Sopenharmony_ci unsigned int i, j; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci for (i = 0; i < dc->soc->num_wgrps; i++) { 11328c2ecf20Sopenharmony_ci const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (wgrp->dc == dc->pipe) { 11358c2ecf20Sopenharmony_ci for (j = 0; j < wgrp->num_windows; j++) { 11368c2ecf20Sopenharmony_ci unsigned int index = wgrp->windows[j]; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci plane = tegra_shared_plane_create(drm, dc, 11398c2ecf20Sopenharmony_ci wgrp->index, 11408c2ecf20Sopenharmony_ci index); 11418c2ecf20Sopenharmony_ci if (IS_ERR(plane)) 11428c2ecf20Sopenharmony_ci return plane; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* 11458c2ecf20Sopenharmony_ci * Choose the first shared plane owned by this 11468c2ecf20Sopenharmony_ci * head as the primary plane. 11478c2ecf20Sopenharmony_ci */ 11488c2ecf20Sopenharmony_ci if (!primary) { 11498c2ecf20Sopenharmony_ci plane->type = DRM_PLANE_TYPE_PRIMARY; 11508c2ecf20Sopenharmony_ci primary = plane; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci return primary; 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, 11608c2ecf20Sopenharmony_ci struct tegra_dc *dc) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct drm_plane *planes[2], *primary; 11638c2ecf20Sopenharmony_ci unsigned int planes_num; 11648c2ecf20Sopenharmony_ci unsigned int i; 11658c2ecf20Sopenharmony_ci int err; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci primary = tegra_primary_plane_create(drm, dc); 11688c2ecf20Sopenharmony_ci if (IS_ERR(primary)) 11698c2ecf20Sopenharmony_ci return primary; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (dc->soc->supports_cursor) 11728c2ecf20Sopenharmony_ci planes_num = 2; 11738c2ecf20Sopenharmony_ci else 11748c2ecf20Sopenharmony_ci planes_num = 1; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci for (i = 0; i < planes_num; i++) { 11778c2ecf20Sopenharmony_ci planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i, 11788c2ecf20Sopenharmony_ci false); 11798c2ecf20Sopenharmony_ci if (IS_ERR(planes[i])) { 11808c2ecf20Sopenharmony_ci err = PTR_ERR(planes[i]); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci while (i--) 11838c2ecf20Sopenharmony_ci tegra_plane_funcs.destroy(planes[i]); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci tegra_plane_funcs.destroy(primary); 11868c2ecf20Sopenharmony_ci return ERR_PTR(err); 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci return primary; 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic void tegra_dc_destroy(struct drm_crtc *crtc) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci drm_crtc_cleanup(crtc); 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic void tegra_crtc_reset(struct drm_crtc *crtc) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (crtc->state) 12038c2ecf20Sopenharmony_ci tegra_crtc_atomic_destroy_state(crtc, crtc->state); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &state->base); 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_cistatic struct drm_crtc_state * 12098c2ecf20Sopenharmony_citegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct tegra_dc_state *state = to_dc_state(crtc->state); 12128c2ecf20Sopenharmony_ci struct tegra_dc_state *copy; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci copy = kmalloc(sizeof(*copy), GFP_KERNEL); 12158c2ecf20Sopenharmony_ci if (!copy) 12168c2ecf20Sopenharmony_ci return NULL; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, ©->base); 12198c2ecf20Sopenharmony_ci copy->clk = state->clk; 12208c2ecf20Sopenharmony_ci copy->pclk = state->pclk; 12218c2ecf20Sopenharmony_ci copy->div = state->div; 12228c2ecf20Sopenharmony_ci copy->planes = state->planes; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci return ©->base; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_cistatic void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, 12288c2ecf20Sopenharmony_ci struct drm_crtc_state *state) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(state); 12318c2ecf20Sopenharmony_ci kfree(state); 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic const struct debugfs_reg32 tegra_dc_regs[] = { 12378c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT), 12388c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL), 12398c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR), 12408c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT), 12418c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL), 12428c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR), 12438c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT), 12448c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL), 12458c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR), 12468c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT), 12478c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL), 12488c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR), 12498c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC), 12508c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0), 12518c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND), 12528c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE), 12538c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL), 12548c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_INT_STATUS), 12558c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_INT_MASK), 12568c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_INT_ENABLE), 12578c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_INT_TYPE), 12588c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_INT_POLARITY), 12598c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1), 12608c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2), 12618c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3), 12628c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_STATE_ACCESS), 12638c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_STATE_CONTROL), 12648c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER), 12658c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL), 12668c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_CRC_CONTROL), 12678c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_CRC_CHECKSUM), 12688c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)), 12698c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)), 12708c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)), 12718c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)), 12728c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)), 12738c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)), 12748c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)), 12758c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)), 12768c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)), 12778c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)), 12788c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)), 12798c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)), 12808c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)), 12818c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)), 12828c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)), 12838c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)), 12848c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)), 12858c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)), 12868c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)), 12878c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)), 12888c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)), 12898c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)), 12908c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)), 12918c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)), 12928c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)), 12938c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL), 12948c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL), 12958c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE), 12968c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL), 12978c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE), 12988c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_SPI_CONTROL), 12998c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_SPI_START_BYTE), 13008c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB), 13018c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD), 13028c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_HSPI_CS_DC), 13038c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A), 13048c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B), 13058c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_GPIO_CTRL), 13068c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER), 13078c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED), 13088c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0), 13098c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1), 13108c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS), 13118c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY), 13128c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER), 13138c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS), 13148c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_REF_TO_SYNC), 13158c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SYNC_WIDTH), 13168c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_BACK_PORCH), 13178c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_ACTIVE), 13188c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_FRONT_PORCH), 13198c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL), 13208c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A), 13218c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B), 13228c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C), 13238c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D), 13248c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL), 13258c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A), 13268c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B), 13278c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C), 13288c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D), 13298c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL), 13308c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A), 13318c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B), 13328c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C), 13338c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D), 13348c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL), 13358c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A), 13368c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B), 13378c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C), 13388c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL), 13398c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A), 13408c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B), 13418c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C), 13428c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL), 13438c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A), 13448c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL), 13458c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A), 13468c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_M0_CONTROL), 13478c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_M1_CONTROL), 13488c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DI_CONTROL), 13498c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_PP_CONTROL), 13508c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_PP_SELECT_A), 13518c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_PP_SELECT_B), 13528c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_PP_SELECT_C), 13538c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_PP_SELECT_D), 13548c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL), 13558c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL), 13568c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL), 13578c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS), 13588c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS), 13598c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS), 13608c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS), 13618c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_BORDER_COLOR), 13628c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER), 13638c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER), 13648c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER), 13658c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER), 13668c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND), 13678c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND), 13688c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR), 13698c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS), 13708c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_POSITION), 13718c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS), 13728c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL), 13738c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A), 13748c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B), 13758c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C), 13768c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D), 13778c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL), 13788c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST), 13798c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST), 13808c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST), 13818c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST), 13828c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL), 13838c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL), 13848c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_CONTROL), 13858c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF), 13868c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(0)), 13878c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(1)), 13888c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(2)), 13898c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(3)), 13908c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(4)), 13918c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(5)), 13928c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(6)), 13938c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(7)), 13948c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(8)), 13958c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL), 13968c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT), 13978c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)), 13988c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)), 13998c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)), 14008c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)), 14018c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)), 14028c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)), 14038c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)), 14048c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)), 14058c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)), 14068c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)), 14078c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)), 14088c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)), 14098c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL), 14108c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES), 14118c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES), 14128c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI), 14138c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL), 14148c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_WIN_OPTIONS), 14158c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BYTE_SWAP), 14168c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL), 14178c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_COLOR_DEPTH), 14188c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_POSITION), 14198c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_SIZE), 14208c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE), 14218c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA), 14228c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA), 14238c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_DDA_INC), 14248c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_LINE_STRIDE), 14258c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BUF_STRIDE), 14268c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE), 14278c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE), 14288c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_DV_CONTROL), 14298c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BLEND_NOKEY), 14308c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BLEND_1WIN), 14318c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X), 14328c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y), 14338c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY), 14348c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL), 14358c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR), 14368c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS), 14378c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR_U), 14388c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS), 14398c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR_V), 14408c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS), 14418c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET), 14428c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS), 14438c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET), 14448c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS), 14458c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS), 14468c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS), 14478c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS), 14488c2ecf20Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS), 14498c2ecf20Sopenharmony_ci}; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic int tegra_dc_show_regs(struct seq_file *s, void *data) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci struct drm_info_node *node = s->private; 14548c2ecf20Sopenharmony_ci struct tegra_dc *dc = node->info_ent->data; 14558c2ecf20Sopenharmony_ci unsigned int i; 14568c2ecf20Sopenharmony_ci int err = 0; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci drm_modeset_lock(&dc->base.mutex, NULL); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci if (!dc->base.state->active) { 14618c2ecf20Sopenharmony_ci err = -EBUSY; 14628c2ecf20Sopenharmony_ci goto unlock; 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { 14668c2ecf20Sopenharmony_ci unsigned int offset = tegra_dc_regs[i].offset; 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name, 14698c2ecf20Sopenharmony_ci offset, tegra_dc_readl(dc, offset)); 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ciunlock: 14738c2ecf20Sopenharmony_ci drm_modeset_unlock(&dc->base.mutex); 14748c2ecf20Sopenharmony_ci return err; 14758c2ecf20Sopenharmony_ci} 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_cistatic int tegra_dc_show_crc(struct seq_file *s, void *data) 14788c2ecf20Sopenharmony_ci{ 14798c2ecf20Sopenharmony_ci struct drm_info_node *node = s->private; 14808c2ecf20Sopenharmony_ci struct tegra_dc *dc = node->info_ent->data; 14818c2ecf20Sopenharmony_ci int err = 0; 14828c2ecf20Sopenharmony_ci u32 value; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci drm_modeset_lock(&dc->base.mutex, NULL); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci if (!dc->base.state->active) { 14878c2ecf20Sopenharmony_ci err = -EBUSY; 14888c2ecf20Sopenharmony_ci goto unlock; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE; 14928c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL); 14938c2ecf20Sopenharmony_ci tegra_dc_commit(dc); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci drm_crtc_wait_one_vblank(&dc->base); 14968c2ecf20Sopenharmony_ci drm_crtc_wait_one_vblank(&dc->base); 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); 14998c2ecf20Sopenharmony_ci seq_printf(s, "%08x\n", value); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ciunlock: 15048c2ecf20Sopenharmony_ci drm_modeset_unlock(&dc->base.mutex); 15058c2ecf20Sopenharmony_ci return err; 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic int tegra_dc_show_stats(struct seq_file *s, void *data) 15098c2ecf20Sopenharmony_ci{ 15108c2ecf20Sopenharmony_ci struct drm_info_node *node = s->private; 15118c2ecf20Sopenharmony_ci struct tegra_dc *dc = node->info_ent->data; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci seq_printf(s, "frames: %lu\n", dc->stats.frames); 15148c2ecf20Sopenharmony_ci seq_printf(s, "vblank: %lu\n", dc->stats.vblank); 15158c2ecf20Sopenharmony_ci seq_printf(s, "underflow: %lu\n", dc->stats.underflow); 15168c2ecf20Sopenharmony_ci seq_printf(s, "overflow: %lu\n", dc->stats.overflow); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci return 0; 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cistatic struct drm_info_list debugfs_files[] = { 15228c2ecf20Sopenharmony_ci { "regs", tegra_dc_show_regs, 0, NULL }, 15238c2ecf20Sopenharmony_ci { "crc", tegra_dc_show_crc, 0, NULL }, 15248c2ecf20Sopenharmony_ci { "stats", tegra_dc_show_stats, 0, NULL }, 15258c2ecf20Sopenharmony_ci}; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_cistatic int tegra_dc_late_register(struct drm_crtc *crtc) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci unsigned int i, count = ARRAY_SIZE(debugfs_files); 15308c2ecf20Sopenharmony_ci struct drm_minor *minor = crtc->dev->primary; 15318c2ecf20Sopenharmony_ci struct dentry *root; 15328c2ecf20Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 15358c2ecf20Sopenharmony_ci root = crtc->debugfs_entry; 15368c2ecf20Sopenharmony_ci#else 15378c2ecf20Sopenharmony_ci root = NULL; 15388c2ecf20Sopenharmony_ci#endif 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), 15418c2ecf20Sopenharmony_ci GFP_KERNEL); 15428c2ecf20Sopenharmony_ci if (!dc->debugfs_files) 15438c2ecf20Sopenharmony_ci return -ENOMEM; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 15468c2ecf20Sopenharmony_ci dc->debugfs_files[i].data = dc; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci drm_debugfs_create_files(dc->debugfs_files, count, root, minor); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci return 0; 15518c2ecf20Sopenharmony_ci} 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_cistatic void tegra_dc_early_unregister(struct drm_crtc *crtc) 15548c2ecf20Sopenharmony_ci{ 15558c2ecf20Sopenharmony_ci unsigned int count = ARRAY_SIZE(debugfs_files); 15568c2ecf20Sopenharmony_ci struct drm_minor *minor = crtc->dev->primary; 15578c2ecf20Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci drm_debugfs_remove_files(dc->debugfs_files, count, minor); 15608c2ecf20Sopenharmony_ci kfree(dc->debugfs_files); 15618c2ecf20Sopenharmony_ci dc->debugfs_files = NULL; 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cistatic u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) 15658c2ecf20Sopenharmony_ci{ 15668c2ecf20Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci /* XXX vblank syncpoints don't work with nvdisplay yet */ 15698c2ecf20Sopenharmony_ci if (dc->syncpt && !dc->soc->has_nvdisplay) 15708c2ecf20Sopenharmony_ci return host1x_syncpt_read(dc->syncpt); 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci /* fallback to software emulated VBLANK counter */ 15738c2ecf20Sopenharmony_ci return (u32)drm_crtc_vblank_count(&dc->base); 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_cistatic int tegra_dc_enable_vblank(struct drm_crtc *crtc) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 15798c2ecf20Sopenharmony_ci u32 value; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_INT_MASK); 15828c2ecf20Sopenharmony_ci value |= VBLANK_INT; 15838c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci return 0; 15868c2ecf20Sopenharmony_ci} 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_cistatic void tegra_dc_disable_vblank(struct drm_crtc *crtc) 15898c2ecf20Sopenharmony_ci{ 15908c2ecf20Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 15918c2ecf20Sopenharmony_ci u32 value; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_INT_MASK); 15948c2ecf20Sopenharmony_ci value &= ~VBLANK_INT; 15958c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs tegra_crtc_funcs = { 15998c2ecf20Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 16008c2ecf20Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 16018c2ecf20Sopenharmony_ci .destroy = tegra_dc_destroy, 16028c2ecf20Sopenharmony_ci .reset = tegra_crtc_reset, 16038c2ecf20Sopenharmony_ci .atomic_duplicate_state = tegra_crtc_atomic_duplicate_state, 16048c2ecf20Sopenharmony_ci .atomic_destroy_state = tegra_crtc_atomic_destroy_state, 16058c2ecf20Sopenharmony_ci .late_register = tegra_dc_late_register, 16068c2ecf20Sopenharmony_ci .early_unregister = tegra_dc_early_unregister, 16078c2ecf20Sopenharmony_ci .get_vblank_counter = tegra_dc_get_vblank_counter, 16088c2ecf20Sopenharmony_ci .enable_vblank = tegra_dc_enable_vblank, 16098c2ecf20Sopenharmony_ci .disable_vblank = tegra_dc_disable_vblank, 16108c2ecf20Sopenharmony_ci}; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_cistatic int tegra_dc_set_timings(struct tegra_dc *dc, 16138c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 16148c2ecf20Sopenharmony_ci{ 16158c2ecf20Sopenharmony_ci unsigned int h_ref_to_sync = 1; 16168c2ecf20Sopenharmony_ci unsigned int v_ref_to_sync = 1; 16178c2ecf20Sopenharmony_ci unsigned long value; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci if (!dc->soc->has_nvdisplay) { 16208c2ecf20Sopenharmony_ci tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci value = (v_ref_to_sync << 16) | h_ref_to_sync; 16238c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC); 16248c2ecf20Sopenharmony_ci } 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci value = ((mode->vsync_end - mode->vsync_start) << 16) | 16278c2ecf20Sopenharmony_ci ((mode->hsync_end - mode->hsync_start) << 0); 16288c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH); 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci value = ((mode->vtotal - mode->vsync_end) << 16) | 16318c2ecf20Sopenharmony_ci ((mode->htotal - mode->hsync_end) << 0); 16328c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci value = ((mode->vsync_start - mode->vdisplay) << 16) | 16358c2ecf20Sopenharmony_ci ((mode->hsync_start - mode->hdisplay) << 0); 16368c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH); 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci value = (mode->vdisplay << 16) | mode->hdisplay; 16398c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_ACTIVE); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci return 0; 16428c2ecf20Sopenharmony_ci} 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci/** 16458c2ecf20Sopenharmony_ci * tegra_dc_state_setup_clock - check clock settings and store them in atomic 16468c2ecf20Sopenharmony_ci * state 16478c2ecf20Sopenharmony_ci * @dc: display controller 16488c2ecf20Sopenharmony_ci * @crtc_state: CRTC atomic state 16498c2ecf20Sopenharmony_ci * @clk: parent clock for display controller 16508c2ecf20Sopenharmony_ci * @pclk: pixel clock 16518c2ecf20Sopenharmony_ci * @div: shift clock divider 16528c2ecf20Sopenharmony_ci * 16538c2ecf20Sopenharmony_ci * Returns: 16548c2ecf20Sopenharmony_ci * 0 on success or a negative error-code on failure. 16558c2ecf20Sopenharmony_ci */ 16568c2ecf20Sopenharmony_ciint tegra_dc_state_setup_clock(struct tegra_dc *dc, 16578c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 16588c2ecf20Sopenharmony_ci struct clk *clk, unsigned long pclk, 16598c2ecf20Sopenharmony_ci unsigned int div) 16608c2ecf20Sopenharmony_ci{ 16618c2ecf20Sopenharmony_ci struct tegra_dc_state *state = to_dc_state(crtc_state); 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci if (!clk_has_parent(dc->clk, clk)) 16648c2ecf20Sopenharmony_ci return -EINVAL; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci state->clk = clk; 16678c2ecf20Sopenharmony_ci state->pclk = pclk; 16688c2ecf20Sopenharmony_ci state->div = div; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci return 0; 16718c2ecf20Sopenharmony_ci} 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_cistatic void tegra_dc_commit_state(struct tegra_dc *dc, 16748c2ecf20Sopenharmony_ci struct tegra_dc_state *state) 16758c2ecf20Sopenharmony_ci{ 16768c2ecf20Sopenharmony_ci u32 value; 16778c2ecf20Sopenharmony_ci int err; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci err = clk_set_parent(dc->clk, state->clk); 16808c2ecf20Sopenharmony_ci if (err < 0) 16818c2ecf20Sopenharmony_ci dev_err(dc->dev, "failed to set parent clock: %d\n", err); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci /* 16848c2ecf20Sopenharmony_ci * Outputs may not want to change the parent clock rate. This is only 16858c2ecf20Sopenharmony_ci * relevant to Tegra20 where only a single display PLL is available. 16868c2ecf20Sopenharmony_ci * Since that PLL would typically be used for HDMI, an internal LVDS 16878c2ecf20Sopenharmony_ci * panel would need to be driven by some other clock such as PLL_P 16888c2ecf20Sopenharmony_ci * which is shared with other peripherals. Changing the clock rate 16898c2ecf20Sopenharmony_ci * should therefore be avoided. 16908c2ecf20Sopenharmony_ci */ 16918c2ecf20Sopenharmony_ci if (state->pclk > 0) { 16928c2ecf20Sopenharmony_ci err = clk_set_rate(state->clk, state->pclk); 16938c2ecf20Sopenharmony_ci if (err < 0) 16948c2ecf20Sopenharmony_ci dev_err(dc->dev, 16958c2ecf20Sopenharmony_ci "failed to set clock rate to %lu Hz\n", 16968c2ecf20Sopenharmony_ci state->pclk); 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci err = clk_set_rate(dc->clk, state->pclk); 16998c2ecf20Sopenharmony_ci if (err < 0) 17008c2ecf20Sopenharmony_ci dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n", 17018c2ecf20Sopenharmony_ci dc->clk, state->pclk, err); 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), 17058c2ecf20Sopenharmony_ci state->div); 17068c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("pclk: %lu\n", state->pclk); 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci if (!dc->soc->has_nvdisplay) { 17098c2ecf20Sopenharmony_ci value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1; 17108c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_cistatic void tegra_dc_stop(struct tegra_dc *dc) 17158c2ecf20Sopenharmony_ci{ 17168c2ecf20Sopenharmony_ci u32 value; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci /* stop the display controller */ 17198c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); 17208c2ecf20Sopenharmony_ci value &= ~DISP_CTRL_MODE_MASK; 17218c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci tegra_dc_commit(dc); 17248c2ecf20Sopenharmony_ci} 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_cistatic bool tegra_dc_idle(struct tegra_dc *dc) 17278c2ecf20Sopenharmony_ci{ 17288c2ecf20Sopenharmony_ci u32 value; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci return (value & DISP_CTRL_MODE_MASK) == 0; 17338c2ecf20Sopenharmony_ci} 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_cistatic int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout) 17368c2ecf20Sopenharmony_ci{ 17378c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(timeout); 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 17408c2ecf20Sopenharmony_ci if (tegra_dc_idle(dc)) 17418c2ecf20Sopenharmony_ci return 0; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 17448c2ecf20Sopenharmony_ci } 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci dev_dbg(dc->dev, "timeout waiting for DC to become idle\n"); 17478c2ecf20Sopenharmony_ci return -ETIMEDOUT; 17488c2ecf20Sopenharmony_ci} 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_cistatic void tegra_crtc_atomic_disable(struct drm_crtc *crtc, 17518c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 17528c2ecf20Sopenharmony_ci{ 17538c2ecf20Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 17548c2ecf20Sopenharmony_ci u32 value; 17558c2ecf20Sopenharmony_ci int err; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci if (!tegra_dc_idle(dc)) { 17588c2ecf20Sopenharmony_ci tegra_dc_stop(dc); 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci /* 17618c2ecf20Sopenharmony_ci * Ignore the return value, there isn't anything useful to do 17628c2ecf20Sopenharmony_ci * in case this fails. 17638c2ecf20Sopenharmony_ci */ 17648c2ecf20Sopenharmony_ci tegra_dc_wait_idle(dc, 100); 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci /* 17688c2ecf20Sopenharmony_ci * This should really be part of the RGB encoder driver, but clearing 17698c2ecf20Sopenharmony_ci * these bits has the side-effect of stopping the display controller. 17708c2ecf20Sopenharmony_ci * When that happens no VBLANK interrupts will be raised. At the same 17718c2ecf20Sopenharmony_ci * time the encoder is disabled before the display controller, so the 17728c2ecf20Sopenharmony_ci * above code is always going to timeout waiting for the controller 17738c2ecf20Sopenharmony_ci * to go idle. 17748c2ecf20Sopenharmony_ci * 17758c2ecf20Sopenharmony_ci * Given the close coupling between the RGB encoder and the display 17768c2ecf20Sopenharmony_ci * controller doing it here is still kind of okay. None of the other 17778c2ecf20Sopenharmony_ci * encoder drivers require these bits to be cleared. 17788c2ecf20Sopenharmony_ci * 17798c2ecf20Sopenharmony_ci * XXX: Perhaps given that the display controller is switched off at 17808c2ecf20Sopenharmony_ci * this point anyway maybe clearing these bits isn't even useful for 17818c2ecf20Sopenharmony_ci * the RGB encoder? 17828c2ecf20Sopenharmony_ci */ 17838c2ecf20Sopenharmony_ci if (dc->rgb) { 17848c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 17858c2ecf20Sopenharmony_ci value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 17868c2ecf20Sopenharmony_ci PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); 17878c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci tegra_dc_stats_reset(&dc->stats); 17918c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci if (crtc->state->event) { 17968c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 17978c2ecf20Sopenharmony_ci crtc->state->event = NULL; 17988c2ecf20Sopenharmony_ci } 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci err = host1x_client_suspend(&dc->client); 18038c2ecf20Sopenharmony_ci if (err < 0) 18048c2ecf20Sopenharmony_ci dev_err(dc->dev, "failed to suspend: %d\n", err); 18058c2ecf20Sopenharmony_ci} 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_cistatic void tegra_crtc_atomic_enable(struct drm_crtc *crtc, 18088c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 18098c2ecf20Sopenharmony_ci{ 18108c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 18118c2ecf20Sopenharmony_ci struct tegra_dc_state *state = to_dc_state(crtc->state); 18128c2ecf20Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 18138c2ecf20Sopenharmony_ci u32 value; 18148c2ecf20Sopenharmony_ci int err; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci err = host1x_client_resume(&dc->client); 18178c2ecf20Sopenharmony_ci if (err < 0) { 18188c2ecf20Sopenharmony_ci dev_err(dc->dev, "failed to resume: %d\n", err); 18198c2ecf20Sopenharmony_ci return; 18208c2ecf20Sopenharmony_ci } 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci /* initialize display controller */ 18238c2ecf20Sopenharmony_ci if (dc->syncpt) { 18248c2ecf20Sopenharmony_ci u32 syncpt = host1x_syncpt_id(dc->syncpt), enable; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci if (dc->soc->has_nvdisplay) 18278c2ecf20Sopenharmony_ci enable = 1 << 31; 18288c2ecf20Sopenharmony_ci else 18298c2ecf20Sopenharmony_ci enable = 1 << 8; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci value = SYNCPT_CNTRL_NO_STALL; 18328c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci value = enable | syncpt; 18358c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); 18368c2ecf20Sopenharmony_ci } 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci if (dc->soc->has_nvdisplay) { 18398c2ecf20Sopenharmony_ci value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | 18408c2ecf20Sopenharmony_ci DSC_OBUF_UF_INT; 18418c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | 18448c2ecf20Sopenharmony_ci DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT | 18458c2ecf20Sopenharmony_ci HEAD_UF_INT | MSF_INT | REG_TMOUT_INT | 18468c2ecf20Sopenharmony_ci REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT | 18478c2ecf20Sopenharmony_ci VBLANK_INT | FRAME_END_INT; 18488c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT | 18518c2ecf20Sopenharmony_ci FRAME_END_INT; 18528c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT; 18558c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); 18588c2ecf20Sopenharmony_ci } else { 18598c2ecf20Sopenharmony_ci value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 18608c2ecf20Sopenharmony_ci WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 18618c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 18648c2ecf20Sopenharmony_ci WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 18658c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci /* initialize timer */ 18688c2ecf20Sopenharmony_ci value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | 18698c2ecf20Sopenharmony_ci WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); 18708c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | 18738c2ecf20Sopenharmony_ci WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); 18748c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 18778c2ecf20Sopenharmony_ci WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 18788c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 18818c2ecf20Sopenharmony_ci WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 18828c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci if (dc->soc->supports_background_color) 18868c2ecf20Sopenharmony_ci tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR); 18878c2ecf20Sopenharmony_ci else 18888c2ecf20Sopenharmony_ci tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci /* apply PLL and pixel clock changes */ 18918c2ecf20Sopenharmony_ci tegra_dc_commit_state(dc, state); 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci /* program display mode */ 18948c2ecf20Sopenharmony_ci tegra_dc_set_timings(dc, mode); 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci /* interlacing isn't supported yet, so disable it */ 18978c2ecf20Sopenharmony_ci if (dc->soc->supports_interlacing) { 18988c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); 18998c2ecf20Sopenharmony_ci value &= ~INTERLACE_ENABLE; 19008c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); 19018c2ecf20Sopenharmony_ci } 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); 19048c2ecf20Sopenharmony_ci value &= ~DISP_CTRL_MODE_MASK; 19058c2ecf20Sopenharmony_ci value |= DISP_CTRL_MODE_C_DISPLAY; 19068c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci if (!dc->soc->has_nvdisplay) { 19098c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 19108c2ecf20Sopenharmony_ci value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 19118c2ecf20Sopenharmony_ci PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; 19128c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 19138c2ecf20Sopenharmony_ci } 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci /* enable underflow reporting and display red for missing pixels */ 19168c2ecf20Sopenharmony_ci if (dc->soc->has_nvdisplay) { 19178c2ecf20Sopenharmony_ci value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE; 19188c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW); 19198c2ecf20Sopenharmony_ci } 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci tegra_dc_commit(dc); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 19248c2ecf20Sopenharmony_ci} 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_cistatic void tegra_crtc_atomic_begin(struct drm_crtc *crtc, 19278c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 19288c2ecf20Sopenharmony_ci{ 19298c2ecf20Sopenharmony_ci unsigned long flags; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci if (crtc->state->event) { 19328c2ecf20Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci if (drm_crtc_vblank_get(crtc) != 0) 19358c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 19368c2ecf20Sopenharmony_ci else 19378c2ecf20Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, crtc->state->event); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci crtc->state->event = NULL; 19428c2ecf20Sopenharmony_ci } 19438c2ecf20Sopenharmony_ci} 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_cistatic void tegra_crtc_atomic_flush(struct drm_crtc *crtc, 19468c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 19478c2ecf20Sopenharmony_ci{ 19488c2ecf20Sopenharmony_ci struct tegra_dc_state *state = to_dc_state(crtc->state); 19498c2ecf20Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 19508c2ecf20Sopenharmony_ci u32 value; 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci value = state->planes << 8 | GENERAL_UPDATE; 19538c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 19548c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci value = state->planes | GENERAL_ACT_REQ; 19578c2ecf20Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 19588c2ecf20Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 19598c2ecf20Sopenharmony_ci} 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { 19628c2ecf20Sopenharmony_ci .atomic_begin = tegra_crtc_atomic_begin, 19638c2ecf20Sopenharmony_ci .atomic_flush = tegra_crtc_atomic_flush, 19648c2ecf20Sopenharmony_ci .atomic_enable = tegra_crtc_atomic_enable, 19658c2ecf20Sopenharmony_ci .atomic_disable = tegra_crtc_atomic_disable, 19668c2ecf20Sopenharmony_ci}; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_cistatic irqreturn_t tegra_dc_irq(int irq, void *data) 19698c2ecf20Sopenharmony_ci{ 19708c2ecf20Sopenharmony_ci struct tegra_dc *dc = data; 19718c2ecf20Sopenharmony_ci unsigned long status; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); 19748c2ecf20Sopenharmony_ci tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci if (status & FRAME_END_INT) { 19778c2ecf20Sopenharmony_ci /* 19788c2ecf20Sopenharmony_ci dev_dbg(dc->dev, "%s(): frame end\n", __func__); 19798c2ecf20Sopenharmony_ci */ 19808c2ecf20Sopenharmony_ci dc->stats.frames++; 19818c2ecf20Sopenharmony_ci } 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci if (status & VBLANK_INT) { 19848c2ecf20Sopenharmony_ci /* 19858c2ecf20Sopenharmony_ci dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); 19868c2ecf20Sopenharmony_ci */ 19878c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(&dc->base); 19888c2ecf20Sopenharmony_ci dc->stats.vblank++; 19898c2ecf20Sopenharmony_ci } 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { 19928c2ecf20Sopenharmony_ci /* 19938c2ecf20Sopenharmony_ci dev_dbg(dc->dev, "%s(): underflow\n", __func__); 19948c2ecf20Sopenharmony_ci */ 19958c2ecf20Sopenharmony_ci dc->stats.underflow++; 19968c2ecf20Sopenharmony_ci } 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) { 19998c2ecf20Sopenharmony_ci /* 20008c2ecf20Sopenharmony_ci dev_dbg(dc->dev, "%s(): overflow\n", __func__); 20018c2ecf20Sopenharmony_ci */ 20028c2ecf20Sopenharmony_ci dc->stats.overflow++; 20038c2ecf20Sopenharmony_ci } 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci if (status & HEAD_UF_INT) { 20068c2ecf20Sopenharmony_ci dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__); 20078c2ecf20Sopenharmony_ci dc->stats.underflow++; 20088c2ecf20Sopenharmony_ci } 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci return IRQ_HANDLED; 20118c2ecf20Sopenharmony_ci} 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_cistatic bool tegra_dc_has_window_groups(struct tegra_dc *dc) 20148c2ecf20Sopenharmony_ci{ 20158c2ecf20Sopenharmony_ci unsigned int i; 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci if (!dc->soc->wgrps) 20188c2ecf20Sopenharmony_ci return true; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci for (i = 0; i < dc->soc->num_wgrps; i++) { 20218c2ecf20Sopenharmony_ci const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci if (wgrp->dc == dc->pipe && wgrp->num_windows > 0) 20248c2ecf20Sopenharmony_ci return true; 20258c2ecf20Sopenharmony_ci } 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci return false; 20288c2ecf20Sopenharmony_ci} 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_cistatic int tegra_dc_init(struct host1x_client *client) 20318c2ecf20Sopenharmony_ci{ 20328c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(client->host); 20338c2ecf20Sopenharmony_ci unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; 20348c2ecf20Sopenharmony_ci struct tegra_dc *dc = host1x_client_to_dc(client); 20358c2ecf20Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 20368c2ecf20Sopenharmony_ci struct drm_plane *primary = NULL; 20378c2ecf20Sopenharmony_ci struct drm_plane *cursor = NULL; 20388c2ecf20Sopenharmony_ci int err; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci /* 20418c2ecf20Sopenharmony_ci * XXX do not register DCs with no window groups because we cannot 20428c2ecf20Sopenharmony_ci * assign a primary plane to them, which in turn will cause KMS to 20438c2ecf20Sopenharmony_ci * crash. 20448c2ecf20Sopenharmony_ci */ 20458c2ecf20Sopenharmony_ci if (!tegra_dc_has_window_groups(dc)) 20468c2ecf20Sopenharmony_ci return 0; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci /* 20498c2ecf20Sopenharmony_ci * Set the display hub as the host1x client parent for the display 20508c2ecf20Sopenharmony_ci * controller. This is needed for the runtime reference counting that 20518c2ecf20Sopenharmony_ci * ensures the display hub is always powered when any of the display 20528c2ecf20Sopenharmony_ci * controllers are. 20538c2ecf20Sopenharmony_ci */ 20548c2ecf20Sopenharmony_ci if (dc->soc->has_nvdisplay) 20558c2ecf20Sopenharmony_ci client->parent = &tegra->hub->client; 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci dc->syncpt = host1x_syncpt_request(client, flags); 20588c2ecf20Sopenharmony_ci if (!dc->syncpt) 20598c2ecf20Sopenharmony_ci dev_warn(dc->dev, "failed to allocate syncpoint\n"); 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci err = host1x_client_iommu_attach(client); 20628c2ecf20Sopenharmony_ci if (err < 0 && err != -ENODEV) { 20638c2ecf20Sopenharmony_ci dev_err(client->dev, "failed to attach to domain: %d\n", err); 20648c2ecf20Sopenharmony_ci return err; 20658c2ecf20Sopenharmony_ci } 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci if (dc->soc->wgrps) 20688c2ecf20Sopenharmony_ci primary = tegra_dc_add_shared_planes(drm, dc); 20698c2ecf20Sopenharmony_ci else 20708c2ecf20Sopenharmony_ci primary = tegra_dc_add_planes(drm, dc); 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci if (IS_ERR(primary)) { 20738c2ecf20Sopenharmony_ci err = PTR_ERR(primary); 20748c2ecf20Sopenharmony_ci goto cleanup; 20758c2ecf20Sopenharmony_ci } 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci if (dc->soc->supports_cursor) { 20788c2ecf20Sopenharmony_ci cursor = tegra_dc_cursor_plane_create(drm, dc); 20798c2ecf20Sopenharmony_ci if (IS_ERR(cursor)) { 20808c2ecf20Sopenharmony_ci err = PTR_ERR(cursor); 20818c2ecf20Sopenharmony_ci goto cleanup; 20828c2ecf20Sopenharmony_ci } 20838c2ecf20Sopenharmony_ci } else { 20848c2ecf20Sopenharmony_ci /* dedicate one overlay to mouse cursor */ 20858c2ecf20Sopenharmony_ci cursor = tegra_dc_overlay_plane_create(drm, dc, 2, true); 20868c2ecf20Sopenharmony_ci if (IS_ERR(cursor)) { 20878c2ecf20Sopenharmony_ci err = PTR_ERR(cursor); 20888c2ecf20Sopenharmony_ci goto cleanup; 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci } 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor, 20938c2ecf20Sopenharmony_ci &tegra_crtc_funcs, NULL); 20948c2ecf20Sopenharmony_ci if (err < 0) 20958c2ecf20Sopenharmony_ci goto cleanup; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci /* 21008c2ecf20Sopenharmony_ci * Keep track of the minimum pitch alignment across all display 21018c2ecf20Sopenharmony_ci * controllers. 21028c2ecf20Sopenharmony_ci */ 21038c2ecf20Sopenharmony_ci if (dc->soc->pitch_align > tegra->pitch_align) 21048c2ecf20Sopenharmony_ci tegra->pitch_align = dc->soc->pitch_align; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci err = tegra_dc_rgb_init(drm, dc); 21078c2ecf20Sopenharmony_ci if (err < 0 && err != -ENODEV) { 21088c2ecf20Sopenharmony_ci dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); 21098c2ecf20Sopenharmony_ci goto cleanup; 21108c2ecf20Sopenharmony_ci } 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0, 21138c2ecf20Sopenharmony_ci dev_name(dc->dev), dc); 21148c2ecf20Sopenharmony_ci if (err < 0) { 21158c2ecf20Sopenharmony_ci dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, 21168c2ecf20Sopenharmony_ci err); 21178c2ecf20Sopenharmony_ci goto cleanup; 21188c2ecf20Sopenharmony_ci } 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci /* 21218c2ecf20Sopenharmony_ci * Inherit the DMA parameters (such as maximum segment size) from the 21228c2ecf20Sopenharmony_ci * parent host1x device. 21238c2ecf20Sopenharmony_ci */ 21248c2ecf20Sopenharmony_ci client->dev->dma_parms = client->host->dma_parms; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci return 0; 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_cicleanup: 21298c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(cursor)) 21308c2ecf20Sopenharmony_ci drm_plane_cleanup(cursor); 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci if (!IS_ERR(primary)) 21338c2ecf20Sopenharmony_ci drm_plane_cleanup(primary); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci host1x_client_iommu_detach(client); 21368c2ecf20Sopenharmony_ci host1x_syncpt_free(dc->syncpt); 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci return err; 21398c2ecf20Sopenharmony_ci} 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_cistatic int tegra_dc_exit(struct host1x_client *client) 21428c2ecf20Sopenharmony_ci{ 21438c2ecf20Sopenharmony_ci struct tegra_dc *dc = host1x_client_to_dc(client); 21448c2ecf20Sopenharmony_ci int err; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci if (!tegra_dc_has_window_groups(dc)) 21478c2ecf20Sopenharmony_ci return 0; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci /* avoid a dangling pointer just in case this disappears */ 21508c2ecf20Sopenharmony_ci client->dev->dma_parms = NULL; 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci devm_free_irq(dc->dev, dc->irq, dc); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci err = tegra_dc_rgb_exit(dc); 21558c2ecf20Sopenharmony_ci if (err) { 21568c2ecf20Sopenharmony_ci dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err); 21578c2ecf20Sopenharmony_ci return err; 21588c2ecf20Sopenharmony_ci } 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci host1x_client_iommu_detach(client); 21618c2ecf20Sopenharmony_ci host1x_syncpt_free(dc->syncpt); 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci return 0; 21648c2ecf20Sopenharmony_ci} 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_cistatic int tegra_dc_runtime_suspend(struct host1x_client *client) 21678c2ecf20Sopenharmony_ci{ 21688c2ecf20Sopenharmony_ci struct tegra_dc *dc = host1x_client_to_dc(client); 21698c2ecf20Sopenharmony_ci struct device *dev = client->dev; 21708c2ecf20Sopenharmony_ci int err; 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci err = reset_control_assert(dc->rst); 21738c2ecf20Sopenharmony_ci if (err < 0) { 21748c2ecf20Sopenharmony_ci dev_err(dev, "failed to assert reset: %d\n", err); 21758c2ecf20Sopenharmony_ci return err; 21768c2ecf20Sopenharmony_ci } 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci if (dc->soc->has_powergate) 21798c2ecf20Sopenharmony_ci tegra_powergate_power_off(dc->powergate); 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci clk_disable_unprepare(dc->clk); 21828c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci return 0; 21858c2ecf20Sopenharmony_ci} 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_cistatic int tegra_dc_runtime_resume(struct host1x_client *client) 21888c2ecf20Sopenharmony_ci{ 21898c2ecf20Sopenharmony_ci struct tegra_dc *dc = host1x_client_to_dc(client); 21908c2ecf20Sopenharmony_ci struct device *dev = client->dev; 21918c2ecf20Sopenharmony_ci int err; 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci err = pm_runtime_resume_and_get(dev); 21948c2ecf20Sopenharmony_ci if (err < 0) { 21958c2ecf20Sopenharmony_ci dev_err(dev, "failed to get runtime PM: %d\n", err); 21968c2ecf20Sopenharmony_ci return err; 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci if (dc->soc->has_powergate) { 22008c2ecf20Sopenharmony_ci err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, 22018c2ecf20Sopenharmony_ci dc->rst); 22028c2ecf20Sopenharmony_ci if (err < 0) { 22038c2ecf20Sopenharmony_ci dev_err(dev, "failed to power partition: %d\n", err); 22048c2ecf20Sopenharmony_ci goto put_rpm; 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci } else { 22078c2ecf20Sopenharmony_ci err = clk_prepare_enable(dc->clk); 22088c2ecf20Sopenharmony_ci if (err < 0) { 22098c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable clock: %d\n", err); 22108c2ecf20Sopenharmony_ci goto put_rpm; 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci err = reset_control_deassert(dc->rst); 22148c2ecf20Sopenharmony_ci if (err < 0) { 22158c2ecf20Sopenharmony_ci dev_err(dev, "failed to deassert reset: %d\n", err); 22168c2ecf20Sopenharmony_ci goto disable_clk; 22178c2ecf20Sopenharmony_ci } 22188c2ecf20Sopenharmony_ci } 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci return 0; 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_cidisable_clk: 22238c2ecf20Sopenharmony_ci clk_disable_unprepare(dc->clk); 22248c2ecf20Sopenharmony_ciput_rpm: 22258c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 22268c2ecf20Sopenharmony_ci return err; 22278c2ecf20Sopenharmony_ci} 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_cistatic const struct host1x_client_ops dc_client_ops = { 22308c2ecf20Sopenharmony_ci .init = tegra_dc_init, 22318c2ecf20Sopenharmony_ci .exit = tegra_dc_exit, 22328c2ecf20Sopenharmony_ci .suspend = tegra_dc_runtime_suspend, 22338c2ecf20Sopenharmony_ci .resume = tegra_dc_runtime_resume, 22348c2ecf20Sopenharmony_ci}; 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_cistatic const struct tegra_dc_soc_info tegra20_dc_soc_info = { 22378c2ecf20Sopenharmony_ci .supports_background_color = false, 22388c2ecf20Sopenharmony_ci .supports_interlacing = false, 22398c2ecf20Sopenharmony_ci .supports_cursor = false, 22408c2ecf20Sopenharmony_ci .supports_block_linear = false, 22418c2ecf20Sopenharmony_ci .has_legacy_blending = true, 22428c2ecf20Sopenharmony_ci .pitch_align = 8, 22438c2ecf20Sopenharmony_ci .has_powergate = false, 22448c2ecf20Sopenharmony_ci .coupled_pm = true, 22458c2ecf20Sopenharmony_ci .has_nvdisplay = false, 22468c2ecf20Sopenharmony_ci .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), 22478c2ecf20Sopenharmony_ci .primary_formats = tegra20_primary_formats, 22488c2ecf20Sopenharmony_ci .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), 22498c2ecf20Sopenharmony_ci .overlay_formats = tegra20_overlay_formats, 22508c2ecf20Sopenharmony_ci .modifiers = tegra20_modifiers, 22518c2ecf20Sopenharmony_ci .has_win_a_without_filters = true, 22528c2ecf20Sopenharmony_ci .has_win_c_without_vert_filter = true, 22538c2ecf20Sopenharmony_ci}; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_cistatic const struct tegra_dc_soc_info tegra30_dc_soc_info = { 22568c2ecf20Sopenharmony_ci .supports_background_color = false, 22578c2ecf20Sopenharmony_ci .supports_interlacing = false, 22588c2ecf20Sopenharmony_ci .supports_cursor = false, 22598c2ecf20Sopenharmony_ci .supports_block_linear = false, 22608c2ecf20Sopenharmony_ci .has_legacy_blending = true, 22618c2ecf20Sopenharmony_ci .pitch_align = 8, 22628c2ecf20Sopenharmony_ci .has_powergate = false, 22638c2ecf20Sopenharmony_ci .coupled_pm = false, 22648c2ecf20Sopenharmony_ci .has_nvdisplay = false, 22658c2ecf20Sopenharmony_ci .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), 22668c2ecf20Sopenharmony_ci .primary_formats = tegra20_primary_formats, 22678c2ecf20Sopenharmony_ci .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), 22688c2ecf20Sopenharmony_ci .overlay_formats = tegra20_overlay_formats, 22698c2ecf20Sopenharmony_ci .modifiers = tegra20_modifiers, 22708c2ecf20Sopenharmony_ci .has_win_a_without_filters = false, 22718c2ecf20Sopenharmony_ci .has_win_c_without_vert_filter = false, 22728c2ecf20Sopenharmony_ci}; 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_cistatic const struct tegra_dc_soc_info tegra114_dc_soc_info = { 22758c2ecf20Sopenharmony_ci .supports_background_color = false, 22768c2ecf20Sopenharmony_ci .supports_interlacing = false, 22778c2ecf20Sopenharmony_ci .supports_cursor = false, 22788c2ecf20Sopenharmony_ci .supports_block_linear = false, 22798c2ecf20Sopenharmony_ci .has_legacy_blending = true, 22808c2ecf20Sopenharmony_ci .pitch_align = 64, 22818c2ecf20Sopenharmony_ci .has_powergate = true, 22828c2ecf20Sopenharmony_ci .coupled_pm = false, 22838c2ecf20Sopenharmony_ci .has_nvdisplay = false, 22848c2ecf20Sopenharmony_ci .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), 22858c2ecf20Sopenharmony_ci .primary_formats = tegra114_primary_formats, 22868c2ecf20Sopenharmony_ci .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), 22878c2ecf20Sopenharmony_ci .overlay_formats = tegra114_overlay_formats, 22888c2ecf20Sopenharmony_ci .modifiers = tegra20_modifiers, 22898c2ecf20Sopenharmony_ci .has_win_a_without_filters = false, 22908c2ecf20Sopenharmony_ci .has_win_c_without_vert_filter = false, 22918c2ecf20Sopenharmony_ci}; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_cistatic const struct tegra_dc_soc_info tegra124_dc_soc_info = { 22948c2ecf20Sopenharmony_ci .supports_background_color = true, 22958c2ecf20Sopenharmony_ci .supports_interlacing = true, 22968c2ecf20Sopenharmony_ci .supports_cursor = true, 22978c2ecf20Sopenharmony_ci .supports_block_linear = true, 22988c2ecf20Sopenharmony_ci .has_legacy_blending = false, 22998c2ecf20Sopenharmony_ci .pitch_align = 64, 23008c2ecf20Sopenharmony_ci .has_powergate = true, 23018c2ecf20Sopenharmony_ci .coupled_pm = false, 23028c2ecf20Sopenharmony_ci .has_nvdisplay = false, 23038c2ecf20Sopenharmony_ci .num_primary_formats = ARRAY_SIZE(tegra124_primary_formats), 23048c2ecf20Sopenharmony_ci .primary_formats = tegra124_primary_formats, 23058c2ecf20Sopenharmony_ci .num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats), 23068c2ecf20Sopenharmony_ci .overlay_formats = tegra124_overlay_formats, 23078c2ecf20Sopenharmony_ci .modifiers = tegra124_modifiers, 23088c2ecf20Sopenharmony_ci .has_win_a_without_filters = false, 23098c2ecf20Sopenharmony_ci .has_win_c_without_vert_filter = false, 23108c2ecf20Sopenharmony_ci}; 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_cistatic const struct tegra_dc_soc_info tegra210_dc_soc_info = { 23138c2ecf20Sopenharmony_ci .supports_background_color = true, 23148c2ecf20Sopenharmony_ci .supports_interlacing = true, 23158c2ecf20Sopenharmony_ci .supports_cursor = true, 23168c2ecf20Sopenharmony_ci .supports_block_linear = true, 23178c2ecf20Sopenharmony_ci .has_legacy_blending = false, 23188c2ecf20Sopenharmony_ci .pitch_align = 64, 23198c2ecf20Sopenharmony_ci .has_powergate = true, 23208c2ecf20Sopenharmony_ci .coupled_pm = false, 23218c2ecf20Sopenharmony_ci .has_nvdisplay = false, 23228c2ecf20Sopenharmony_ci .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), 23238c2ecf20Sopenharmony_ci .primary_formats = tegra114_primary_formats, 23248c2ecf20Sopenharmony_ci .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), 23258c2ecf20Sopenharmony_ci .overlay_formats = tegra114_overlay_formats, 23268c2ecf20Sopenharmony_ci .modifiers = tegra124_modifiers, 23278c2ecf20Sopenharmony_ci .has_win_a_without_filters = false, 23288c2ecf20Sopenharmony_ci .has_win_c_without_vert_filter = false, 23298c2ecf20Sopenharmony_ci}; 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_cistatic const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { 23328c2ecf20Sopenharmony_ci { 23338c2ecf20Sopenharmony_ci .index = 0, 23348c2ecf20Sopenharmony_ci .dc = 0, 23358c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 0 }, 23368c2ecf20Sopenharmony_ci .num_windows = 1, 23378c2ecf20Sopenharmony_ci }, { 23388c2ecf20Sopenharmony_ci .index = 1, 23398c2ecf20Sopenharmony_ci .dc = 1, 23408c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 1 }, 23418c2ecf20Sopenharmony_ci .num_windows = 1, 23428c2ecf20Sopenharmony_ci }, { 23438c2ecf20Sopenharmony_ci .index = 2, 23448c2ecf20Sopenharmony_ci .dc = 1, 23458c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 2 }, 23468c2ecf20Sopenharmony_ci .num_windows = 1, 23478c2ecf20Sopenharmony_ci }, { 23488c2ecf20Sopenharmony_ci .index = 3, 23498c2ecf20Sopenharmony_ci .dc = 2, 23508c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 3 }, 23518c2ecf20Sopenharmony_ci .num_windows = 1, 23528c2ecf20Sopenharmony_ci }, { 23538c2ecf20Sopenharmony_ci .index = 4, 23548c2ecf20Sopenharmony_ci .dc = 2, 23558c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 4 }, 23568c2ecf20Sopenharmony_ci .num_windows = 1, 23578c2ecf20Sopenharmony_ci }, { 23588c2ecf20Sopenharmony_ci .index = 5, 23598c2ecf20Sopenharmony_ci .dc = 2, 23608c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 5 }, 23618c2ecf20Sopenharmony_ci .num_windows = 1, 23628c2ecf20Sopenharmony_ci }, 23638c2ecf20Sopenharmony_ci}; 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_cistatic const struct tegra_dc_soc_info tegra186_dc_soc_info = { 23668c2ecf20Sopenharmony_ci .supports_background_color = true, 23678c2ecf20Sopenharmony_ci .supports_interlacing = true, 23688c2ecf20Sopenharmony_ci .supports_cursor = true, 23698c2ecf20Sopenharmony_ci .supports_block_linear = true, 23708c2ecf20Sopenharmony_ci .has_legacy_blending = false, 23718c2ecf20Sopenharmony_ci .pitch_align = 64, 23728c2ecf20Sopenharmony_ci .has_powergate = false, 23738c2ecf20Sopenharmony_ci .coupled_pm = false, 23748c2ecf20Sopenharmony_ci .has_nvdisplay = true, 23758c2ecf20Sopenharmony_ci .wgrps = tegra186_dc_wgrps, 23768c2ecf20Sopenharmony_ci .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), 23778c2ecf20Sopenharmony_ci}; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_cistatic const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = { 23808c2ecf20Sopenharmony_ci { 23818c2ecf20Sopenharmony_ci .index = 0, 23828c2ecf20Sopenharmony_ci .dc = 0, 23838c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 0 }, 23848c2ecf20Sopenharmony_ci .num_windows = 1, 23858c2ecf20Sopenharmony_ci }, { 23868c2ecf20Sopenharmony_ci .index = 1, 23878c2ecf20Sopenharmony_ci .dc = 1, 23888c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 1 }, 23898c2ecf20Sopenharmony_ci .num_windows = 1, 23908c2ecf20Sopenharmony_ci }, { 23918c2ecf20Sopenharmony_ci .index = 2, 23928c2ecf20Sopenharmony_ci .dc = 1, 23938c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 2 }, 23948c2ecf20Sopenharmony_ci .num_windows = 1, 23958c2ecf20Sopenharmony_ci }, { 23968c2ecf20Sopenharmony_ci .index = 3, 23978c2ecf20Sopenharmony_ci .dc = 2, 23988c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 3 }, 23998c2ecf20Sopenharmony_ci .num_windows = 1, 24008c2ecf20Sopenharmony_ci }, { 24018c2ecf20Sopenharmony_ci .index = 4, 24028c2ecf20Sopenharmony_ci .dc = 2, 24038c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 4 }, 24048c2ecf20Sopenharmony_ci .num_windows = 1, 24058c2ecf20Sopenharmony_ci }, { 24068c2ecf20Sopenharmony_ci .index = 5, 24078c2ecf20Sopenharmony_ci .dc = 2, 24088c2ecf20Sopenharmony_ci .windows = (const unsigned int[]) { 5 }, 24098c2ecf20Sopenharmony_ci .num_windows = 1, 24108c2ecf20Sopenharmony_ci }, 24118c2ecf20Sopenharmony_ci}; 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_cistatic const struct tegra_dc_soc_info tegra194_dc_soc_info = { 24148c2ecf20Sopenharmony_ci .supports_background_color = true, 24158c2ecf20Sopenharmony_ci .supports_interlacing = true, 24168c2ecf20Sopenharmony_ci .supports_cursor = true, 24178c2ecf20Sopenharmony_ci .supports_block_linear = true, 24188c2ecf20Sopenharmony_ci .has_legacy_blending = false, 24198c2ecf20Sopenharmony_ci .pitch_align = 64, 24208c2ecf20Sopenharmony_ci .has_powergate = false, 24218c2ecf20Sopenharmony_ci .coupled_pm = false, 24228c2ecf20Sopenharmony_ci .has_nvdisplay = true, 24238c2ecf20Sopenharmony_ci .wgrps = tegra194_dc_wgrps, 24248c2ecf20Sopenharmony_ci .num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps), 24258c2ecf20Sopenharmony_ci}; 24268c2ecf20Sopenharmony_ci 24278c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_dc_of_match[] = { 24288c2ecf20Sopenharmony_ci { 24298c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra194-dc", 24308c2ecf20Sopenharmony_ci .data = &tegra194_dc_soc_info, 24318c2ecf20Sopenharmony_ci }, { 24328c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra186-dc", 24338c2ecf20Sopenharmony_ci .data = &tegra186_dc_soc_info, 24348c2ecf20Sopenharmony_ci }, { 24358c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra210-dc", 24368c2ecf20Sopenharmony_ci .data = &tegra210_dc_soc_info, 24378c2ecf20Sopenharmony_ci }, { 24388c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra124-dc", 24398c2ecf20Sopenharmony_ci .data = &tegra124_dc_soc_info, 24408c2ecf20Sopenharmony_ci }, { 24418c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra114-dc", 24428c2ecf20Sopenharmony_ci .data = &tegra114_dc_soc_info, 24438c2ecf20Sopenharmony_ci }, { 24448c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra30-dc", 24458c2ecf20Sopenharmony_ci .data = &tegra30_dc_soc_info, 24468c2ecf20Sopenharmony_ci }, { 24478c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra20-dc", 24488c2ecf20Sopenharmony_ci .data = &tegra20_dc_soc_info, 24498c2ecf20Sopenharmony_ci }, { 24508c2ecf20Sopenharmony_ci /* sentinel */ 24518c2ecf20Sopenharmony_ci } 24528c2ecf20Sopenharmony_ci}; 24538c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_dc_of_match); 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_cistatic int tegra_dc_parse_dt(struct tegra_dc *dc) 24568c2ecf20Sopenharmony_ci{ 24578c2ecf20Sopenharmony_ci struct device_node *np; 24588c2ecf20Sopenharmony_ci u32 value = 0; 24598c2ecf20Sopenharmony_ci int err; 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci err = of_property_read_u32(dc->dev->of_node, "nvidia,head", &value); 24628c2ecf20Sopenharmony_ci if (err < 0) { 24638c2ecf20Sopenharmony_ci dev_err(dc->dev, "missing \"nvidia,head\" property\n"); 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci /* 24668c2ecf20Sopenharmony_ci * If the nvidia,head property isn't present, try to find the 24678c2ecf20Sopenharmony_ci * correct head number by looking up the position of this 24688c2ecf20Sopenharmony_ci * display controller's node within the device tree. Assuming 24698c2ecf20Sopenharmony_ci * that the nodes are ordered properly in the DTS file and 24708c2ecf20Sopenharmony_ci * that the translation into a flattened device tree blob 24718c2ecf20Sopenharmony_ci * preserves that ordering this will actually yield the right 24728c2ecf20Sopenharmony_ci * head number. 24738c2ecf20Sopenharmony_ci * 24748c2ecf20Sopenharmony_ci * If those assumptions don't hold, this will still work for 24758c2ecf20Sopenharmony_ci * cases where only a single display controller is used. 24768c2ecf20Sopenharmony_ci */ 24778c2ecf20Sopenharmony_ci for_each_matching_node(np, tegra_dc_of_match) { 24788c2ecf20Sopenharmony_ci if (np == dc->dev->of_node) { 24798c2ecf20Sopenharmony_ci of_node_put(np); 24808c2ecf20Sopenharmony_ci break; 24818c2ecf20Sopenharmony_ci } 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci value++; 24848c2ecf20Sopenharmony_ci } 24858c2ecf20Sopenharmony_ci } 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci dc->pipe = value; 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci return 0; 24908c2ecf20Sopenharmony_ci} 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_cistatic int tegra_dc_match_by_pipe(struct device *dev, const void *data) 24938c2ecf20Sopenharmony_ci{ 24948c2ecf20Sopenharmony_ci struct tegra_dc *dc = dev_get_drvdata(dev); 24958c2ecf20Sopenharmony_ci unsigned int pipe = (unsigned long)(void *)data; 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_ci return dc->pipe == pipe; 24988c2ecf20Sopenharmony_ci} 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_cistatic int tegra_dc_couple(struct tegra_dc *dc) 25018c2ecf20Sopenharmony_ci{ 25028c2ecf20Sopenharmony_ci /* 25038c2ecf20Sopenharmony_ci * On Tegra20, DC1 requires DC0 to be taken out of reset in order to 25048c2ecf20Sopenharmony_ci * be enabled, otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND / 25058c2ecf20Sopenharmony_ci * POWER_CONTROL registers during CRTC enabling. 25068c2ecf20Sopenharmony_ci */ 25078c2ecf20Sopenharmony_ci if (dc->soc->coupled_pm && dc->pipe == 1) { 25088c2ecf20Sopenharmony_ci struct device *companion; 25098c2ecf20Sopenharmony_ci struct tegra_dc *parent; 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci companion = driver_find_device(dc->dev->driver, NULL, (const void *)0, 25128c2ecf20Sopenharmony_ci tegra_dc_match_by_pipe); 25138c2ecf20Sopenharmony_ci if (!companion) 25148c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_ci parent = dev_get_drvdata(companion); 25178c2ecf20Sopenharmony_ci dc->client.parent = &parent->client; 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion)); 25208c2ecf20Sopenharmony_ci } 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci return 0; 25238c2ecf20Sopenharmony_ci} 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_cistatic int tegra_dc_probe(struct platform_device *pdev) 25268c2ecf20Sopenharmony_ci{ 25278c2ecf20Sopenharmony_ci struct tegra_dc *dc; 25288c2ecf20Sopenharmony_ci int err; 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL); 25318c2ecf20Sopenharmony_ci if (!dc) 25328c2ecf20Sopenharmony_ci return -ENOMEM; 25338c2ecf20Sopenharmony_ci 25348c2ecf20Sopenharmony_ci dc->soc = of_device_get_match_data(&pdev->dev); 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dc->list); 25378c2ecf20Sopenharmony_ci dc->dev = &pdev->dev; 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci err = tegra_dc_parse_dt(dc); 25408c2ecf20Sopenharmony_ci if (err < 0) 25418c2ecf20Sopenharmony_ci return err; 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ci err = tegra_dc_couple(dc); 25448c2ecf20Sopenharmony_ci if (err < 0) 25458c2ecf20Sopenharmony_ci return err; 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci dc->clk = devm_clk_get(&pdev->dev, NULL); 25488c2ecf20Sopenharmony_ci if (IS_ERR(dc->clk)) { 25498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get clock\n"); 25508c2ecf20Sopenharmony_ci return PTR_ERR(dc->clk); 25518c2ecf20Sopenharmony_ci } 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci dc->rst = devm_reset_control_get(&pdev->dev, "dc"); 25548c2ecf20Sopenharmony_ci if (IS_ERR(dc->rst)) { 25558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get reset\n"); 25568c2ecf20Sopenharmony_ci return PTR_ERR(dc->rst); 25578c2ecf20Sopenharmony_ci } 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci /* assert reset and disable clock */ 25608c2ecf20Sopenharmony_ci err = clk_prepare_enable(dc->clk); 25618c2ecf20Sopenharmony_ci if (err < 0) 25628c2ecf20Sopenharmony_ci return err; 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci usleep_range(2000, 4000); 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci err = reset_control_assert(dc->rst); 25678c2ecf20Sopenharmony_ci if (err < 0) { 25688c2ecf20Sopenharmony_ci clk_disable_unprepare(dc->clk); 25698c2ecf20Sopenharmony_ci return err; 25708c2ecf20Sopenharmony_ci } 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci usleep_range(2000, 4000); 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci clk_disable_unprepare(dc->clk); 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci if (dc->soc->has_powergate) { 25778c2ecf20Sopenharmony_ci if (dc->pipe == 0) 25788c2ecf20Sopenharmony_ci dc->powergate = TEGRA_POWERGATE_DIS; 25798c2ecf20Sopenharmony_ci else 25808c2ecf20Sopenharmony_ci dc->powergate = TEGRA_POWERGATE_DISB; 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci tegra_powergate_power_off(dc->powergate); 25838c2ecf20Sopenharmony_ci } 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci dc->regs = devm_platform_ioremap_resource(pdev, 0); 25868c2ecf20Sopenharmony_ci if (IS_ERR(dc->regs)) 25878c2ecf20Sopenharmony_ci return PTR_ERR(dc->regs); 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci dc->irq = platform_get_irq(pdev, 0); 25908c2ecf20Sopenharmony_ci if (dc->irq < 0) 25918c2ecf20Sopenharmony_ci return -ENXIO; 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci err = tegra_dc_rgb_probe(dc); 25948c2ecf20Sopenharmony_ci if (err < 0 && err != -ENODEV) { 25958c2ecf20Sopenharmony_ci const char *level = KERN_ERR; 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci if (err == -EPROBE_DEFER) 25988c2ecf20Sopenharmony_ci level = KERN_DEBUG; 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci dev_printk(level, dc->dev, "failed to probe RGB output: %d\n", 26018c2ecf20Sopenharmony_ci err); 26028c2ecf20Sopenharmony_ci return err; 26038c2ecf20Sopenharmony_ci } 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dc); 26068c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dc->client.list); 26098c2ecf20Sopenharmony_ci dc->client.ops = &dc_client_ops; 26108c2ecf20Sopenharmony_ci dc->client.dev = &pdev->dev; 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci err = host1x_client_register(&dc->client); 26138c2ecf20Sopenharmony_ci if (err < 0) { 26148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register host1x client: %d\n", 26158c2ecf20Sopenharmony_ci err); 26168c2ecf20Sopenharmony_ci goto disable_pm; 26178c2ecf20Sopenharmony_ci } 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci return 0; 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_cidisable_pm: 26228c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 26238c2ecf20Sopenharmony_ci tegra_dc_rgb_remove(dc); 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci return err; 26268c2ecf20Sopenharmony_ci} 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_cistatic int tegra_dc_remove(struct platform_device *pdev) 26298c2ecf20Sopenharmony_ci{ 26308c2ecf20Sopenharmony_ci struct tegra_dc *dc = platform_get_drvdata(pdev); 26318c2ecf20Sopenharmony_ci int err; 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_ci err = host1x_client_unregister(&dc->client); 26348c2ecf20Sopenharmony_ci if (err < 0) { 26358c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", 26368c2ecf20Sopenharmony_ci err); 26378c2ecf20Sopenharmony_ci return err; 26388c2ecf20Sopenharmony_ci } 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci err = tegra_dc_rgb_remove(dc); 26418c2ecf20Sopenharmony_ci if (err < 0) { 26428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err); 26438c2ecf20Sopenharmony_ci return err; 26448c2ecf20Sopenharmony_ci } 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci return 0; 26498c2ecf20Sopenharmony_ci} 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_cistruct platform_driver tegra_dc_driver = { 26528c2ecf20Sopenharmony_ci .driver = { 26538c2ecf20Sopenharmony_ci .name = "tegra-dc", 26548c2ecf20Sopenharmony_ci .of_match_table = tegra_dc_of_match, 26558c2ecf20Sopenharmony_ci }, 26568c2ecf20Sopenharmony_ci .probe = tegra_dc_probe, 26578c2ecf20Sopenharmony_ci .remove = tegra_dc_remove, 26588c2ecf20Sopenharmony_ci}; 2659