1dee8268fSThierry Reding /* 2dee8268fSThierry Reding * Copyright (C) 2012 Avionic Design GmbH 3dee8268fSThierry Reding * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 4dee8268fSThierry Reding * 5dee8268fSThierry Reding * This program is free software; you can redistribute it and/or modify 6dee8268fSThierry Reding * it under the terms of the GNU General Public License version 2 as 7dee8268fSThierry Reding * published by the Free Software Foundation. 8dee8268fSThierry Reding */ 9dee8268fSThierry Reding 10dee8268fSThierry Reding #include <linux/clk.h> 11dee8268fSThierry Reding #include <linux/debugfs.h> 12df06b759SThierry Reding #include <linux/iommu.h> 13b9ff7aeaSThierry Reding #include <linux/of_device.h> 1433a8eb8dSThierry Reding #include <linux/pm_runtime.h> 15ca48080aSStephen Warren #include <linux/reset.h> 16dee8268fSThierry Reding 179c012700SThierry Reding #include <soc/tegra/pmc.h> 189c012700SThierry Reding 19dee8268fSThierry Reding #include "dc.h" 20dee8268fSThierry Reding #include "drm.h" 21dee8268fSThierry Reding #include "gem.h" 2247307954SThierry Reding #include "hub.h" 235acd3514SThierry Reding #include "plane.h" 24dee8268fSThierry Reding 259d44189fSThierry Reding #include <drm/drm_atomic.h> 264aa3df71SThierry Reding #include <drm/drm_atomic_helper.h> 273cb9ae4fSDaniel Vetter #include <drm/drm_plane_helper.h> 283cb9ae4fSDaniel Vetter 29791ddb1eSThierry Reding static void tegra_dc_stats_reset(struct tegra_dc_stats *stats) 30791ddb1eSThierry Reding { 31791ddb1eSThierry Reding stats->frames = 0; 32791ddb1eSThierry Reding stats->vblank = 0; 33791ddb1eSThierry Reding stats->underflow = 0; 34791ddb1eSThierry Reding stats->overflow = 0; 35791ddb1eSThierry Reding } 36791ddb1eSThierry Reding 371087fac1SThierry Reding /* Reads the active copy of a register. */ 3886df256fSThierry Reding static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) 3986df256fSThierry Reding { 4086df256fSThierry Reding u32 value; 4186df256fSThierry Reding 4286df256fSThierry Reding tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); 4386df256fSThierry Reding value = tegra_dc_readl(dc, offset); 4486df256fSThierry Reding tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); 4586df256fSThierry Reding 4686df256fSThierry Reding return value; 4786df256fSThierry Reding } 4886df256fSThierry Reding 491087fac1SThierry Reding static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, 501087fac1SThierry Reding unsigned int offset) 511087fac1SThierry Reding { 521087fac1SThierry Reding if (offset >= 0x500 && offset <= 0x638) { 531087fac1SThierry Reding offset = 0x000 + (offset - 0x500); 541087fac1SThierry Reding return plane->offset + offset; 551087fac1SThierry Reding } 561087fac1SThierry Reding 571087fac1SThierry Reding if (offset >= 0x700 && offset <= 0x719) { 581087fac1SThierry Reding offset = 0x180 + (offset - 0x700); 591087fac1SThierry Reding return plane->offset + offset; 601087fac1SThierry Reding } 611087fac1SThierry Reding 621087fac1SThierry Reding if (offset >= 0x800 && offset <= 0x839) { 631087fac1SThierry Reding offset = 0x1c0 + (offset - 0x800); 641087fac1SThierry Reding return plane->offset + offset; 651087fac1SThierry Reding } 661087fac1SThierry Reding 671087fac1SThierry Reding dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); 681087fac1SThierry Reding 691087fac1SThierry Reding return plane->offset + offset; 701087fac1SThierry Reding } 711087fac1SThierry Reding 721087fac1SThierry Reding static inline u32 tegra_plane_readl(struct tegra_plane *plane, 731087fac1SThierry Reding unsigned int offset) 741087fac1SThierry Reding { 751087fac1SThierry Reding return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); 761087fac1SThierry Reding } 771087fac1SThierry Reding 781087fac1SThierry Reding static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value, 791087fac1SThierry Reding unsigned int offset) 801087fac1SThierry Reding { 811087fac1SThierry Reding tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); 821087fac1SThierry Reding } 831087fac1SThierry Reding 84c57997bcSThierry Reding bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) 85c57997bcSThierry Reding { 86c57997bcSThierry Reding struct device_node *np = dc->dev->of_node; 87c57997bcSThierry Reding struct of_phandle_iterator it; 88c57997bcSThierry Reding int err; 89c57997bcSThierry Reding 90c57997bcSThierry Reding of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) 91c57997bcSThierry Reding if (it.node == dev->of_node) 92c57997bcSThierry Reding return true; 93c57997bcSThierry Reding 94c57997bcSThierry Reding return false; 95c57997bcSThierry Reding } 96c57997bcSThierry Reding 9786df256fSThierry Reding /* 98d700ba7aSThierry Reding * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the 99d700ba7aSThierry Reding * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy. 100d700ba7aSThierry Reding * Latching happens mmediately if the display controller is in STOP mode or 101d700ba7aSThierry Reding * on the next frame boundary otherwise. 102d700ba7aSThierry Reding * 103d700ba7aSThierry Reding * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The 104d700ba7aSThierry Reding * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits 105d700ba7aSThierry Reding * are written. When the *_ACT_REQ bits are written, the ARM copy is latched 106d700ba7aSThierry Reding * into the ACTIVE copy, either immediately if the display controller is in 107d700ba7aSThierry Reding * STOP mode, or at the next frame boundary otherwise. 108d700ba7aSThierry Reding */ 10962b9e063SThierry Reding void tegra_dc_commit(struct tegra_dc *dc) 110205d48edSThierry Reding { 111205d48edSThierry Reding tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); 112205d48edSThierry Reding tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); 113205d48edSThierry Reding } 114205d48edSThierry Reding 11510288eeaSThierry Reding static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v, 11610288eeaSThierry Reding unsigned int bpp) 11710288eeaSThierry Reding { 11810288eeaSThierry Reding fixed20_12 outf = dfixed_init(out); 11910288eeaSThierry Reding fixed20_12 inf = dfixed_init(in); 12010288eeaSThierry Reding u32 dda_inc; 12110288eeaSThierry Reding int max; 12210288eeaSThierry Reding 12310288eeaSThierry Reding if (v) 12410288eeaSThierry Reding max = 15; 12510288eeaSThierry Reding else { 12610288eeaSThierry Reding switch (bpp) { 12710288eeaSThierry Reding case 2: 12810288eeaSThierry Reding max = 8; 12910288eeaSThierry Reding break; 13010288eeaSThierry Reding 13110288eeaSThierry Reding default: 13210288eeaSThierry Reding WARN_ON_ONCE(1); 13310288eeaSThierry Reding /* fallthrough */ 13410288eeaSThierry Reding case 4: 13510288eeaSThierry Reding max = 4; 13610288eeaSThierry Reding break; 13710288eeaSThierry Reding } 13810288eeaSThierry Reding } 13910288eeaSThierry Reding 14010288eeaSThierry Reding outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1)); 14110288eeaSThierry Reding inf.full -= dfixed_const(1); 14210288eeaSThierry Reding 14310288eeaSThierry Reding dda_inc = dfixed_div(inf, outf); 14410288eeaSThierry Reding dda_inc = min_t(u32, dda_inc, dfixed_const(max)); 14510288eeaSThierry Reding 14610288eeaSThierry Reding return dda_inc; 14710288eeaSThierry Reding } 14810288eeaSThierry Reding 14910288eeaSThierry Reding static inline u32 compute_initial_dda(unsigned int in) 15010288eeaSThierry Reding { 15110288eeaSThierry Reding fixed20_12 inf = dfixed_init(in); 15210288eeaSThierry Reding return dfixed_frac(inf); 15310288eeaSThierry Reding } 15410288eeaSThierry Reding 1551087fac1SThierry Reding static void tegra_dc_setup_window(struct tegra_plane *plane, 15610288eeaSThierry Reding const struct tegra_dc_window *window) 15710288eeaSThierry Reding { 15810288eeaSThierry Reding unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; 1591087fac1SThierry Reding struct tegra_dc *dc = plane->dc; 16010288eeaSThierry Reding bool yuv, planar; 1611087fac1SThierry Reding u32 value; 16210288eeaSThierry Reding 16310288eeaSThierry Reding /* 16410288eeaSThierry Reding * For YUV planar modes, the number of bytes per pixel takes into 16510288eeaSThierry Reding * account only the luma component and therefore is 1. 16610288eeaSThierry Reding */ 1675acd3514SThierry Reding yuv = tegra_plane_format_is_yuv(window->format, &planar); 16810288eeaSThierry Reding if (!yuv) 16910288eeaSThierry Reding bpp = window->bits_per_pixel / 8; 17010288eeaSThierry Reding else 17110288eeaSThierry Reding bpp = planar ? 1 : 2; 17210288eeaSThierry Reding 1731087fac1SThierry Reding tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH); 1741087fac1SThierry Reding tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP); 17510288eeaSThierry Reding 17610288eeaSThierry Reding value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x); 1771087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_POSITION); 17810288eeaSThierry Reding 17910288eeaSThierry Reding value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w); 1801087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_SIZE); 18110288eeaSThierry Reding 18210288eeaSThierry Reding h_offset = window->src.x * bpp; 18310288eeaSThierry Reding v_offset = window->src.y; 18410288eeaSThierry Reding h_size = window->src.w * bpp; 18510288eeaSThierry Reding v_size = window->src.h; 18610288eeaSThierry Reding 18710288eeaSThierry Reding value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); 1881087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE); 18910288eeaSThierry Reding 19010288eeaSThierry Reding /* 19110288eeaSThierry Reding * For DDA computations the number of bytes per pixel for YUV planar 19210288eeaSThierry Reding * modes needs to take into account all Y, U and V components. 19310288eeaSThierry Reding */ 19410288eeaSThierry Reding if (yuv && planar) 19510288eeaSThierry Reding bpp = 2; 19610288eeaSThierry Reding 19710288eeaSThierry Reding h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp); 19810288eeaSThierry Reding v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp); 19910288eeaSThierry Reding 20010288eeaSThierry Reding value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); 2011087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_DDA_INC); 20210288eeaSThierry Reding 20310288eeaSThierry Reding h_dda = compute_initial_dda(window->src.x); 20410288eeaSThierry Reding v_dda = compute_initial_dda(window->src.y); 20510288eeaSThierry Reding 2061087fac1SThierry Reding tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA); 2071087fac1SThierry Reding tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA); 20810288eeaSThierry Reding 2091087fac1SThierry Reding tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE); 2101087fac1SThierry Reding tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE); 21110288eeaSThierry Reding 2121087fac1SThierry Reding tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR); 21310288eeaSThierry Reding 21410288eeaSThierry Reding if (yuv && planar) { 2151087fac1SThierry Reding tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U); 2161087fac1SThierry Reding tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V); 21710288eeaSThierry Reding value = window->stride[1] << 16 | window->stride[0]; 2181087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE); 21910288eeaSThierry Reding } else { 2201087fac1SThierry Reding tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE); 22110288eeaSThierry Reding } 22210288eeaSThierry Reding 22310288eeaSThierry Reding if (window->bottom_up) 22410288eeaSThierry Reding v_offset += window->src.h - 1; 22510288eeaSThierry Reding 2261087fac1SThierry Reding tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET); 2271087fac1SThierry Reding tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET); 22810288eeaSThierry Reding 229c134f019SThierry Reding if (dc->soc->supports_block_linear) { 230c134f019SThierry Reding unsigned long height = window->tiling.value; 231c134f019SThierry Reding 232c134f019SThierry Reding switch (window->tiling.mode) { 233c134f019SThierry Reding case TEGRA_BO_TILING_MODE_PITCH: 234c134f019SThierry Reding value = DC_WINBUF_SURFACE_KIND_PITCH; 235c134f019SThierry Reding break; 236c134f019SThierry Reding 237c134f019SThierry Reding case TEGRA_BO_TILING_MODE_TILED: 238c134f019SThierry Reding value = DC_WINBUF_SURFACE_KIND_TILED; 239c134f019SThierry Reding break; 240c134f019SThierry Reding 241c134f019SThierry Reding case TEGRA_BO_TILING_MODE_BLOCK: 242c134f019SThierry Reding value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) | 243c134f019SThierry Reding DC_WINBUF_SURFACE_KIND_BLOCK; 244c134f019SThierry Reding break; 245c134f019SThierry Reding } 246c134f019SThierry Reding 2471087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND); 24810288eeaSThierry Reding } else { 249c134f019SThierry Reding switch (window->tiling.mode) { 250c134f019SThierry Reding case TEGRA_BO_TILING_MODE_PITCH: 25110288eeaSThierry Reding value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV | 25210288eeaSThierry Reding DC_WIN_BUFFER_ADDR_MODE_LINEAR; 253c134f019SThierry Reding break; 254c134f019SThierry Reding 255c134f019SThierry Reding case TEGRA_BO_TILING_MODE_TILED: 256c134f019SThierry Reding value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | 257c134f019SThierry Reding DC_WIN_BUFFER_ADDR_MODE_TILE; 258c134f019SThierry Reding break; 259c134f019SThierry Reding 260c134f019SThierry Reding case TEGRA_BO_TILING_MODE_BLOCK: 2614aa3df71SThierry Reding /* 2624aa3df71SThierry Reding * No need to handle this here because ->atomic_check 2634aa3df71SThierry Reding * will already have filtered it out. 2644aa3df71SThierry Reding */ 2654aa3df71SThierry Reding break; 26610288eeaSThierry Reding } 26710288eeaSThierry Reding 2681087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE); 269c134f019SThierry Reding } 27010288eeaSThierry Reding 27110288eeaSThierry Reding value = WIN_ENABLE; 27210288eeaSThierry Reding 27310288eeaSThierry Reding if (yuv) { 27410288eeaSThierry Reding /* setup default colorspace conversion coefficients */ 2751087fac1SThierry Reding tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF); 2761087fac1SThierry Reding tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB); 2771087fac1SThierry Reding tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR); 2781087fac1SThierry Reding tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR); 2791087fac1SThierry Reding tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG); 2801087fac1SThierry Reding tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG); 2811087fac1SThierry Reding tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB); 2821087fac1SThierry Reding tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB); 28310288eeaSThierry Reding 28410288eeaSThierry Reding value |= CSC_ENABLE; 28510288eeaSThierry Reding } else if (window->bits_per_pixel < 24) { 28610288eeaSThierry Reding value |= COLOR_EXPAND; 28710288eeaSThierry Reding } 28810288eeaSThierry Reding 28910288eeaSThierry Reding if (window->bottom_up) 29010288eeaSThierry Reding value |= V_DIRECTION; 29110288eeaSThierry Reding 2921087fac1SThierry Reding tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS); 29310288eeaSThierry Reding 29410288eeaSThierry Reding /* 29510288eeaSThierry Reding * Disable blending and assume Window A is the bottom-most window, 29610288eeaSThierry Reding * Window C is the top-most window and Window B is in the middle. 29710288eeaSThierry Reding */ 2981087fac1SThierry Reding tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY); 2991087fac1SThierry Reding tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); 30010288eeaSThierry Reding 3011087fac1SThierry Reding switch (plane->index) { 30210288eeaSThierry Reding case 0: 3031087fac1SThierry Reding tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X); 3041087fac1SThierry Reding tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); 3051087fac1SThierry Reding tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); 30610288eeaSThierry Reding break; 30710288eeaSThierry Reding 30810288eeaSThierry Reding case 1: 3091087fac1SThierry Reding tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); 3101087fac1SThierry Reding tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); 3111087fac1SThierry Reding tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); 31210288eeaSThierry Reding break; 31310288eeaSThierry Reding 31410288eeaSThierry Reding case 2: 3151087fac1SThierry Reding tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); 3161087fac1SThierry Reding tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); 3171087fac1SThierry Reding tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY); 31810288eeaSThierry Reding break; 31910288eeaSThierry Reding } 320c7679306SThierry Reding } 321c7679306SThierry Reding 322511c7023SThierry Reding static const u32 tegra20_primary_formats[] = { 323511c7023SThierry Reding DRM_FORMAT_ARGB4444, 324511c7023SThierry Reding DRM_FORMAT_ARGB1555, 325c7679306SThierry Reding DRM_FORMAT_RGB565, 326511c7023SThierry Reding DRM_FORMAT_RGBA5551, 327511c7023SThierry Reding DRM_FORMAT_ABGR8888, 328511c7023SThierry Reding DRM_FORMAT_ARGB8888, 329511c7023SThierry Reding }; 330511c7023SThierry Reding 331511c7023SThierry Reding static const u32 tegra114_primary_formats[] = { 332511c7023SThierry Reding DRM_FORMAT_ARGB4444, 333511c7023SThierry Reding DRM_FORMAT_ARGB1555, 334511c7023SThierry Reding DRM_FORMAT_RGB565, 335511c7023SThierry Reding DRM_FORMAT_RGBA5551, 336511c7023SThierry Reding DRM_FORMAT_ABGR8888, 337511c7023SThierry Reding DRM_FORMAT_ARGB8888, 338511c7023SThierry Reding /* new on Tegra114 */ 339511c7023SThierry Reding DRM_FORMAT_ABGR4444, 340511c7023SThierry Reding DRM_FORMAT_ABGR1555, 341511c7023SThierry Reding DRM_FORMAT_BGRA5551, 342511c7023SThierry Reding DRM_FORMAT_XRGB1555, 343511c7023SThierry Reding DRM_FORMAT_RGBX5551, 344511c7023SThierry Reding DRM_FORMAT_XBGR1555, 345511c7023SThierry Reding DRM_FORMAT_BGRX5551, 346511c7023SThierry Reding DRM_FORMAT_BGR565, 347511c7023SThierry Reding DRM_FORMAT_BGRA8888, 348511c7023SThierry Reding DRM_FORMAT_RGBA8888, 349511c7023SThierry Reding DRM_FORMAT_XRGB8888, 350511c7023SThierry Reding DRM_FORMAT_XBGR8888, 351511c7023SThierry Reding }; 352511c7023SThierry Reding 353511c7023SThierry Reding static const u32 tegra124_primary_formats[] = { 354511c7023SThierry Reding DRM_FORMAT_ARGB4444, 355511c7023SThierry Reding DRM_FORMAT_ARGB1555, 356511c7023SThierry Reding DRM_FORMAT_RGB565, 357511c7023SThierry Reding DRM_FORMAT_RGBA5551, 358511c7023SThierry Reding DRM_FORMAT_ABGR8888, 359511c7023SThierry Reding DRM_FORMAT_ARGB8888, 360511c7023SThierry Reding /* new on Tegra114 */ 361511c7023SThierry Reding DRM_FORMAT_ABGR4444, 362511c7023SThierry Reding DRM_FORMAT_ABGR1555, 363511c7023SThierry Reding DRM_FORMAT_BGRA5551, 364511c7023SThierry Reding DRM_FORMAT_XRGB1555, 365511c7023SThierry Reding DRM_FORMAT_RGBX5551, 366511c7023SThierry Reding DRM_FORMAT_XBGR1555, 367511c7023SThierry Reding DRM_FORMAT_BGRX5551, 368511c7023SThierry Reding DRM_FORMAT_BGR565, 369511c7023SThierry Reding DRM_FORMAT_BGRA8888, 370511c7023SThierry Reding DRM_FORMAT_RGBA8888, 371511c7023SThierry Reding DRM_FORMAT_XRGB8888, 372511c7023SThierry Reding DRM_FORMAT_XBGR8888, 373511c7023SThierry Reding /* new on Tegra124 */ 374511c7023SThierry Reding DRM_FORMAT_RGBX8888, 375511c7023SThierry Reding DRM_FORMAT_BGRX8888, 376c7679306SThierry Reding }; 377c7679306SThierry Reding 3784aa3df71SThierry Reding static int tegra_plane_atomic_check(struct drm_plane *plane, 3794aa3df71SThierry Reding struct drm_plane_state *state) 3804aa3df71SThierry Reding { 3818f604f8cSThierry Reding struct tegra_plane_state *plane_state = to_tegra_plane_state(state); 3828f604f8cSThierry Reding struct tegra_bo_tiling *tiling = &plane_state->tiling; 38347802b09SThierry Reding struct tegra_plane *tegra = to_tegra_plane(plane); 3844aa3df71SThierry Reding struct tegra_dc *dc = to_tegra_dc(state->crtc); 385c7679306SThierry Reding int err; 386c7679306SThierry Reding 3874aa3df71SThierry Reding /* no need for further checks if the plane is being disabled */ 3884aa3df71SThierry Reding if (!state->crtc) 3894aa3df71SThierry Reding return 0; 3904aa3df71SThierry Reding 3915acd3514SThierry Reding err = tegra_plane_format(state->fb->format->format, 3925acd3514SThierry Reding &plane_state->format, 3938f604f8cSThierry Reding &plane_state->swap); 3944aa3df71SThierry Reding if (err < 0) 3954aa3df71SThierry Reding return err; 3964aa3df71SThierry Reding 3978f604f8cSThierry Reding err = tegra_fb_get_tiling(state->fb, tiling); 3988f604f8cSThierry Reding if (err < 0) 3998f604f8cSThierry Reding return err; 4008f604f8cSThierry Reding 4018f604f8cSThierry Reding if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK && 4024aa3df71SThierry Reding !dc->soc->supports_block_linear) { 4034aa3df71SThierry Reding DRM_ERROR("hardware doesn't support block linear mode\n"); 4044aa3df71SThierry Reding return -EINVAL; 4054aa3df71SThierry Reding } 4064aa3df71SThierry Reding 4074aa3df71SThierry Reding /* 4084aa3df71SThierry Reding * Tegra doesn't support different strides for U and V planes so we 4094aa3df71SThierry Reding * error out if the user tries to display a framebuffer with such a 4104aa3df71SThierry Reding * configuration. 4114aa3df71SThierry Reding */ 412bcb0b461SVille Syrjälä if (state->fb->format->num_planes > 2) { 4134aa3df71SThierry Reding if (state->fb->pitches[2] != state->fb->pitches[1]) { 4144aa3df71SThierry Reding DRM_ERROR("unsupported UV-plane configuration\n"); 4154aa3df71SThierry Reding return -EINVAL; 4164aa3df71SThierry Reding } 4174aa3df71SThierry Reding } 4184aa3df71SThierry Reding 41947802b09SThierry Reding err = tegra_plane_state_add(tegra, state); 42047802b09SThierry Reding if (err < 0) 42147802b09SThierry Reding return err; 42247802b09SThierry Reding 4234aa3df71SThierry Reding return 0; 4244aa3df71SThierry Reding } 4254aa3df71SThierry Reding 426a4bfa096SThierry Reding static void tegra_plane_atomic_disable(struct drm_plane *plane, 427a4bfa096SThierry Reding struct drm_plane_state *old_state) 42880d3eef1SDmitry Osipenko { 429a4bfa096SThierry Reding struct tegra_plane *p = to_tegra_plane(plane); 43080d3eef1SDmitry Osipenko u32 value; 43180d3eef1SDmitry Osipenko 432a4bfa096SThierry Reding /* rien ne va plus */ 433a4bfa096SThierry Reding if (!old_state || !old_state->crtc) 434a4bfa096SThierry Reding return; 435a4bfa096SThierry Reding 4361087fac1SThierry Reding value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS); 43780d3eef1SDmitry Osipenko value &= ~WIN_ENABLE; 4381087fac1SThierry Reding tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); 43980d3eef1SDmitry Osipenko } 44080d3eef1SDmitry Osipenko 4414aa3df71SThierry Reding static void tegra_plane_atomic_update(struct drm_plane *plane, 4424aa3df71SThierry Reding struct drm_plane_state *old_state) 4434aa3df71SThierry Reding { 4448f604f8cSThierry Reding struct tegra_plane_state *state = to_tegra_plane_state(plane->state); 4454aa3df71SThierry Reding struct drm_framebuffer *fb = plane->state->fb; 4464aa3df71SThierry Reding struct tegra_plane *p = to_tegra_plane(plane); 4474aa3df71SThierry Reding struct tegra_dc_window window; 4484aa3df71SThierry Reding unsigned int i; 4494aa3df71SThierry Reding 4504aa3df71SThierry Reding /* rien ne va plus */ 4514aa3df71SThierry Reding if (!plane->state->crtc || !plane->state->fb) 4524aa3df71SThierry Reding return; 4534aa3df71SThierry Reding 45480d3eef1SDmitry Osipenko if (!plane->state->visible) 455a4bfa096SThierry Reding return tegra_plane_atomic_disable(plane, old_state); 45680d3eef1SDmitry Osipenko 457c7679306SThierry Reding memset(&window, 0, sizeof(window)); 4587d205857SDmitry Osipenko window.src.x = plane->state->src.x1 >> 16; 4597d205857SDmitry Osipenko window.src.y = plane->state->src.y1 >> 16; 4607d205857SDmitry Osipenko window.src.w = drm_rect_width(&plane->state->src) >> 16; 4617d205857SDmitry Osipenko window.src.h = drm_rect_height(&plane->state->src) >> 16; 4627d205857SDmitry Osipenko window.dst.x = plane->state->dst.x1; 4637d205857SDmitry Osipenko window.dst.y = plane->state->dst.y1; 4647d205857SDmitry Osipenko window.dst.w = drm_rect_width(&plane->state->dst); 4657d205857SDmitry Osipenko window.dst.h = drm_rect_height(&plane->state->dst); 466272725c7SVille Syrjälä window.bits_per_pixel = fb->format->cpp[0] * 8; 467c7679306SThierry Reding window.bottom_up = tegra_fb_is_bottom_up(fb); 468c7679306SThierry Reding 4698f604f8cSThierry Reding /* copy from state */ 4708f604f8cSThierry Reding window.tiling = state->tiling; 4718f604f8cSThierry Reding window.format = state->format; 4728f604f8cSThierry Reding window.swap = state->swap; 473c7679306SThierry Reding 474bcb0b461SVille Syrjälä for (i = 0; i < fb->format->num_planes; i++) { 4754aa3df71SThierry Reding struct tegra_bo *bo = tegra_fb_get_plane(fb, i); 476c7679306SThierry Reding 4774aa3df71SThierry Reding window.base[i] = bo->paddr + fb->offsets[i]; 47808ee0178SDmitry Osipenko 47908ee0178SDmitry Osipenko /* 48008ee0178SDmitry Osipenko * Tegra uses a shared stride for UV planes. Framebuffers are 48108ee0178SDmitry Osipenko * already checked for this in the tegra_plane_atomic_check() 48208ee0178SDmitry Osipenko * function, so it's safe to ignore the V-plane pitch here. 48308ee0178SDmitry Osipenko */ 48408ee0178SDmitry Osipenko if (i < 2) 4854aa3df71SThierry Reding window.stride[i] = fb->pitches[i]; 486c7679306SThierry Reding } 487c7679306SThierry Reding 4881087fac1SThierry Reding tegra_dc_setup_window(p, &window); 4894aa3df71SThierry Reding } 4904aa3df71SThierry Reding 491a4bfa096SThierry Reding static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { 4924aa3df71SThierry Reding .atomic_check = tegra_plane_atomic_check, 4934aa3df71SThierry Reding .atomic_disable = tegra_plane_atomic_disable, 494a4bfa096SThierry Reding .atomic_update = tegra_plane_atomic_update, 495c7679306SThierry Reding }; 496c7679306SThierry Reding 49747307954SThierry Reding static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, 498c7679306SThierry Reding struct tegra_dc *dc) 499c7679306SThierry Reding { 500518e6227SThierry Reding /* 501518e6227SThierry Reding * Ideally this would use drm_crtc_mask(), but that would require the 502518e6227SThierry Reding * CRTC to already be in the mode_config's list of CRTCs. However, it 503518e6227SThierry Reding * will only be added to that list in the drm_crtc_init_with_planes() 504518e6227SThierry Reding * (in tegra_dc_init()), which in turn requires registration of these 505518e6227SThierry Reding * planes. So we have ourselves a nice little chicken and egg problem 506518e6227SThierry Reding * here. 507518e6227SThierry Reding * 508518e6227SThierry Reding * We work around this by manually creating the mask from the number 509518e6227SThierry Reding * of CRTCs that have been registered, and should therefore always be 510518e6227SThierry Reding * the same as drm_crtc_index() after registration. 511518e6227SThierry Reding */ 512518e6227SThierry Reding unsigned long possible_crtcs = 1 << drm->mode_config.num_crtc; 51347307954SThierry Reding enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY; 514c7679306SThierry Reding struct tegra_plane *plane; 515c7679306SThierry Reding unsigned int num_formats; 516c7679306SThierry Reding const u32 *formats; 517c7679306SThierry Reding int err; 518c7679306SThierry Reding 519c7679306SThierry Reding plane = kzalloc(sizeof(*plane), GFP_KERNEL); 520c7679306SThierry Reding if (!plane) 521c7679306SThierry Reding return ERR_PTR(-ENOMEM); 522c7679306SThierry Reding 5231087fac1SThierry Reding /* Always use window A as primary window */ 5241087fac1SThierry Reding plane->offset = 0xa00; 525c4755fb9SThierry Reding plane->index = 0; 526c4755fb9SThierry Reding plane->depth = 255; 5271087fac1SThierry Reding plane->dc = dc; 5281087fac1SThierry Reding 5291087fac1SThierry Reding num_formats = dc->soc->num_primary_formats; 5301087fac1SThierry Reding formats = dc->soc->primary_formats; 531c4755fb9SThierry Reding 532518e6227SThierry Reding err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 533c1cb4b61SThierry Reding &tegra_plane_funcs, formats, 53447307954SThierry Reding num_formats, NULL, type, NULL); 535c7679306SThierry Reding if (err < 0) { 536c7679306SThierry Reding kfree(plane); 537c7679306SThierry Reding return ERR_PTR(err); 538c7679306SThierry Reding } 539c7679306SThierry Reding 540a4bfa096SThierry Reding drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); 5414aa3df71SThierry Reding 542c7679306SThierry Reding return &plane->base; 543c7679306SThierry Reding } 544c7679306SThierry Reding 545c7679306SThierry Reding static const u32 tegra_cursor_plane_formats[] = { 546c7679306SThierry Reding DRM_FORMAT_RGBA8888, 547c7679306SThierry Reding }; 548c7679306SThierry Reding 5494aa3df71SThierry Reding static int tegra_cursor_atomic_check(struct drm_plane *plane, 5504aa3df71SThierry Reding struct drm_plane_state *state) 551c7679306SThierry Reding { 55247802b09SThierry Reding struct tegra_plane *tegra = to_tegra_plane(plane); 55347802b09SThierry Reding int err; 55447802b09SThierry Reding 5554aa3df71SThierry Reding /* no need for further checks if the plane is being disabled */ 5564aa3df71SThierry Reding if (!state->crtc) 5574aa3df71SThierry Reding return 0; 558c7679306SThierry Reding 559c7679306SThierry Reding /* scaling not supported for cursor */ 5604aa3df71SThierry Reding if ((state->src_w >> 16 != state->crtc_w) || 5614aa3df71SThierry Reding (state->src_h >> 16 != state->crtc_h)) 562c7679306SThierry Reding return -EINVAL; 563c7679306SThierry Reding 564c7679306SThierry Reding /* only square cursors supported */ 5654aa3df71SThierry Reding if (state->src_w != state->src_h) 566c7679306SThierry Reding return -EINVAL; 567c7679306SThierry Reding 5684aa3df71SThierry Reding if (state->crtc_w != 32 && state->crtc_w != 64 && 5694aa3df71SThierry Reding state->crtc_w != 128 && state->crtc_w != 256) 5704aa3df71SThierry Reding return -EINVAL; 5714aa3df71SThierry Reding 57247802b09SThierry Reding err = tegra_plane_state_add(tegra, state); 57347802b09SThierry Reding if (err < 0) 57447802b09SThierry Reding return err; 57547802b09SThierry Reding 5764aa3df71SThierry Reding return 0; 5774aa3df71SThierry Reding } 5784aa3df71SThierry Reding 5794aa3df71SThierry Reding static void tegra_cursor_atomic_update(struct drm_plane *plane, 5804aa3df71SThierry Reding struct drm_plane_state *old_state) 5814aa3df71SThierry Reding { 5824aa3df71SThierry Reding struct tegra_bo *bo = tegra_fb_get_plane(plane->state->fb, 0); 5834aa3df71SThierry Reding struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); 5844aa3df71SThierry Reding struct drm_plane_state *state = plane->state; 5854aa3df71SThierry Reding u32 value = CURSOR_CLIP_DISPLAY; 5864aa3df71SThierry Reding 5874aa3df71SThierry Reding /* rien ne va plus */ 5884aa3df71SThierry Reding if (!plane->state->crtc || !plane->state->fb) 5894aa3df71SThierry Reding return; 5904aa3df71SThierry Reding 5914aa3df71SThierry Reding switch (state->crtc_w) { 592c7679306SThierry Reding case 32: 593c7679306SThierry Reding value |= CURSOR_SIZE_32x32; 594c7679306SThierry Reding break; 595c7679306SThierry Reding 596c7679306SThierry Reding case 64: 597c7679306SThierry Reding value |= CURSOR_SIZE_64x64; 598c7679306SThierry Reding break; 599c7679306SThierry Reding 600c7679306SThierry Reding case 128: 601c7679306SThierry Reding value |= CURSOR_SIZE_128x128; 602c7679306SThierry Reding break; 603c7679306SThierry Reding 604c7679306SThierry Reding case 256: 605c7679306SThierry Reding value |= CURSOR_SIZE_256x256; 606c7679306SThierry Reding break; 607c7679306SThierry Reding 608c7679306SThierry Reding default: 6094aa3df71SThierry Reding WARN(1, "cursor size %ux%u not supported\n", state->crtc_w, 6104aa3df71SThierry Reding state->crtc_h); 6114aa3df71SThierry Reding return; 612c7679306SThierry Reding } 613c7679306SThierry Reding 614c7679306SThierry Reding value |= (bo->paddr >> 10) & 0x3fffff; 615c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR); 616c7679306SThierry Reding 617c7679306SThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 618c7679306SThierry Reding value = (bo->paddr >> 32) & 0x3; 619c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI); 620c7679306SThierry Reding #endif 621c7679306SThierry Reding 622c7679306SThierry Reding /* enable cursor and set blend mode */ 623c7679306SThierry Reding value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 624c7679306SThierry Reding value |= CURSOR_ENABLE; 625c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 626c7679306SThierry Reding 627c7679306SThierry Reding value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL); 628c7679306SThierry Reding value &= ~CURSOR_DST_BLEND_MASK; 629c7679306SThierry Reding value &= ~CURSOR_SRC_BLEND_MASK; 630c7679306SThierry Reding value |= CURSOR_MODE_NORMAL; 631c7679306SThierry Reding value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC; 632c7679306SThierry Reding value |= CURSOR_SRC_BLEND_K1_TIMES_SRC; 633c7679306SThierry Reding value |= CURSOR_ALPHA; 634c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); 635c7679306SThierry Reding 636c7679306SThierry Reding /* position the cursor */ 6374aa3df71SThierry Reding value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff); 638c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); 639c7679306SThierry Reding } 640c7679306SThierry Reding 6414aa3df71SThierry Reding static void tegra_cursor_atomic_disable(struct drm_plane *plane, 6424aa3df71SThierry Reding struct drm_plane_state *old_state) 643c7679306SThierry Reding { 6444aa3df71SThierry Reding struct tegra_dc *dc; 645c7679306SThierry Reding u32 value; 646c7679306SThierry Reding 6474aa3df71SThierry Reding /* rien ne va plus */ 6484aa3df71SThierry Reding if (!old_state || !old_state->crtc) 6494aa3df71SThierry Reding return; 6504aa3df71SThierry Reding 6514aa3df71SThierry Reding dc = to_tegra_dc(old_state->crtc); 652c7679306SThierry Reding 653c7679306SThierry Reding value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 654c7679306SThierry Reding value &= ~CURSOR_ENABLE; 655c7679306SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 656c7679306SThierry Reding } 657c7679306SThierry Reding 6584aa3df71SThierry Reding static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { 6594aa3df71SThierry Reding .atomic_check = tegra_cursor_atomic_check, 6604aa3df71SThierry Reding .atomic_update = tegra_cursor_atomic_update, 6614aa3df71SThierry Reding .atomic_disable = tegra_cursor_atomic_disable, 662c7679306SThierry Reding }; 663c7679306SThierry Reding 664c7679306SThierry Reding static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, 665c7679306SThierry Reding struct tegra_dc *dc) 666c7679306SThierry Reding { 667c7679306SThierry Reding struct tegra_plane *plane; 668c7679306SThierry Reding unsigned int num_formats; 669c7679306SThierry Reding const u32 *formats; 670c7679306SThierry Reding int err; 671c7679306SThierry Reding 672c7679306SThierry Reding plane = kzalloc(sizeof(*plane), GFP_KERNEL); 673c7679306SThierry Reding if (!plane) 674c7679306SThierry Reding return ERR_PTR(-ENOMEM); 675c7679306SThierry Reding 67647802b09SThierry Reding /* 677a1df3b24SThierry Reding * This index is kind of fake. The cursor isn't a regular plane, but 678a1df3b24SThierry Reding * its update and activation request bits in DC_CMD_STATE_CONTROL do 679a1df3b24SThierry Reding * use the same programming. Setting this fake index here allows the 680a1df3b24SThierry Reding * code in tegra_add_plane_state() to do the right thing without the 681a1df3b24SThierry Reding * need to special-casing the cursor plane. 68247802b09SThierry Reding */ 68347802b09SThierry Reding plane->index = 6; 6841087fac1SThierry Reding plane->dc = dc; 68547802b09SThierry Reding 686c7679306SThierry Reding num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); 687c7679306SThierry Reding formats = tegra_cursor_plane_formats; 688c7679306SThierry Reding 689c7679306SThierry Reding err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, 690c1cb4b61SThierry Reding &tegra_plane_funcs, formats, 691e6fc3b68SBen Widawsky num_formats, NULL, 692e6fc3b68SBen Widawsky DRM_PLANE_TYPE_CURSOR, NULL); 693c7679306SThierry Reding if (err < 0) { 694c7679306SThierry Reding kfree(plane); 695c7679306SThierry Reding return ERR_PTR(err); 696c7679306SThierry Reding } 697c7679306SThierry Reding 6984aa3df71SThierry Reding drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs); 6994aa3df71SThierry Reding 700c7679306SThierry Reding return &plane->base; 701c7679306SThierry Reding } 702c7679306SThierry Reding 703511c7023SThierry Reding static const u32 tegra20_overlay_formats[] = { 704511c7023SThierry Reding DRM_FORMAT_ARGB4444, 705511c7023SThierry Reding DRM_FORMAT_ARGB1555, 706dee8268fSThierry Reding DRM_FORMAT_RGB565, 707511c7023SThierry Reding DRM_FORMAT_RGBA5551, 708511c7023SThierry Reding DRM_FORMAT_ABGR8888, 709511c7023SThierry Reding DRM_FORMAT_ARGB8888, 710511c7023SThierry Reding /* planar formats */ 711511c7023SThierry Reding DRM_FORMAT_UYVY, 712511c7023SThierry Reding DRM_FORMAT_YUYV, 713511c7023SThierry Reding DRM_FORMAT_YUV420, 714511c7023SThierry Reding DRM_FORMAT_YUV422, 715511c7023SThierry Reding }; 716511c7023SThierry Reding 717511c7023SThierry Reding static const u32 tegra114_overlay_formats[] = { 718511c7023SThierry Reding DRM_FORMAT_ARGB4444, 719511c7023SThierry Reding DRM_FORMAT_ARGB1555, 720511c7023SThierry Reding DRM_FORMAT_RGB565, 721511c7023SThierry Reding DRM_FORMAT_RGBA5551, 722511c7023SThierry Reding DRM_FORMAT_ABGR8888, 723511c7023SThierry Reding DRM_FORMAT_ARGB8888, 724511c7023SThierry Reding /* new on Tegra114 */ 725511c7023SThierry Reding DRM_FORMAT_ABGR4444, 726511c7023SThierry Reding DRM_FORMAT_ABGR1555, 727511c7023SThierry Reding DRM_FORMAT_BGRA5551, 728511c7023SThierry Reding DRM_FORMAT_XRGB1555, 729511c7023SThierry Reding DRM_FORMAT_RGBX5551, 730511c7023SThierry Reding DRM_FORMAT_XBGR1555, 731511c7023SThierry Reding DRM_FORMAT_BGRX5551, 732511c7023SThierry Reding DRM_FORMAT_BGR565, 733511c7023SThierry Reding DRM_FORMAT_BGRA8888, 734511c7023SThierry Reding DRM_FORMAT_RGBA8888, 735511c7023SThierry Reding DRM_FORMAT_XRGB8888, 736511c7023SThierry Reding DRM_FORMAT_XBGR8888, 737511c7023SThierry Reding /* planar formats */ 738511c7023SThierry Reding DRM_FORMAT_UYVY, 739511c7023SThierry Reding DRM_FORMAT_YUYV, 740511c7023SThierry Reding DRM_FORMAT_YUV420, 741511c7023SThierry Reding DRM_FORMAT_YUV422, 742511c7023SThierry Reding }; 743511c7023SThierry Reding 744511c7023SThierry Reding static const u32 tegra124_overlay_formats[] = { 745511c7023SThierry Reding DRM_FORMAT_ARGB4444, 746511c7023SThierry Reding DRM_FORMAT_ARGB1555, 747511c7023SThierry Reding DRM_FORMAT_RGB565, 748511c7023SThierry Reding DRM_FORMAT_RGBA5551, 749511c7023SThierry Reding DRM_FORMAT_ABGR8888, 750511c7023SThierry Reding DRM_FORMAT_ARGB8888, 751511c7023SThierry Reding /* new on Tegra114 */ 752511c7023SThierry Reding DRM_FORMAT_ABGR4444, 753511c7023SThierry Reding DRM_FORMAT_ABGR1555, 754511c7023SThierry Reding DRM_FORMAT_BGRA5551, 755511c7023SThierry Reding DRM_FORMAT_XRGB1555, 756511c7023SThierry Reding DRM_FORMAT_RGBX5551, 757511c7023SThierry Reding DRM_FORMAT_XBGR1555, 758511c7023SThierry Reding DRM_FORMAT_BGRX5551, 759511c7023SThierry Reding DRM_FORMAT_BGR565, 760511c7023SThierry Reding DRM_FORMAT_BGRA8888, 761511c7023SThierry Reding DRM_FORMAT_RGBA8888, 762511c7023SThierry Reding DRM_FORMAT_XRGB8888, 763511c7023SThierry Reding DRM_FORMAT_XBGR8888, 764511c7023SThierry Reding /* new on Tegra124 */ 765511c7023SThierry Reding DRM_FORMAT_RGBX8888, 766511c7023SThierry Reding DRM_FORMAT_BGRX8888, 767511c7023SThierry Reding /* planar formats */ 768dee8268fSThierry Reding DRM_FORMAT_UYVY, 769f925390eSThierry Reding DRM_FORMAT_YUYV, 770dee8268fSThierry Reding DRM_FORMAT_YUV420, 771dee8268fSThierry Reding DRM_FORMAT_YUV422, 772dee8268fSThierry Reding }; 773dee8268fSThierry Reding 774c7679306SThierry Reding static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, 775c7679306SThierry Reding struct tegra_dc *dc, 776c7679306SThierry Reding unsigned int index) 777dee8268fSThierry Reding { 778dee8268fSThierry Reding struct tegra_plane *plane; 779c7679306SThierry Reding unsigned int num_formats; 780c7679306SThierry Reding const u32 *formats; 781c7679306SThierry Reding int err; 782dee8268fSThierry Reding 783f002abc1SThierry Reding plane = kzalloc(sizeof(*plane), GFP_KERNEL); 784dee8268fSThierry Reding if (!plane) 785c7679306SThierry Reding return ERR_PTR(-ENOMEM); 786dee8268fSThierry Reding 7871087fac1SThierry Reding plane->offset = 0xa00 + 0x200 * index; 788c7679306SThierry Reding plane->index = index; 789c4755fb9SThierry Reding plane->depth = 0; 7901087fac1SThierry Reding plane->dc = dc; 791dee8268fSThierry Reding 792511c7023SThierry Reding num_formats = dc->soc->num_overlay_formats; 793511c7023SThierry Reding formats = dc->soc->overlay_formats; 794c7679306SThierry Reding 795c7679306SThierry Reding err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, 796301e0ddbSThierry Reding &tegra_plane_funcs, formats, 797e6fc3b68SBen Widawsky num_formats, NULL, 798e6fc3b68SBen Widawsky DRM_PLANE_TYPE_OVERLAY, NULL); 799f002abc1SThierry Reding if (err < 0) { 800f002abc1SThierry Reding kfree(plane); 801c7679306SThierry Reding return ERR_PTR(err); 802dee8268fSThierry Reding } 803c7679306SThierry Reding 804a4bfa096SThierry Reding drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); 8054aa3df71SThierry Reding 806c7679306SThierry Reding return &plane->base; 807c7679306SThierry Reding } 808c7679306SThierry Reding 80947307954SThierry Reding static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm, 81047307954SThierry Reding struct tegra_dc *dc) 811c7679306SThierry Reding { 81247307954SThierry Reding struct drm_plane *plane, *primary = NULL; 81347307954SThierry Reding unsigned int i, j; 81447307954SThierry Reding 81547307954SThierry Reding for (i = 0; i < dc->soc->num_wgrps; i++) { 81647307954SThierry Reding const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; 81747307954SThierry Reding 81847307954SThierry Reding if (wgrp->dc == dc->pipe) { 81947307954SThierry Reding for (j = 0; j < wgrp->num_windows; j++) { 82047307954SThierry Reding unsigned int index = wgrp->windows[j]; 82147307954SThierry Reding 82247307954SThierry Reding plane = tegra_shared_plane_create(drm, dc, 82347307954SThierry Reding wgrp->index, 82447307954SThierry Reding index); 82547307954SThierry Reding if (IS_ERR(plane)) 82647307954SThierry Reding return plane; 82747307954SThierry Reding 82847307954SThierry Reding /* 82947307954SThierry Reding * Choose the first shared plane owned by this 83047307954SThierry Reding * head as the primary plane. 83147307954SThierry Reding */ 83247307954SThierry Reding if (!primary) { 83347307954SThierry Reding plane->type = DRM_PLANE_TYPE_PRIMARY; 83447307954SThierry Reding primary = plane; 83547307954SThierry Reding } 83647307954SThierry Reding } 83747307954SThierry Reding } 83847307954SThierry Reding } 83947307954SThierry Reding 84047307954SThierry Reding return primary; 84147307954SThierry Reding } 84247307954SThierry Reding 84347307954SThierry Reding static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm, 84447307954SThierry Reding struct tegra_dc *dc) 84547307954SThierry Reding { 84647307954SThierry Reding struct drm_plane *plane, *primary; 847c7679306SThierry Reding unsigned int i; 848c7679306SThierry Reding 84947307954SThierry Reding primary = tegra_primary_plane_create(drm, dc); 85047307954SThierry Reding if (IS_ERR(primary)) 85147307954SThierry Reding return primary; 85247307954SThierry Reding 853c7679306SThierry Reding for (i = 0; i < 2; i++) { 854c7679306SThierry Reding plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i); 85547307954SThierry Reding if (IS_ERR(plane)) { 85647307954SThierry Reding /* XXX tegra_plane_destroy() */ 85747307954SThierry Reding drm_plane_cleanup(primary); 85847307954SThierry Reding kfree(primary); 85947307954SThierry Reding return plane; 86047307954SThierry Reding } 861f002abc1SThierry Reding } 862dee8268fSThierry Reding 86347307954SThierry Reding return primary; 864dee8268fSThierry Reding } 865dee8268fSThierry Reding 866f002abc1SThierry Reding static void tegra_dc_destroy(struct drm_crtc *crtc) 867f002abc1SThierry Reding { 868f002abc1SThierry Reding drm_crtc_cleanup(crtc); 869f002abc1SThierry Reding } 870f002abc1SThierry Reding 871ca915b10SThierry Reding static void tegra_crtc_reset(struct drm_crtc *crtc) 872ca915b10SThierry Reding { 873ca915b10SThierry Reding struct tegra_dc_state *state; 874ca915b10SThierry Reding 8753b59b7acSThierry Reding if (crtc->state) 876ec2dc6a0SDaniel Vetter __drm_atomic_helper_crtc_destroy_state(crtc->state); 8773b59b7acSThierry Reding 878ca915b10SThierry Reding kfree(crtc->state); 879ca915b10SThierry Reding crtc->state = NULL; 880ca915b10SThierry Reding 881ca915b10SThierry Reding state = kzalloc(sizeof(*state), GFP_KERNEL); 882332bbe70SThierry Reding if (state) { 883ca915b10SThierry Reding crtc->state = &state->base; 884332bbe70SThierry Reding crtc->state->crtc = crtc; 885332bbe70SThierry Reding } 88631930d4dSThierry Reding 88731930d4dSThierry Reding drm_crtc_vblank_reset(crtc); 888ca915b10SThierry Reding } 889ca915b10SThierry Reding 890ca915b10SThierry Reding static struct drm_crtc_state * 891ca915b10SThierry Reding tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) 892ca915b10SThierry Reding { 893ca915b10SThierry Reding struct tegra_dc_state *state = to_dc_state(crtc->state); 894ca915b10SThierry Reding struct tegra_dc_state *copy; 895ca915b10SThierry Reding 8963b59b7acSThierry Reding copy = kmalloc(sizeof(*copy), GFP_KERNEL); 897ca915b10SThierry Reding if (!copy) 898ca915b10SThierry Reding return NULL; 899ca915b10SThierry Reding 9003b59b7acSThierry Reding __drm_atomic_helper_crtc_duplicate_state(crtc, ©->base); 9013b59b7acSThierry Reding copy->clk = state->clk; 9023b59b7acSThierry Reding copy->pclk = state->pclk; 9033b59b7acSThierry Reding copy->div = state->div; 9043b59b7acSThierry Reding copy->planes = state->planes; 905ca915b10SThierry Reding 906ca915b10SThierry Reding return ©->base; 907ca915b10SThierry Reding } 908ca915b10SThierry Reding 909ca915b10SThierry Reding static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, 910ca915b10SThierry Reding struct drm_crtc_state *state) 911ca915b10SThierry Reding { 912ec2dc6a0SDaniel Vetter __drm_atomic_helper_crtc_destroy_state(state); 913ca915b10SThierry Reding kfree(state); 914ca915b10SThierry Reding } 915ca915b10SThierry Reding 916b95800eeSThierry Reding #define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } 917b95800eeSThierry Reding 918b95800eeSThierry Reding static const struct debugfs_reg32 tegra_dc_regs[] = { 919b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT), 920b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL), 921b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR), 922b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT), 923b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL), 924b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR), 925b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT), 926b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL), 927b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR), 928b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT), 929b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL), 930b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR), 931b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC), 932b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0), 933b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND), 934b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE), 935b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL), 936b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_INT_STATUS), 937b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_INT_MASK), 938b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_INT_ENABLE), 939b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_INT_TYPE), 940b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_INT_POLARITY), 941b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1), 942b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2), 943b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3), 944b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_STATE_ACCESS), 945b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_STATE_CONTROL), 946b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER), 947b95800eeSThierry Reding DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL), 948b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_CRC_CONTROL), 949b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_CRC_CHECKSUM), 950b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)), 951b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)), 952b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)), 953b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)), 954b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)), 955b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)), 956b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)), 957b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)), 958b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)), 959b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)), 960b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)), 961b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)), 962b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)), 963b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)), 964b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)), 965b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)), 966b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)), 967b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)), 968b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)), 969b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)), 970b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)), 971b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)), 972b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)), 973b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)), 974b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)), 975b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL), 976b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL), 977b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE), 978b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL), 979b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE), 980b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_SPI_CONTROL), 981b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_SPI_START_BYTE), 982b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB), 983b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD), 984b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_HSPI_CS_DC), 985b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A), 986b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B), 987b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_GPIO_CTRL), 988b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER), 989b95800eeSThierry Reding DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED), 990b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0), 991b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1), 992b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS), 993b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY), 994b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER), 995b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS), 996b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_REF_TO_SYNC), 997b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SYNC_WIDTH), 998b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_BACK_PORCH), 999b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_ACTIVE), 1000b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_FRONT_PORCH), 1001b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL), 1002b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A), 1003b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B), 1004b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C), 1005b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D), 1006b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL), 1007b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A), 1008b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B), 1009b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C), 1010b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D), 1011b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL), 1012b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A), 1013b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B), 1014b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C), 1015b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D), 1016b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL), 1017b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A), 1018b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B), 1019b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C), 1020b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL), 1021b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A), 1022b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B), 1023b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C), 1024b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL), 1025b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A), 1026b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL), 1027b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A), 1028b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_M0_CONTROL), 1029b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_M1_CONTROL), 1030b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DI_CONTROL), 1031b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_PP_CONTROL), 1032b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_PP_SELECT_A), 1033b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_PP_SELECT_B), 1034b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_PP_SELECT_C), 1035b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_PP_SELECT_D), 1036b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL), 1037b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL), 1038b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL), 1039b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS), 1040b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS), 1041b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS), 1042b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS), 1043b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_BORDER_COLOR), 1044b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER), 1045b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER), 1046b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER), 1047b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER), 1048b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND), 1049b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND), 1050b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR), 1051b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS), 1052b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_POSITION), 1053b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS), 1054b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL), 1055b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A), 1056b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B), 1057b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C), 1058b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D), 1059b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL), 1060b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST), 1061b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST), 1062b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST), 1063b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST), 1064b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL), 1065b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL), 1066b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_CONTROL), 1067b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF), 1068b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(0)), 1069b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(1)), 1070b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(2)), 1071b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(3)), 1072b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(4)), 1073b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(5)), 1074b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(6)), 1075b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(7)), 1076b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_LUT(8)), 1077b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL), 1078b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT), 1079b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)), 1080b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)), 1081b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)), 1082b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)), 1083b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)), 1084b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)), 1085b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)), 1086b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)), 1087b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)), 1088b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)), 1089b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)), 1090b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)), 1091b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL), 1092b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES), 1093b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES), 1094b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI), 1095b95800eeSThierry Reding DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL), 1096b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_WIN_OPTIONS), 1097b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BYTE_SWAP), 1098b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL), 1099b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_COLOR_DEPTH), 1100b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_POSITION), 1101b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_SIZE), 1102b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE), 1103b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA), 1104b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA), 1105b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_DDA_INC), 1106b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_LINE_STRIDE), 1107b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BUF_STRIDE), 1108b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE), 1109b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE), 1110b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_DV_CONTROL), 1111b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BLEND_NOKEY), 1112b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BLEND_1WIN), 1113b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X), 1114b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y), 1115b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY), 1116b95800eeSThierry Reding DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL), 1117b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR), 1118b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS), 1119b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR_U), 1120b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS), 1121b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR_V), 1122b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS), 1123b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET), 1124b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS), 1125b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET), 1126b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS), 1127b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS), 1128b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS), 1129b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS), 1130b95800eeSThierry Reding DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS), 1131b95800eeSThierry Reding }; 1132b95800eeSThierry Reding 1133b95800eeSThierry Reding static int tegra_dc_show_regs(struct seq_file *s, void *data) 1134b95800eeSThierry Reding { 1135b95800eeSThierry Reding struct drm_info_node *node = s->private; 1136b95800eeSThierry Reding struct tegra_dc *dc = node->info_ent->data; 1137b95800eeSThierry Reding unsigned int i; 1138b95800eeSThierry Reding int err = 0; 1139b95800eeSThierry Reding 1140b95800eeSThierry Reding drm_modeset_lock(&dc->base.mutex, NULL); 1141b95800eeSThierry Reding 1142b95800eeSThierry Reding if (!dc->base.state->active) { 1143b95800eeSThierry Reding err = -EBUSY; 1144b95800eeSThierry Reding goto unlock; 1145b95800eeSThierry Reding } 1146b95800eeSThierry Reding 1147b95800eeSThierry Reding for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) { 1148b95800eeSThierry Reding unsigned int offset = tegra_dc_regs[i].offset; 1149b95800eeSThierry Reding 1150b95800eeSThierry Reding seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name, 1151b95800eeSThierry Reding offset, tegra_dc_readl(dc, offset)); 1152b95800eeSThierry Reding } 1153b95800eeSThierry Reding 1154b95800eeSThierry Reding unlock: 1155b95800eeSThierry Reding drm_modeset_unlock(&dc->base.mutex); 1156b95800eeSThierry Reding return err; 1157b95800eeSThierry Reding } 1158b95800eeSThierry Reding 1159b95800eeSThierry Reding static int tegra_dc_show_crc(struct seq_file *s, void *data) 1160b95800eeSThierry Reding { 1161b95800eeSThierry Reding struct drm_info_node *node = s->private; 1162b95800eeSThierry Reding struct tegra_dc *dc = node->info_ent->data; 1163b95800eeSThierry Reding int err = 0; 1164b95800eeSThierry Reding u32 value; 1165b95800eeSThierry Reding 1166b95800eeSThierry Reding drm_modeset_lock(&dc->base.mutex, NULL); 1167b95800eeSThierry Reding 1168b95800eeSThierry Reding if (!dc->base.state->active) { 1169b95800eeSThierry Reding err = -EBUSY; 1170b95800eeSThierry Reding goto unlock; 1171b95800eeSThierry Reding } 1172b95800eeSThierry Reding 1173b95800eeSThierry Reding value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE; 1174b95800eeSThierry Reding tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL); 1175b95800eeSThierry Reding tegra_dc_commit(dc); 1176b95800eeSThierry Reding 1177b95800eeSThierry Reding drm_crtc_wait_one_vblank(&dc->base); 1178b95800eeSThierry Reding drm_crtc_wait_one_vblank(&dc->base); 1179b95800eeSThierry Reding 1180b95800eeSThierry Reding value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM); 1181b95800eeSThierry Reding seq_printf(s, "%08x\n", value); 1182b95800eeSThierry Reding 1183b95800eeSThierry Reding tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL); 1184b95800eeSThierry Reding 1185b95800eeSThierry Reding unlock: 1186b95800eeSThierry Reding drm_modeset_unlock(&dc->base.mutex); 1187b95800eeSThierry Reding return err; 1188b95800eeSThierry Reding } 1189b95800eeSThierry Reding 1190b95800eeSThierry Reding static int tegra_dc_show_stats(struct seq_file *s, void *data) 1191b95800eeSThierry Reding { 1192b95800eeSThierry Reding struct drm_info_node *node = s->private; 1193b95800eeSThierry Reding struct tegra_dc *dc = node->info_ent->data; 1194b95800eeSThierry Reding 1195b95800eeSThierry Reding seq_printf(s, "frames: %lu\n", dc->stats.frames); 1196b95800eeSThierry Reding seq_printf(s, "vblank: %lu\n", dc->stats.vblank); 1197b95800eeSThierry Reding seq_printf(s, "underflow: %lu\n", dc->stats.underflow); 1198b95800eeSThierry Reding seq_printf(s, "overflow: %lu\n", dc->stats.overflow); 1199b95800eeSThierry Reding 1200b95800eeSThierry Reding return 0; 1201b95800eeSThierry Reding } 1202b95800eeSThierry Reding 1203b95800eeSThierry Reding static struct drm_info_list debugfs_files[] = { 1204b95800eeSThierry Reding { "regs", tegra_dc_show_regs, 0, NULL }, 1205b95800eeSThierry Reding { "crc", tegra_dc_show_crc, 0, NULL }, 1206b95800eeSThierry Reding { "stats", tegra_dc_show_stats, 0, NULL }, 1207b95800eeSThierry Reding }; 1208b95800eeSThierry Reding 1209b95800eeSThierry Reding static int tegra_dc_late_register(struct drm_crtc *crtc) 1210b95800eeSThierry Reding { 1211b95800eeSThierry Reding unsigned int i, count = ARRAY_SIZE(debugfs_files); 1212b95800eeSThierry Reding struct drm_minor *minor = crtc->dev->primary; 1213b95800eeSThierry Reding struct dentry *root = crtc->debugfs_entry; 1214b95800eeSThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1215b95800eeSThierry Reding int err; 1216b95800eeSThierry Reding 1217b95800eeSThierry Reding dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), 1218b95800eeSThierry Reding GFP_KERNEL); 1219b95800eeSThierry Reding if (!dc->debugfs_files) 1220b95800eeSThierry Reding return -ENOMEM; 1221b95800eeSThierry Reding 1222b95800eeSThierry Reding for (i = 0; i < count; i++) 1223b95800eeSThierry Reding dc->debugfs_files[i].data = dc; 1224b95800eeSThierry Reding 1225b95800eeSThierry Reding err = drm_debugfs_create_files(dc->debugfs_files, count, root, minor); 1226b95800eeSThierry Reding if (err < 0) 1227b95800eeSThierry Reding goto free; 1228b95800eeSThierry Reding 1229b95800eeSThierry Reding return 0; 1230b95800eeSThierry Reding 1231b95800eeSThierry Reding free: 1232b95800eeSThierry Reding kfree(dc->debugfs_files); 1233b95800eeSThierry Reding dc->debugfs_files = NULL; 1234b95800eeSThierry Reding 1235b95800eeSThierry Reding return err; 1236b95800eeSThierry Reding } 1237b95800eeSThierry Reding 1238b95800eeSThierry Reding static void tegra_dc_early_unregister(struct drm_crtc *crtc) 1239b95800eeSThierry Reding { 1240b95800eeSThierry Reding unsigned int count = ARRAY_SIZE(debugfs_files); 1241b95800eeSThierry Reding struct drm_minor *minor = crtc->dev->primary; 1242b95800eeSThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1243b95800eeSThierry Reding 1244b95800eeSThierry Reding drm_debugfs_remove_files(dc->debugfs_files, count, minor); 1245b95800eeSThierry Reding kfree(dc->debugfs_files); 1246b95800eeSThierry Reding dc->debugfs_files = NULL; 1247b95800eeSThierry Reding } 1248b95800eeSThierry Reding 1249c49c81e2SThierry Reding static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc) 1250c49c81e2SThierry Reding { 1251c49c81e2SThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1252c49c81e2SThierry Reding 125347307954SThierry Reding /* XXX vblank syncpoints don't work with nvdisplay yet */ 125447307954SThierry Reding if (dc->syncpt && !dc->soc->has_nvdisplay) 1255c49c81e2SThierry Reding return host1x_syncpt_read(dc->syncpt); 1256c49c81e2SThierry Reding 1257c49c81e2SThierry Reding /* fallback to software emulated VBLANK counter */ 1258c49c81e2SThierry Reding return drm_crtc_vblank_count(&dc->base); 1259c49c81e2SThierry Reding } 1260c49c81e2SThierry Reding 1261c49c81e2SThierry Reding static int tegra_dc_enable_vblank(struct drm_crtc *crtc) 1262c49c81e2SThierry Reding { 1263c49c81e2SThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1264*363541e8SThierry Reding u32 value; 1265c49c81e2SThierry Reding 1266c49c81e2SThierry Reding value = tegra_dc_readl(dc, DC_CMD_INT_MASK); 1267c49c81e2SThierry Reding value |= VBLANK_INT; 1268c49c81e2SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 1269c49c81e2SThierry Reding 1270c49c81e2SThierry Reding return 0; 1271c49c81e2SThierry Reding } 1272c49c81e2SThierry Reding 1273c49c81e2SThierry Reding static void tegra_dc_disable_vblank(struct drm_crtc *crtc) 1274c49c81e2SThierry Reding { 1275c49c81e2SThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1276*363541e8SThierry Reding u32 value; 1277c49c81e2SThierry Reding 1278c49c81e2SThierry Reding value = tegra_dc_readl(dc, DC_CMD_INT_MASK); 1279c49c81e2SThierry Reding value &= ~VBLANK_INT; 1280c49c81e2SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 1281c49c81e2SThierry Reding } 1282c49c81e2SThierry Reding 1283dee8268fSThierry Reding static const struct drm_crtc_funcs tegra_crtc_funcs = { 12841503ca47SThierry Reding .page_flip = drm_atomic_helper_page_flip, 128574f48791SThierry Reding .set_config = drm_atomic_helper_set_config, 1286f002abc1SThierry Reding .destroy = tegra_dc_destroy, 1287ca915b10SThierry Reding .reset = tegra_crtc_reset, 1288ca915b10SThierry Reding .atomic_duplicate_state = tegra_crtc_atomic_duplicate_state, 1289ca915b10SThierry Reding .atomic_destroy_state = tegra_crtc_atomic_destroy_state, 1290b95800eeSThierry Reding .late_register = tegra_dc_late_register, 1291b95800eeSThierry Reding .early_unregister = tegra_dc_early_unregister, 129210437d9bSShawn Guo .get_vblank_counter = tegra_dc_get_vblank_counter, 129310437d9bSShawn Guo .enable_vblank = tegra_dc_enable_vblank, 129410437d9bSShawn Guo .disable_vblank = tegra_dc_disable_vblank, 1295dee8268fSThierry Reding }; 1296dee8268fSThierry Reding 1297dee8268fSThierry Reding static int tegra_dc_set_timings(struct tegra_dc *dc, 1298dee8268fSThierry Reding struct drm_display_mode *mode) 1299dee8268fSThierry Reding { 13000444c0ffSThierry Reding unsigned int h_ref_to_sync = 1; 13010444c0ffSThierry Reding unsigned int v_ref_to_sync = 1; 1302dee8268fSThierry Reding unsigned long value; 1303dee8268fSThierry Reding 130447307954SThierry Reding if (!dc->soc->has_nvdisplay) { 1305dee8268fSThierry Reding tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); 1306dee8268fSThierry Reding 1307dee8268fSThierry Reding value = (v_ref_to_sync << 16) | h_ref_to_sync; 1308dee8268fSThierry Reding tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC); 130947307954SThierry Reding } 1310dee8268fSThierry Reding 1311dee8268fSThierry Reding value = ((mode->vsync_end - mode->vsync_start) << 16) | 1312dee8268fSThierry Reding ((mode->hsync_end - mode->hsync_start) << 0); 1313dee8268fSThierry Reding tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH); 1314dee8268fSThierry Reding 1315dee8268fSThierry Reding value = ((mode->vtotal - mode->vsync_end) << 16) | 1316dee8268fSThierry Reding ((mode->htotal - mode->hsync_end) << 0); 1317dee8268fSThierry Reding tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH); 1318dee8268fSThierry Reding 1319dee8268fSThierry Reding value = ((mode->vsync_start - mode->vdisplay) << 16) | 1320dee8268fSThierry Reding ((mode->hsync_start - mode->hdisplay) << 0); 1321dee8268fSThierry Reding tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH); 1322dee8268fSThierry Reding 1323dee8268fSThierry Reding value = (mode->vdisplay << 16) | mode->hdisplay; 1324dee8268fSThierry Reding tegra_dc_writel(dc, value, DC_DISP_ACTIVE); 1325dee8268fSThierry Reding 1326dee8268fSThierry Reding return 0; 1327dee8268fSThierry Reding } 1328dee8268fSThierry Reding 13299d910b60SThierry Reding /** 13309d910b60SThierry Reding * tegra_dc_state_setup_clock - check clock settings and store them in atomic 13319d910b60SThierry Reding * state 13329d910b60SThierry Reding * @dc: display controller 13339d910b60SThierry Reding * @crtc_state: CRTC atomic state 13349d910b60SThierry Reding * @clk: parent clock for display controller 13359d910b60SThierry Reding * @pclk: pixel clock 13369d910b60SThierry Reding * @div: shift clock divider 13379d910b60SThierry Reding * 13389d910b60SThierry Reding * Returns: 13399d910b60SThierry Reding * 0 on success or a negative error-code on failure. 13409d910b60SThierry Reding */ 1341ca915b10SThierry Reding int tegra_dc_state_setup_clock(struct tegra_dc *dc, 1342ca915b10SThierry Reding struct drm_crtc_state *crtc_state, 1343ca915b10SThierry Reding struct clk *clk, unsigned long pclk, 1344ca915b10SThierry Reding unsigned int div) 1345ca915b10SThierry Reding { 1346ca915b10SThierry Reding struct tegra_dc_state *state = to_dc_state(crtc_state); 1347ca915b10SThierry Reding 1348d2982748SThierry Reding if (!clk_has_parent(dc->clk, clk)) 1349d2982748SThierry Reding return -EINVAL; 1350d2982748SThierry Reding 1351ca915b10SThierry Reding state->clk = clk; 1352ca915b10SThierry Reding state->pclk = pclk; 1353ca915b10SThierry Reding state->div = div; 1354ca915b10SThierry Reding 1355ca915b10SThierry Reding return 0; 1356ca915b10SThierry Reding } 1357ca915b10SThierry Reding 135876d59ed0SThierry Reding static void tegra_dc_commit_state(struct tegra_dc *dc, 135976d59ed0SThierry Reding struct tegra_dc_state *state) 136076d59ed0SThierry Reding { 136176d59ed0SThierry Reding u32 value; 136276d59ed0SThierry Reding int err; 136376d59ed0SThierry Reding 136476d59ed0SThierry Reding err = clk_set_parent(dc->clk, state->clk); 136576d59ed0SThierry Reding if (err < 0) 136676d59ed0SThierry Reding dev_err(dc->dev, "failed to set parent clock: %d\n", err); 136776d59ed0SThierry Reding 136876d59ed0SThierry Reding /* 136976d59ed0SThierry Reding * Outputs may not want to change the parent clock rate. This is only 137076d59ed0SThierry Reding * relevant to Tegra20 where only a single display PLL is available. 137176d59ed0SThierry Reding * Since that PLL would typically be used for HDMI, an internal LVDS 137276d59ed0SThierry Reding * panel would need to be driven by some other clock such as PLL_P 137376d59ed0SThierry Reding * which is shared with other peripherals. Changing the clock rate 137476d59ed0SThierry Reding * should therefore be avoided. 137576d59ed0SThierry Reding */ 137676d59ed0SThierry Reding if (state->pclk > 0) { 137776d59ed0SThierry Reding err = clk_set_rate(state->clk, state->pclk); 137876d59ed0SThierry Reding if (err < 0) 137976d59ed0SThierry Reding dev_err(dc->dev, 138076d59ed0SThierry Reding "failed to set clock rate to %lu Hz\n", 138176d59ed0SThierry Reding state->pclk); 138276d59ed0SThierry Reding } 138376d59ed0SThierry Reding 138476d59ed0SThierry Reding DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), 138576d59ed0SThierry Reding state->div); 138676d59ed0SThierry Reding DRM_DEBUG_KMS("pclk: %lu\n", state->pclk); 138776d59ed0SThierry Reding 138847307954SThierry Reding if (!dc->soc->has_nvdisplay) { 138976d59ed0SThierry Reding value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1; 139076d59ed0SThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); 139147307954SThierry Reding } 139239e08affSThierry Reding 139339e08affSThierry Reding err = clk_set_rate(dc->clk, state->pclk); 139439e08affSThierry Reding if (err < 0) 139539e08affSThierry Reding dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n", 139639e08affSThierry Reding dc->clk, state->pclk, err); 139776d59ed0SThierry Reding } 139876d59ed0SThierry Reding 1399003fc848SThierry Reding static void tegra_dc_stop(struct tegra_dc *dc) 1400003fc848SThierry Reding { 1401003fc848SThierry Reding u32 value; 1402003fc848SThierry Reding 1403003fc848SThierry Reding /* stop the display controller */ 1404003fc848SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); 1405003fc848SThierry Reding value &= ~DISP_CTRL_MODE_MASK; 1406003fc848SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); 1407003fc848SThierry Reding 1408003fc848SThierry Reding tegra_dc_commit(dc); 1409003fc848SThierry Reding } 1410003fc848SThierry Reding 1411003fc848SThierry Reding static bool tegra_dc_idle(struct tegra_dc *dc) 1412003fc848SThierry Reding { 1413003fc848SThierry Reding u32 value; 1414003fc848SThierry Reding 1415003fc848SThierry Reding value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND); 1416003fc848SThierry Reding 1417003fc848SThierry Reding return (value & DISP_CTRL_MODE_MASK) == 0; 1418003fc848SThierry Reding } 1419003fc848SThierry Reding 1420003fc848SThierry Reding static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout) 1421003fc848SThierry Reding { 1422003fc848SThierry Reding timeout = jiffies + msecs_to_jiffies(timeout); 1423003fc848SThierry Reding 1424003fc848SThierry Reding while (time_before(jiffies, timeout)) { 1425003fc848SThierry Reding if (tegra_dc_idle(dc)) 1426003fc848SThierry Reding return 0; 1427003fc848SThierry Reding 1428003fc848SThierry Reding usleep_range(1000, 2000); 1429003fc848SThierry Reding } 1430003fc848SThierry Reding 1431003fc848SThierry Reding dev_dbg(dc->dev, "timeout waiting for DC to become idle\n"); 1432003fc848SThierry Reding return -ETIMEDOUT; 1433003fc848SThierry Reding } 1434003fc848SThierry Reding 143564581714SLaurent Pinchart static void tegra_crtc_atomic_disable(struct drm_crtc *crtc, 143664581714SLaurent Pinchart struct drm_crtc_state *old_state) 1437003fc848SThierry Reding { 1438003fc848SThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1439003fc848SThierry Reding u32 value; 1440003fc848SThierry Reding 1441003fc848SThierry Reding if (!tegra_dc_idle(dc)) { 1442003fc848SThierry Reding tegra_dc_stop(dc); 1443003fc848SThierry Reding 1444003fc848SThierry Reding /* 1445003fc848SThierry Reding * Ignore the return value, there isn't anything useful to do 1446003fc848SThierry Reding * in case this fails. 1447003fc848SThierry Reding */ 1448003fc848SThierry Reding tegra_dc_wait_idle(dc, 100); 1449003fc848SThierry Reding } 1450003fc848SThierry Reding 1451003fc848SThierry Reding /* 1452003fc848SThierry Reding * This should really be part of the RGB encoder driver, but clearing 1453003fc848SThierry Reding * these bits has the side-effect of stopping the display controller. 1454003fc848SThierry Reding * When that happens no VBLANK interrupts will be raised. At the same 1455003fc848SThierry Reding * time the encoder is disabled before the display controller, so the 1456003fc848SThierry Reding * above code is always going to timeout waiting for the controller 1457003fc848SThierry Reding * to go idle. 1458003fc848SThierry Reding * 1459003fc848SThierry Reding * Given the close coupling between the RGB encoder and the display 1460003fc848SThierry Reding * controller doing it here is still kind of okay. None of the other 1461003fc848SThierry Reding * encoder drivers require these bits to be cleared. 1462003fc848SThierry Reding * 1463003fc848SThierry Reding * XXX: Perhaps given that the display controller is switched off at 1464003fc848SThierry Reding * this point anyway maybe clearing these bits isn't even useful for 1465003fc848SThierry Reding * the RGB encoder? 1466003fc848SThierry Reding */ 1467003fc848SThierry Reding if (dc->rgb) { 1468003fc848SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 1469003fc848SThierry Reding value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 1470003fc848SThierry Reding PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); 1471003fc848SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 1472003fc848SThierry Reding } 1473003fc848SThierry Reding 1474003fc848SThierry Reding tegra_dc_stats_reset(&dc->stats); 1475003fc848SThierry Reding drm_crtc_vblank_off(crtc); 147633a8eb8dSThierry Reding 14779d99ab6eSThierry Reding spin_lock_irq(&crtc->dev->event_lock); 14789d99ab6eSThierry Reding 14799d99ab6eSThierry Reding if (crtc->state->event) { 14809d99ab6eSThierry Reding drm_crtc_send_vblank_event(crtc, crtc->state->event); 14819d99ab6eSThierry Reding crtc->state->event = NULL; 14829d99ab6eSThierry Reding } 14839d99ab6eSThierry Reding 14849d99ab6eSThierry Reding spin_unlock_irq(&crtc->dev->event_lock); 14859d99ab6eSThierry Reding 148633a8eb8dSThierry Reding pm_runtime_put_sync(dc->dev); 1487003fc848SThierry Reding } 1488003fc848SThierry Reding 14890b20a0f8SLaurent Pinchart static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, 14900b20a0f8SLaurent Pinchart struct drm_crtc_state *old_state) 1491dee8268fSThierry Reding { 14924aa3df71SThierry Reding struct drm_display_mode *mode = &crtc->state->adjusted_mode; 149376d59ed0SThierry Reding struct tegra_dc_state *state = to_dc_state(crtc->state); 1494dee8268fSThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 1495dbb3f2f7SThierry Reding u32 value; 1496dee8268fSThierry Reding 149733a8eb8dSThierry Reding pm_runtime_get_sync(dc->dev); 149833a8eb8dSThierry Reding 149933a8eb8dSThierry Reding /* initialize display controller */ 150033a8eb8dSThierry Reding if (dc->syncpt) { 150147307954SThierry Reding u32 syncpt = host1x_syncpt_id(dc->syncpt), enable; 150247307954SThierry Reding 150347307954SThierry Reding if (dc->soc->has_nvdisplay) 150447307954SThierry Reding enable = 1 << 31; 150547307954SThierry Reding else 150647307954SThierry Reding enable = 1 << 8; 150733a8eb8dSThierry Reding 150833a8eb8dSThierry Reding value = SYNCPT_CNTRL_NO_STALL; 150933a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); 151033a8eb8dSThierry Reding 151147307954SThierry Reding value = enable | syncpt; 151233a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); 151333a8eb8dSThierry Reding } 151433a8eb8dSThierry Reding 151547307954SThierry Reding if (dc->soc->has_nvdisplay) { 151647307954SThierry Reding value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | 151747307954SThierry Reding DSC_OBUF_UF_INT; 151847307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); 151947307954SThierry Reding 152047307954SThierry Reding value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT | 152147307954SThierry Reding DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT | 152247307954SThierry Reding HEAD_UF_INT | MSF_INT | REG_TMOUT_INT | 152347307954SThierry Reding REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT | 152447307954SThierry Reding VBLANK_INT | FRAME_END_INT; 152547307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); 152647307954SThierry Reding 152747307954SThierry Reding value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT | 152847307954SThierry Reding FRAME_END_INT; 152947307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 153047307954SThierry Reding 153147307954SThierry Reding value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT; 153247307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 153347307954SThierry Reding 153447307954SThierry Reding tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); 153547307954SThierry Reding } else { 153633a8eb8dSThierry Reding value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 153733a8eb8dSThierry Reding WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 153833a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); 153933a8eb8dSThierry Reding 154033a8eb8dSThierry Reding value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 154133a8eb8dSThierry Reding WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 154233a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); 154333a8eb8dSThierry Reding 154433a8eb8dSThierry Reding /* initialize timer */ 154533a8eb8dSThierry Reding value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | 154633a8eb8dSThierry Reding WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); 154733a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); 154833a8eb8dSThierry Reding 154933a8eb8dSThierry Reding value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | 155033a8eb8dSThierry Reding WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); 155133a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); 155233a8eb8dSThierry Reding 155333a8eb8dSThierry Reding value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 155433a8eb8dSThierry Reding WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 155533a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 155633a8eb8dSThierry Reding 155733a8eb8dSThierry Reding value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | 155833a8eb8dSThierry Reding WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; 155933a8eb8dSThierry Reding tegra_dc_writel(dc, value, DC_CMD_INT_MASK); 156047307954SThierry Reding } 156133a8eb8dSThierry Reding 15627116e9a8SThierry Reding if (dc->soc->supports_background_color) 15637116e9a8SThierry Reding tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR); 15647116e9a8SThierry Reding else 156533a8eb8dSThierry Reding tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); 156633a8eb8dSThierry Reding 156733a8eb8dSThierry Reding /* apply PLL and pixel clock changes */ 156876d59ed0SThierry Reding tegra_dc_commit_state(dc, state); 156976d59ed0SThierry Reding 1570dee8268fSThierry Reding /* program display mode */ 1571dee8268fSThierry Reding tegra_dc_set_timings(dc, mode); 1572dee8268fSThierry Reding 15738620fc62SThierry Reding /* interlacing isn't supported yet, so disable it */ 15748620fc62SThierry Reding if (dc->soc->supports_interlacing) { 15758620fc62SThierry Reding value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); 15768620fc62SThierry Reding value &= ~INTERLACE_ENABLE; 15778620fc62SThierry Reding tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); 15788620fc62SThierry Reding } 1579666cb873SThierry Reding 1580666cb873SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); 1581666cb873SThierry Reding value &= ~DISP_CTRL_MODE_MASK; 1582666cb873SThierry Reding value |= DISP_CTRL_MODE_C_DISPLAY; 1583666cb873SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); 1584666cb873SThierry Reding 158547307954SThierry Reding if (!dc->soc->has_nvdisplay) { 1586666cb873SThierry Reding value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); 1587666cb873SThierry Reding value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | 1588666cb873SThierry Reding PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; 1589666cb873SThierry Reding tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); 159047307954SThierry Reding } 159147307954SThierry Reding 159247307954SThierry Reding /* enable underflow reporting and display red for missing pixels */ 159347307954SThierry Reding if (dc->soc->has_nvdisplay) { 159447307954SThierry Reding value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE; 159547307954SThierry Reding tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW); 159647307954SThierry Reding } 1597666cb873SThierry Reding 1598666cb873SThierry Reding tegra_dc_commit(dc); 1599dee8268fSThierry Reding 16008ff64c17SThierry Reding drm_crtc_vblank_on(crtc); 1601dee8268fSThierry Reding } 1602dee8268fSThierry Reding 16034aa3df71SThierry Reding static int tegra_crtc_atomic_check(struct drm_crtc *crtc, 16044aa3df71SThierry Reding struct drm_crtc_state *state) 16054aa3df71SThierry Reding { 1606c4755fb9SThierry Reding struct tegra_atomic_state *s = to_tegra_atomic_state(state->state); 1607c4755fb9SThierry Reding struct tegra_dc_state *tegra = to_dc_state(state); 1608c4755fb9SThierry Reding 1609c4755fb9SThierry Reding /* 1610c4755fb9SThierry Reding * The display hub display clock needs to be fed by the display clock 1611c4755fb9SThierry Reding * with the highest frequency to ensure proper functioning of all the 1612c4755fb9SThierry Reding * displays. 1613c4755fb9SThierry Reding * 1614c4755fb9SThierry Reding * Note that this isn't used before Tegra186, but it doesn't hurt and 1615c4755fb9SThierry Reding * conditionalizing it would make the code less clean. 1616c4755fb9SThierry Reding */ 1617c4755fb9SThierry Reding if (state->active) { 1618c4755fb9SThierry Reding if (!s->clk_disp || tegra->pclk > s->rate) { 1619c4755fb9SThierry Reding s->dc = to_tegra_dc(crtc); 1620c4755fb9SThierry Reding s->clk_disp = s->dc->clk; 1621c4755fb9SThierry Reding s->rate = tegra->pclk; 1622c4755fb9SThierry Reding } 1623c4755fb9SThierry Reding } 1624c4755fb9SThierry Reding 16254aa3df71SThierry Reding return 0; 16264aa3df71SThierry Reding } 16274aa3df71SThierry Reding 1628613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_begin(struct drm_crtc *crtc, 1629613d2b27SMaarten Lankhorst struct drm_crtc_state *old_crtc_state) 16304aa3df71SThierry Reding { 16319d99ab6eSThierry Reding unsigned long flags; 16321503ca47SThierry Reding 16331503ca47SThierry Reding if (crtc->state->event) { 16349d99ab6eSThierry Reding spin_lock_irqsave(&crtc->dev->event_lock, flags); 16351503ca47SThierry Reding 16369d99ab6eSThierry Reding if (drm_crtc_vblank_get(crtc) != 0) 16379d99ab6eSThierry Reding drm_crtc_send_vblank_event(crtc, crtc->state->event); 16389d99ab6eSThierry Reding else 16399d99ab6eSThierry Reding drm_crtc_arm_vblank_event(crtc, crtc->state->event); 16401503ca47SThierry Reding 16419d99ab6eSThierry Reding spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 16429d99ab6eSThierry Reding 16431503ca47SThierry Reding crtc->state->event = NULL; 16441503ca47SThierry Reding } 16454aa3df71SThierry Reding } 16464aa3df71SThierry Reding 1647613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, 1648613d2b27SMaarten Lankhorst struct drm_crtc_state *old_crtc_state) 16494aa3df71SThierry Reding { 165047802b09SThierry Reding struct tegra_dc_state *state = to_dc_state(crtc->state); 165147802b09SThierry Reding struct tegra_dc *dc = to_tegra_dc(crtc); 165247307954SThierry Reding u32 value; 165347802b09SThierry Reding 165447307954SThierry Reding value = state->planes << 8 | GENERAL_UPDATE; 165547307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 165647307954SThierry Reding value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 165747307954SThierry Reding 165847307954SThierry Reding value = state->planes | GENERAL_ACT_REQ; 165947307954SThierry Reding tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); 166047307954SThierry Reding value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 16614aa3df71SThierry Reding } 16624aa3df71SThierry Reding 1663dee8268fSThierry Reding static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { 16644aa3df71SThierry Reding .atomic_check = tegra_crtc_atomic_check, 16654aa3df71SThierry Reding .atomic_begin = tegra_crtc_atomic_begin, 16664aa3df71SThierry Reding .atomic_flush = tegra_crtc_atomic_flush, 16670b20a0f8SLaurent Pinchart .atomic_enable = tegra_crtc_atomic_enable, 166864581714SLaurent Pinchart .atomic_disable = tegra_crtc_atomic_disable, 1669dee8268fSThierry Reding }; 1670dee8268fSThierry Reding 1671dee8268fSThierry Reding static irqreturn_t tegra_dc_irq(int irq, void *data) 1672dee8268fSThierry Reding { 1673dee8268fSThierry Reding struct tegra_dc *dc = data; 1674dee8268fSThierry Reding unsigned long status; 1675dee8268fSThierry Reding 1676dee8268fSThierry Reding status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); 1677dee8268fSThierry Reding tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); 1678dee8268fSThierry Reding 1679dee8268fSThierry Reding if (status & FRAME_END_INT) { 1680dee8268fSThierry Reding /* 1681dee8268fSThierry Reding dev_dbg(dc->dev, "%s(): frame end\n", __func__); 1682dee8268fSThierry Reding */ 1683791ddb1eSThierry Reding dc->stats.frames++; 1684dee8268fSThierry Reding } 1685dee8268fSThierry Reding 1686dee8268fSThierry Reding if (status & VBLANK_INT) { 1687dee8268fSThierry Reding /* 1688dee8268fSThierry Reding dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); 1689dee8268fSThierry Reding */ 1690ed7dae58SThierry Reding drm_crtc_handle_vblank(&dc->base); 1691791ddb1eSThierry Reding dc->stats.vblank++; 1692dee8268fSThierry Reding } 1693dee8268fSThierry Reding 1694dee8268fSThierry Reding if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { 1695dee8268fSThierry Reding /* 1696dee8268fSThierry Reding dev_dbg(dc->dev, "%s(): underflow\n", __func__); 1697dee8268fSThierry Reding */ 1698791ddb1eSThierry Reding dc->stats.underflow++; 1699791ddb1eSThierry Reding } 1700791ddb1eSThierry Reding 1701791ddb1eSThierry Reding if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) { 1702791ddb1eSThierry Reding /* 1703791ddb1eSThierry Reding dev_dbg(dc->dev, "%s(): overflow\n", __func__); 1704791ddb1eSThierry Reding */ 1705791ddb1eSThierry Reding dc->stats.overflow++; 1706dee8268fSThierry Reding } 1707dee8268fSThierry Reding 170847307954SThierry Reding if (status & HEAD_UF_INT) { 170947307954SThierry Reding dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__); 171047307954SThierry Reding dc->stats.underflow++; 171147307954SThierry Reding } 171247307954SThierry Reding 1713dee8268fSThierry Reding return IRQ_HANDLED; 1714dee8268fSThierry Reding } 1715dee8268fSThierry Reding 1716dee8268fSThierry Reding static int tegra_dc_init(struct host1x_client *client) 1717dee8268fSThierry Reding { 17189910f5c4SThierry Reding struct drm_device *drm = dev_get_drvdata(client->parent); 17192bcdcbfaSThierry Reding unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; 1720dee8268fSThierry Reding struct tegra_dc *dc = host1x_client_to_dc(client); 1721d1f3e1e0SThierry Reding struct tegra_drm *tegra = drm->dev_private; 1722c7679306SThierry Reding struct drm_plane *primary = NULL; 1723c7679306SThierry Reding struct drm_plane *cursor = NULL; 1724dee8268fSThierry Reding int err; 1725dee8268fSThierry Reding 1726617dd7ccSThierry Reding dc->syncpt = host1x_syncpt_request(client, flags); 17272bcdcbfaSThierry Reding if (!dc->syncpt) 17282bcdcbfaSThierry Reding dev_warn(dc->dev, "failed to allocate syncpoint\n"); 17292bcdcbfaSThierry Reding 1730df06b759SThierry Reding if (tegra->domain) { 1731df06b759SThierry Reding err = iommu_attach_device(tegra->domain, dc->dev); 1732df06b759SThierry Reding if (err < 0) { 1733df06b759SThierry Reding dev_err(dc->dev, "failed to attach to domain: %d\n", 1734df06b759SThierry Reding err); 1735df06b759SThierry Reding return err; 1736df06b759SThierry Reding } 1737df06b759SThierry Reding 1738df06b759SThierry Reding dc->domain = tegra->domain; 1739df06b759SThierry Reding } 1740df06b759SThierry Reding 174147307954SThierry Reding if (dc->soc->wgrps) 174247307954SThierry Reding primary = tegra_dc_add_shared_planes(drm, dc); 174347307954SThierry Reding else 174447307954SThierry Reding primary = tegra_dc_add_planes(drm, dc); 174547307954SThierry Reding 1746c7679306SThierry Reding if (IS_ERR(primary)) { 1747c7679306SThierry Reding err = PTR_ERR(primary); 1748c7679306SThierry Reding goto cleanup; 1749c7679306SThierry Reding } 1750c7679306SThierry Reding 1751c7679306SThierry Reding if (dc->soc->supports_cursor) { 1752c7679306SThierry Reding cursor = tegra_dc_cursor_plane_create(drm, dc); 1753c7679306SThierry Reding if (IS_ERR(cursor)) { 1754c7679306SThierry Reding err = PTR_ERR(cursor); 1755c7679306SThierry Reding goto cleanup; 1756c7679306SThierry Reding } 1757c7679306SThierry Reding } 1758c7679306SThierry Reding 1759c7679306SThierry Reding err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor, 1760f9882876SVille Syrjälä &tegra_crtc_funcs, NULL); 1761c7679306SThierry Reding if (err < 0) 1762c7679306SThierry Reding goto cleanup; 1763c7679306SThierry Reding 1764dee8268fSThierry Reding drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); 1765dee8268fSThierry Reding 1766d1f3e1e0SThierry Reding /* 1767d1f3e1e0SThierry Reding * Keep track of the minimum pitch alignment across all display 1768d1f3e1e0SThierry Reding * controllers. 1769d1f3e1e0SThierry Reding */ 1770d1f3e1e0SThierry Reding if (dc->soc->pitch_align > tegra->pitch_align) 1771d1f3e1e0SThierry Reding tegra->pitch_align = dc->soc->pitch_align; 1772d1f3e1e0SThierry Reding 17739910f5c4SThierry Reding err = tegra_dc_rgb_init(drm, dc); 1774dee8268fSThierry Reding if (err < 0 && err != -ENODEV) { 1775dee8268fSThierry Reding dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); 1776c7679306SThierry Reding goto cleanup; 1777dee8268fSThierry Reding } 1778dee8268fSThierry Reding 1779dee8268fSThierry Reding err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0, 1780dee8268fSThierry Reding dev_name(dc->dev), dc); 1781dee8268fSThierry Reding if (err < 0) { 1782dee8268fSThierry Reding dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, 1783dee8268fSThierry Reding err); 1784c7679306SThierry Reding goto cleanup; 1785dee8268fSThierry Reding } 1786dee8268fSThierry Reding 1787dee8268fSThierry Reding return 0; 1788c7679306SThierry Reding 1789c7679306SThierry Reding cleanup: 179047307954SThierry Reding if (!IS_ERR_OR_NULL(cursor)) 1791c7679306SThierry Reding drm_plane_cleanup(cursor); 1792c7679306SThierry Reding 179347307954SThierry Reding if (!IS_ERR(primary)) 1794c7679306SThierry Reding drm_plane_cleanup(primary); 1795c7679306SThierry Reding 1796c7679306SThierry Reding if (tegra->domain) { 1797c7679306SThierry Reding iommu_detach_device(tegra->domain, dc->dev); 1798c7679306SThierry Reding dc->domain = NULL; 1799c7679306SThierry Reding } 1800c7679306SThierry Reding 1801c7679306SThierry Reding return err; 1802dee8268fSThierry Reding } 1803dee8268fSThierry Reding 1804dee8268fSThierry Reding static int tegra_dc_exit(struct host1x_client *client) 1805dee8268fSThierry Reding { 1806dee8268fSThierry Reding struct tegra_dc *dc = host1x_client_to_dc(client); 1807dee8268fSThierry Reding int err; 1808dee8268fSThierry Reding 1809dee8268fSThierry Reding devm_free_irq(dc->dev, dc->irq, dc); 1810dee8268fSThierry Reding 1811dee8268fSThierry Reding err = tegra_dc_rgb_exit(dc); 1812dee8268fSThierry Reding if (err) { 1813dee8268fSThierry Reding dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err); 1814dee8268fSThierry Reding return err; 1815dee8268fSThierry Reding } 1816dee8268fSThierry Reding 1817df06b759SThierry Reding if (dc->domain) { 1818df06b759SThierry Reding iommu_detach_device(dc->domain, dc->dev); 1819df06b759SThierry Reding dc->domain = NULL; 1820df06b759SThierry Reding } 1821df06b759SThierry Reding 18222bcdcbfaSThierry Reding host1x_syncpt_free(dc->syncpt); 18232bcdcbfaSThierry Reding 1824dee8268fSThierry Reding return 0; 1825dee8268fSThierry Reding } 1826dee8268fSThierry Reding 1827dee8268fSThierry Reding static const struct host1x_client_ops dc_client_ops = { 1828dee8268fSThierry Reding .init = tegra_dc_init, 1829dee8268fSThierry Reding .exit = tegra_dc_exit, 1830dee8268fSThierry Reding }; 1831dee8268fSThierry Reding 18328620fc62SThierry Reding static const struct tegra_dc_soc_info tegra20_dc_soc_info = { 18337116e9a8SThierry Reding .supports_background_color = false, 18348620fc62SThierry Reding .supports_interlacing = false, 1835e687651bSThierry Reding .supports_cursor = false, 1836c134f019SThierry Reding .supports_block_linear = false, 1837d1f3e1e0SThierry Reding .pitch_align = 8, 18389c012700SThierry Reding .has_powergate = false, 18396ac1571bSDmitry Osipenko .broken_reset = true, 184047307954SThierry Reding .has_nvdisplay = false, 1841511c7023SThierry Reding .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), 1842511c7023SThierry Reding .primary_formats = tegra20_primary_formats, 1843511c7023SThierry Reding .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), 1844511c7023SThierry Reding .overlay_formats = tegra20_overlay_formats, 18458620fc62SThierry Reding }; 18468620fc62SThierry Reding 18478620fc62SThierry Reding static const struct tegra_dc_soc_info tegra30_dc_soc_info = { 18487116e9a8SThierry Reding .supports_background_color = false, 18498620fc62SThierry Reding .supports_interlacing = false, 1850e687651bSThierry Reding .supports_cursor = false, 1851c134f019SThierry Reding .supports_block_linear = false, 1852d1f3e1e0SThierry Reding .pitch_align = 8, 18539c012700SThierry Reding .has_powergate = false, 18546ac1571bSDmitry Osipenko .broken_reset = false, 185547307954SThierry Reding .has_nvdisplay = false, 1856511c7023SThierry Reding .num_primary_formats = ARRAY_SIZE(tegra20_primary_formats), 1857511c7023SThierry Reding .primary_formats = tegra20_primary_formats, 1858511c7023SThierry Reding .num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats), 1859511c7023SThierry Reding .overlay_formats = tegra20_overlay_formats, 1860d1f3e1e0SThierry Reding }; 1861d1f3e1e0SThierry Reding 1862d1f3e1e0SThierry Reding static const struct tegra_dc_soc_info tegra114_dc_soc_info = { 18637116e9a8SThierry Reding .supports_background_color = false, 1864d1f3e1e0SThierry Reding .supports_interlacing = false, 1865d1f3e1e0SThierry Reding .supports_cursor = false, 1866d1f3e1e0SThierry Reding .supports_block_linear = false, 1867d1f3e1e0SThierry Reding .pitch_align = 64, 18689c012700SThierry Reding .has_powergate = true, 18696ac1571bSDmitry Osipenko .broken_reset = false, 187047307954SThierry Reding .has_nvdisplay = false, 1871511c7023SThierry Reding .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), 1872511c7023SThierry Reding .primary_formats = tegra114_primary_formats, 1873511c7023SThierry Reding .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), 1874511c7023SThierry Reding .overlay_formats = tegra114_overlay_formats, 18758620fc62SThierry Reding }; 18768620fc62SThierry Reding 18778620fc62SThierry Reding static const struct tegra_dc_soc_info tegra124_dc_soc_info = { 18787116e9a8SThierry Reding .supports_background_color = true, 18798620fc62SThierry Reding .supports_interlacing = true, 1880e687651bSThierry Reding .supports_cursor = true, 1881c134f019SThierry Reding .supports_block_linear = true, 1882d1f3e1e0SThierry Reding .pitch_align = 64, 18839c012700SThierry Reding .has_powergate = true, 18846ac1571bSDmitry Osipenko .broken_reset = false, 188547307954SThierry Reding .has_nvdisplay = false, 1886511c7023SThierry Reding .num_primary_formats = ARRAY_SIZE(tegra124_primary_formats), 1887511c7023SThierry Reding .primary_formats = tegra114_primary_formats, 1888511c7023SThierry Reding .num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats), 1889511c7023SThierry Reding .overlay_formats = tegra114_overlay_formats, 18908620fc62SThierry Reding }; 18918620fc62SThierry Reding 18925b4f516fSThierry Reding static const struct tegra_dc_soc_info tegra210_dc_soc_info = { 18937116e9a8SThierry Reding .supports_background_color = true, 18945b4f516fSThierry Reding .supports_interlacing = true, 18955b4f516fSThierry Reding .supports_cursor = true, 18965b4f516fSThierry Reding .supports_block_linear = true, 18975b4f516fSThierry Reding .pitch_align = 64, 18985b4f516fSThierry Reding .has_powergate = true, 18996ac1571bSDmitry Osipenko .broken_reset = false, 190047307954SThierry Reding .has_nvdisplay = false, 1901511c7023SThierry Reding .num_primary_formats = ARRAY_SIZE(tegra114_primary_formats), 1902511c7023SThierry Reding .primary_formats = tegra114_primary_formats, 1903511c7023SThierry Reding .num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats), 1904511c7023SThierry Reding .overlay_formats = tegra114_overlay_formats, 190547307954SThierry Reding }; 190647307954SThierry Reding 190747307954SThierry Reding static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { 190847307954SThierry Reding { 190947307954SThierry Reding .index = 0, 191047307954SThierry Reding .dc = 0, 191147307954SThierry Reding .windows = (const unsigned int[]) { 0 }, 191247307954SThierry Reding .num_windows = 1, 191347307954SThierry Reding }, { 191447307954SThierry Reding .index = 1, 191547307954SThierry Reding .dc = 1, 191647307954SThierry Reding .windows = (const unsigned int[]) { 1 }, 191747307954SThierry Reding .num_windows = 1, 191847307954SThierry Reding }, { 191947307954SThierry Reding .index = 2, 192047307954SThierry Reding .dc = 1, 192147307954SThierry Reding .windows = (const unsigned int[]) { 2 }, 192247307954SThierry Reding .num_windows = 1, 192347307954SThierry Reding }, { 192447307954SThierry Reding .index = 3, 192547307954SThierry Reding .dc = 2, 192647307954SThierry Reding .windows = (const unsigned int[]) { 3 }, 192747307954SThierry Reding .num_windows = 1, 192847307954SThierry Reding }, { 192947307954SThierry Reding .index = 4, 193047307954SThierry Reding .dc = 2, 193147307954SThierry Reding .windows = (const unsigned int[]) { 4 }, 193247307954SThierry Reding .num_windows = 1, 193347307954SThierry Reding }, { 193447307954SThierry Reding .index = 5, 193547307954SThierry Reding .dc = 2, 193647307954SThierry Reding .windows = (const unsigned int[]) { 5 }, 193747307954SThierry Reding .num_windows = 1, 193847307954SThierry Reding }, 193947307954SThierry Reding }; 194047307954SThierry Reding 194147307954SThierry Reding static const struct tegra_dc_soc_info tegra186_dc_soc_info = { 194247307954SThierry Reding .supports_background_color = true, 194347307954SThierry Reding .supports_interlacing = true, 194447307954SThierry Reding .supports_cursor = true, 194547307954SThierry Reding .supports_block_linear = true, 194647307954SThierry Reding .pitch_align = 64, 194747307954SThierry Reding .has_powergate = false, 194847307954SThierry Reding .broken_reset = false, 194947307954SThierry Reding .has_nvdisplay = true, 195047307954SThierry Reding .wgrps = tegra186_dc_wgrps, 195147307954SThierry Reding .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), 19525b4f516fSThierry Reding }; 19535b4f516fSThierry Reding 19548620fc62SThierry Reding static const struct of_device_id tegra_dc_of_match[] = { 19558620fc62SThierry Reding { 195647307954SThierry Reding .compatible = "nvidia,tegra186-dc", 195747307954SThierry Reding .data = &tegra186_dc_soc_info, 195847307954SThierry Reding }, { 19595b4f516fSThierry Reding .compatible = "nvidia,tegra210-dc", 19605b4f516fSThierry Reding .data = &tegra210_dc_soc_info, 19615b4f516fSThierry Reding }, { 19628620fc62SThierry Reding .compatible = "nvidia,tegra124-dc", 19638620fc62SThierry Reding .data = &tegra124_dc_soc_info, 19648620fc62SThierry Reding }, { 19659c012700SThierry Reding .compatible = "nvidia,tegra114-dc", 19669c012700SThierry Reding .data = &tegra114_dc_soc_info, 19679c012700SThierry Reding }, { 19688620fc62SThierry Reding .compatible = "nvidia,tegra30-dc", 19698620fc62SThierry Reding .data = &tegra30_dc_soc_info, 19708620fc62SThierry Reding }, { 19718620fc62SThierry Reding .compatible = "nvidia,tegra20-dc", 19728620fc62SThierry Reding .data = &tegra20_dc_soc_info, 19738620fc62SThierry Reding }, { 19748620fc62SThierry Reding /* sentinel */ 19758620fc62SThierry Reding } 19768620fc62SThierry Reding }; 1977ef70728cSStephen Warren MODULE_DEVICE_TABLE(of, tegra_dc_of_match); 19788620fc62SThierry Reding 197913411dddSThierry Reding static int tegra_dc_parse_dt(struct tegra_dc *dc) 198013411dddSThierry Reding { 198113411dddSThierry Reding struct device_node *np; 198213411dddSThierry Reding u32 value = 0; 198313411dddSThierry Reding int err; 198413411dddSThierry Reding 198513411dddSThierry Reding err = of_property_read_u32(dc->dev->of_node, "nvidia,head", &value); 198613411dddSThierry Reding if (err < 0) { 198713411dddSThierry Reding dev_err(dc->dev, "missing \"nvidia,head\" property\n"); 198813411dddSThierry Reding 198913411dddSThierry Reding /* 199013411dddSThierry Reding * If the nvidia,head property isn't present, try to find the 199113411dddSThierry Reding * correct head number by looking up the position of this 199213411dddSThierry Reding * display controller's node within the device tree. Assuming 199313411dddSThierry Reding * that the nodes are ordered properly in the DTS file and 199413411dddSThierry Reding * that the translation into a flattened device tree blob 199513411dddSThierry Reding * preserves that ordering this will actually yield the right 199613411dddSThierry Reding * head number. 199713411dddSThierry Reding * 199813411dddSThierry Reding * If those assumptions don't hold, this will still work for 199913411dddSThierry Reding * cases where only a single display controller is used. 200013411dddSThierry Reding */ 200113411dddSThierry Reding for_each_matching_node(np, tegra_dc_of_match) { 2002cf6b1744SJulia Lawall if (np == dc->dev->of_node) { 2003cf6b1744SJulia Lawall of_node_put(np); 200413411dddSThierry Reding break; 2005cf6b1744SJulia Lawall } 200613411dddSThierry Reding 200713411dddSThierry Reding value++; 200813411dddSThierry Reding } 200913411dddSThierry Reding } 201013411dddSThierry Reding 201113411dddSThierry Reding dc->pipe = value; 201213411dddSThierry Reding 201313411dddSThierry Reding return 0; 201413411dddSThierry Reding } 201513411dddSThierry Reding 2016dee8268fSThierry Reding static int tegra_dc_probe(struct platform_device *pdev) 2017dee8268fSThierry Reding { 2018dee8268fSThierry Reding struct resource *regs; 2019dee8268fSThierry Reding struct tegra_dc *dc; 2020dee8268fSThierry Reding int err; 2021dee8268fSThierry Reding 2022dee8268fSThierry Reding dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL); 2023dee8268fSThierry Reding if (!dc) 2024dee8268fSThierry Reding return -ENOMEM; 2025dee8268fSThierry Reding 2026b9ff7aeaSThierry Reding dc->soc = of_device_get_match_data(&pdev->dev); 20278620fc62SThierry Reding 2028dee8268fSThierry Reding INIT_LIST_HEAD(&dc->list); 2029dee8268fSThierry Reding dc->dev = &pdev->dev; 2030dee8268fSThierry Reding 203113411dddSThierry Reding err = tegra_dc_parse_dt(dc); 203213411dddSThierry Reding if (err < 0) 203313411dddSThierry Reding return err; 203413411dddSThierry Reding 2035dee8268fSThierry Reding dc->clk = devm_clk_get(&pdev->dev, NULL); 2036dee8268fSThierry Reding if (IS_ERR(dc->clk)) { 2037dee8268fSThierry Reding dev_err(&pdev->dev, "failed to get clock\n"); 2038dee8268fSThierry Reding return PTR_ERR(dc->clk); 2039dee8268fSThierry Reding } 2040dee8268fSThierry Reding 2041ca48080aSStephen Warren dc->rst = devm_reset_control_get(&pdev->dev, "dc"); 2042ca48080aSStephen Warren if (IS_ERR(dc->rst)) { 2043ca48080aSStephen Warren dev_err(&pdev->dev, "failed to get reset\n"); 2044ca48080aSStephen Warren return PTR_ERR(dc->rst); 2045ca48080aSStephen Warren } 2046ca48080aSStephen Warren 2047a2f2f740SThierry Reding /* assert reset and disable clock */ 2048a2f2f740SThierry Reding if (!dc->soc->broken_reset) { 2049a2f2f740SThierry Reding err = clk_prepare_enable(dc->clk); 2050a2f2f740SThierry Reding if (err < 0) 2051a2f2f740SThierry Reding return err; 2052a2f2f740SThierry Reding 2053a2f2f740SThierry Reding usleep_range(2000, 4000); 2054a2f2f740SThierry Reding 2055a2f2f740SThierry Reding err = reset_control_assert(dc->rst); 2056a2f2f740SThierry Reding if (err < 0) 2057a2f2f740SThierry Reding return err; 2058a2f2f740SThierry Reding 2059a2f2f740SThierry Reding usleep_range(2000, 4000); 2060a2f2f740SThierry Reding 2061a2f2f740SThierry Reding clk_disable_unprepare(dc->clk); 2062a2f2f740SThierry Reding } 206333a8eb8dSThierry Reding 20649c012700SThierry Reding if (dc->soc->has_powergate) { 20659c012700SThierry Reding if (dc->pipe == 0) 20669c012700SThierry Reding dc->powergate = TEGRA_POWERGATE_DIS; 20679c012700SThierry Reding else 20689c012700SThierry Reding dc->powergate = TEGRA_POWERGATE_DISB; 20699c012700SThierry Reding 207033a8eb8dSThierry Reding tegra_powergate_power_off(dc->powergate); 20719c012700SThierry Reding } 2072dee8268fSThierry Reding 2073dee8268fSThierry Reding regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2074dee8268fSThierry Reding dc->regs = devm_ioremap_resource(&pdev->dev, regs); 2075dee8268fSThierry Reding if (IS_ERR(dc->regs)) 2076dee8268fSThierry Reding return PTR_ERR(dc->regs); 2077dee8268fSThierry Reding 2078dee8268fSThierry Reding dc->irq = platform_get_irq(pdev, 0); 2079dee8268fSThierry Reding if (dc->irq < 0) { 2080dee8268fSThierry Reding dev_err(&pdev->dev, "failed to get IRQ\n"); 2081dee8268fSThierry Reding return -ENXIO; 2082dee8268fSThierry Reding } 2083dee8268fSThierry Reding 2084dee8268fSThierry Reding err = tegra_dc_rgb_probe(dc); 2085dee8268fSThierry Reding if (err < 0 && err != -ENODEV) { 2086dee8268fSThierry Reding dev_err(&pdev->dev, "failed to probe RGB output: %d\n", err); 2087dee8268fSThierry Reding return err; 2088dee8268fSThierry Reding } 2089dee8268fSThierry Reding 209033a8eb8dSThierry Reding platform_set_drvdata(pdev, dc); 209133a8eb8dSThierry Reding pm_runtime_enable(&pdev->dev); 209233a8eb8dSThierry Reding 209333a8eb8dSThierry Reding INIT_LIST_HEAD(&dc->client.list); 209433a8eb8dSThierry Reding dc->client.ops = &dc_client_ops; 209533a8eb8dSThierry Reding dc->client.dev = &pdev->dev; 209633a8eb8dSThierry Reding 2097dee8268fSThierry Reding err = host1x_client_register(&dc->client); 2098dee8268fSThierry Reding if (err < 0) { 2099dee8268fSThierry Reding dev_err(&pdev->dev, "failed to register host1x client: %d\n", 2100dee8268fSThierry Reding err); 2101dee8268fSThierry Reding return err; 2102dee8268fSThierry Reding } 2103dee8268fSThierry Reding 2104dee8268fSThierry Reding return 0; 2105dee8268fSThierry Reding } 2106dee8268fSThierry Reding 2107dee8268fSThierry Reding static int tegra_dc_remove(struct platform_device *pdev) 2108dee8268fSThierry Reding { 2109dee8268fSThierry Reding struct tegra_dc *dc = platform_get_drvdata(pdev); 2110dee8268fSThierry Reding int err; 2111dee8268fSThierry Reding 2112dee8268fSThierry Reding err = host1x_client_unregister(&dc->client); 2113dee8268fSThierry Reding if (err < 0) { 2114dee8268fSThierry Reding dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", 2115dee8268fSThierry Reding err); 2116dee8268fSThierry Reding return err; 2117dee8268fSThierry Reding } 2118dee8268fSThierry Reding 211959d29c0eSThierry Reding err = tegra_dc_rgb_remove(dc); 212059d29c0eSThierry Reding if (err < 0) { 212159d29c0eSThierry Reding dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err); 212259d29c0eSThierry Reding return err; 212359d29c0eSThierry Reding } 212459d29c0eSThierry Reding 212533a8eb8dSThierry Reding pm_runtime_disable(&pdev->dev); 212633a8eb8dSThierry Reding 212733a8eb8dSThierry Reding return 0; 212833a8eb8dSThierry Reding } 212933a8eb8dSThierry Reding 213033a8eb8dSThierry Reding #ifdef CONFIG_PM 213133a8eb8dSThierry Reding static int tegra_dc_suspend(struct device *dev) 213233a8eb8dSThierry Reding { 213333a8eb8dSThierry Reding struct tegra_dc *dc = dev_get_drvdata(dev); 213433a8eb8dSThierry Reding int err; 213533a8eb8dSThierry Reding 21366ac1571bSDmitry Osipenko if (!dc->soc->broken_reset) { 213733a8eb8dSThierry Reding err = reset_control_assert(dc->rst); 213833a8eb8dSThierry Reding if (err < 0) { 213933a8eb8dSThierry Reding dev_err(dev, "failed to assert reset: %d\n", err); 214033a8eb8dSThierry Reding return err; 214133a8eb8dSThierry Reding } 21426ac1571bSDmitry Osipenko } 21439c012700SThierry Reding 21449c012700SThierry Reding if (dc->soc->has_powergate) 21459c012700SThierry Reding tegra_powergate_power_off(dc->powergate); 21469c012700SThierry Reding 2147dee8268fSThierry Reding clk_disable_unprepare(dc->clk); 2148dee8268fSThierry Reding 2149dee8268fSThierry Reding return 0; 2150dee8268fSThierry Reding } 2151dee8268fSThierry Reding 215233a8eb8dSThierry Reding static int tegra_dc_resume(struct device *dev) 215333a8eb8dSThierry Reding { 215433a8eb8dSThierry Reding struct tegra_dc *dc = dev_get_drvdata(dev); 215533a8eb8dSThierry Reding int err; 215633a8eb8dSThierry Reding 215733a8eb8dSThierry Reding if (dc->soc->has_powergate) { 215833a8eb8dSThierry Reding err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, 215933a8eb8dSThierry Reding dc->rst); 216033a8eb8dSThierry Reding if (err < 0) { 216133a8eb8dSThierry Reding dev_err(dev, "failed to power partition: %d\n", err); 216233a8eb8dSThierry Reding return err; 216333a8eb8dSThierry Reding } 216433a8eb8dSThierry Reding } else { 216533a8eb8dSThierry Reding err = clk_prepare_enable(dc->clk); 216633a8eb8dSThierry Reding if (err < 0) { 216733a8eb8dSThierry Reding dev_err(dev, "failed to enable clock: %d\n", err); 216833a8eb8dSThierry Reding return err; 216933a8eb8dSThierry Reding } 217033a8eb8dSThierry Reding 21716ac1571bSDmitry Osipenko if (!dc->soc->broken_reset) { 217233a8eb8dSThierry Reding err = reset_control_deassert(dc->rst); 217333a8eb8dSThierry Reding if (err < 0) { 21746ac1571bSDmitry Osipenko dev_err(dev, 21756ac1571bSDmitry Osipenko "failed to deassert reset: %d\n", err); 217633a8eb8dSThierry Reding return err; 217733a8eb8dSThierry Reding } 217833a8eb8dSThierry Reding } 21796ac1571bSDmitry Osipenko } 218033a8eb8dSThierry Reding 218133a8eb8dSThierry Reding return 0; 218233a8eb8dSThierry Reding } 218333a8eb8dSThierry Reding #endif 218433a8eb8dSThierry Reding 218533a8eb8dSThierry Reding static const struct dev_pm_ops tegra_dc_pm_ops = { 218633a8eb8dSThierry Reding SET_RUNTIME_PM_OPS(tegra_dc_suspend, tegra_dc_resume, NULL) 218733a8eb8dSThierry Reding }; 218833a8eb8dSThierry Reding 2189dee8268fSThierry Reding struct platform_driver tegra_dc_driver = { 2190dee8268fSThierry Reding .driver = { 2191dee8268fSThierry Reding .name = "tegra-dc", 2192dee8268fSThierry Reding .of_match_table = tegra_dc_of_match, 219333a8eb8dSThierry Reding .pm = &tegra_dc_pm_ops, 2194dee8268fSThierry Reding }, 2195dee8268fSThierry Reding .probe = tegra_dc_probe, 2196dee8268fSThierry Reding .remove = tegra_dc_remove, 2197dee8268fSThierry Reding }; 2198