162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Avionic Design GmbH 462306a36Sopenharmony_ci * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/debugfs.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/iommu.h> 1262306a36Sopenharmony_ci#include <linux/interconnect.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/pm_domain.h> 1762306a36Sopenharmony_ci#include <linux/pm_opp.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci#include <linux/reset.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <soc/tegra/common.h> 2262306a36Sopenharmony_ci#include <soc/tegra/pmc.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <drm/drm_atomic.h> 2562306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2662306a36Sopenharmony_ci#include <drm/drm_blend.h> 2762306a36Sopenharmony_ci#include <drm/drm_debugfs.h> 2862306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2962306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 3062306a36Sopenharmony_ci#include <drm/drm_vblank.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "dc.h" 3362306a36Sopenharmony_ci#include "drm.h" 3462306a36Sopenharmony_ci#include "gem.h" 3562306a36Sopenharmony_ci#include "hub.h" 3662306a36Sopenharmony_ci#include "plane.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, 3962306a36Sopenharmony_ci struct drm_crtc_state *state); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void tegra_dc_stats_reset(struct tegra_dc_stats *stats) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci stats->frames = 0; 4462306a36Sopenharmony_ci stats->vblank = 0; 4562306a36Sopenharmony_ci stats->underflow = 0; 4662306a36Sopenharmony_ci stats->overflow = 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Reads the active copy of a register. */ 5062306a36Sopenharmony_cistatic u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci u32 value; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); 5562306a36Sopenharmony_ci value = tegra_dc_readl(dc, offset); 5662306a36Sopenharmony_ci tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return value; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic inline unsigned int tegra_plane_offset(struct tegra_plane *plane, 6262306a36Sopenharmony_ci unsigned int offset) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci if (offset >= 0x500 && offset <= 0x638) { 6562306a36Sopenharmony_ci offset = 0x000 + (offset - 0x500); 6662306a36Sopenharmony_ci return plane->offset + offset; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (offset >= 0x700 && offset <= 0x719) { 7062306a36Sopenharmony_ci offset = 0x180 + (offset - 0x700); 7162306a36Sopenharmony_ci return plane->offset + offset; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (offset >= 0x800 && offset <= 0x839) { 7562306a36Sopenharmony_ci offset = 0x1c0 + (offset - 0x800); 7662306a36Sopenharmony_ci return plane->offset + offset; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return plane->offset + offset; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic inline u32 tegra_plane_readl(struct tegra_plane *plane, 8562306a36Sopenharmony_ci unsigned int offset) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic inline void tegra_plane_writel(struct tegra_plane *plane, u32 value, 9162306a36Sopenharmony_ci unsigned int offset) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cibool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct device_node *np = dc->dev->of_node; 9962306a36Sopenharmony_ci struct of_phandle_iterator it; 10062306a36Sopenharmony_ci int err; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) 10362306a36Sopenharmony_ci if (it.node == dev->of_node) 10462306a36Sopenharmony_ci return true; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return false; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* 11062306a36Sopenharmony_ci * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the 11162306a36Sopenharmony_ci * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy. 11262306a36Sopenharmony_ci * Latching happens mmediately if the display controller is in STOP mode or 11362306a36Sopenharmony_ci * on the next frame boundary otherwise. 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The 11662306a36Sopenharmony_ci * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits 11762306a36Sopenharmony_ci * are written. When the *_ACT_REQ bits are written, the ARM copy is latched 11862306a36Sopenharmony_ci * into the ACTIVE copy, either immediately if the display controller is in 11962306a36Sopenharmony_ci * STOP mode, or at the next frame boundary otherwise. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_civoid tegra_dc_commit(struct tegra_dc *dc) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); 12462306a36Sopenharmony_ci tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v, 12862306a36Sopenharmony_ci unsigned int bpp) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci fixed20_12 outf = dfixed_init(out); 13162306a36Sopenharmony_ci fixed20_12 inf = dfixed_init(in); 13262306a36Sopenharmony_ci u32 dda_inc; 13362306a36Sopenharmony_ci int max; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (v) 13662306a36Sopenharmony_ci max = 15; 13762306a36Sopenharmony_ci else { 13862306a36Sopenharmony_ci switch (bpp) { 13962306a36Sopenharmony_ci case 2: 14062306a36Sopenharmony_ci max = 8; 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci default: 14462306a36Sopenharmony_ci WARN_ON_ONCE(1); 14562306a36Sopenharmony_ci fallthrough; 14662306a36Sopenharmony_ci case 4: 14762306a36Sopenharmony_ci max = 4; 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1)); 15362306a36Sopenharmony_ci inf.full -= dfixed_const(1); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci dda_inc = dfixed_div(inf, outf); 15662306a36Sopenharmony_ci dda_inc = min_t(u32, dda_inc, dfixed_const(max)); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return dda_inc; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic inline u32 compute_initial_dda(unsigned int in) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci fixed20_12 inf = dfixed_init(in); 16462306a36Sopenharmony_ci return dfixed_frac(inf); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci u32 background[3] = { 17062306a36Sopenharmony_ci BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, 17162306a36Sopenharmony_ci BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, 17262306a36Sopenharmony_ci BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, 17362306a36Sopenharmony_ci }; 17462306a36Sopenharmony_ci u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) | 17562306a36Sopenharmony_ci BLEND_COLOR_KEY_NONE; 17662306a36Sopenharmony_ci u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); 17762306a36Sopenharmony_ci struct tegra_plane_state *state; 17862306a36Sopenharmony_ci u32 blending[2]; 17962306a36Sopenharmony_ci unsigned int i; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* disable blending for non-overlapping case */ 18262306a36Sopenharmony_ci tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); 18362306a36Sopenharmony_ci tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci state = to_tegra_plane_state(plane->base.state); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (state->opaque) { 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * Since custom fix-weight blending isn't utilized and weight 19062306a36Sopenharmony_ci * of top window is set to max, we can enforce dependent 19162306a36Sopenharmony_ci * blending which in this case results in transparent bottom 19262306a36Sopenharmony_ci * window if top window is opaque and if top window enables 19362306a36Sopenharmony_ci * alpha blending, then bottom window is getting alpha value 19462306a36Sopenharmony_ci * of 1 minus the sum of alpha components of the overlapping 19562306a36Sopenharmony_ci * plane. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci background[0] |= BLEND_CONTROL_DEPENDENT; 19862306a36Sopenharmony_ci background[1] |= BLEND_CONTROL_DEPENDENT; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* 20162306a36Sopenharmony_ci * The region where three windows overlap is the intersection 20262306a36Sopenharmony_ci * of the two regions where two windows overlap. It contributes 20362306a36Sopenharmony_ci * to the area if all of the windows on top of it have an alpha 20462306a36Sopenharmony_ci * component. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci switch (state->base.normalized_zpos) { 20762306a36Sopenharmony_ci case 0: 20862306a36Sopenharmony_ci if (state->blending[0].alpha && 20962306a36Sopenharmony_ci state->blending[1].alpha) 21062306a36Sopenharmony_ci background[2] |= BLEND_CONTROL_DEPENDENT; 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci case 1: 21462306a36Sopenharmony_ci background[2] |= BLEND_CONTROL_DEPENDENT; 21562306a36Sopenharmony_ci break; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci } else { 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * Enable alpha blending if pixel format has an alpha 22062306a36Sopenharmony_ci * component. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci foreground |= BLEND_CONTROL_ALPHA; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * If any of the windows on top of this window is opaque, it 22662306a36Sopenharmony_ci * will completely conceal this window within that area. If 22762306a36Sopenharmony_ci * top window has an alpha component, it is blended over the 22862306a36Sopenharmony_ci * bottom window. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 23162306a36Sopenharmony_ci if (state->blending[i].alpha && 23262306a36Sopenharmony_ci state->blending[i].top) 23362306a36Sopenharmony_ci background[i] |= BLEND_CONTROL_DEPENDENT; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci switch (state->base.normalized_zpos) { 23762306a36Sopenharmony_ci case 0: 23862306a36Sopenharmony_ci if (state->blending[0].alpha && 23962306a36Sopenharmony_ci state->blending[1].alpha) 24062306a36Sopenharmony_ci background[2] |= BLEND_CONTROL_DEPENDENT; 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci case 1: 24462306a36Sopenharmony_ci /* 24562306a36Sopenharmony_ci * When both middle and topmost windows have an alpha, 24662306a36Sopenharmony_ci * these windows a mixed together and then the result 24762306a36Sopenharmony_ci * is blended over the bottom window. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci if (state->blending[0].alpha && 25062306a36Sopenharmony_ci state->blending[0].top) 25162306a36Sopenharmony_ci background[2] |= BLEND_CONTROL_ALPHA; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (state->blending[1].alpha && 25462306a36Sopenharmony_ci state->blending[1].top) 25562306a36Sopenharmony_ci background[2] |= BLEND_CONTROL_ALPHA; 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci switch (state->base.normalized_zpos) { 26162306a36Sopenharmony_ci case 0: 26262306a36Sopenharmony_ci tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X); 26362306a36Sopenharmony_ci tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); 26462306a36Sopenharmony_ci tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci case 1: 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * If window B / C is topmost, then X / Y registers are 27062306a36Sopenharmony_ci * matching the order of blending[...] state indices, 27162306a36Sopenharmony_ci * otherwise a swap is required. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci if (!state->blending[0].top && state->blending[1].top) { 27462306a36Sopenharmony_ci blending[0] = foreground; 27562306a36Sopenharmony_ci blending[1] = background[1]; 27662306a36Sopenharmony_ci } else { 27762306a36Sopenharmony_ci blending[0] = background[0]; 27862306a36Sopenharmony_ci blending[1] = foreground; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X); 28262306a36Sopenharmony_ci tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y); 28362306a36Sopenharmony_ci tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci case 2: 28762306a36Sopenharmony_ci tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); 28862306a36Sopenharmony_ci tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y); 28962306a36Sopenharmony_ci tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY); 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void tegra_plane_setup_blending(struct tegra_plane *plane, 29562306a36Sopenharmony_ci const struct tegra_dc_window *window) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci u32 value; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | 30062306a36Sopenharmony_ci BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | 30162306a36Sopenharmony_ci BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; 30262306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | 30562306a36Sopenharmony_ci BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | 30662306a36Sopenharmony_ci BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; 30762306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos); 31062306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic bool 31462306a36Sopenharmony_citegra_plane_use_horizontal_filtering(struct tegra_plane *plane, 31562306a36Sopenharmony_ci const struct tegra_dc_window *window) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct tegra_dc *dc = plane->dc; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (window->src.w == window->dst.w) 32062306a36Sopenharmony_ci return false; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (plane->index == 0 && dc->soc->has_win_a_without_filters) 32362306a36Sopenharmony_ci return false; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return true; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic bool 32962306a36Sopenharmony_citegra_plane_use_vertical_filtering(struct tegra_plane *plane, 33062306a36Sopenharmony_ci const struct tegra_dc_window *window) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct tegra_dc *dc = plane->dc; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (window->src.h == window->dst.h) 33562306a36Sopenharmony_ci return false; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (plane->index == 0 && dc->soc->has_win_a_without_filters) 33862306a36Sopenharmony_ci return false; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter) 34162306a36Sopenharmony_ci return false; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return true; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic void tegra_dc_setup_window(struct tegra_plane *plane, 34762306a36Sopenharmony_ci const struct tegra_dc_window *window) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; 35062306a36Sopenharmony_ci struct tegra_dc *dc = plane->dc; 35162306a36Sopenharmony_ci unsigned int planes; 35262306a36Sopenharmony_ci u32 value; 35362306a36Sopenharmony_ci bool yuv; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* 35662306a36Sopenharmony_ci * For YUV planar modes, the number of bytes per pixel takes into 35762306a36Sopenharmony_ci * account only the luma component and therefore is 1. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci yuv = tegra_plane_format_is_yuv(window->format, &planes, NULL); 36062306a36Sopenharmony_ci if (!yuv) 36162306a36Sopenharmony_ci bpp = window->bits_per_pixel / 8; 36262306a36Sopenharmony_ci else 36362306a36Sopenharmony_ci bpp = (planes > 1) ? 1 : 2; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH); 36662306a36Sopenharmony_ci tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x); 36962306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_POSITION); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w); 37262306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_SIZE); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci h_offset = window->src.x * bpp; 37562306a36Sopenharmony_ci v_offset = window->src.y; 37662306a36Sopenharmony_ci h_size = window->src.w * bpp; 37762306a36Sopenharmony_ci v_size = window->src.h; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (window->reflect_x) 38062306a36Sopenharmony_ci h_offset += (window->src.w - 1) * bpp; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (window->reflect_y) 38362306a36Sopenharmony_ci v_offset += window->src.h - 1; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); 38662306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* 38962306a36Sopenharmony_ci * For DDA computations the number of bytes per pixel for YUV planar 39062306a36Sopenharmony_ci * modes needs to take into account all Y, U and V components. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci if (yuv && planes > 1) 39362306a36Sopenharmony_ci bpp = 2; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp); 39662306a36Sopenharmony_ci v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); 39962306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_DDA_INC); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci h_dda = compute_initial_dda(window->src.x); 40262306a36Sopenharmony_ci v_dda = compute_initial_dda(window->src.y); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA); 40562306a36Sopenharmony_ci tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE); 40862306a36Sopenharmony_ci tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (yuv && planes > 1) { 41362306a36Sopenharmony_ci tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (planes > 2) 41662306a36Sopenharmony_ci tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci value = window->stride[1] << 16 | window->stride[0]; 41962306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE); 42062306a36Sopenharmony_ci } else { 42162306a36Sopenharmony_ci tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET); 42562306a36Sopenharmony_ci tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (dc->soc->supports_block_linear) { 42862306a36Sopenharmony_ci unsigned long height = window->tiling.value; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci switch (window->tiling.mode) { 43162306a36Sopenharmony_ci case TEGRA_BO_TILING_MODE_PITCH: 43262306a36Sopenharmony_ci value = DC_WINBUF_SURFACE_KIND_PITCH; 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci case TEGRA_BO_TILING_MODE_TILED: 43662306a36Sopenharmony_ci value = DC_WINBUF_SURFACE_KIND_TILED; 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci case TEGRA_BO_TILING_MODE_BLOCK: 44062306a36Sopenharmony_ci value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) | 44162306a36Sopenharmony_ci DC_WINBUF_SURFACE_KIND_BLOCK; 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND); 44662306a36Sopenharmony_ci } else { 44762306a36Sopenharmony_ci switch (window->tiling.mode) { 44862306a36Sopenharmony_ci case TEGRA_BO_TILING_MODE_PITCH: 44962306a36Sopenharmony_ci value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV | 45062306a36Sopenharmony_ci DC_WIN_BUFFER_ADDR_MODE_LINEAR; 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci case TEGRA_BO_TILING_MODE_TILED: 45462306a36Sopenharmony_ci value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | 45562306a36Sopenharmony_ci DC_WIN_BUFFER_ADDR_MODE_TILE; 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci case TEGRA_BO_TILING_MODE_BLOCK: 45962306a36Sopenharmony_ci /* 46062306a36Sopenharmony_ci * No need to handle this here because ->atomic_check 46162306a36Sopenharmony_ci * will already have filtered it out. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci value = WIN_ENABLE; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (yuv) { 47262306a36Sopenharmony_ci /* setup default colorspace conversion coefficients */ 47362306a36Sopenharmony_ci tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF); 47462306a36Sopenharmony_ci tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB); 47562306a36Sopenharmony_ci tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR); 47662306a36Sopenharmony_ci tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR); 47762306a36Sopenharmony_ci tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG); 47862306a36Sopenharmony_ci tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG); 47962306a36Sopenharmony_ci tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB); 48062306a36Sopenharmony_ci tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci value |= CSC_ENABLE; 48362306a36Sopenharmony_ci } else if (window->bits_per_pixel < 24) { 48462306a36Sopenharmony_ci value |= COLOR_EXPAND; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (window->reflect_x) 48862306a36Sopenharmony_ci value |= H_DIRECTION; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (window->reflect_y) 49162306a36Sopenharmony_ci value |= V_DIRECTION; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (tegra_plane_use_horizontal_filtering(plane, window)) { 49462306a36Sopenharmony_ci /* 49562306a36Sopenharmony_ci * Enable horizontal 6-tap filter and set filtering 49662306a36Sopenharmony_ci * coefficients to the default values defined in TRM. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0)); 49962306a36Sopenharmony_ci tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1)); 50062306a36Sopenharmony_ci tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2)); 50162306a36Sopenharmony_ci tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3)); 50262306a36Sopenharmony_ci tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4)); 50362306a36Sopenharmony_ci tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5)); 50462306a36Sopenharmony_ci tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6)); 50562306a36Sopenharmony_ci tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7)); 50662306a36Sopenharmony_ci tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8)); 50762306a36Sopenharmony_ci tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9)); 50862306a36Sopenharmony_ci tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10)); 50962306a36Sopenharmony_ci tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11)); 51062306a36Sopenharmony_ci tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12)); 51162306a36Sopenharmony_ci tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13)); 51262306a36Sopenharmony_ci tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14)); 51362306a36Sopenharmony_ci tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15)); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci value |= H_FILTER; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (tegra_plane_use_vertical_filtering(plane, window)) { 51962306a36Sopenharmony_ci unsigned int i, k; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * Enable vertical 2-tap filter and set filtering 52362306a36Sopenharmony_ci * coefficients to the default values defined in TRM. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci for (i = 0, k = 128; i < 16; i++, k -= 8) 52662306a36Sopenharmony_ci tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i)); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci value |= V_FILTER; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (dc->soc->has_legacy_blending) 53462306a36Sopenharmony_ci tegra_plane_setup_blending_legacy(plane); 53562306a36Sopenharmony_ci else 53662306a36Sopenharmony_ci tegra_plane_setup_blending(plane, window); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic const u32 tegra20_primary_formats[] = { 54062306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, 54162306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, 54262306a36Sopenharmony_ci DRM_FORMAT_RGB565, 54362306a36Sopenharmony_ci DRM_FORMAT_RGBA5551, 54462306a36Sopenharmony_ci DRM_FORMAT_ABGR8888, 54562306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 54662306a36Sopenharmony_ci /* non-native formats */ 54762306a36Sopenharmony_ci DRM_FORMAT_XRGB1555, 54862306a36Sopenharmony_ci DRM_FORMAT_RGBX5551, 54962306a36Sopenharmony_ci DRM_FORMAT_XBGR8888, 55062306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 55162306a36Sopenharmony_ci}; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic const u64 tegra20_modifiers[] = { 55462306a36Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 55562306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED, 55662306a36Sopenharmony_ci DRM_FORMAT_MOD_INVALID 55762306a36Sopenharmony_ci}; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic const u32 tegra114_primary_formats[] = { 56062306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, 56162306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, 56262306a36Sopenharmony_ci DRM_FORMAT_RGB565, 56362306a36Sopenharmony_ci DRM_FORMAT_RGBA5551, 56462306a36Sopenharmony_ci DRM_FORMAT_ABGR8888, 56562306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 56662306a36Sopenharmony_ci /* new on Tegra114 */ 56762306a36Sopenharmony_ci DRM_FORMAT_ABGR4444, 56862306a36Sopenharmony_ci DRM_FORMAT_ABGR1555, 56962306a36Sopenharmony_ci DRM_FORMAT_BGRA5551, 57062306a36Sopenharmony_ci DRM_FORMAT_XRGB1555, 57162306a36Sopenharmony_ci DRM_FORMAT_RGBX5551, 57262306a36Sopenharmony_ci DRM_FORMAT_XBGR1555, 57362306a36Sopenharmony_ci DRM_FORMAT_BGRX5551, 57462306a36Sopenharmony_ci DRM_FORMAT_BGR565, 57562306a36Sopenharmony_ci DRM_FORMAT_BGRA8888, 57662306a36Sopenharmony_ci DRM_FORMAT_RGBA8888, 57762306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 57862306a36Sopenharmony_ci DRM_FORMAT_XBGR8888, 57962306a36Sopenharmony_ci}; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic const u32 tegra124_primary_formats[] = { 58262306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, 58362306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, 58462306a36Sopenharmony_ci DRM_FORMAT_RGB565, 58562306a36Sopenharmony_ci DRM_FORMAT_RGBA5551, 58662306a36Sopenharmony_ci DRM_FORMAT_ABGR8888, 58762306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 58862306a36Sopenharmony_ci /* new on Tegra114 */ 58962306a36Sopenharmony_ci DRM_FORMAT_ABGR4444, 59062306a36Sopenharmony_ci DRM_FORMAT_ABGR1555, 59162306a36Sopenharmony_ci DRM_FORMAT_BGRA5551, 59262306a36Sopenharmony_ci DRM_FORMAT_XRGB1555, 59362306a36Sopenharmony_ci DRM_FORMAT_RGBX5551, 59462306a36Sopenharmony_ci DRM_FORMAT_XBGR1555, 59562306a36Sopenharmony_ci DRM_FORMAT_BGRX5551, 59662306a36Sopenharmony_ci DRM_FORMAT_BGR565, 59762306a36Sopenharmony_ci DRM_FORMAT_BGRA8888, 59862306a36Sopenharmony_ci DRM_FORMAT_RGBA8888, 59962306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 60062306a36Sopenharmony_ci DRM_FORMAT_XBGR8888, 60162306a36Sopenharmony_ci /* new on Tegra124 */ 60262306a36Sopenharmony_ci DRM_FORMAT_RGBX8888, 60362306a36Sopenharmony_ci DRM_FORMAT_BGRX8888, 60462306a36Sopenharmony_ci}; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic const u64 tegra124_modifiers[] = { 60762306a36Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 60862306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0), 60962306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1), 61062306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2), 61162306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3), 61262306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4), 61362306a36Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5), 61462306a36Sopenharmony_ci DRM_FORMAT_MOD_INVALID 61562306a36Sopenharmony_ci}; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic int tegra_plane_atomic_check(struct drm_plane *plane, 61862306a36Sopenharmony_ci struct drm_atomic_state *state) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 62162306a36Sopenharmony_ci plane); 62262306a36Sopenharmony_ci struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state); 62362306a36Sopenharmony_ci unsigned int supported_rotation = DRM_MODE_ROTATE_0 | 62462306a36Sopenharmony_ci DRM_MODE_REFLECT_X | 62562306a36Sopenharmony_ci DRM_MODE_REFLECT_Y; 62662306a36Sopenharmony_ci unsigned int rotation = new_plane_state->rotation; 62762306a36Sopenharmony_ci struct tegra_bo_tiling *tiling = &plane_state->tiling; 62862306a36Sopenharmony_ci struct tegra_plane *tegra = to_tegra_plane(plane); 62962306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc); 63062306a36Sopenharmony_ci int err; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci plane_state->peak_memory_bandwidth = 0; 63362306a36Sopenharmony_ci plane_state->avg_memory_bandwidth = 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* no need for further checks if the plane is being disabled */ 63662306a36Sopenharmony_ci if (!new_plane_state->crtc) { 63762306a36Sopenharmony_ci plane_state->total_peak_memory_bandwidth = 0; 63862306a36Sopenharmony_ci return 0; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci err = tegra_plane_format(new_plane_state->fb->format->format, 64262306a36Sopenharmony_ci &plane_state->format, 64362306a36Sopenharmony_ci &plane_state->swap); 64462306a36Sopenharmony_ci if (err < 0) 64562306a36Sopenharmony_ci return err; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * Tegra20 and Tegra30 are special cases here because they support 64962306a36Sopenharmony_ci * only variants of specific formats with an alpha component, but not 65062306a36Sopenharmony_ci * the corresponding opaque formats. However, the opaque formats can 65162306a36Sopenharmony_ci * be emulated by disabling alpha blending for the plane. 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_ci if (dc->soc->has_legacy_blending) { 65462306a36Sopenharmony_ci err = tegra_plane_setup_legacy_state(tegra, plane_state); 65562306a36Sopenharmony_ci if (err < 0) 65662306a36Sopenharmony_ci return err; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci err = tegra_fb_get_tiling(new_plane_state->fb, tiling); 66062306a36Sopenharmony_ci if (err < 0) 66162306a36Sopenharmony_ci return err; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK && 66462306a36Sopenharmony_ci !dc->soc->supports_block_linear) { 66562306a36Sopenharmony_ci DRM_ERROR("hardware doesn't support block linear mode\n"); 66662306a36Sopenharmony_ci return -EINVAL; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* 67062306a36Sopenharmony_ci * Older userspace used custom BO flag in order to specify the Y 67162306a36Sopenharmony_ci * reflection, while modern userspace uses the generic DRM rotation 67262306a36Sopenharmony_ci * property in order to achieve the same result. The legacy BO flag 67362306a36Sopenharmony_ci * duplicates the DRM rotation property when both are set. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci if (tegra_fb_is_bottom_up(new_plane_state->fb)) 67662306a36Sopenharmony_ci rotation |= DRM_MODE_REFLECT_Y; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci rotation = drm_rotation_simplify(rotation, supported_rotation); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (rotation & DRM_MODE_REFLECT_X) 68162306a36Sopenharmony_ci plane_state->reflect_x = true; 68262306a36Sopenharmony_ci else 68362306a36Sopenharmony_ci plane_state->reflect_x = false; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (rotation & DRM_MODE_REFLECT_Y) 68662306a36Sopenharmony_ci plane_state->reflect_y = true; 68762306a36Sopenharmony_ci else 68862306a36Sopenharmony_ci plane_state->reflect_y = false; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* 69162306a36Sopenharmony_ci * Tegra doesn't support different strides for U and V planes so we 69262306a36Sopenharmony_ci * error out if the user tries to display a framebuffer with such a 69362306a36Sopenharmony_ci * configuration. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_ci if (new_plane_state->fb->format->num_planes > 2) { 69662306a36Sopenharmony_ci if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) { 69762306a36Sopenharmony_ci DRM_ERROR("unsupported UV-plane configuration\n"); 69862306a36Sopenharmony_ci return -EINVAL; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci err = tegra_plane_state_add(tegra, new_plane_state); 70362306a36Sopenharmony_ci if (err < 0) 70462306a36Sopenharmony_ci return err; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return 0; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic void tegra_plane_atomic_disable(struct drm_plane *plane, 71062306a36Sopenharmony_ci struct drm_atomic_state *state) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 71362306a36Sopenharmony_ci plane); 71462306a36Sopenharmony_ci struct tegra_plane *p = to_tegra_plane(plane); 71562306a36Sopenharmony_ci u32 value; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* rien ne va plus */ 71862306a36Sopenharmony_ci if (!old_state || !old_state->crtc) 71962306a36Sopenharmony_ci return; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS); 72262306a36Sopenharmony_ci value &= ~WIN_ENABLE; 72362306a36Sopenharmony_ci tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic void tegra_plane_atomic_update(struct drm_plane *plane, 72762306a36Sopenharmony_ci struct drm_atomic_state *state) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 73062306a36Sopenharmony_ci plane); 73162306a36Sopenharmony_ci struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state); 73262306a36Sopenharmony_ci struct drm_framebuffer *fb = new_state->fb; 73362306a36Sopenharmony_ci struct tegra_plane *p = to_tegra_plane(plane); 73462306a36Sopenharmony_ci struct tegra_dc_window window; 73562306a36Sopenharmony_ci unsigned int i; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* rien ne va plus */ 73862306a36Sopenharmony_ci if (!new_state->crtc || !new_state->fb) 73962306a36Sopenharmony_ci return; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (!new_state->visible) 74262306a36Sopenharmony_ci return tegra_plane_atomic_disable(plane, state); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci memset(&window, 0, sizeof(window)); 74562306a36Sopenharmony_ci window.src.x = new_state->src.x1 >> 16; 74662306a36Sopenharmony_ci window.src.y = new_state->src.y1 >> 16; 74762306a36Sopenharmony_ci window.src.w = drm_rect_width(&new_state->src) >> 16; 74862306a36Sopenharmony_ci window.src.h = drm_rect_height(&new_state->src) >> 16; 74962306a36Sopenharmony_ci window.dst.x = new_state->dst.x1; 75062306a36Sopenharmony_ci window.dst.y = new_state->dst.y1; 75162306a36Sopenharmony_ci window.dst.w = drm_rect_width(&new_state->dst); 75262306a36Sopenharmony_ci window.dst.h = drm_rect_height(&new_state->dst); 75362306a36Sopenharmony_ci window.bits_per_pixel = fb->format->cpp[0] * 8; 75462306a36Sopenharmony_ci window.reflect_x = tegra_plane_state->reflect_x; 75562306a36Sopenharmony_ci window.reflect_y = tegra_plane_state->reflect_y; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* copy from state */ 75862306a36Sopenharmony_ci window.zpos = new_state->normalized_zpos; 75962306a36Sopenharmony_ci window.tiling = tegra_plane_state->tiling; 76062306a36Sopenharmony_ci window.format = tegra_plane_state->format; 76162306a36Sopenharmony_ci window.swap = tegra_plane_state->swap; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci for (i = 0; i < fb->format->num_planes; i++) { 76462306a36Sopenharmony_ci window.base[i] = tegra_plane_state->iova[i] + fb->offsets[i]; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* 76762306a36Sopenharmony_ci * Tegra uses a shared stride for UV planes. Framebuffers are 76862306a36Sopenharmony_ci * already checked for this in the tegra_plane_atomic_check() 76962306a36Sopenharmony_ci * function, so it's safe to ignore the V-plane pitch here. 77062306a36Sopenharmony_ci */ 77162306a36Sopenharmony_ci if (i < 2) 77262306a36Sopenharmony_ci window.stride[i] = fb->pitches[i]; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci tegra_dc_setup_window(p, &window); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { 77962306a36Sopenharmony_ci .prepare_fb = tegra_plane_prepare_fb, 78062306a36Sopenharmony_ci .cleanup_fb = tegra_plane_cleanup_fb, 78162306a36Sopenharmony_ci .atomic_check = tegra_plane_atomic_check, 78262306a36Sopenharmony_ci .atomic_disable = tegra_plane_atomic_disable, 78362306a36Sopenharmony_ci .atomic_update = tegra_plane_atomic_update, 78462306a36Sopenharmony_ci}; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci /* 78962306a36Sopenharmony_ci * Ideally this would use drm_crtc_mask(), but that would require the 79062306a36Sopenharmony_ci * CRTC to already be in the mode_config's list of CRTCs. However, it 79162306a36Sopenharmony_ci * will only be added to that list in the drm_crtc_init_with_planes() 79262306a36Sopenharmony_ci * (in tegra_dc_init()), which in turn requires registration of these 79362306a36Sopenharmony_ci * planes. So we have ourselves a nice little chicken and egg problem 79462306a36Sopenharmony_ci * here. 79562306a36Sopenharmony_ci * 79662306a36Sopenharmony_ci * We work around this by manually creating the mask from the number 79762306a36Sopenharmony_ci * of CRTCs that have been registered, and should therefore always be 79862306a36Sopenharmony_ci * the same as drm_crtc_index() after registration. 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ci return 1 << drm->mode_config.num_crtc; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, 80462306a36Sopenharmony_ci struct tegra_dc *dc) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); 80762306a36Sopenharmony_ci enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY; 80862306a36Sopenharmony_ci struct tegra_plane *plane; 80962306a36Sopenharmony_ci unsigned int num_formats; 81062306a36Sopenharmony_ci const u64 *modifiers; 81162306a36Sopenharmony_ci const u32 *formats; 81262306a36Sopenharmony_ci int err; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci plane = kzalloc(sizeof(*plane), GFP_KERNEL); 81562306a36Sopenharmony_ci if (!plane) 81662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Always use window A as primary window */ 81962306a36Sopenharmony_ci plane->offset = 0xa00; 82062306a36Sopenharmony_ci plane->index = 0; 82162306a36Sopenharmony_ci plane->dc = dc; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci num_formats = dc->soc->num_primary_formats; 82462306a36Sopenharmony_ci formats = dc->soc->primary_formats; 82562306a36Sopenharmony_ci modifiers = dc->soc->modifiers; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci err = tegra_plane_interconnect_init(plane); 82862306a36Sopenharmony_ci if (err) { 82962306a36Sopenharmony_ci kfree(plane); 83062306a36Sopenharmony_ci return ERR_PTR(err); 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 83462306a36Sopenharmony_ci &tegra_plane_funcs, formats, 83562306a36Sopenharmony_ci num_formats, modifiers, type, NULL); 83662306a36Sopenharmony_ci if (err < 0) { 83762306a36Sopenharmony_ci kfree(plane); 83862306a36Sopenharmony_ci return ERR_PTR(err); 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); 84262306a36Sopenharmony_ci drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci err = drm_plane_create_rotation_property(&plane->base, 84562306a36Sopenharmony_ci DRM_MODE_ROTATE_0, 84662306a36Sopenharmony_ci DRM_MODE_ROTATE_0 | 84762306a36Sopenharmony_ci DRM_MODE_ROTATE_180 | 84862306a36Sopenharmony_ci DRM_MODE_REFLECT_X | 84962306a36Sopenharmony_ci DRM_MODE_REFLECT_Y); 85062306a36Sopenharmony_ci if (err < 0) 85162306a36Sopenharmony_ci dev_err(dc->dev, "failed to create rotation property: %d\n", 85262306a36Sopenharmony_ci err); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci return &plane->base; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic const u32 tegra_legacy_cursor_plane_formats[] = { 85862306a36Sopenharmony_ci DRM_FORMAT_RGBA8888, 85962306a36Sopenharmony_ci}; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic const u32 tegra_cursor_plane_formats[] = { 86262306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 86362306a36Sopenharmony_ci}; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic int tegra_cursor_atomic_check(struct drm_plane *plane, 86662306a36Sopenharmony_ci struct drm_atomic_state *state) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 86962306a36Sopenharmony_ci plane); 87062306a36Sopenharmony_ci struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state); 87162306a36Sopenharmony_ci struct tegra_plane *tegra = to_tegra_plane(plane); 87262306a36Sopenharmony_ci int err; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci plane_state->peak_memory_bandwidth = 0; 87562306a36Sopenharmony_ci plane_state->avg_memory_bandwidth = 0; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* no need for further checks if the plane is being disabled */ 87862306a36Sopenharmony_ci if (!new_plane_state->crtc) { 87962306a36Sopenharmony_ci plane_state->total_peak_memory_bandwidth = 0; 88062306a36Sopenharmony_ci return 0; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci /* scaling not supported for cursor */ 88462306a36Sopenharmony_ci if ((new_plane_state->src_w >> 16 != new_plane_state->crtc_w) || 88562306a36Sopenharmony_ci (new_plane_state->src_h >> 16 != new_plane_state->crtc_h)) 88662306a36Sopenharmony_ci return -EINVAL; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* only square cursors supported */ 88962306a36Sopenharmony_ci if (new_plane_state->src_w != new_plane_state->src_h) 89062306a36Sopenharmony_ci return -EINVAL; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (new_plane_state->crtc_w != 32 && new_plane_state->crtc_w != 64 && 89362306a36Sopenharmony_ci new_plane_state->crtc_w != 128 && new_plane_state->crtc_w != 256) 89462306a36Sopenharmony_ci return -EINVAL; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci err = tegra_plane_state_add(tegra, new_plane_state); 89762306a36Sopenharmony_ci if (err < 0) 89862306a36Sopenharmony_ci return err; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci return 0; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic void __tegra_cursor_atomic_update(struct drm_plane *plane, 90462306a36Sopenharmony_ci struct drm_plane_state *new_state) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state); 90762306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(new_state->crtc); 90862306a36Sopenharmony_ci struct tegra_drm *tegra = plane->dev->dev_private; 90962306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 91062306a36Sopenharmony_ci u64 dma_mask = *dc->dev->dma_mask; 91162306a36Sopenharmony_ci#endif 91262306a36Sopenharmony_ci unsigned int x, y; 91362306a36Sopenharmony_ci u32 value = 0; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* rien ne va plus */ 91662306a36Sopenharmony_ci if (!new_state->crtc || !new_state->fb) 91762306a36Sopenharmony_ci return; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* 92062306a36Sopenharmony_ci * Legacy display supports hardware clipping of the cursor, but 92162306a36Sopenharmony_ci * nvdisplay relies on software to clip the cursor to the screen. 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_ci if (!dc->soc->has_nvdisplay) 92462306a36Sopenharmony_ci value |= CURSOR_CLIP_DISPLAY; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci switch (new_state->crtc_w) { 92762306a36Sopenharmony_ci case 32: 92862306a36Sopenharmony_ci value |= CURSOR_SIZE_32x32; 92962306a36Sopenharmony_ci break; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci case 64: 93262306a36Sopenharmony_ci value |= CURSOR_SIZE_64x64; 93362306a36Sopenharmony_ci break; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci case 128: 93662306a36Sopenharmony_ci value |= CURSOR_SIZE_128x128; 93762306a36Sopenharmony_ci break; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci case 256: 94062306a36Sopenharmony_ci value |= CURSOR_SIZE_256x256; 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci default: 94462306a36Sopenharmony_ci WARN(1, "cursor size %ux%u not supported\n", 94562306a36Sopenharmony_ci new_state->crtc_w, new_state->crtc_h); 94662306a36Sopenharmony_ci return; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci value |= (tegra_plane_state->iova[0] >> 10) & 0x3fffff; 95062306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 95362306a36Sopenharmony_ci value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32); 95462306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI); 95562306a36Sopenharmony_ci#endif 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* enable cursor and set blend mode */ 95862306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 95962306a36Sopenharmony_ci value |= CURSOR_ENABLE; 96062306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL); 96362306a36Sopenharmony_ci value &= ~CURSOR_DST_BLEND_MASK; 96462306a36Sopenharmony_ci value &= ~CURSOR_SRC_BLEND_MASK; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (dc->soc->has_nvdisplay) 96762306a36Sopenharmony_ci value &= ~CURSOR_COMPOSITION_MODE_XOR; 96862306a36Sopenharmony_ci else 96962306a36Sopenharmony_ci value |= CURSOR_MODE_NORMAL; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC; 97262306a36Sopenharmony_ci value |= CURSOR_SRC_BLEND_K1_TIMES_SRC; 97362306a36Sopenharmony_ci value |= CURSOR_ALPHA; 97462306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* nvdisplay relies on software for clipping */ 97762306a36Sopenharmony_ci if (dc->soc->has_nvdisplay) { 97862306a36Sopenharmony_ci struct drm_rect src; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci x = new_state->dst.x1; 98162306a36Sopenharmony_ci y = new_state->dst.y1; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci drm_rect_fp_to_int(&src, &new_state->src); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask); 98662306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci value = (drm_rect_height(&src) & tegra->vmask) << 16 | 98962306a36Sopenharmony_ci (drm_rect_width(&src) & tegra->hmask); 99062306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR); 99162306a36Sopenharmony_ci } else { 99262306a36Sopenharmony_ci x = new_state->crtc_x; 99362306a36Sopenharmony_ci y = new_state->crtc_y; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci /* position the cursor */ 99762306a36Sopenharmony_ci value = ((y & tegra->vmask) << 16) | (x & tegra->hmask); 99862306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic void tegra_cursor_atomic_update(struct drm_plane *plane, 100262306a36Sopenharmony_ci struct drm_atomic_state *state) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci __tegra_cursor_atomic_update(plane, new_state); 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistatic void tegra_cursor_atomic_disable(struct drm_plane *plane, 101062306a36Sopenharmony_ci struct drm_atomic_state *state) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 101362306a36Sopenharmony_ci plane); 101462306a36Sopenharmony_ci struct tegra_dc *dc; 101562306a36Sopenharmony_ci u32 value; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci /* rien ne va plus */ 101862306a36Sopenharmony_ci if (!old_state || !old_state->crtc) 101962306a36Sopenharmony_ci return; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci dc = to_tegra_dc(old_state->crtc); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 102462306a36Sopenharmony_ci value &= ~CURSOR_ENABLE; 102562306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic int tegra_cursor_atomic_async_check(struct drm_plane *plane, struct drm_atomic_state *state) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); 103162306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 103262306a36Sopenharmony_ci int min_scale, max_scale; 103362306a36Sopenharmony_ci int err; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc); 103662306a36Sopenharmony_ci if (WARN_ON(!crtc_state)) 103762306a36Sopenharmony_ci return -EINVAL; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci if (!crtc_state->active) 104062306a36Sopenharmony_ci return -EINVAL; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci if (plane->state->crtc != new_state->crtc || 104362306a36Sopenharmony_ci plane->state->src_w != new_state->src_w || 104462306a36Sopenharmony_ci plane->state->src_h != new_state->src_h || 104562306a36Sopenharmony_ci plane->state->crtc_w != new_state->crtc_w || 104662306a36Sopenharmony_ci plane->state->crtc_h != new_state->crtc_h || 104762306a36Sopenharmony_ci plane->state->fb != new_state->fb || 104862306a36Sopenharmony_ci plane->state->fb == NULL) 104962306a36Sopenharmony_ci return -EINVAL; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci min_scale = (1 << 16) / 8; 105262306a36Sopenharmony_ci max_scale = (8 << 16) / 1; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci err = drm_atomic_helper_check_plane_state(new_state, crtc_state, min_scale, max_scale, 105562306a36Sopenharmony_ci true, true); 105662306a36Sopenharmony_ci if (err < 0) 105762306a36Sopenharmony_ci return err; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (new_state->visible != plane->state->visible) 106062306a36Sopenharmony_ci return -EINVAL; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci return 0; 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistatic void tegra_cursor_atomic_async_update(struct drm_plane *plane, 106662306a36Sopenharmony_ci struct drm_atomic_state *state) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); 106962306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(new_state->crtc); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci plane->state->src_x = new_state->src_x; 107262306a36Sopenharmony_ci plane->state->src_y = new_state->src_y; 107362306a36Sopenharmony_ci plane->state->crtc_x = new_state->crtc_x; 107462306a36Sopenharmony_ci plane->state->crtc_y = new_state->crtc_y; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (new_state->visible) { 107762306a36Sopenharmony_ci struct tegra_plane *p = to_tegra_plane(plane); 107862306a36Sopenharmony_ci u32 value; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci __tegra_cursor_atomic_update(plane, new_state); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci value = (WIN_A_ACT_REQ << p->index) << 8 | GENERAL_UPDATE; 108362306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 108462306a36Sopenharmony_ci (void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci value = (WIN_A_ACT_REQ << p->index) | GENERAL_ACT_REQ; 108762306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 108862306a36Sopenharmony_ci (void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { 109362306a36Sopenharmony_ci .prepare_fb = tegra_plane_prepare_fb, 109462306a36Sopenharmony_ci .cleanup_fb = tegra_plane_cleanup_fb, 109562306a36Sopenharmony_ci .atomic_check = tegra_cursor_atomic_check, 109662306a36Sopenharmony_ci .atomic_update = tegra_cursor_atomic_update, 109762306a36Sopenharmony_ci .atomic_disable = tegra_cursor_atomic_disable, 109862306a36Sopenharmony_ci .atomic_async_check = tegra_cursor_atomic_async_check, 109962306a36Sopenharmony_ci .atomic_async_update = tegra_cursor_atomic_async_update, 110062306a36Sopenharmony_ci}; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_cistatic const uint64_t linear_modifiers[] = { 110362306a36Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 110462306a36Sopenharmony_ci DRM_FORMAT_MOD_INVALID 110562306a36Sopenharmony_ci}; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, 110862306a36Sopenharmony_ci struct tegra_dc *dc) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); 111162306a36Sopenharmony_ci struct tegra_plane *plane; 111262306a36Sopenharmony_ci unsigned int num_formats; 111362306a36Sopenharmony_ci const u32 *formats; 111462306a36Sopenharmony_ci int err; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci plane = kzalloc(sizeof(*plane), GFP_KERNEL); 111762306a36Sopenharmony_ci if (!plane) 111862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci /* 112162306a36Sopenharmony_ci * This index is kind of fake. The cursor isn't a regular plane, but 112262306a36Sopenharmony_ci * its update and activation request bits in DC_CMD_STATE_CONTROL do 112362306a36Sopenharmony_ci * use the same programming. Setting this fake index here allows the 112462306a36Sopenharmony_ci * code in tegra_add_plane_state() to do the right thing without the 112562306a36Sopenharmony_ci * need to special-casing the cursor plane. 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_ci plane->index = 6; 112862306a36Sopenharmony_ci plane->dc = dc; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (!dc->soc->has_nvdisplay) { 113162306a36Sopenharmony_ci num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats); 113262306a36Sopenharmony_ci formats = tegra_legacy_cursor_plane_formats; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci err = tegra_plane_interconnect_init(plane); 113562306a36Sopenharmony_ci if (err) { 113662306a36Sopenharmony_ci kfree(plane); 113762306a36Sopenharmony_ci return ERR_PTR(err); 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci } else { 114062306a36Sopenharmony_ci num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); 114162306a36Sopenharmony_ci formats = tegra_cursor_plane_formats; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 114562306a36Sopenharmony_ci &tegra_plane_funcs, formats, 114662306a36Sopenharmony_ci num_formats, linear_modifiers, 114762306a36Sopenharmony_ci DRM_PLANE_TYPE_CURSOR, NULL); 114862306a36Sopenharmony_ci if (err < 0) { 114962306a36Sopenharmony_ci kfree(plane); 115062306a36Sopenharmony_ci return ERR_PTR(err); 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs); 115462306a36Sopenharmony_ci drm_plane_create_zpos_immutable_property(&plane->base, 255); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci return &plane->base; 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic const u32 tegra20_overlay_formats[] = { 116062306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, 116162306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, 116262306a36Sopenharmony_ci DRM_FORMAT_RGB565, 116362306a36Sopenharmony_ci DRM_FORMAT_RGBA5551, 116462306a36Sopenharmony_ci DRM_FORMAT_ABGR8888, 116562306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 116662306a36Sopenharmony_ci /* non-native formats */ 116762306a36Sopenharmony_ci DRM_FORMAT_XRGB1555, 116862306a36Sopenharmony_ci DRM_FORMAT_RGBX5551, 116962306a36Sopenharmony_ci DRM_FORMAT_XBGR8888, 117062306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 117162306a36Sopenharmony_ci /* planar formats */ 117262306a36Sopenharmony_ci DRM_FORMAT_UYVY, 117362306a36Sopenharmony_ci DRM_FORMAT_YUYV, 117462306a36Sopenharmony_ci DRM_FORMAT_YUV420, 117562306a36Sopenharmony_ci DRM_FORMAT_YUV422, 117662306a36Sopenharmony_ci}; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_cistatic const u32 tegra114_overlay_formats[] = { 117962306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, 118062306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, 118162306a36Sopenharmony_ci DRM_FORMAT_RGB565, 118262306a36Sopenharmony_ci DRM_FORMAT_RGBA5551, 118362306a36Sopenharmony_ci DRM_FORMAT_ABGR8888, 118462306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 118562306a36Sopenharmony_ci /* new on Tegra114 */ 118662306a36Sopenharmony_ci DRM_FORMAT_ABGR4444, 118762306a36Sopenharmony_ci DRM_FORMAT_ABGR1555, 118862306a36Sopenharmony_ci DRM_FORMAT_BGRA5551, 118962306a36Sopenharmony_ci DRM_FORMAT_XRGB1555, 119062306a36Sopenharmony_ci DRM_FORMAT_RGBX5551, 119162306a36Sopenharmony_ci DRM_FORMAT_XBGR1555, 119262306a36Sopenharmony_ci DRM_FORMAT_BGRX5551, 119362306a36Sopenharmony_ci DRM_FORMAT_BGR565, 119462306a36Sopenharmony_ci DRM_FORMAT_BGRA8888, 119562306a36Sopenharmony_ci DRM_FORMAT_RGBA8888, 119662306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 119762306a36Sopenharmony_ci DRM_FORMAT_XBGR8888, 119862306a36Sopenharmony_ci /* planar formats */ 119962306a36Sopenharmony_ci DRM_FORMAT_UYVY, 120062306a36Sopenharmony_ci DRM_FORMAT_YUYV, 120162306a36Sopenharmony_ci DRM_FORMAT_YUV420, 120262306a36Sopenharmony_ci DRM_FORMAT_YUV422, 120362306a36Sopenharmony_ci /* semi-planar formats */ 120462306a36Sopenharmony_ci DRM_FORMAT_NV12, 120562306a36Sopenharmony_ci DRM_FORMAT_NV21, 120662306a36Sopenharmony_ci DRM_FORMAT_NV16, 120762306a36Sopenharmony_ci DRM_FORMAT_NV61, 120862306a36Sopenharmony_ci DRM_FORMAT_NV24, 120962306a36Sopenharmony_ci DRM_FORMAT_NV42, 121062306a36Sopenharmony_ci}; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic const u32 tegra124_overlay_formats[] = { 121362306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, 121462306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, 121562306a36Sopenharmony_ci DRM_FORMAT_RGB565, 121662306a36Sopenharmony_ci DRM_FORMAT_RGBA5551, 121762306a36Sopenharmony_ci DRM_FORMAT_ABGR8888, 121862306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 121962306a36Sopenharmony_ci /* new on Tegra114 */ 122062306a36Sopenharmony_ci DRM_FORMAT_ABGR4444, 122162306a36Sopenharmony_ci DRM_FORMAT_ABGR1555, 122262306a36Sopenharmony_ci DRM_FORMAT_BGRA5551, 122362306a36Sopenharmony_ci DRM_FORMAT_XRGB1555, 122462306a36Sopenharmony_ci DRM_FORMAT_RGBX5551, 122562306a36Sopenharmony_ci DRM_FORMAT_XBGR1555, 122662306a36Sopenharmony_ci DRM_FORMAT_BGRX5551, 122762306a36Sopenharmony_ci DRM_FORMAT_BGR565, 122862306a36Sopenharmony_ci DRM_FORMAT_BGRA8888, 122962306a36Sopenharmony_ci DRM_FORMAT_RGBA8888, 123062306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 123162306a36Sopenharmony_ci DRM_FORMAT_XBGR8888, 123262306a36Sopenharmony_ci /* new on Tegra124 */ 123362306a36Sopenharmony_ci DRM_FORMAT_RGBX8888, 123462306a36Sopenharmony_ci DRM_FORMAT_BGRX8888, 123562306a36Sopenharmony_ci /* planar formats */ 123662306a36Sopenharmony_ci DRM_FORMAT_UYVY, 123762306a36Sopenharmony_ci DRM_FORMAT_YUYV, 123862306a36Sopenharmony_ci DRM_FORMAT_YVYU, 123962306a36Sopenharmony_ci DRM_FORMAT_VYUY, 124062306a36Sopenharmony_ci DRM_FORMAT_YUV420, /* YU12 */ 124162306a36Sopenharmony_ci DRM_FORMAT_YUV422, /* YU16 */ 124262306a36Sopenharmony_ci DRM_FORMAT_YUV444, /* YU24 */ 124362306a36Sopenharmony_ci /* semi-planar formats */ 124462306a36Sopenharmony_ci DRM_FORMAT_NV12, 124562306a36Sopenharmony_ci DRM_FORMAT_NV21, 124662306a36Sopenharmony_ci DRM_FORMAT_NV16, 124762306a36Sopenharmony_ci DRM_FORMAT_NV61, 124862306a36Sopenharmony_ci DRM_FORMAT_NV24, 124962306a36Sopenharmony_ci DRM_FORMAT_NV42, 125062306a36Sopenharmony_ci}; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_cistatic struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, 125362306a36Sopenharmony_ci struct tegra_dc *dc, 125462306a36Sopenharmony_ci unsigned int index, 125562306a36Sopenharmony_ci bool cursor) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); 125862306a36Sopenharmony_ci struct tegra_plane *plane; 125962306a36Sopenharmony_ci unsigned int num_formats; 126062306a36Sopenharmony_ci enum drm_plane_type type; 126162306a36Sopenharmony_ci const u32 *formats; 126262306a36Sopenharmony_ci int err; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci plane = kzalloc(sizeof(*plane), GFP_KERNEL); 126562306a36Sopenharmony_ci if (!plane) 126662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci plane->offset = 0xa00 + 0x200 * index; 126962306a36Sopenharmony_ci plane->index = index; 127062306a36Sopenharmony_ci plane->dc = dc; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci num_formats = dc->soc->num_overlay_formats; 127362306a36Sopenharmony_ci formats = dc->soc->overlay_formats; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci err = tegra_plane_interconnect_init(plane); 127662306a36Sopenharmony_ci if (err) { 127762306a36Sopenharmony_ci kfree(plane); 127862306a36Sopenharmony_ci return ERR_PTR(err); 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci if (!cursor) 128262306a36Sopenharmony_ci type = DRM_PLANE_TYPE_OVERLAY; 128362306a36Sopenharmony_ci else 128462306a36Sopenharmony_ci type = DRM_PLANE_TYPE_CURSOR; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 128762306a36Sopenharmony_ci &tegra_plane_funcs, formats, 128862306a36Sopenharmony_ci num_formats, linear_modifiers, 128962306a36Sopenharmony_ci type, NULL); 129062306a36Sopenharmony_ci if (err < 0) { 129162306a36Sopenharmony_ci kfree(plane); 129262306a36Sopenharmony_ci return ERR_PTR(err); 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); 129662306a36Sopenharmony_ci drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci err = drm_plane_create_rotation_property(&plane->base, 129962306a36Sopenharmony_ci DRM_MODE_ROTATE_0, 130062306a36Sopenharmony_ci DRM_MODE_ROTATE_0 | 130162306a36Sopenharmony_ci DRM_MODE_ROTATE_180 | 130262306a36Sopenharmony_ci DRM_MODE_REFLECT_X | 130362306a36Sopenharmony_ci DRM_MODE_REFLECT_Y); 130462306a36Sopenharmony_ci if (err < 0) 130562306a36Sopenharmony_ci dev_err(dc->dev, "failed to create rotation property: %d\n", 130662306a36Sopenharmony_ci err); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci return &plane->base; 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm, 131262306a36Sopenharmony_ci struct tegra_dc *dc) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci struct drm_plane *plane, *primary = NULL; 131562306a36Sopenharmony_ci unsigned int i, j; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci for (i = 0; i < dc->soc->num_wgrps; i++) { 131862306a36Sopenharmony_ci const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci if (wgrp->dc == dc->pipe) { 132162306a36Sopenharmony_ci for (j = 0; j < wgrp->num_windows; j++) { 132262306a36Sopenharmony_ci unsigned int index = wgrp->windows[j]; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci plane = tegra_shared_plane_create(drm, dc, 132562306a36Sopenharmony_ci wgrp->index, 132662306a36Sopenharmony_ci index); 132762306a36Sopenharmony_ci if (IS_ERR(plane)) 132862306a36Sopenharmony_ci return plane; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* 133162306a36Sopenharmony_ci * Choose the first shared plane owned by this 133262306a36Sopenharmony_ci * head as the primary plane. 133362306a36Sopenharmony_ci */ 133462306a36Sopenharmony_ci if (!primary) { 133562306a36Sopenharmony_ci plane->type = DRM_PLANE_TYPE_PRIMARY; 133662306a36Sopenharmony_ci primary = plane; 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci return primary; 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_cistatic struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, 134662306a36Sopenharmony_ci struct tegra_dc *dc) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci struct drm_plane *planes[2], *primary; 134962306a36Sopenharmony_ci unsigned int planes_num; 135062306a36Sopenharmony_ci unsigned int i; 135162306a36Sopenharmony_ci int err; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci primary = tegra_primary_plane_create(drm, dc); 135462306a36Sopenharmony_ci if (IS_ERR(primary)) 135562306a36Sopenharmony_ci return primary; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci if (dc->soc->supports_cursor) 135862306a36Sopenharmony_ci planes_num = 2; 135962306a36Sopenharmony_ci else 136062306a36Sopenharmony_ci planes_num = 1; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci for (i = 0; i < planes_num; i++) { 136362306a36Sopenharmony_ci planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i, 136462306a36Sopenharmony_ci false); 136562306a36Sopenharmony_ci if (IS_ERR(planes[i])) { 136662306a36Sopenharmony_ci err = PTR_ERR(planes[i]); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci while (i--) 136962306a36Sopenharmony_ci planes[i]->funcs->destroy(planes[i]); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci primary->funcs->destroy(primary); 137262306a36Sopenharmony_ci return ERR_PTR(err); 137362306a36Sopenharmony_ci } 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci return primary; 137762306a36Sopenharmony_ci} 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic void tegra_dc_destroy(struct drm_crtc *crtc) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic void tegra_crtc_reset(struct drm_crtc *crtc) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci if (crtc->state) 138962306a36Sopenharmony_ci tegra_crtc_atomic_destroy_state(crtc, crtc->state); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &state->base); 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic struct drm_crtc_state * 139562306a36Sopenharmony_citegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) 139662306a36Sopenharmony_ci{ 139762306a36Sopenharmony_ci struct tegra_dc_state *state = to_dc_state(crtc->state); 139862306a36Sopenharmony_ci struct tegra_dc_state *copy; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci copy = kmalloc(sizeof(*copy), GFP_KERNEL); 140162306a36Sopenharmony_ci if (!copy) 140262306a36Sopenharmony_ci return NULL; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, ©->base); 140562306a36Sopenharmony_ci copy->clk = state->clk; 140662306a36Sopenharmony_ci copy->pclk = state->pclk; 140762306a36Sopenharmony_ci copy->div = state->div; 140862306a36Sopenharmony_ci copy->planes = state->planes; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci return ©->base; 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_cistatic void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, 141462306a36Sopenharmony_ci struct drm_crtc_state *state) 141562306a36Sopenharmony_ci{ 141662306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(state); 141762306a36Sopenharmony_ci kfree(state); 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic const struct debugfs_reg32 tegra_dc_regs[] = { 142362306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT), 142462306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL), 142562306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR), 142662306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT), 142762306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL), 142862306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR), 142962306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT), 143062306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL), 143162306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR), 143262306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT), 143362306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL), 143462306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR), 143562306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC), 143662306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0), 143762306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND), 143862306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE), 143962306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL), 144062306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_INT_STATUS), 144162306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_INT_MASK), 144262306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_INT_ENABLE), 144362306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_INT_TYPE), 144462306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_INT_POLARITY), 144562306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1), 144662306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2), 144762306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3), 144862306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_STATE_ACCESS), 144962306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_STATE_CONTROL), 145062306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER), 145162306a36Sopenharmony_ci DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL), 145262306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_CRC_CONTROL), 145362306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_CRC_CHECKSUM), 145462306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)), 145562306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)), 145662306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)), 145762306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)), 145862306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)), 145962306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)), 146062306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)), 146162306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)), 146262306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)), 146362306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)), 146462306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)), 146562306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)), 146662306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)), 146762306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)), 146862306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)), 146962306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)), 147062306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)), 147162306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)), 147262306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)), 147362306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)), 147462306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)), 147562306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)), 147662306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)), 147762306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)), 147862306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)), 147962306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL), 148062306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL), 148162306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE), 148262306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL), 148362306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE), 148462306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_SPI_CONTROL), 148562306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_SPI_START_BYTE), 148662306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB), 148762306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD), 148862306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_HSPI_CS_DC), 148962306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A), 149062306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B), 149162306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_GPIO_CTRL), 149262306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER), 149362306a36Sopenharmony_ci DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED), 149462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0), 149562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1), 149662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS), 149762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY), 149862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER), 149962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS), 150062306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_REF_TO_SYNC), 150162306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SYNC_WIDTH), 150262306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_BACK_PORCH), 150362306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_ACTIVE), 150462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_FRONT_PORCH), 150562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL), 150662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A), 150762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B), 150862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C), 150962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D), 151062306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL), 151162306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A), 151262306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B), 151362306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C), 151462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D), 151562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL), 151662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A), 151762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B), 151862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C), 151962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D), 152062306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL), 152162306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A), 152262306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B), 152362306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C), 152462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL), 152562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A), 152662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B), 152762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C), 152862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL), 152962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A), 153062306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL), 153162306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A), 153262306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_M0_CONTROL), 153362306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_M1_CONTROL), 153462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DI_CONTROL), 153562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_PP_CONTROL), 153662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_PP_SELECT_A), 153762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_PP_SELECT_B), 153862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_PP_SELECT_C), 153962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_PP_SELECT_D), 154062306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL), 154162306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL), 154262306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL), 154362306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS), 154462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS), 154562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS), 154662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS), 154762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_BORDER_COLOR), 154862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER), 154962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER), 155062306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER), 155162306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER), 155262306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND), 155362306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND), 155462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR), 155562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS), 155662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_POSITION), 155762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS), 155862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL), 155962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A), 156062306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B), 156162306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C), 156262306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D), 156362306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL), 156462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST), 156562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST), 156662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST), 156762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST), 156862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL), 156962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL), 157062306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_CONTROL), 157162306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF), 157262306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(0)), 157362306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(1)), 157462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(2)), 157562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(3)), 157662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(4)), 157762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(5)), 157862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(6)), 157962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(7)), 158062306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_LUT(8)), 158162306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL), 158262306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT), 158362306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)), 158462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)), 158562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)), 158662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)), 158762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)), 158862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)), 158962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)), 159062306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)), 159162306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)), 159262306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)), 159362306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)), 159462306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)), 159562306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL), 159662306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES), 159762306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES), 159862306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI), 159962306a36Sopenharmony_ci DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL), 160062306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_WIN_OPTIONS), 160162306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BYTE_SWAP), 160262306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL), 160362306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_COLOR_DEPTH), 160462306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_POSITION), 160562306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_SIZE), 160662306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE), 160762306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA), 160862306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA), 160962306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_DDA_INC), 161062306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_LINE_STRIDE), 161162306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BUF_STRIDE), 161262306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE), 161362306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE), 161462306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_DV_CONTROL), 161562306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BLEND_NOKEY), 161662306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BLEND_1WIN), 161762306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X), 161862306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y), 161962306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY), 162062306a36Sopenharmony_ci DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL), 162162306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR), 162262306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS), 162362306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR_U), 162462306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS), 162562306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR_V), 162662306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS), 162762306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET), 162862306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS), 162962306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET), 163062306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS), 163162306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS), 163262306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS), 163362306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS), 163462306a36Sopenharmony_ci DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS), 163562306a36Sopenharmony_ci}; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_cistatic int tegra_dc_show_regs(struct seq_file *s, void *data) 163862306a36Sopenharmony_ci{ 163962306a36Sopenharmony_ci struct drm_info_node *node = s->private; 164062306a36Sopenharmony_ci struct tegra_dc *dc = node->info_ent->data; 164162306a36Sopenharmony_ci unsigned int i; 164262306a36Sopenharmony_ci int err = 0; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci drm_modeset_lock(&dc->base.mutex, NULL); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci if (!dc->base.state->active) { 164762306a36Sopenharmony_ci err = -EBUSY; 164862306a36Sopenharmony_ci goto unlock; 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { 165262306a36Sopenharmony_ci unsigned int offset = tegra_dc_regs[i].offset; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name, 165562306a36Sopenharmony_ci offset, tegra_dc_readl(dc, offset)); 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ciunlock: 165962306a36Sopenharmony_ci drm_modeset_unlock(&dc->base.mutex); 166062306a36Sopenharmony_ci return err; 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_cistatic int tegra_dc_show_crc(struct seq_file *s, void *data) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci struct drm_info_node *node = s->private; 166662306a36Sopenharmony_ci struct tegra_dc *dc = node->info_ent->data; 166762306a36Sopenharmony_ci int err = 0; 166862306a36Sopenharmony_ci u32 value; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci drm_modeset_lock(&dc->base.mutex, NULL); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci if (!dc->base.state->active) { 167362306a36Sopenharmony_ci err = -EBUSY; 167462306a36Sopenharmony_ci goto unlock; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE; 167862306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL); 167962306a36Sopenharmony_ci tegra_dc_commit(dc); 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci drm_crtc_wait_one_vblank(&dc->base); 168262306a36Sopenharmony_ci drm_crtc_wait_one_vblank(&dc->base); 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); 168562306a36Sopenharmony_ci seq_printf(s, "%08x\n", value); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ciunlock: 169062306a36Sopenharmony_ci drm_modeset_unlock(&dc->base.mutex); 169162306a36Sopenharmony_ci return err; 169262306a36Sopenharmony_ci} 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_cistatic int tegra_dc_show_stats(struct seq_file *s, void *data) 169562306a36Sopenharmony_ci{ 169662306a36Sopenharmony_ci struct drm_info_node *node = s->private; 169762306a36Sopenharmony_ci struct tegra_dc *dc = node->info_ent->data; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci seq_printf(s, "frames: %lu\n", dc->stats.frames); 170062306a36Sopenharmony_ci seq_printf(s, "vblank: %lu\n", dc->stats.vblank); 170162306a36Sopenharmony_ci seq_printf(s, "underflow: %lu\n", dc->stats.underflow); 170262306a36Sopenharmony_ci seq_printf(s, "overflow: %lu\n", dc->stats.overflow); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci seq_printf(s, "frames total: %lu\n", dc->stats.frames_total); 170562306a36Sopenharmony_ci seq_printf(s, "vblank total: %lu\n", dc->stats.vblank_total); 170662306a36Sopenharmony_ci seq_printf(s, "underflow total: %lu\n", dc->stats.underflow_total); 170762306a36Sopenharmony_ci seq_printf(s, "overflow total: %lu\n", dc->stats.overflow_total); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci return 0; 171062306a36Sopenharmony_ci} 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_cistatic struct drm_info_list debugfs_files[] = { 171362306a36Sopenharmony_ci { "regs", tegra_dc_show_regs, 0, NULL }, 171462306a36Sopenharmony_ci { "crc", tegra_dc_show_crc, 0, NULL }, 171562306a36Sopenharmony_ci { "stats", tegra_dc_show_stats, 0, NULL }, 171662306a36Sopenharmony_ci}; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_cistatic int tegra_dc_late_register(struct drm_crtc *crtc) 171962306a36Sopenharmony_ci{ 172062306a36Sopenharmony_ci unsigned int i, count = ARRAY_SIZE(debugfs_files); 172162306a36Sopenharmony_ci struct drm_minor *minor = crtc->dev->primary; 172262306a36Sopenharmony_ci struct dentry *root; 172362306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 172662306a36Sopenharmony_ci root = crtc->debugfs_entry; 172762306a36Sopenharmony_ci#else 172862306a36Sopenharmony_ci root = NULL; 172962306a36Sopenharmony_ci#endif 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), 173262306a36Sopenharmony_ci GFP_KERNEL); 173362306a36Sopenharmony_ci if (!dc->debugfs_files) 173462306a36Sopenharmony_ci return -ENOMEM; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci for (i = 0; i < count; i++) 173762306a36Sopenharmony_ci dc->debugfs_files[i].data = dc; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci drm_debugfs_create_files(dc->debugfs_files, count, root, minor); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci return 0; 174262306a36Sopenharmony_ci} 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_cistatic void tegra_dc_early_unregister(struct drm_crtc *crtc) 174562306a36Sopenharmony_ci{ 174662306a36Sopenharmony_ci unsigned int count = ARRAY_SIZE(debugfs_files); 174762306a36Sopenharmony_ci struct drm_minor *minor = crtc->dev->primary; 174862306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci drm_debugfs_remove_files(dc->debugfs_files, count, minor); 175162306a36Sopenharmony_ci kfree(dc->debugfs_files); 175262306a36Sopenharmony_ci dc->debugfs_files = NULL; 175362306a36Sopenharmony_ci} 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_cistatic u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) 175662306a36Sopenharmony_ci{ 175762306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci /* XXX vblank syncpoints don't work with nvdisplay yet */ 176062306a36Sopenharmony_ci if (dc->syncpt && !dc->soc->has_nvdisplay) 176162306a36Sopenharmony_ci return host1x_syncpt_read(dc->syncpt); 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci /* fallback to software emulated VBLANK counter */ 176462306a36Sopenharmony_ci return (u32)drm_crtc_vblank_count(&dc->base); 176562306a36Sopenharmony_ci} 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_cistatic int tegra_dc_enable_vblank(struct drm_crtc *crtc) 176862306a36Sopenharmony_ci{ 176962306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 177062306a36Sopenharmony_ci u32 value; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_INT_MASK); 177362306a36Sopenharmony_ci value |= VBLANK_INT; 177462306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci return 0; 177762306a36Sopenharmony_ci} 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_cistatic void tegra_dc_disable_vblank(struct drm_crtc *crtc) 178062306a36Sopenharmony_ci{ 178162306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 178262306a36Sopenharmony_ci u32 value; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_INT_MASK); 178562306a36Sopenharmony_ci value &= ~VBLANK_INT; 178662306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 178762306a36Sopenharmony_ci} 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_cistatic const struct drm_crtc_funcs tegra_crtc_funcs = { 179062306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 179162306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 179262306a36Sopenharmony_ci .destroy = tegra_dc_destroy, 179362306a36Sopenharmony_ci .reset = tegra_crtc_reset, 179462306a36Sopenharmony_ci .atomic_duplicate_state = tegra_crtc_atomic_duplicate_state, 179562306a36Sopenharmony_ci .atomic_destroy_state = tegra_crtc_atomic_destroy_state, 179662306a36Sopenharmony_ci .late_register = tegra_dc_late_register, 179762306a36Sopenharmony_ci .early_unregister = tegra_dc_early_unregister, 179862306a36Sopenharmony_ci .get_vblank_counter = tegra_dc_get_vblank_counter, 179962306a36Sopenharmony_ci .enable_vblank = tegra_dc_enable_vblank, 180062306a36Sopenharmony_ci .disable_vblank = tegra_dc_disable_vblank, 180162306a36Sopenharmony_ci}; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cistatic int tegra_dc_set_timings(struct tegra_dc *dc, 180462306a36Sopenharmony_ci struct drm_display_mode *mode) 180562306a36Sopenharmony_ci{ 180662306a36Sopenharmony_ci unsigned int h_ref_to_sync = 1; 180762306a36Sopenharmony_ci unsigned int v_ref_to_sync = 1; 180862306a36Sopenharmony_ci unsigned long value; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci if (!dc->soc->has_nvdisplay) { 181162306a36Sopenharmony_ci tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci value = (v_ref_to_sync << 16) | h_ref_to_sync; 181462306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC); 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci value = ((mode->vsync_end - mode->vsync_start) << 16) | 181862306a36Sopenharmony_ci ((mode->hsync_end - mode->hsync_start) << 0); 181962306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH); 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci value = ((mode->vtotal - mode->vsync_end) << 16) | 182262306a36Sopenharmony_ci ((mode->htotal - mode->hsync_end) << 0); 182362306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci value = ((mode->vsync_start - mode->vdisplay) << 16) | 182662306a36Sopenharmony_ci ((mode->hsync_start - mode->hdisplay) << 0); 182762306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci value = (mode->vdisplay << 16) | mode->hdisplay; 183062306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_ACTIVE); 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci return 0; 183362306a36Sopenharmony_ci} 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci/** 183662306a36Sopenharmony_ci * tegra_dc_state_setup_clock - check clock settings and store them in atomic 183762306a36Sopenharmony_ci * state 183862306a36Sopenharmony_ci * @dc: display controller 183962306a36Sopenharmony_ci * @crtc_state: CRTC atomic state 184062306a36Sopenharmony_ci * @clk: parent clock for display controller 184162306a36Sopenharmony_ci * @pclk: pixel clock 184262306a36Sopenharmony_ci * @div: shift clock divider 184362306a36Sopenharmony_ci * 184462306a36Sopenharmony_ci * Returns: 184562306a36Sopenharmony_ci * 0 on success or a negative error-code on failure. 184662306a36Sopenharmony_ci */ 184762306a36Sopenharmony_ciint tegra_dc_state_setup_clock(struct tegra_dc *dc, 184862306a36Sopenharmony_ci struct drm_crtc_state *crtc_state, 184962306a36Sopenharmony_ci struct clk *clk, unsigned long pclk, 185062306a36Sopenharmony_ci unsigned int div) 185162306a36Sopenharmony_ci{ 185262306a36Sopenharmony_ci struct tegra_dc_state *state = to_dc_state(crtc_state); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci if (!clk_has_parent(dc->clk, clk)) 185562306a36Sopenharmony_ci return -EINVAL; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci state->clk = clk; 185862306a36Sopenharmony_ci state->pclk = pclk; 185962306a36Sopenharmony_ci state->div = div; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci return 0; 186262306a36Sopenharmony_ci} 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_cistatic void tegra_dc_update_voltage_state(struct tegra_dc *dc, 186562306a36Sopenharmony_ci struct tegra_dc_state *state) 186662306a36Sopenharmony_ci{ 186762306a36Sopenharmony_ci unsigned long rate, pstate; 186862306a36Sopenharmony_ci struct dev_pm_opp *opp; 186962306a36Sopenharmony_ci int err; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci if (!dc->has_opp_table) 187262306a36Sopenharmony_ci return; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci /* calculate actual pixel clock rate which depends on internal divider */ 187562306a36Sopenharmony_ci rate = DIV_ROUND_UP(clk_get_rate(dc->clk) * 2, state->div + 2); 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci /* find suitable OPP for the rate */ 187862306a36Sopenharmony_ci opp = dev_pm_opp_find_freq_ceil(dc->dev, &rate); 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci /* 188162306a36Sopenharmony_ci * Very high resolution modes may results in a clock rate that is 188262306a36Sopenharmony_ci * above the characterized maximum. In this case it's okay to fall 188362306a36Sopenharmony_ci * back to the characterized maximum. 188462306a36Sopenharmony_ci */ 188562306a36Sopenharmony_ci if (opp == ERR_PTR(-ERANGE)) 188662306a36Sopenharmony_ci opp = dev_pm_opp_find_freq_floor(dc->dev, &rate); 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci if (IS_ERR(opp)) { 188962306a36Sopenharmony_ci dev_err(dc->dev, "failed to find OPP for %luHz: %pe\n", 189062306a36Sopenharmony_ci rate, opp); 189162306a36Sopenharmony_ci return; 189262306a36Sopenharmony_ci } 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci pstate = dev_pm_opp_get_required_pstate(opp, 0); 189562306a36Sopenharmony_ci dev_pm_opp_put(opp); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci /* 189862306a36Sopenharmony_ci * The minimum core voltage depends on the pixel clock rate (which 189962306a36Sopenharmony_ci * depends on internal clock divider of the CRTC) and not on the 190062306a36Sopenharmony_ci * rate of the display controller clock. This is why we're not using 190162306a36Sopenharmony_ci * dev_pm_opp_set_rate() API and instead controlling the power domain 190262306a36Sopenharmony_ci * directly. 190362306a36Sopenharmony_ci */ 190462306a36Sopenharmony_ci err = dev_pm_genpd_set_performance_state(dc->dev, pstate); 190562306a36Sopenharmony_ci if (err) 190662306a36Sopenharmony_ci dev_err(dc->dev, "failed to set power domain state to %lu: %d\n", 190762306a36Sopenharmony_ci pstate, err); 190862306a36Sopenharmony_ci} 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_cistatic void tegra_dc_set_clock_rate(struct tegra_dc *dc, 191162306a36Sopenharmony_ci struct tegra_dc_state *state) 191262306a36Sopenharmony_ci{ 191362306a36Sopenharmony_ci int err; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci err = clk_set_parent(dc->clk, state->clk); 191662306a36Sopenharmony_ci if (err < 0) 191762306a36Sopenharmony_ci dev_err(dc->dev, "failed to set parent clock: %d\n", err); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci /* 192062306a36Sopenharmony_ci * Outputs may not want to change the parent clock rate. This is only 192162306a36Sopenharmony_ci * relevant to Tegra20 where only a single display PLL is available. 192262306a36Sopenharmony_ci * Since that PLL would typically be used for HDMI, an internal LVDS 192362306a36Sopenharmony_ci * panel would need to be driven by some other clock such as PLL_P 192462306a36Sopenharmony_ci * which is shared with other peripherals. Changing the clock rate 192562306a36Sopenharmony_ci * should therefore be avoided. 192662306a36Sopenharmony_ci */ 192762306a36Sopenharmony_ci if (state->pclk > 0) { 192862306a36Sopenharmony_ci err = clk_set_rate(state->clk, state->pclk); 192962306a36Sopenharmony_ci if (err < 0) 193062306a36Sopenharmony_ci dev_err(dc->dev, 193162306a36Sopenharmony_ci "failed to set clock rate to %lu Hz\n", 193262306a36Sopenharmony_ci state->pclk); 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci err = clk_set_rate(dc->clk, state->pclk); 193562306a36Sopenharmony_ci if (err < 0) 193662306a36Sopenharmony_ci dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n", 193762306a36Sopenharmony_ci dc->clk, state->pclk, err); 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), 194162306a36Sopenharmony_ci state->div); 194262306a36Sopenharmony_ci DRM_DEBUG_KMS("pclk: %lu\n", state->pclk); 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci tegra_dc_update_voltage_state(dc, state); 194562306a36Sopenharmony_ci} 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_cistatic void tegra_dc_stop(struct tegra_dc *dc) 194862306a36Sopenharmony_ci{ 194962306a36Sopenharmony_ci u32 value; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci /* stop the display controller */ 195262306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); 195362306a36Sopenharmony_ci value &= ~DISP_CTRL_MODE_MASK; 195462306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci tegra_dc_commit(dc); 195762306a36Sopenharmony_ci} 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_cistatic bool tegra_dc_idle(struct tegra_dc *dc) 196062306a36Sopenharmony_ci{ 196162306a36Sopenharmony_ci u32 value; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND); 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci return (value & DISP_CTRL_MODE_MASK) == 0; 196662306a36Sopenharmony_ci} 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_cistatic int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout) 196962306a36Sopenharmony_ci{ 197062306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(timeout); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci while (time_before(jiffies, timeout)) { 197362306a36Sopenharmony_ci if (tegra_dc_idle(dc)) 197462306a36Sopenharmony_ci return 0; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci usleep_range(1000, 2000); 197762306a36Sopenharmony_ci } 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci dev_dbg(dc->dev, "timeout waiting for DC to become idle\n"); 198062306a36Sopenharmony_ci return -ETIMEDOUT; 198162306a36Sopenharmony_ci} 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_cistatic void 198462306a36Sopenharmony_citegra_crtc_update_memory_bandwidth(struct drm_crtc *crtc, 198562306a36Sopenharmony_ci struct drm_atomic_state *state, 198662306a36Sopenharmony_ci bool prepare_bandwidth_transition) 198762306a36Sopenharmony_ci{ 198862306a36Sopenharmony_ci const struct tegra_plane_state *old_tegra_state, *new_tegra_state; 198962306a36Sopenharmony_ci u32 i, new_avg_bw, old_avg_bw, new_peak_bw, old_peak_bw; 199062306a36Sopenharmony_ci const struct drm_plane_state *old_plane_state; 199162306a36Sopenharmony_ci const struct drm_crtc_state *old_crtc_state; 199262306a36Sopenharmony_ci struct tegra_dc_window window, old_window; 199362306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 199462306a36Sopenharmony_ci struct tegra_plane *tegra; 199562306a36Sopenharmony_ci struct drm_plane *plane; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci if (dc->soc->has_nvdisplay) 199862306a36Sopenharmony_ci return; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci if (!crtc->state->active) { 200362306a36Sopenharmony_ci if (!old_crtc_state->active) 200462306a36Sopenharmony_ci return; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci /* 200762306a36Sopenharmony_ci * When CRTC is disabled on DPMS, the state of attached planes 200862306a36Sopenharmony_ci * is kept unchanged. Hence we need to enforce removal of the 200962306a36Sopenharmony_ci * bandwidths from the ICC paths. 201062306a36Sopenharmony_ci */ 201162306a36Sopenharmony_ci drm_atomic_crtc_for_each_plane(plane, crtc) { 201262306a36Sopenharmony_ci tegra = to_tegra_plane(plane); 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci icc_set_bw(tegra->icc_mem, 0, 0); 201562306a36Sopenharmony_ci icc_set_bw(tegra->icc_mem_vfilter, 0, 0); 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci return; 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci for_each_old_plane_in_state(old_crtc_state->state, plane, 202262306a36Sopenharmony_ci old_plane_state, i) { 202362306a36Sopenharmony_ci old_tegra_state = to_const_tegra_plane_state(old_plane_state); 202462306a36Sopenharmony_ci new_tegra_state = to_const_tegra_plane_state(plane->state); 202562306a36Sopenharmony_ci tegra = to_tegra_plane(plane); 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci /* 202862306a36Sopenharmony_ci * We're iterating over the global atomic state and it contains 202962306a36Sopenharmony_ci * planes from another CRTC, hence we need to filter out the 203062306a36Sopenharmony_ci * planes unrelated to this CRTC. 203162306a36Sopenharmony_ci */ 203262306a36Sopenharmony_ci if (tegra->dc != dc) 203362306a36Sopenharmony_ci continue; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci new_avg_bw = new_tegra_state->avg_memory_bandwidth; 203662306a36Sopenharmony_ci old_avg_bw = old_tegra_state->avg_memory_bandwidth; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci new_peak_bw = new_tegra_state->total_peak_memory_bandwidth; 203962306a36Sopenharmony_ci old_peak_bw = old_tegra_state->total_peak_memory_bandwidth; 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci /* 204262306a36Sopenharmony_ci * See the comment related to !crtc->state->active above, 204362306a36Sopenharmony_ci * which explains why bandwidths need to be updated when 204462306a36Sopenharmony_ci * CRTC is turning ON. 204562306a36Sopenharmony_ci */ 204662306a36Sopenharmony_ci if (new_avg_bw == old_avg_bw && new_peak_bw == old_peak_bw && 204762306a36Sopenharmony_ci old_crtc_state->active) 204862306a36Sopenharmony_ci continue; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci window.src.h = drm_rect_height(&plane->state->src) >> 16; 205162306a36Sopenharmony_ci window.dst.h = drm_rect_height(&plane->state->dst); 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci old_window.src.h = drm_rect_height(&old_plane_state->src) >> 16; 205462306a36Sopenharmony_ci old_window.dst.h = drm_rect_height(&old_plane_state->dst); 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci /* 205762306a36Sopenharmony_ci * During the preparation phase (atomic_begin), the memory 205862306a36Sopenharmony_ci * freq should go high before the DC changes are committed 205962306a36Sopenharmony_ci * if bandwidth requirement goes up, otherwise memory freq 206062306a36Sopenharmony_ci * should to stay high if BW requirement goes down. The 206162306a36Sopenharmony_ci * opposite applies to the completion phase (post_commit). 206262306a36Sopenharmony_ci */ 206362306a36Sopenharmony_ci if (prepare_bandwidth_transition) { 206462306a36Sopenharmony_ci new_avg_bw = max(old_avg_bw, new_avg_bw); 206562306a36Sopenharmony_ci new_peak_bw = max(old_peak_bw, new_peak_bw); 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (tegra_plane_use_vertical_filtering(tegra, &old_window)) 206862306a36Sopenharmony_ci window = old_window; 206962306a36Sopenharmony_ci } 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci icc_set_bw(tegra->icc_mem, new_avg_bw, new_peak_bw); 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci if (tegra_plane_use_vertical_filtering(tegra, &window)) 207462306a36Sopenharmony_ci icc_set_bw(tegra->icc_mem_vfilter, new_avg_bw, new_peak_bw); 207562306a36Sopenharmony_ci else 207662306a36Sopenharmony_ci icc_set_bw(tegra->icc_mem_vfilter, 0, 0); 207762306a36Sopenharmony_ci } 207862306a36Sopenharmony_ci} 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_cistatic void tegra_crtc_atomic_disable(struct drm_crtc *crtc, 208162306a36Sopenharmony_ci struct drm_atomic_state *state) 208262306a36Sopenharmony_ci{ 208362306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 208462306a36Sopenharmony_ci u32 value; 208562306a36Sopenharmony_ci int err; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci if (!tegra_dc_idle(dc)) { 208862306a36Sopenharmony_ci tegra_dc_stop(dc); 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci /* 209162306a36Sopenharmony_ci * Ignore the return value, there isn't anything useful to do 209262306a36Sopenharmony_ci * in case this fails. 209362306a36Sopenharmony_ci */ 209462306a36Sopenharmony_ci tegra_dc_wait_idle(dc, 100); 209562306a36Sopenharmony_ci } 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci /* 209862306a36Sopenharmony_ci * This should really be part of the RGB encoder driver, but clearing 209962306a36Sopenharmony_ci * these bits has the side-effect of stopping the display controller. 210062306a36Sopenharmony_ci * When that happens no VBLANK interrupts will be raised. At the same 210162306a36Sopenharmony_ci * time the encoder is disabled before the display controller, so the 210262306a36Sopenharmony_ci * above code is always going to timeout waiting for the controller 210362306a36Sopenharmony_ci * to go idle. 210462306a36Sopenharmony_ci * 210562306a36Sopenharmony_ci * Given the close coupling between the RGB encoder and the display 210662306a36Sopenharmony_ci * controller doing it here is still kind of okay. None of the other 210762306a36Sopenharmony_ci * encoder drivers require these bits to be cleared. 210862306a36Sopenharmony_ci * 210962306a36Sopenharmony_ci * XXX: Perhaps given that the display controller is switched off at 211062306a36Sopenharmony_ci * this point anyway maybe clearing these bits isn't even useful for 211162306a36Sopenharmony_ci * the RGB encoder? 211262306a36Sopenharmony_ci */ 211362306a36Sopenharmony_ci if (dc->rgb) { 211462306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 211562306a36Sopenharmony_ci value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 211662306a36Sopenharmony_ci PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); 211762306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 211862306a36Sopenharmony_ci } 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci tegra_dc_stats_reset(&dc->stats); 212162306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci if (crtc->state->event) { 212662306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 212762306a36Sopenharmony_ci crtc->state->event = NULL; 212862306a36Sopenharmony_ci } 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci err = host1x_client_suspend(&dc->client); 213362306a36Sopenharmony_ci if (err < 0) 213462306a36Sopenharmony_ci dev_err(dc->dev, "failed to suspend: %d\n", err); 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci if (dc->has_opp_table) { 213762306a36Sopenharmony_ci err = dev_pm_genpd_set_performance_state(dc->dev, 0); 213862306a36Sopenharmony_ci if (err) 213962306a36Sopenharmony_ci dev_err(dc->dev, 214062306a36Sopenharmony_ci "failed to clear power domain state: %d\n", err); 214162306a36Sopenharmony_ci } 214262306a36Sopenharmony_ci} 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_cistatic void tegra_crtc_atomic_enable(struct drm_crtc *crtc, 214562306a36Sopenharmony_ci struct drm_atomic_state *state) 214662306a36Sopenharmony_ci{ 214762306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 214862306a36Sopenharmony_ci struct tegra_dc_state *crtc_state = to_dc_state(crtc->state); 214962306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 215062306a36Sopenharmony_ci u32 value; 215162306a36Sopenharmony_ci int err; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci /* apply PLL changes */ 215462306a36Sopenharmony_ci tegra_dc_set_clock_rate(dc, crtc_state); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci err = host1x_client_resume(&dc->client); 215762306a36Sopenharmony_ci if (err < 0) { 215862306a36Sopenharmony_ci dev_err(dc->dev, "failed to resume: %d\n", err); 215962306a36Sopenharmony_ci return; 216062306a36Sopenharmony_ci } 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci /* initialize display controller */ 216362306a36Sopenharmony_ci if (dc->syncpt) { 216462306a36Sopenharmony_ci u32 syncpt = host1x_syncpt_id(dc->syncpt), enable; 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci if (dc->soc->has_nvdisplay) 216762306a36Sopenharmony_ci enable = 1 << 31; 216862306a36Sopenharmony_ci else 216962306a36Sopenharmony_ci enable = 1 << 8; 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci value = SYNCPT_CNTRL_NO_STALL; 217262306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci value = enable | syncpt; 217562306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); 217662306a36Sopenharmony_ci } 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci if (dc->soc->has_nvdisplay) { 217962306a36Sopenharmony_ci value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | 218062306a36Sopenharmony_ci DSC_OBUF_UF_INT; 218162306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | 218462306a36Sopenharmony_ci DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT | 218562306a36Sopenharmony_ci HEAD_UF_INT | MSF_INT | REG_TMOUT_INT | 218662306a36Sopenharmony_ci REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT | 218762306a36Sopenharmony_ci VBLANK_INT | FRAME_END_INT; 218862306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT | 219162306a36Sopenharmony_ci FRAME_END_INT; 219262306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT; 219562306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); 219862306a36Sopenharmony_ci } else { 219962306a36Sopenharmony_ci value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 220062306a36Sopenharmony_ci WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 220162306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 220462306a36Sopenharmony_ci WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 220562306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci /* initialize timer */ 220862306a36Sopenharmony_ci value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | 220962306a36Sopenharmony_ci WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); 221062306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | 221362306a36Sopenharmony_ci WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); 221462306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 221762306a36Sopenharmony_ci WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 221862306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 222162306a36Sopenharmony_ci WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 222262306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 222362306a36Sopenharmony_ci } 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci if (dc->soc->supports_background_color) 222662306a36Sopenharmony_ci tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR); 222762306a36Sopenharmony_ci else 222862306a36Sopenharmony_ci tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci /* apply pixel clock changes */ 223162306a36Sopenharmony_ci if (!dc->soc->has_nvdisplay) { 223262306a36Sopenharmony_ci value = SHIFT_CLK_DIVIDER(crtc_state->div) | PIXEL_CLK_DIVIDER_PCD1; 223362306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); 223462306a36Sopenharmony_ci } 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci /* program display mode */ 223762306a36Sopenharmony_ci tegra_dc_set_timings(dc, mode); 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci /* interlacing isn't supported yet, so disable it */ 224062306a36Sopenharmony_ci if (dc->soc->supports_interlacing) { 224162306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); 224262306a36Sopenharmony_ci value &= ~INTERLACE_ENABLE; 224362306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); 224462306a36Sopenharmony_ci } 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); 224762306a36Sopenharmony_ci value &= ~DISP_CTRL_MODE_MASK; 224862306a36Sopenharmony_ci value |= DISP_CTRL_MODE_C_DISPLAY; 224962306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci if (!dc->soc->has_nvdisplay) { 225262306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 225362306a36Sopenharmony_ci value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 225462306a36Sopenharmony_ci PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; 225562306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 225662306a36Sopenharmony_ci } 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci /* enable underflow reporting and display red for missing pixels */ 225962306a36Sopenharmony_ci if (dc->soc->has_nvdisplay) { 226062306a36Sopenharmony_ci value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE; 226162306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW); 226262306a36Sopenharmony_ci } 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci if (dc->rgb) { 226562306a36Sopenharmony_ci /* XXX: parameterize? */ 226662306a36Sopenharmony_ci value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE; 226762306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS); 226862306a36Sopenharmony_ci } 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci tegra_dc_commit(dc); 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 227362306a36Sopenharmony_ci} 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_cistatic void tegra_crtc_atomic_begin(struct drm_crtc *crtc, 227662306a36Sopenharmony_ci struct drm_atomic_state *state) 227762306a36Sopenharmony_ci{ 227862306a36Sopenharmony_ci unsigned long flags; 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci tegra_crtc_update_memory_bandwidth(crtc, state, true); 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci if (crtc->state->event) { 228362306a36Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci if (drm_crtc_vblank_get(crtc) != 0) 228662306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 228762306a36Sopenharmony_ci else 228862306a36Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, crtc->state->event); 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci crtc->state->event = NULL; 229362306a36Sopenharmony_ci } 229462306a36Sopenharmony_ci} 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_cistatic void tegra_crtc_atomic_flush(struct drm_crtc *crtc, 229762306a36Sopenharmony_ci struct drm_atomic_state *state) 229862306a36Sopenharmony_ci{ 229962306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 230062306a36Sopenharmony_ci crtc); 230162306a36Sopenharmony_ci struct tegra_dc_state *dc_state = to_dc_state(crtc_state); 230262306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 230362306a36Sopenharmony_ci u32 value; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci value = dc_state->planes << 8 | GENERAL_UPDATE; 230662306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 230762306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci value = dc_state->planes | GENERAL_ACT_REQ; 231062306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 231162306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 231262306a36Sopenharmony_ci} 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_cistatic bool tegra_plane_is_cursor(const struct drm_plane_state *state) 231562306a36Sopenharmony_ci{ 231662306a36Sopenharmony_ci const struct tegra_dc_soc_info *soc = to_tegra_dc(state->crtc)->soc; 231762306a36Sopenharmony_ci const struct drm_format_info *fmt = state->fb->format; 231862306a36Sopenharmony_ci unsigned int src_w = drm_rect_width(&state->src) >> 16; 231962306a36Sopenharmony_ci unsigned int dst_w = drm_rect_width(&state->dst); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci if (state->plane->type != DRM_PLANE_TYPE_CURSOR) 232262306a36Sopenharmony_ci return false; 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci if (soc->supports_cursor) 232562306a36Sopenharmony_ci return true; 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci if (src_w != dst_w || fmt->num_planes != 1 || src_w * fmt->cpp[0] > 256) 232862306a36Sopenharmony_ci return false; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci return true; 233162306a36Sopenharmony_ci} 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_cistatic unsigned long 233462306a36Sopenharmony_citegra_plane_overlap_mask(struct drm_crtc_state *state, 233562306a36Sopenharmony_ci const struct drm_plane_state *plane_state) 233662306a36Sopenharmony_ci{ 233762306a36Sopenharmony_ci const struct drm_plane_state *other_state; 233862306a36Sopenharmony_ci const struct tegra_plane *tegra; 233962306a36Sopenharmony_ci unsigned long overlap_mask = 0; 234062306a36Sopenharmony_ci struct drm_plane *plane; 234162306a36Sopenharmony_ci struct drm_rect rect; 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci if (!plane_state->visible || !plane_state->fb) 234462306a36Sopenharmony_ci return 0; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci /* 234762306a36Sopenharmony_ci * Data-prefetch FIFO will easily help to overcome temporal memory 234862306a36Sopenharmony_ci * pressure if other plane overlaps with the cursor plane. 234962306a36Sopenharmony_ci */ 235062306a36Sopenharmony_ci if (tegra_plane_is_cursor(plane_state)) 235162306a36Sopenharmony_ci return 0; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci drm_atomic_crtc_state_for_each_plane_state(plane, other_state, state) { 235462306a36Sopenharmony_ci rect = plane_state->dst; 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci tegra = to_tegra_plane(other_state->plane); 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci if (!other_state->visible || !other_state->fb) 235962306a36Sopenharmony_ci continue; 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci /* 236262306a36Sopenharmony_ci * Ignore cursor plane overlaps because it's not practical to 236362306a36Sopenharmony_ci * assume that it contributes to the bandwidth in overlapping 236462306a36Sopenharmony_ci * area if window width is small. 236562306a36Sopenharmony_ci */ 236662306a36Sopenharmony_ci if (tegra_plane_is_cursor(other_state)) 236762306a36Sopenharmony_ci continue; 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci if (drm_rect_intersect(&rect, &other_state->dst)) 237062306a36Sopenharmony_ci overlap_mask |= BIT(tegra->index); 237162306a36Sopenharmony_ci } 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci return overlap_mask; 237462306a36Sopenharmony_ci} 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_cistatic int tegra_crtc_calculate_memory_bandwidth(struct drm_crtc *crtc, 237762306a36Sopenharmony_ci struct drm_atomic_state *state) 237862306a36Sopenharmony_ci{ 237962306a36Sopenharmony_ci ulong overlap_mask[TEGRA_DC_LEGACY_PLANES_NUM] = {}, mask; 238062306a36Sopenharmony_ci u32 plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM] = {}; 238162306a36Sopenharmony_ci bool all_planes_overlap_simultaneously = true; 238262306a36Sopenharmony_ci const struct tegra_plane_state *tegra_state; 238362306a36Sopenharmony_ci const struct drm_plane_state *plane_state; 238462306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(crtc); 238562306a36Sopenharmony_ci struct drm_crtc_state *new_state; 238662306a36Sopenharmony_ci struct tegra_plane *tegra; 238762306a36Sopenharmony_ci struct drm_plane *plane; 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci /* 239062306a36Sopenharmony_ci * The nv-display uses shared planes. The algorithm below assumes 239162306a36Sopenharmony_ci * maximum 3 planes per-CRTC, this assumption isn't applicable to 239262306a36Sopenharmony_ci * the nv-display. Note that T124 support has additional windows, 239362306a36Sopenharmony_ci * but currently they aren't supported by the driver. 239462306a36Sopenharmony_ci */ 239562306a36Sopenharmony_ci if (dc->soc->has_nvdisplay) 239662306a36Sopenharmony_ci return 0; 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci new_state = drm_atomic_get_new_crtc_state(state, crtc); 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci /* 240162306a36Sopenharmony_ci * For overlapping planes pixel's data is fetched for each plane at 240262306a36Sopenharmony_ci * the same time, hence bandwidths are accumulated in this case. 240362306a36Sopenharmony_ci * This needs to be taken into account for calculating total bandwidth 240462306a36Sopenharmony_ci * consumed by all planes. 240562306a36Sopenharmony_ci * 240662306a36Sopenharmony_ci * Here we get the overlapping state of each plane, which is a 240762306a36Sopenharmony_ci * bitmask of plane indices telling with what planes there is an 240862306a36Sopenharmony_ci * overlap. Note that bitmask[plane] includes BIT(plane) in order 240962306a36Sopenharmony_ci * to make further code nicer and simpler. 241062306a36Sopenharmony_ci */ 241162306a36Sopenharmony_ci drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) { 241262306a36Sopenharmony_ci tegra_state = to_const_tegra_plane_state(plane_state); 241362306a36Sopenharmony_ci tegra = to_tegra_plane(plane); 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci if (WARN_ON_ONCE(tegra->index >= TEGRA_DC_LEGACY_PLANES_NUM)) 241662306a36Sopenharmony_ci return -EINVAL; 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci plane_peak_bw[tegra->index] = tegra_state->peak_memory_bandwidth; 241962306a36Sopenharmony_ci mask = tegra_plane_overlap_mask(new_state, plane_state); 242062306a36Sopenharmony_ci overlap_mask[tegra->index] = mask; 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci if (hweight_long(mask) != 3) 242362306a36Sopenharmony_ci all_planes_overlap_simultaneously = false; 242462306a36Sopenharmony_ci } 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci /* 242762306a36Sopenharmony_ci * Then we calculate maximum bandwidth of each plane state. 242862306a36Sopenharmony_ci * The bandwidth includes the plane BW + BW of the "simultaneously" 242962306a36Sopenharmony_ci * overlapping planes, where "simultaneously" means areas where DC 243062306a36Sopenharmony_ci * fetches from the planes simultaneously during of scan-out process. 243162306a36Sopenharmony_ci * 243262306a36Sopenharmony_ci * For example, if plane A overlaps with planes B and C, but B and C 243362306a36Sopenharmony_ci * don't overlap, then the peak bandwidth will be either in area where 243462306a36Sopenharmony_ci * A-and-B or A-and-C planes overlap. 243562306a36Sopenharmony_ci * 243662306a36Sopenharmony_ci * The plane_peak_bw[] contains peak memory bandwidth values of 243762306a36Sopenharmony_ci * each plane, this information is needed by interconnect provider 243862306a36Sopenharmony_ci * in order to set up latency allowance based on the peak BW, see 243962306a36Sopenharmony_ci * tegra_crtc_update_memory_bandwidth(). 244062306a36Sopenharmony_ci */ 244162306a36Sopenharmony_ci drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) { 244262306a36Sopenharmony_ci u32 i, old_peak_bw, new_peak_bw, overlap_bw = 0; 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci /* 244562306a36Sopenharmony_ci * Note that plane's atomic check doesn't touch the 244662306a36Sopenharmony_ci * total_peak_memory_bandwidth of enabled plane, hence the 244762306a36Sopenharmony_ci * current state contains the old bandwidth state from the 244862306a36Sopenharmony_ci * previous CRTC commit. 244962306a36Sopenharmony_ci */ 245062306a36Sopenharmony_ci tegra_state = to_const_tegra_plane_state(plane_state); 245162306a36Sopenharmony_ci tegra = to_tegra_plane(plane); 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci for_each_set_bit(i, &overlap_mask[tegra->index], 3) { 245462306a36Sopenharmony_ci if (i == tegra->index) 245562306a36Sopenharmony_ci continue; 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci if (all_planes_overlap_simultaneously) 245862306a36Sopenharmony_ci overlap_bw += plane_peak_bw[i]; 245962306a36Sopenharmony_ci else 246062306a36Sopenharmony_ci overlap_bw = max(overlap_bw, plane_peak_bw[i]); 246162306a36Sopenharmony_ci } 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci new_peak_bw = plane_peak_bw[tegra->index] + overlap_bw; 246462306a36Sopenharmony_ci old_peak_bw = tegra_state->total_peak_memory_bandwidth; 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci /* 246762306a36Sopenharmony_ci * If plane's peak bandwidth changed (for example plane isn't 246862306a36Sopenharmony_ci * overlapped anymore) and plane isn't in the atomic state, 246962306a36Sopenharmony_ci * then add plane to the state in order to have the bandwidth 247062306a36Sopenharmony_ci * updated. 247162306a36Sopenharmony_ci */ 247262306a36Sopenharmony_ci if (old_peak_bw != new_peak_bw) { 247362306a36Sopenharmony_ci struct tegra_plane_state *new_tegra_state; 247462306a36Sopenharmony_ci struct drm_plane_state *new_plane_state; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci new_plane_state = drm_atomic_get_plane_state(state, plane); 247762306a36Sopenharmony_ci if (IS_ERR(new_plane_state)) 247862306a36Sopenharmony_ci return PTR_ERR(new_plane_state); 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci new_tegra_state = to_tegra_plane_state(new_plane_state); 248162306a36Sopenharmony_ci new_tegra_state->total_peak_memory_bandwidth = new_peak_bw; 248262306a36Sopenharmony_ci } 248362306a36Sopenharmony_ci } 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci return 0; 248662306a36Sopenharmony_ci} 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_cistatic int tegra_crtc_atomic_check(struct drm_crtc *crtc, 248962306a36Sopenharmony_ci struct drm_atomic_state *state) 249062306a36Sopenharmony_ci{ 249162306a36Sopenharmony_ci int err; 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci err = tegra_crtc_calculate_memory_bandwidth(crtc, state); 249462306a36Sopenharmony_ci if (err) 249562306a36Sopenharmony_ci return err; 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci return 0; 249862306a36Sopenharmony_ci} 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_civoid tegra_crtc_atomic_post_commit(struct drm_crtc *crtc, 250162306a36Sopenharmony_ci struct drm_atomic_state *state) 250262306a36Sopenharmony_ci{ 250362306a36Sopenharmony_ci /* 250462306a36Sopenharmony_ci * Display bandwidth is allowed to go down only once hardware state 250562306a36Sopenharmony_ci * is known to be armed, i.e. state was committed and VBLANK event 250662306a36Sopenharmony_ci * received. 250762306a36Sopenharmony_ci */ 250862306a36Sopenharmony_ci tegra_crtc_update_memory_bandwidth(crtc, state, false); 250962306a36Sopenharmony_ci} 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { 251262306a36Sopenharmony_ci .atomic_check = tegra_crtc_atomic_check, 251362306a36Sopenharmony_ci .atomic_begin = tegra_crtc_atomic_begin, 251462306a36Sopenharmony_ci .atomic_flush = tegra_crtc_atomic_flush, 251562306a36Sopenharmony_ci .atomic_enable = tegra_crtc_atomic_enable, 251662306a36Sopenharmony_ci .atomic_disable = tegra_crtc_atomic_disable, 251762306a36Sopenharmony_ci}; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_cistatic irqreturn_t tegra_dc_irq(int irq, void *data) 252062306a36Sopenharmony_ci{ 252162306a36Sopenharmony_ci struct tegra_dc *dc = data; 252262306a36Sopenharmony_ci unsigned long status; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); 252562306a36Sopenharmony_ci tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci if (status & FRAME_END_INT) { 252862306a36Sopenharmony_ci /* 252962306a36Sopenharmony_ci dev_dbg(dc->dev, "%s(): frame end\n", __func__); 253062306a36Sopenharmony_ci */ 253162306a36Sopenharmony_ci dc->stats.frames_total++; 253262306a36Sopenharmony_ci dc->stats.frames++; 253362306a36Sopenharmony_ci } 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci if (status & VBLANK_INT) { 253662306a36Sopenharmony_ci /* 253762306a36Sopenharmony_ci dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); 253862306a36Sopenharmony_ci */ 253962306a36Sopenharmony_ci drm_crtc_handle_vblank(&dc->base); 254062306a36Sopenharmony_ci dc->stats.vblank_total++; 254162306a36Sopenharmony_ci dc->stats.vblank++; 254262306a36Sopenharmony_ci } 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { 254562306a36Sopenharmony_ci /* 254662306a36Sopenharmony_ci dev_dbg(dc->dev, "%s(): underflow\n", __func__); 254762306a36Sopenharmony_ci */ 254862306a36Sopenharmony_ci dc->stats.underflow_total++; 254962306a36Sopenharmony_ci dc->stats.underflow++; 255062306a36Sopenharmony_ci } 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) { 255362306a36Sopenharmony_ci /* 255462306a36Sopenharmony_ci dev_dbg(dc->dev, "%s(): overflow\n", __func__); 255562306a36Sopenharmony_ci */ 255662306a36Sopenharmony_ci dc->stats.overflow_total++; 255762306a36Sopenharmony_ci dc->stats.overflow++; 255862306a36Sopenharmony_ci } 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci if (status & HEAD_UF_INT) { 256162306a36Sopenharmony_ci dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__); 256262306a36Sopenharmony_ci dc->stats.underflow_total++; 256362306a36Sopenharmony_ci dc->stats.underflow++; 256462306a36Sopenharmony_ci } 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci return IRQ_HANDLED; 256762306a36Sopenharmony_ci} 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_cistatic bool tegra_dc_has_window_groups(struct tegra_dc *dc) 257062306a36Sopenharmony_ci{ 257162306a36Sopenharmony_ci unsigned int i; 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci if (!dc->soc->wgrps) 257462306a36Sopenharmony_ci return true; 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci for (i = 0; i < dc->soc->num_wgrps; i++) { 257762306a36Sopenharmony_ci const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci if (wgrp->dc == dc->pipe && wgrp->num_windows > 0) 258062306a36Sopenharmony_ci return true; 258162306a36Sopenharmony_ci } 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci return false; 258462306a36Sopenharmony_ci} 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_cistatic int tegra_dc_early_init(struct host1x_client *client) 258762306a36Sopenharmony_ci{ 258862306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(client->host); 258962306a36Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci tegra->num_crtcs++; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci return 0; 259462306a36Sopenharmony_ci} 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_cistatic int tegra_dc_init(struct host1x_client *client) 259762306a36Sopenharmony_ci{ 259862306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(client->host); 259962306a36Sopenharmony_ci unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; 260062306a36Sopenharmony_ci struct tegra_dc *dc = host1x_client_to_dc(client); 260162306a36Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 260262306a36Sopenharmony_ci struct drm_plane *primary = NULL; 260362306a36Sopenharmony_ci struct drm_plane *cursor = NULL; 260462306a36Sopenharmony_ci int err; 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_ci /* 260762306a36Sopenharmony_ci * DC has been reset by now, so VBLANK syncpoint can be released 260862306a36Sopenharmony_ci * for general use. 260962306a36Sopenharmony_ci */ 261062306a36Sopenharmony_ci host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe); 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci /* 261362306a36Sopenharmony_ci * XXX do not register DCs with no window groups because we cannot 261462306a36Sopenharmony_ci * assign a primary plane to them, which in turn will cause KMS to 261562306a36Sopenharmony_ci * crash. 261662306a36Sopenharmony_ci */ 261762306a36Sopenharmony_ci if (!tegra_dc_has_window_groups(dc)) 261862306a36Sopenharmony_ci return 0; 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci /* 262162306a36Sopenharmony_ci * Set the display hub as the host1x client parent for the display 262262306a36Sopenharmony_ci * controller. This is needed for the runtime reference counting that 262362306a36Sopenharmony_ci * ensures the display hub is always powered when any of the display 262462306a36Sopenharmony_ci * controllers are. 262562306a36Sopenharmony_ci */ 262662306a36Sopenharmony_ci if (dc->soc->has_nvdisplay) 262762306a36Sopenharmony_ci client->parent = &tegra->hub->client; 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci dc->syncpt = host1x_syncpt_request(client, flags); 263062306a36Sopenharmony_ci if (!dc->syncpt) 263162306a36Sopenharmony_ci dev_warn(dc->dev, "failed to allocate syncpoint\n"); 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_ci err = host1x_client_iommu_attach(client); 263462306a36Sopenharmony_ci if (err < 0 && err != -ENODEV) { 263562306a36Sopenharmony_ci dev_err(client->dev, "failed to attach to domain: %d\n", err); 263662306a36Sopenharmony_ci return err; 263762306a36Sopenharmony_ci } 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci if (dc->soc->wgrps) 264062306a36Sopenharmony_ci primary = tegra_dc_add_shared_planes(drm, dc); 264162306a36Sopenharmony_ci else 264262306a36Sopenharmony_ci primary = tegra_dc_add_planes(drm, dc); 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci if (IS_ERR(primary)) { 264562306a36Sopenharmony_ci err = PTR_ERR(primary); 264662306a36Sopenharmony_ci goto cleanup; 264762306a36Sopenharmony_ci } 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci if (dc->soc->supports_cursor) { 265062306a36Sopenharmony_ci cursor = tegra_dc_cursor_plane_create(drm, dc); 265162306a36Sopenharmony_ci if (IS_ERR(cursor)) { 265262306a36Sopenharmony_ci err = PTR_ERR(cursor); 265362306a36Sopenharmony_ci goto cleanup; 265462306a36Sopenharmony_ci } 265562306a36Sopenharmony_ci } else { 265662306a36Sopenharmony_ci /* dedicate one overlay to mouse cursor */ 265762306a36Sopenharmony_ci cursor = tegra_dc_overlay_plane_create(drm, dc, 2, true); 265862306a36Sopenharmony_ci if (IS_ERR(cursor)) { 265962306a36Sopenharmony_ci err = PTR_ERR(cursor); 266062306a36Sopenharmony_ci goto cleanup; 266162306a36Sopenharmony_ci } 266262306a36Sopenharmony_ci } 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor, 266562306a36Sopenharmony_ci &tegra_crtc_funcs, NULL); 266662306a36Sopenharmony_ci if (err < 0) 266762306a36Sopenharmony_ci goto cleanup; 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci /* 267262306a36Sopenharmony_ci * Keep track of the minimum pitch alignment across all display 267362306a36Sopenharmony_ci * controllers. 267462306a36Sopenharmony_ci */ 267562306a36Sopenharmony_ci if (dc->soc->pitch_align > tegra->pitch_align) 267662306a36Sopenharmony_ci tegra->pitch_align = dc->soc->pitch_align; 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci /* track maximum resolution */ 267962306a36Sopenharmony_ci if (dc->soc->has_nvdisplay) 268062306a36Sopenharmony_ci drm->mode_config.max_width = drm->mode_config.max_height = 16384; 268162306a36Sopenharmony_ci else 268262306a36Sopenharmony_ci drm->mode_config.max_width = drm->mode_config.max_height = 4096; 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci err = tegra_dc_rgb_init(drm, dc); 268562306a36Sopenharmony_ci if (err < 0 && err != -ENODEV) { 268662306a36Sopenharmony_ci dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); 268762306a36Sopenharmony_ci goto cleanup; 268862306a36Sopenharmony_ci } 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0, 269162306a36Sopenharmony_ci dev_name(dc->dev), dc); 269262306a36Sopenharmony_ci if (err < 0) { 269362306a36Sopenharmony_ci dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, 269462306a36Sopenharmony_ci err); 269562306a36Sopenharmony_ci goto cleanup; 269662306a36Sopenharmony_ci } 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci /* 269962306a36Sopenharmony_ci * Inherit the DMA parameters (such as maximum segment size) from the 270062306a36Sopenharmony_ci * parent host1x device. 270162306a36Sopenharmony_ci */ 270262306a36Sopenharmony_ci client->dev->dma_parms = client->host->dma_parms; 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci return 0; 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_cicleanup: 270762306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(cursor)) 270862306a36Sopenharmony_ci drm_plane_cleanup(cursor); 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci if (!IS_ERR(primary)) 271162306a36Sopenharmony_ci drm_plane_cleanup(primary); 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci host1x_client_iommu_detach(client); 271462306a36Sopenharmony_ci host1x_syncpt_put(dc->syncpt); 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci return err; 271762306a36Sopenharmony_ci} 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_cistatic int tegra_dc_exit(struct host1x_client *client) 272062306a36Sopenharmony_ci{ 272162306a36Sopenharmony_ci struct tegra_dc *dc = host1x_client_to_dc(client); 272262306a36Sopenharmony_ci int err; 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci if (!tegra_dc_has_window_groups(dc)) 272562306a36Sopenharmony_ci return 0; 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci /* avoid a dangling pointer just in case this disappears */ 272862306a36Sopenharmony_ci client->dev->dma_parms = NULL; 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci devm_free_irq(dc->dev, dc->irq, dc); 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci err = tegra_dc_rgb_exit(dc); 273362306a36Sopenharmony_ci if (err) { 273462306a36Sopenharmony_ci dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err); 273562306a36Sopenharmony_ci return err; 273662306a36Sopenharmony_ci } 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci host1x_client_iommu_detach(client); 273962306a36Sopenharmony_ci host1x_syncpt_put(dc->syncpt); 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_ci return 0; 274262306a36Sopenharmony_ci} 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_cistatic int tegra_dc_late_exit(struct host1x_client *client) 274562306a36Sopenharmony_ci{ 274662306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(client->host); 274762306a36Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci tegra->num_crtcs--; 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci return 0; 275262306a36Sopenharmony_ci} 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_cistatic int tegra_dc_runtime_suspend(struct host1x_client *client) 275562306a36Sopenharmony_ci{ 275662306a36Sopenharmony_ci struct tegra_dc *dc = host1x_client_to_dc(client); 275762306a36Sopenharmony_ci struct device *dev = client->dev; 275862306a36Sopenharmony_ci int err; 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci err = reset_control_assert(dc->rst); 276162306a36Sopenharmony_ci if (err < 0) { 276262306a36Sopenharmony_ci dev_err(dev, "failed to assert reset: %d\n", err); 276362306a36Sopenharmony_ci return err; 276462306a36Sopenharmony_ci } 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ci if (dc->soc->has_powergate) 276762306a36Sopenharmony_ci tegra_powergate_power_off(dc->powergate); 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci clk_disable_unprepare(dc->clk); 277062306a36Sopenharmony_ci pm_runtime_put_sync(dev); 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci return 0; 277362306a36Sopenharmony_ci} 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_cistatic int tegra_dc_runtime_resume(struct host1x_client *client) 277662306a36Sopenharmony_ci{ 277762306a36Sopenharmony_ci struct tegra_dc *dc = host1x_client_to_dc(client); 277862306a36Sopenharmony_ci struct device *dev = client->dev; 277962306a36Sopenharmony_ci int err; 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci err = pm_runtime_resume_and_get(dev); 278262306a36Sopenharmony_ci if (err < 0) { 278362306a36Sopenharmony_ci dev_err(dev, "failed to get runtime PM: %d\n", err); 278462306a36Sopenharmony_ci return err; 278562306a36Sopenharmony_ci } 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci if (dc->soc->has_powergate) { 278862306a36Sopenharmony_ci err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, 278962306a36Sopenharmony_ci dc->rst); 279062306a36Sopenharmony_ci if (err < 0) { 279162306a36Sopenharmony_ci dev_err(dev, "failed to power partition: %d\n", err); 279262306a36Sopenharmony_ci goto put_rpm; 279362306a36Sopenharmony_ci } 279462306a36Sopenharmony_ci } else { 279562306a36Sopenharmony_ci err = clk_prepare_enable(dc->clk); 279662306a36Sopenharmony_ci if (err < 0) { 279762306a36Sopenharmony_ci dev_err(dev, "failed to enable clock: %d\n", err); 279862306a36Sopenharmony_ci goto put_rpm; 279962306a36Sopenharmony_ci } 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci err = reset_control_deassert(dc->rst); 280262306a36Sopenharmony_ci if (err < 0) { 280362306a36Sopenharmony_ci dev_err(dev, "failed to deassert reset: %d\n", err); 280462306a36Sopenharmony_ci goto disable_clk; 280562306a36Sopenharmony_ci } 280662306a36Sopenharmony_ci } 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci return 0; 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_cidisable_clk: 281162306a36Sopenharmony_ci clk_disable_unprepare(dc->clk); 281262306a36Sopenharmony_ciput_rpm: 281362306a36Sopenharmony_ci pm_runtime_put_sync(dev); 281462306a36Sopenharmony_ci return err; 281562306a36Sopenharmony_ci} 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_cistatic const struct host1x_client_ops dc_client_ops = { 281862306a36Sopenharmony_ci .early_init = tegra_dc_early_init, 281962306a36Sopenharmony_ci .init = tegra_dc_init, 282062306a36Sopenharmony_ci .exit = tegra_dc_exit, 282162306a36Sopenharmony_ci .late_exit = tegra_dc_late_exit, 282262306a36Sopenharmony_ci .suspend = tegra_dc_runtime_suspend, 282362306a36Sopenharmony_ci .resume = tegra_dc_runtime_resume, 282462306a36Sopenharmony_ci}; 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra20_dc_soc_info = { 282762306a36Sopenharmony_ci .supports_background_color = false, 282862306a36Sopenharmony_ci .supports_interlacing = false, 282962306a36Sopenharmony_ci .supports_cursor = false, 283062306a36Sopenharmony_ci .supports_block_linear = false, 283162306a36Sopenharmony_ci .supports_sector_layout = false, 283262306a36Sopenharmony_ci .has_legacy_blending = true, 283362306a36Sopenharmony_ci .pitch_align = 8, 283462306a36Sopenharmony_ci .has_powergate = false, 283562306a36Sopenharmony_ci .coupled_pm = true, 283662306a36Sopenharmony_ci .has_nvdisplay = false, 283762306a36Sopenharmony_ci .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), 283862306a36Sopenharmony_ci .primary_formats = tegra20_primary_formats, 283962306a36Sopenharmony_ci .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), 284062306a36Sopenharmony_ci .overlay_formats = tegra20_overlay_formats, 284162306a36Sopenharmony_ci .modifiers = tegra20_modifiers, 284262306a36Sopenharmony_ci .has_win_a_without_filters = true, 284362306a36Sopenharmony_ci .has_win_b_vfilter_mem_client = true, 284462306a36Sopenharmony_ci .has_win_c_without_vert_filter = true, 284562306a36Sopenharmony_ci .plane_tiled_memory_bandwidth_x2 = false, 284662306a36Sopenharmony_ci .has_pll_d2_out0 = false, 284762306a36Sopenharmony_ci}; 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra30_dc_soc_info = { 285062306a36Sopenharmony_ci .supports_background_color = false, 285162306a36Sopenharmony_ci .supports_interlacing = false, 285262306a36Sopenharmony_ci .supports_cursor = false, 285362306a36Sopenharmony_ci .supports_block_linear = false, 285462306a36Sopenharmony_ci .supports_sector_layout = false, 285562306a36Sopenharmony_ci .has_legacy_blending = true, 285662306a36Sopenharmony_ci .pitch_align = 8, 285762306a36Sopenharmony_ci .has_powergate = false, 285862306a36Sopenharmony_ci .coupled_pm = false, 285962306a36Sopenharmony_ci .has_nvdisplay = false, 286062306a36Sopenharmony_ci .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), 286162306a36Sopenharmony_ci .primary_formats = tegra20_primary_formats, 286262306a36Sopenharmony_ci .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), 286362306a36Sopenharmony_ci .overlay_formats = tegra20_overlay_formats, 286462306a36Sopenharmony_ci .modifiers = tegra20_modifiers, 286562306a36Sopenharmony_ci .has_win_a_without_filters = false, 286662306a36Sopenharmony_ci .has_win_b_vfilter_mem_client = true, 286762306a36Sopenharmony_ci .has_win_c_without_vert_filter = false, 286862306a36Sopenharmony_ci .plane_tiled_memory_bandwidth_x2 = true, 286962306a36Sopenharmony_ci .has_pll_d2_out0 = true, 287062306a36Sopenharmony_ci}; 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra114_dc_soc_info = { 287362306a36Sopenharmony_ci .supports_background_color = false, 287462306a36Sopenharmony_ci .supports_interlacing = false, 287562306a36Sopenharmony_ci .supports_cursor = false, 287662306a36Sopenharmony_ci .supports_block_linear = false, 287762306a36Sopenharmony_ci .supports_sector_layout = false, 287862306a36Sopenharmony_ci .has_legacy_blending = true, 287962306a36Sopenharmony_ci .pitch_align = 64, 288062306a36Sopenharmony_ci .has_powergate = true, 288162306a36Sopenharmony_ci .coupled_pm = false, 288262306a36Sopenharmony_ci .has_nvdisplay = false, 288362306a36Sopenharmony_ci .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), 288462306a36Sopenharmony_ci .primary_formats = tegra114_primary_formats, 288562306a36Sopenharmony_ci .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), 288662306a36Sopenharmony_ci .overlay_formats = tegra114_overlay_formats, 288762306a36Sopenharmony_ci .modifiers = tegra20_modifiers, 288862306a36Sopenharmony_ci .has_win_a_without_filters = false, 288962306a36Sopenharmony_ci .has_win_b_vfilter_mem_client = false, 289062306a36Sopenharmony_ci .has_win_c_without_vert_filter = false, 289162306a36Sopenharmony_ci .plane_tiled_memory_bandwidth_x2 = true, 289262306a36Sopenharmony_ci .has_pll_d2_out0 = true, 289362306a36Sopenharmony_ci}; 289462306a36Sopenharmony_ci 289562306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra124_dc_soc_info = { 289662306a36Sopenharmony_ci .supports_background_color = true, 289762306a36Sopenharmony_ci .supports_interlacing = true, 289862306a36Sopenharmony_ci .supports_cursor = true, 289962306a36Sopenharmony_ci .supports_block_linear = true, 290062306a36Sopenharmony_ci .supports_sector_layout = false, 290162306a36Sopenharmony_ci .has_legacy_blending = false, 290262306a36Sopenharmony_ci .pitch_align = 64, 290362306a36Sopenharmony_ci .has_powergate = true, 290462306a36Sopenharmony_ci .coupled_pm = false, 290562306a36Sopenharmony_ci .has_nvdisplay = false, 290662306a36Sopenharmony_ci .num_primary_formats = ARRAY_SIZE(tegra124_primary_formats), 290762306a36Sopenharmony_ci .primary_formats = tegra124_primary_formats, 290862306a36Sopenharmony_ci .num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats), 290962306a36Sopenharmony_ci .overlay_formats = tegra124_overlay_formats, 291062306a36Sopenharmony_ci .modifiers = tegra124_modifiers, 291162306a36Sopenharmony_ci .has_win_a_without_filters = false, 291262306a36Sopenharmony_ci .has_win_b_vfilter_mem_client = false, 291362306a36Sopenharmony_ci .has_win_c_without_vert_filter = false, 291462306a36Sopenharmony_ci .plane_tiled_memory_bandwidth_x2 = false, 291562306a36Sopenharmony_ci .has_pll_d2_out0 = true, 291662306a36Sopenharmony_ci}; 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra210_dc_soc_info = { 291962306a36Sopenharmony_ci .supports_background_color = true, 292062306a36Sopenharmony_ci .supports_interlacing = true, 292162306a36Sopenharmony_ci .supports_cursor = true, 292262306a36Sopenharmony_ci .supports_block_linear = true, 292362306a36Sopenharmony_ci .supports_sector_layout = false, 292462306a36Sopenharmony_ci .has_legacy_blending = false, 292562306a36Sopenharmony_ci .pitch_align = 64, 292662306a36Sopenharmony_ci .has_powergate = true, 292762306a36Sopenharmony_ci .coupled_pm = false, 292862306a36Sopenharmony_ci .has_nvdisplay = false, 292962306a36Sopenharmony_ci .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), 293062306a36Sopenharmony_ci .primary_formats = tegra114_primary_formats, 293162306a36Sopenharmony_ci .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), 293262306a36Sopenharmony_ci .overlay_formats = tegra114_overlay_formats, 293362306a36Sopenharmony_ci .modifiers = tegra124_modifiers, 293462306a36Sopenharmony_ci .has_win_a_without_filters = false, 293562306a36Sopenharmony_ci .has_win_b_vfilter_mem_client = false, 293662306a36Sopenharmony_ci .has_win_c_without_vert_filter = false, 293762306a36Sopenharmony_ci .plane_tiled_memory_bandwidth_x2 = false, 293862306a36Sopenharmony_ci .has_pll_d2_out0 = true, 293962306a36Sopenharmony_ci}; 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_cistatic const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { 294262306a36Sopenharmony_ci { 294362306a36Sopenharmony_ci .index = 0, 294462306a36Sopenharmony_ci .dc = 0, 294562306a36Sopenharmony_ci .windows = (const unsigned int[]) { 0 }, 294662306a36Sopenharmony_ci .num_windows = 1, 294762306a36Sopenharmony_ci }, { 294862306a36Sopenharmony_ci .index = 1, 294962306a36Sopenharmony_ci .dc = 1, 295062306a36Sopenharmony_ci .windows = (const unsigned int[]) { 1 }, 295162306a36Sopenharmony_ci .num_windows = 1, 295262306a36Sopenharmony_ci }, { 295362306a36Sopenharmony_ci .index = 2, 295462306a36Sopenharmony_ci .dc = 1, 295562306a36Sopenharmony_ci .windows = (const unsigned int[]) { 2 }, 295662306a36Sopenharmony_ci .num_windows = 1, 295762306a36Sopenharmony_ci }, { 295862306a36Sopenharmony_ci .index = 3, 295962306a36Sopenharmony_ci .dc = 2, 296062306a36Sopenharmony_ci .windows = (const unsigned int[]) { 3 }, 296162306a36Sopenharmony_ci .num_windows = 1, 296262306a36Sopenharmony_ci }, { 296362306a36Sopenharmony_ci .index = 4, 296462306a36Sopenharmony_ci .dc = 2, 296562306a36Sopenharmony_ci .windows = (const unsigned int[]) { 4 }, 296662306a36Sopenharmony_ci .num_windows = 1, 296762306a36Sopenharmony_ci }, { 296862306a36Sopenharmony_ci .index = 5, 296962306a36Sopenharmony_ci .dc = 2, 297062306a36Sopenharmony_ci .windows = (const unsigned int[]) { 5 }, 297162306a36Sopenharmony_ci .num_windows = 1, 297262306a36Sopenharmony_ci }, 297362306a36Sopenharmony_ci}; 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra186_dc_soc_info = { 297662306a36Sopenharmony_ci .supports_background_color = true, 297762306a36Sopenharmony_ci .supports_interlacing = true, 297862306a36Sopenharmony_ci .supports_cursor = true, 297962306a36Sopenharmony_ci .supports_block_linear = true, 298062306a36Sopenharmony_ci .supports_sector_layout = false, 298162306a36Sopenharmony_ci .has_legacy_blending = false, 298262306a36Sopenharmony_ci .pitch_align = 64, 298362306a36Sopenharmony_ci .has_powergate = false, 298462306a36Sopenharmony_ci .coupled_pm = false, 298562306a36Sopenharmony_ci .has_nvdisplay = true, 298662306a36Sopenharmony_ci .wgrps = tegra186_dc_wgrps, 298762306a36Sopenharmony_ci .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), 298862306a36Sopenharmony_ci .plane_tiled_memory_bandwidth_x2 = false, 298962306a36Sopenharmony_ci .has_pll_d2_out0 = false, 299062306a36Sopenharmony_ci}; 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_cistatic const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = { 299362306a36Sopenharmony_ci { 299462306a36Sopenharmony_ci .index = 0, 299562306a36Sopenharmony_ci .dc = 0, 299662306a36Sopenharmony_ci .windows = (const unsigned int[]) { 0 }, 299762306a36Sopenharmony_ci .num_windows = 1, 299862306a36Sopenharmony_ci }, { 299962306a36Sopenharmony_ci .index = 1, 300062306a36Sopenharmony_ci .dc = 1, 300162306a36Sopenharmony_ci .windows = (const unsigned int[]) { 1 }, 300262306a36Sopenharmony_ci .num_windows = 1, 300362306a36Sopenharmony_ci }, { 300462306a36Sopenharmony_ci .index = 2, 300562306a36Sopenharmony_ci .dc = 1, 300662306a36Sopenharmony_ci .windows = (const unsigned int[]) { 2 }, 300762306a36Sopenharmony_ci .num_windows = 1, 300862306a36Sopenharmony_ci }, { 300962306a36Sopenharmony_ci .index = 3, 301062306a36Sopenharmony_ci .dc = 2, 301162306a36Sopenharmony_ci .windows = (const unsigned int[]) { 3 }, 301262306a36Sopenharmony_ci .num_windows = 1, 301362306a36Sopenharmony_ci }, { 301462306a36Sopenharmony_ci .index = 4, 301562306a36Sopenharmony_ci .dc = 2, 301662306a36Sopenharmony_ci .windows = (const unsigned int[]) { 4 }, 301762306a36Sopenharmony_ci .num_windows = 1, 301862306a36Sopenharmony_ci }, { 301962306a36Sopenharmony_ci .index = 5, 302062306a36Sopenharmony_ci .dc = 2, 302162306a36Sopenharmony_ci .windows = (const unsigned int[]) { 5 }, 302262306a36Sopenharmony_ci .num_windows = 1, 302362306a36Sopenharmony_ci }, 302462306a36Sopenharmony_ci}; 302562306a36Sopenharmony_ci 302662306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra194_dc_soc_info = { 302762306a36Sopenharmony_ci .supports_background_color = true, 302862306a36Sopenharmony_ci .supports_interlacing = true, 302962306a36Sopenharmony_ci .supports_cursor = true, 303062306a36Sopenharmony_ci .supports_block_linear = true, 303162306a36Sopenharmony_ci .supports_sector_layout = true, 303262306a36Sopenharmony_ci .has_legacy_blending = false, 303362306a36Sopenharmony_ci .pitch_align = 64, 303462306a36Sopenharmony_ci .has_powergate = false, 303562306a36Sopenharmony_ci .coupled_pm = false, 303662306a36Sopenharmony_ci .has_nvdisplay = true, 303762306a36Sopenharmony_ci .wgrps = tegra194_dc_wgrps, 303862306a36Sopenharmony_ci .num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps), 303962306a36Sopenharmony_ci .plane_tiled_memory_bandwidth_x2 = false, 304062306a36Sopenharmony_ci .has_pll_d2_out0 = false, 304162306a36Sopenharmony_ci}; 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_cistatic const struct of_device_id tegra_dc_of_match[] = { 304462306a36Sopenharmony_ci { 304562306a36Sopenharmony_ci .compatible = "nvidia,tegra194-dc", 304662306a36Sopenharmony_ci .data = &tegra194_dc_soc_info, 304762306a36Sopenharmony_ci }, { 304862306a36Sopenharmony_ci .compatible = "nvidia,tegra186-dc", 304962306a36Sopenharmony_ci .data = &tegra186_dc_soc_info, 305062306a36Sopenharmony_ci }, { 305162306a36Sopenharmony_ci .compatible = "nvidia,tegra210-dc", 305262306a36Sopenharmony_ci .data = &tegra210_dc_soc_info, 305362306a36Sopenharmony_ci }, { 305462306a36Sopenharmony_ci .compatible = "nvidia,tegra124-dc", 305562306a36Sopenharmony_ci .data = &tegra124_dc_soc_info, 305662306a36Sopenharmony_ci }, { 305762306a36Sopenharmony_ci .compatible = "nvidia,tegra114-dc", 305862306a36Sopenharmony_ci .data = &tegra114_dc_soc_info, 305962306a36Sopenharmony_ci }, { 306062306a36Sopenharmony_ci .compatible = "nvidia,tegra30-dc", 306162306a36Sopenharmony_ci .data = &tegra30_dc_soc_info, 306262306a36Sopenharmony_ci }, { 306362306a36Sopenharmony_ci .compatible = "nvidia,tegra20-dc", 306462306a36Sopenharmony_ci .data = &tegra20_dc_soc_info, 306562306a36Sopenharmony_ci }, { 306662306a36Sopenharmony_ci /* sentinel */ 306762306a36Sopenharmony_ci } 306862306a36Sopenharmony_ci}; 306962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_dc_of_match); 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_cistatic int tegra_dc_parse_dt(struct tegra_dc *dc) 307262306a36Sopenharmony_ci{ 307362306a36Sopenharmony_ci struct device_node *np; 307462306a36Sopenharmony_ci u32 value = 0; 307562306a36Sopenharmony_ci int err; 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci err = of_property_read_u32(dc->dev->of_node, "nvidia,head", &value); 307862306a36Sopenharmony_ci if (err < 0) { 307962306a36Sopenharmony_ci dev_err(dc->dev, "missing \"nvidia,head\" property\n"); 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci /* 308262306a36Sopenharmony_ci * If the nvidia,head property isn't present, try to find the 308362306a36Sopenharmony_ci * correct head number by looking up the position of this 308462306a36Sopenharmony_ci * display controller's node within the device tree. Assuming 308562306a36Sopenharmony_ci * that the nodes are ordered properly in the DTS file and 308662306a36Sopenharmony_ci * that the translation into a flattened device tree blob 308762306a36Sopenharmony_ci * preserves that ordering this will actually yield the right 308862306a36Sopenharmony_ci * head number. 308962306a36Sopenharmony_ci * 309062306a36Sopenharmony_ci * If those assumptions don't hold, this will still work for 309162306a36Sopenharmony_ci * cases where only a single display controller is used. 309262306a36Sopenharmony_ci */ 309362306a36Sopenharmony_ci for_each_matching_node(np, tegra_dc_of_match) { 309462306a36Sopenharmony_ci if (np == dc->dev->of_node) { 309562306a36Sopenharmony_ci of_node_put(np); 309662306a36Sopenharmony_ci break; 309762306a36Sopenharmony_ci } 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci value++; 310062306a36Sopenharmony_ci } 310162306a36Sopenharmony_ci } 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci dc->pipe = value; 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci return 0; 310662306a36Sopenharmony_ci} 310762306a36Sopenharmony_ci 310862306a36Sopenharmony_cistatic int tegra_dc_match_by_pipe(struct device *dev, const void *data) 310962306a36Sopenharmony_ci{ 311062306a36Sopenharmony_ci struct tegra_dc *dc = dev_get_drvdata(dev); 311162306a36Sopenharmony_ci unsigned int pipe = (unsigned long)(void *)data; 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci return dc->pipe == pipe; 311462306a36Sopenharmony_ci} 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_cistatic int tegra_dc_couple(struct tegra_dc *dc) 311762306a36Sopenharmony_ci{ 311862306a36Sopenharmony_ci /* 311962306a36Sopenharmony_ci * On Tegra20, DC1 requires DC0 to be taken out of reset in order to 312062306a36Sopenharmony_ci * be enabled, otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND / 312162306a36Sopenharmony_ci * POWER_CONTROL registers during CRTC enabling. 312262306a36Sopenharmony_ci */ 312362306a36Sopenharmony_ci if (dc->soc->coupled_pm && dc->pipe == 1) { 312462306a36Sopenharmony_ci struct device *companion; 312562306a36Sopenharmony_ci struct tegra_dc *parent; 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci companion = driver_find_device(dc->dev->driver, NULL, (const void *)0, 312862306a36Sopenharmony_ci tegra_dc_match_by_pipe); 312962306a36Sopenharmony_ci if (!companion) 313062306a36Sopenharmony_ci return -EPROBE_DEFER; 313162306a36Sopenharmony_ci 313262306a36Sopenharmony_ci parent = dev_get_drvdata(companion); 313362306a36Sopenharmony_ci dc->client.parent = &parent->client; 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion)); 313662306a36Sopenharmony_ci } 313762306a36Sopenharmony_ci 313862306a36Sopenharmony_ci return 0; 313962306a36Sopenharmony_ci} 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_cistatic int tegra_dc_init_opp_table(struct tegra_dc *dc) 314262306a36Sopenharmony_ci{ 314362306a36Sopenharmony_ci struct tegra_core_opp_params opp_params = {}; 314462306a36Sopenharmony_ci int err; 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci err = devm_tegra_core_dev_init_opp_table(dc->dev, &opp_params); 314762306a36Sopenharmony_ci if (err && err != -ENODEV) 314862306a36Sopenharmony_ci return err; 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci if (err) 315162306a36Sopenharmony_ci dc->has_opp_table = false; 315262306a36Sopenharmony_ci else 315362306a36Sopenharmony_ci dc->has_opp_table = true; 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci return 0; 315662306a36Sopenharmony_ci} 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_cistatic int tegra_dc_probe(struct platform_device *pdev) 315962306a36Sopenharmony_ci{ 316062306a36Sopenharmony_ci u64 dma_mask = dma_get_mask(pdev->dev.parent); 316162306a36Sopenharmony_ci struct tegra_dc *dc; 316262306a36Sopenharmony_ci int err; 316362306a36Sopenharmony_ci 316462306a36Sopenharmony_ci err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask); 316562306a36Sopenharmony_ci if (err < 0) { 316662306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); 316762306a36Sopenharmony_ci return err; 316862306a36Sopenharmony_ci } 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL); 317162306a36Sopenharmony_ci if (!dc) 317262306a36Sopenharmony_ci return -ENOMEM; 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci dc->soc = of_device_get_match_data(&pdev->dev); 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_ci INIT_LIST_HEAD(&dc->list); 317762306a36Sopenharmony_ci dc->dev = &pdev->dev; 317862306a36Sopenharmony_ci 317962306a36Sopenharmony_ci err = tegra_dc_parse_dt(dc); 318062306a36Sopenharmony_ci if (err < 0) 318162306a36Sopenharmony_ci return err; 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci err = tegra_dc_couple(dc); 318462306a36Sopenharmony_ci if (err < 0) 318562306a36Sopenharmony_ci return err; 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci dc->clk = devm_clk_get(&pdev->dev, NULL); 318862306a36Sopenharmony_ci if (IS_ERR(dc->clk)) { 318962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get clock\n"); 319062306a36Sopenharmony_ci return PTR_ERR(dc->clk); 319162306a36Sopenharmony_ci } 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci dc->rst = devm_reset_control_get(&pdev->dev, "dc"); 319462306a36Sopenharmony_ci if (IS_ERR(dc->rst)) { 319562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get reset\n"); 319662306a36Sopenharmony_ci return PTR_ERR(dc->rst); 319762306a36Sopenharmony_ci } 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci /* assert reset and disable clock */ 320062306a36Sopenharmony_ci err = clk_prepare_enable(dc->clk); 320162306a36Sopenharmony_ci if (err < 0) 320262306a36Sopenharmony_ci return err; 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_ci usleep_range(2000, 4000); 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_ci err = reset_control_assert(dc->rst); 320762306a36Sopenharmony_ci if (err < 0) { 320862306a36Sopenharmony_ci clk_disable_unprepare(dc->clk); 320962306a36Sopenharmony_ci return err; 321062306a36Sopenharmony_ci } 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci usleep_range(2000, 4000); 321362306a36Sopenharmony_ci 321462306a36Sopenharmony_ci clk_disable_unprepare(dc->clk); 321562306a36Sopenharmony_ci 321662306a36Sopenharmony_ci if (dc->soc->has_powergate) { 321762306a36Sopenharmony_ci if (dc->pipe == 0) 321862306a36Sopenharmony_ci dc->powergate = TEGRA_POWERGATE_DIS; 321962306a36Sopenharmony_ci else 322062306a36Sopenharmony_ci dc->powergate = TEGRA_POWERGATE_DISB; 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci tegra_powergate_power_off(dc->powergate); 322362306a36Sopenharmony_ci } 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci err = tegra_dc_init_opp_table(dc); 322662306a36Sopenharmony_ci if (err < 0) 322762306a36Sopenharmony_ci return err; 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci dc->regs = devm_platform_ioremap_resource(pdev, 0); 323062306a36Sopenharmony_ci if (IS_ERR(dc->regs)) 323162306a36Sopenharmony_ci return PTR_ERR(dc->regs); 323262306a36Sopenharmony_ci 323362306a36Sopenharmony_ci dc->irq = platform_get_irq(pdev, 0); 323462306a36Sopenharmony_ci if (dc->irq < 0) 323562306a36Sopenharmony_ci return -ENXIO; 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_ci err = tegra_dc_rgb_probe(dc); 323862306a36Sopenharmony_ci if (err < 0 && err != -ENODEV) 323962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, err, 324062306a36Sopenharmony_ci "failed to probe RGB output\n"); 324162306a36Sopenharmony_ci 324262306a36Sopenharmony_ci platform_set_drvdata(pdev, dc); 324362306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 324462306a36Sopenharmony_ci 324562306a36Sopenharmony_ci INIT_LIST_HEAD(&dc->client.list); 324662306a36Sopenharmony_ci dc->client.ops = &dc_client_ops; 324762306a36Sopenharmony_ci dc->client.dev = &pdev->dev; 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci err = host1x_client_register(&dc->client); 325062306a36Sopenharmony_ci if (err < 0) { 325162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register host1x client: %d\n", 325262306a36Sopenharmony_ci err); 325362306a36Sopenharmony_ci goto disable_pm; 325462306a36Sopenharmony_ci } 325562306a36Sopenharmony_ci 325662306a36Sopenharmony_ci return 0; 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_cidisable_pm: 325962306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 326062306a36Sopenharmony_ci tegra_dc_rgb_remove(dc); 326162306a36Sopenharmony_ci 326262306a36Sopenharmony_ci return err; 326362306a36Sopenharmony_ci} 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_cistatic void tegra_dc_remove(struct platform_device *pdev) 326662306a36Sopenharmony_ci{ 326762306a36Sopenharmony_ci struct tegra_dc *dc = platform_get_drvdata(pdev); 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_ci host1x_client_unregister(&dc->client); 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci tegra_dc_rgb_remove(dc); 327262306a36Sopenharmony_ci 327362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 327462306a36Sopenharmony_ci} 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_cistruct platform_driver tegra_dc_driver = { 327762306a36Sopenharmony_ci .driver = { 327862306a36Sopenharmony_ci .name = "tegra-dc", 327962306a36Sopenharmony_ci .of_match_table = tegra_dc_of_match, 328062306a36Sopenharmony_ci }, 328162306a36Sopenharmony_ci .probe = tegra_dc_probe, 328262306a36Sopenharmony_ci .remove_new = tegra_dc_remove, 328362306a36Sopenharmony_ci}; 3284