18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2018 Broadcom 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Eric Anholt <eric@anholt.net> 78c2ecf20Sopenharmony_ci * Boris Brezillon <boris.brezillon@bootlin.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/component.h> 128c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 138c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_writeback.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "vc4_drv.h" 268c2ecf20Sopenharmony_ci#include "vc4_regs.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Base address of the output. Raster formats must be 4-byte aligned, 298c2ecf20Sopenharmony_ci * T and LT must be 16-byte aligned or maybe utile-aligned (docs are 308c2ecf20Sopenharmony_ci * inconsistent, but probably utile). 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci#define TXP_DST_PTR 0x00 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Pitch in bytes for raster images, 16-byte aligned. For tiled, it's 358c2ecf20Sopenharmony_ci * the width in tiles. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci#define TXP_DST_PITCH 0x04 388c2ecf20Sopenharmony_ci/* For T-tiled imgaes, DST_PITCH should be the number of tiles wide, 398c2ecf20Sopenharmony_ci * shifted up. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci# define TXP_T_TILE_WIDTH_SHIFT 7 428c2ecf20Sopenharmony_ci/* For LT-tiled images, DST_PITCH should be the number of utiles wide, 438c2ecf20Sopenharmony_ci * shifted up. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci# define TXP_LT_TILE_WIDTH_SHIFT 4 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Pre-rotation width/height of the image. Must match HVS config. 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * If TFORMAT and 32-bit, limit is 1920 for 32-bit and 3840 to 16-bit 508c2ecf20Sopenharmony_ci * and width/height must be tile or utile-aligned as appropriate. If 518c2ecf20Sopenharmony_ci * transposing (rotating), width is limited to 1920. 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * Height is limited to various numbers between 4088 and 4095. I'd 548c2ecf20Sopenharmony_ci * just use 4088 to be safe. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci#define TXP_DIM 0x08 578c2ecf20Sopenharmony_ci# define TXP_HEIGHT_SHIFT 16 588c2ecf20Sopenharmony_ci# define TXP_HEIGHT_MASK GENMASK(31, 16) 598c2ecf20Sopenharmony_ci# define TXP_WIDTH_SHIFT 0 608c2ecf20Sopenharmony_ci# define TXP_WIDTH_MASK GENMASK(15, 0) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define TXP_DST_CTRL 0x0c 638c2ecf20Sopenharmony_ci/* These bits are set to 0x54 */ 648c2ecf20Sopenharmony_ci#define TXP_PILOT_SHIFT 24 658c2ecf20Sopenharmony_ci#define TXP_PILOT_MASK GENMASK(31, 24) 668c2ecf20Sopenharmony_ci/* Bits 22-23 are set to 0x01 */ 678c2ecf20Sopenharmony_ci#define TXP_VERSION_SHIFT 22 688c2ecf20Sopenharmony_ci#define TXP_VERSION_MASK GENMASK(23, 22) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Powers down the internal memory. */ 718c2ecf20Sopenharmony_ci# define TXP_POWERDOWN BIT(21) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* Enables storing the alpha component in 8888/4444, instead of 748c2ecf20Sopenharmony_ci * filling with ~ALPHA_INVERT. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci# define TXP_ALPHA_ENABLE BIT(20) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 4 bits, each enables stores for a channel in each set of 4 bytes. 798c2ecf20Sopenharmony_ci * Set to 0xf for normal operation. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci# define TXP_BYTE_ENABLE_SHIFT 16 828c2ecf20Sopenharmony_ci# define TXP_BYTE_ENABLE_MASK GENMASK(19, 16) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* Debug: Generate VSTART again at EOF. */ 858c2ecf20Sopenharmony_ci# define TXP_VSTART_AT_EOF BIT(15) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* Debug: Terminate the current frame immediately. Stops AXI 888c2ecf20Sopenharmony_ci * writes. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci# define TXP_ABORT BIT(14) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci# define TXP_DITHER BIT(13) 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* Inverts alpha if TXP_ALPHA_ENABLE, chooses fill value for 958c2ecf20Sopenharmony_ci * !TXP_ALPHA_ENABLE. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci# define TXP_ALPHA_INVERT BIT(12) 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* Note: I've listed the channels here in high bit (in byte 3/2/1) to 1008c2ecf20Sopenharmony_ci * low bit (in byte 0) order. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci# define TXP_FORMAT_SHIFT 8 1038c2ecf20Sopenharmony_ci# define TXP_FORMAT_MASK GENMASK(11, 8) 1048c2ecf20Sopenharmony_ci# define TXP_FORMAT_ABGR4444 0 1058c2ecf20Sopenharmony_ci# define TXP_FORMAT_ARGB4444 1 1068c2ecf20Sopenharmony_ci# define TXP_FORMAT_BGRA4444 2 1078c2ecf20Sopenharmony_ci# define TXP_FORMAT_RGBA4444 3 1088c2ecf20Sopenharmony_ci# define TXP_FORMAT_BGR565 6 1098c2ecf20Sopenharmony_ci# define TXP_FORMAT_RGB565 7 1108c2ecf20Sopenharmony_ci/* 888s are non-rotated, raster-only */ 1118c2ecf20Sopenharmony_ci# define TXP_FORMAT_BGR888 8 1128c2ecf20Sopenharmony_ci# define TXP_FORMAT_RGB888 9 1138c2ecf20Sopenharmony_ci# define TXP_FORMAT_ABGR8888 12 1148c2ecf20Sopenharmony_ci# define TXP_FORMAT_ARGB8888 13 1158c2ecf20Sopenharmony_ci# define TXP_FORMAT_BGRA8888 14 1168c2ecf20Sopenharmony_ci# define TXP_FORMAT_RGBA8888 15 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* If TFORMAT is set, generates LT instead of T format. */ 1198c2ecf20Sopenharmony_ci# define TXP_LINEAR_UTILE BIT(7) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* Rotate output by 90 degrees. */ 1228c2ecf20Sopenharmony_ci# define TXP_TRANSPOSE BIT(6) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* Generate a tiled format for V3D. */ 1258c2ecf20Sopenharmony_ci# define TXP_TFORMAT BIT(5) 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* Generates some undefined test mode output. */ 1288c2ecf20Sopenharmony_ci# define TXP_TEST_MODE BIT(4) 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* Request odd field from HVS. */ 1318c2ecf20Sopenharmony_ci# define TXP_FIELD BIT(3) 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* Raise interrupt when idle. */ 1348c2ecf20Sopenharmony_ci# define TXP_EI BIT(2) 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* Set when generating a frame, clears when idle. */ 1378c2ecf20Sopenharmony_ci# define TXP_BUSY BIT(1) 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* Starts a frame. Self-clearing. */ 1408c2ecf20Sopenharmony_ci# define TXP_GO BIT(0) 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* Number of lines received and committed to memory. */ 1438c2ecf20Sopenharmony_ci#define TXP_PROGRESS 0x10 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define TXP_READ(offset) readl(txp->regs + (offset)) 1468c2ecf20Sopenharmony_ci#define TXP_WRITE(offset, val) writel(val, txp->regs + (offset)) 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistruct vc4_txp { 1498c2ecf20Sopenharmony_ci struct vc4_crtc base; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci struct platform_device *pdev; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci struct drm_writeback_connector connector; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci void __iomem *regs; 1568c2ecf20Sopenharmony_ci struct debugfs_regset32 regset; 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic inline struct vc4_txp *encoder_to_vc4_txp(struct drm_encoder *encoder) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci return container_of(encoder, struct vc4_txp, connector.encoder); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic inline struct vc4_txp *connector_to_vc4_txp(struct drm_connector *conn) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci return container_of(conn, struct vc4_txp, connector.base); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic const struct debugfs_reg32 txp_regs[] = { 1708c2ecf20Sopenharmony_ci VC4_REG32(TXP_DST_PTR), 1718c2ecf20Sopenharmony_ci VC4_REG32(TXP_DST_PITCH), 1728c2ecf20Sopenharmony_ci VC4_REG32(TXP_DIM), 1738c2ecf20Sopenharmony_ci VC4_REG32(TXP_DST_CTRL), 1748c2ecf20Sopenharmony_ci VC4_REG32(TXP_PROGRESS), 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int vc4_txp_connector_get_modes(struct drm_connector *connector) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return drm_add_modes_noedid(connector, dev->mode_config.max_width, 1828c2ecf20Sopenharmony_ci dev->mode_config.max_height); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic enum drm_mode_status 1868c2ecf20Sopenharmony_civc4_txp_connector_mode_valid(struct drm_connector *connector, 1878c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 1908c2ecf20Sopenharmony_ci struct drm_mode_config *mode_config = &dev->mode_config; 1918c2ecf20Sopenharmony_ci int w = mode->hdisplay, h = mode->vdisplay; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (w < mode_config->min_width || w > mode_config->max_width) 1948c2ecf20Sopenharmony_ci return MODE_BAD_HVALUE; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (h < mode_config->min_height || h > mode_config->max_height) 1978c2ecf20Sopenharmony_ci return MODE_BAD_VVALUE; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return MODE_OK; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic const u32 drm_fmts[] = { 2038c2ecf20Sopenharmony_ci DRM_FORMAT_RGB888, 2048c2ecf20Sopenharmony_ci DRM_FORMAT_BGR888, 2058c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 2068c2ecf20Sopenharmony_ci DRM_FORMAT_XBGR8888, 2078c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 2088c2ecf20Sopenharmony_ci DRM_FORMAT_ABGR8888, 2098c2ecf20Sopenharmony_ci DRM_FORMAT_RGBX8888, 2108c2ecf20Sopenharmony_ci DRM_FORMAT_BGRX8888, 2118c2ecf20Sopenharmony_ci DRM_FORMAT_RGBA8888, 2128c2ecf20Sopenharmony_ci DRM_FORMAT_BGRA8888, 2138c2ecf20Sopenharmony_ci}; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic const u32 txp_fmts[] = { 2168c2ecf20Sopenharmony_ci TXP_FORMAT_RGB888, 2178c2ecf20Sopenharmony_ci TXP_FORMAT_BGR888, 2188c2ecf20Sopenharmony_ci TXP_FORMAT_ARGB8888, 2198c2ecf20Sopenharmony_ci TXP_FORMAT_ABGR8888, 2208c2ecf20Sopenharmony_ci TXP_FORMAT_ARGB8888, 2218c2ecf20Sopenharmony_ci TXP_FORMAT_ABGR8888, 2228c2ecf20Sopenharmony_ci TXP_FORMAT_RGBA8888, 2238c2ecf20Sopenharmony_ci TXP_FORMAT_BGRA8888, 2248c2ecf20Sopenharmony_ci TXP_FORMAT_RGBA8888, 2258c2ecf20Sopenharmony_ci TXP_FORMAT_BGRA8888, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void vc4_txp_armed(struct drm_crtc_state *state) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci vc4_state->txp_armed = true; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int vc4_txp_connector_atomic_check(struct drm_connector *conn, 2368c2ecf20Sopenharmony_ci struct drm_atomic_state *state) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state; 2398c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 2408c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 2418c2ecf20Sopenharmony_ci int i; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci conn_state = drm_atomic_get_new_connector_state(state, conn); 2448c2ecf20Sopenharmony_ci if (!conn_state->writeback_job) 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci fb = conn_state->writeback_job->fb; 2508c2ecf20Sopenharmony_ci if (fb->width != crtc_state->mode.hdisplay || 2518c2ecf20Sopenharmony_ci fb->height != crtc_state->mode.vdisplay) { 2528c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n", 2538c2ecf20Sopenharmony_ci fb->width, fb->height); 2548c2ecf20Sopenharmony_ci return -EINVAL; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(drm_fmts); i++) { 2588c2ecf20Sopenharmony_ci if (fb->format->format == drm_fmts[i]) 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(drm_fmts)) 2638c2ecf20Sopenharmony_ci return -EINVAL; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* Pitch must be aligned on 16 bytes. */ 2668c2ecf20Sopenharmony_ci if (fb->pitches[0] & GENMASK(3, 0)) 2678c2ecf20Sopenharmony_ci return -EINVAL; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci vc4_txp_armed(crtc_state); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void vc4_txp_connector_atomic_commit(struct drm_connector *conn, 2758c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct vc4_txp *txp = connector_to_vc4_txp(conn); 2788c2ecf20Sopenharmony_ci struct drm_gem_cma_object *gem; 2798c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 2808c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 2818c2ecf20Sopenharmony_ci u32 ctrl; 2828c2ecf20Sopenharmony_ci int i; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (WARN_ON(!conn_state->writeback_job)) 2858c2ecf20Sopenharmony_ci return; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci mode = &conn_state->crtc->state->adjusted_mode; 2888c2ecf20Sopenharmony_ci fb = conn_state->writeback_job->fb; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(drm_fmts); i++) { 2918c2ecf20Sopenharmony_ci if (fb->format->format == drm_fmts[i]) 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (WARN_ON(i == ARRAY_SIZE(drm_fmts))) 2968c2ecf20Sopenharmony_ci return; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci ctrl = TXP_GO | TXP_EI | 2998c2ecf20Sopenharmony_ci VC4_SET_FIELD(0xf, TXP_BYTE_ENABLE) | 3008c2ecf20Sopenharmony_ci VC4_SET_FIELD(txp_fmts[i], TXP_FORMAT); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (fb->format->has_alpha) 3038c2ecf20Sopenharmony_ci ctrl |= TXP_ALPHA_ENABLE; 3048c2ecf20Sopenharmony_ci else 3058c2ecf20Sopenharmony_ci /* 3068c2ecf20Sopenharmony_ci * If TXP_ALPHA_ENABLE isn't set and TXP_ALPHA_INVERT is, the 3078c2ecf20Sopenharmony_ci * hardware will force the output padding to be 0xff. 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_ci ctrl |= TXP_ALPHA_INVERT; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci gem = drm_fb_cma_get_gem_obj(fb, 0); 3128c2ecf20Sopenharmony_ci TXP_WRITE(TXP_DST_PTR, gem->paddr + fb->offsets[0]); 3138c2ecf20Sopenharmony_ci TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]); 3148c2ecf20Sopenharmony_ci TXP_WRITE(TXP_DIM, 3158c2ecf20Sopenharmony_ci VC4_SET_FIELD(mode->hdisplay, TXP_WIDTH) | 3168c2ecf20Sopenharmony_ci VC4_SET_FIELD(mode->vdisplay, TXP_HEIGHT)); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci TXP_WRITE(TXP_DST_CTRL, ctrl); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci drm_writeback_queue_job(&txp->connector, conn_state); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = { 3248c2ecf20Sopenharmony_ci .get_modes = vc4_txp_connector_get_modes, 3258c2ecf20Sopenharmony_ci .mode_valid = vc4_txp_connector_mode_valid, 3268c2ecf20Sopenharmony_ci .atomic_check = vc4_txp_connector_atomic_check, 3278c2ecf20Sopenharmony_ci .atomic_commit = vc4_txp_connector_atomic_commit, 3288c2ecf20Sopenharmony_ci}; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic enum drm_connector_status 3318c2ecf20Sopenharmony_civc4_txp_connector_detect(struct drm_connector *connector, bool force) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci return connector_status_connected; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic void vc4_txp_connector_destroy(struct drm_connector *connector) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci drm_connector_unregister(connector); 3398c2ecf20Sopenharmony_ci drm_connector_cleanup(connector); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs vc4_txp_connector_funcs = { 3438c2ecf20Sopenharmony_ci .detect = vc4_txp_connector_detect, 3448c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 3458c2ecf20Sopenharmony_ci .destroy = vc4_txp_connector_destroy, 3468c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 3478c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 3488c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void vc4_txp_encoder_disable(struct drm_encoder *encoder) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct vc4_txp *txp = encoder_to_vc4_txp(encoder); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (TXP_READ(TXP_DST_CTRL) & TXP_BUSY) { 3568c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(1000); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci TXP_WRITE(TXP_DST_CTRL, TXP_ABORT); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci while (TXP_READ(TXP_DST_CTRL) & TXP_BUSY && 3618c2ecf20Sopenharmony_ci time_before(jiffies, timeout)) 3628c2ecf20Sopenharmony_ci ; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci WARN_ON(TXP_READ(TXP_DST_CTRL) & TXP_BUSY); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci TXP_WRITE(TXP_DST_CTRL, TXP_POWERDOWN); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs vc4_txp_encoder_helper_funcs = { 3718c2ecf20Sopenharmony_ci .disable = vc4_txp_encoder_disable, 3728c2ecf20Sopenharmony_ci}; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int vc4_txp_enable_vblank(struct drm_crtc *crtc) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic void vc4_txp_disable_vblank(struct drm_crtc *crtc) {} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs vc4_txp_crtc_funcs = { 3828c2ecf20Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 3838c2ecf20Sopenharmony_ci .destroy = vc4_crtc_destroy, 3848c2ecf20Sopenharmony_ci .page_flip = vc4_page_flip, 3858c2ecf20Sopenharmony_ci .reset = vc4_crtc_reset, 3868c2ecf20Sopenharmony_ci .atomic_duplicate_state = vc4_crtc_duplicate_state, 3878c2ecf20Sopenharmony_ci .atomic_destroy_state = vc4_crtc_destroy_state, 3888c2ecf20Sopenharmony_ci .gamma_set = drm_atomic_helper_legacy_gamma_set, 3898c2ecf20Sopenharmony_ci .enable_vblank = vc4_txp_enable_vblank, 3908c2ecf20Sopenharmony_ci .disable_vblank = vc4_txp_disable_vblank, 3918c2ecf20Sopenharmony_ci}; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int vc4_txp_atomic_check(struct drm_crtc *crtc, 3948c2ecf20Sopenharmony_ci struct drm_crtc_state *state) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); 3978c2ecf20Sopenharmony_ci int ret; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci ret = vc4_hvs_atomic_check(crtc, state); 4008c2ecf20Sopenharmony_ci if (ret) 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci state->no_vblank = true; 4048c2ecf20Sopenharmony_ci vc4_state->feed_txp = true; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic void vc4_txp_atomic_enable(struct drm_crtc *crtc, 4108c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 4138c2ecf20Sopenharmony_ci vc4_hvs_atomic_enable(crtc, old_state); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic void vc4_txp_atomic_disable(struct drm_crtc *crtc, 4178c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* Disable vblank irq handling before crtc is disabled. */ 4228c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci vc4_hvs_atomic_disable(crtc, old_state); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* 4278c2ecf20Sopenharmony_ci * Make sure we issue a vblank event after disabling the CRTC if 4288c2ecf20Sopenharmony_ci * someone was waiting it. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci if (crtc->state->event) { 4318c2ecf20Sopenharmony_ci unsigned long flags; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->event_lock, flags); 4348c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 4358c2ecf20Sopenharmony_ci crtc->state->event = NULL; 4368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->event_lock, flags); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs vc4_txp_crtc_helper_funcs = { 4418c2ecf20Sopenharmony_ci .atomic_check = vc4_txp_atomic_check, 4428c2ecf20Sopenharmony_ci .atomic_flush = vc4_hvs_atomic_flush, 4438c2ecf20Sopenharmony_ci .atomic_enable = vc4_txp_atomic_enable, 4448c2ecf20Sopenharmony_ci .atomic_disable = vc4_txp_atomic_disable, 4458c2ecf20Sopenharmony_ci}; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic irqreturn_t vc4_txp_interrupt(int irq, void *data) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct vc4_txp *txp = data; 4508c2ecf20Sopenharmony_ci struct vc4_crtc *vc4_crtc = &txp->base; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci TXP_WRITE(TXP_DST_CTRL, TXP_READ(TXP_DST_CTRL) & ~TXP_EI); 4538c2ecf20Sopenharmony_ci vc4_crtc_handle_vblank(vc4_crtc); 4548c2ecf20Sopenharmony_ci drm_writeback_signal_completion(&txp->connector, 0); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic const struct vc4_crtc_data vc4_txp_crtc_data = { 4608c2ecf20Sopenharmony_ci .hvs_available_channels = BIT(2), 4618c2ecf20Sopenharmony_ci .hvs_output = 2, 4628c2ecf20Sopenharmony_ci}; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int vc4_txp_bind(struct device *dev, struct device *master, void *data) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 4678c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(master); 4688c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(drm); 4698c2ecf20Sopenharmony_ci struct vc4_crtc *vc4_crtc; 4708c2ecf20Sopenharmony_ci struct vc4_txp *txp; 4718c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 4728c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 4738c2ecf20Sopenharmony_ci int ret, irq; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 4768c2ecf20Sopenharmony_ci if (irq < 0) 4778c2ecf20Sopenharmony_ci return irq; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci txp = devm_kzalloc(dev, sizeof(*txp), GFP_KERNEL); 4808c2ecf20Sopenharmony_ci if (!txp) 4818c2ecf20Sopenharmony_ci return -ENOMEM; 4828c2ecf20Sopenharmony_ci vc4_crtc = &txp->base; 4838c2ecf20Sopenharmony_ci crtc = &vc4_crtc->base; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci vc4_crtc->pdev = pdev; 4868c2ecf20Sopenharmony_ci vc4_crtc->data = &vc4_txp_crtc_data; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci txp->pdev = pdev; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci txp->regs = vc4_ioremap_regs(pdev, 0); 4918c2ecf20Sopenharmony_ci if (IS_ERR(txp->regs)) 4928c2ecf20Sopenharmony_ci return PTR_ERR(txp->regs); 4938c2ecf20Sopenharmony_ci txp->regset.base = txp->regs; 4948c2ecf20Sopenharmony_ci txp->regset.regs = txp_regs; 4958c2ecf20Sopenharmony_ci txp->regset.nregs = ARRAY_SIZE(txp_regs); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci drm_connector_helper_add(&txp->connector.base, 4988c2ecf20Sopenharmony_ci &vc4_txp_connector_helper_funcs); 4998c2ecf20Sopenharmony_ci ret = drm_writeback_connector_init(drm, &txp->connector, 5008c2ecf20Sopenharmony_ci &vc4_txp_connector_funcs, 5018c2ecf20Sopenharmony_ci &vc4_txp_encoder_helper_funcs, 5028c2ecf20Sopenharmony_ci drm_fmts, ARRAY_SIZE(drm_fmts)); 5038c2ecf20Sopenharmony_ci if (ret) 5048c2ecf20Sopenharmony_ci return ret; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci ret = vc4_crtc_init(drm, vc4_crtc, 5078c2ecf20Sopenharmony_ci &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs); 5088c2ecf20Sopenharmony_ci if (ret) 5098c2ecf20Sopenharmony_ci return ret; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci encoder = &txp->connector.encoder; 5128c2ecf20Sopenharmony_ci encoder->possible_crtcs = drm_crtc_mask(crtc); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0, 5158c2ecf20Sopenharmony_ci dev_name(dev), txp); 5168c2ecf20Sopenharmony_ci if (ret) 5178c2ecf20Sopenharmony_ci return ret; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci dev_set_drvdata(dev, txp); 5208c2ecf20Sopenharmony_ci vc4->txp = txp; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci vc4_debugfs_add_regset32(drm, "txp_regs", &txp->regset); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return 0; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void vc4_txp_unbind(struct device *dev, struct device *master, 5288c2ecf20Sopenharmony_ci void *data) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(master); 5318c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(drm); 5328c2ecf20Sopenharmony_ci struct vc4_txp *txp = dev_get_drvdata(dev); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci vc4_txp_connector_destroy(&txp->connector.base); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci vc4->txp = NULL; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic const struct component_ops vc4_txp_ops = { 5408c2ecf20Sopenharmony_ci .bind = vc4_txp_bind, 5418c2ecf20Sopenharmony_ci .unbind = vc4_txp_unbind, 5428c2ecf20Sopenharmony_ci}; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int vc4_txp_probe(struct platform_device *pdev) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci return component_add(&pdev->dev, &vc4_txp_ops); 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int vc4_txp_remove(struct platform_device *pdev) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci component_del(&pdev->dev, &vc4_txp_ops); 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic const struct of_device_id vc4_txp_dt_match[] = { 5568c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm2835-txp" }, 5578c2ecf20Sopenharmony_ci { /* sentinel */ }, 5588c2ecf20Sopenharmony_ci}; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistruct platform_driver vc4_txp_driver = { 5618c2ecf20Sopenharmony_ci .probe = vc4_txp_probe, 5628c2ecf20Sopenharmony_ci .remove = vc4_txp_remove, 5638c2ecf20Sopenharmony_ci .driver = { 5648c2ecf20Sopenharmony_ci .name = "vc4_txp", 5658c2ecf20Sopenharmony_ci .of_match_table = vc4_txp_dt_match, 5668c2ecf20Sopenharmony_ci }, 5678c2ecf20Sopenharmony_ci}; 568