18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Broadcom 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/** 78c2ecf20Sopenharmony_ci * DOC: VC4 DSI0/DSI1 module 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * BCM2835 contains two DSI modules, DSI0 and DSI1. DSI0 is a 108c2ecf20Sopenharmony_ci * single-lane DSI controller, while DSI1 is a more modern 4-lane DSI 118c2ecf20Sopenharmony_ci * controller. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Most Raspberry Pi boards expose DSI1 as their "DISPLAY" connector, 148c2ecf20Sopenharmony_ci * while the compute module brings both DSI0 and DSI1 out. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This driver has been tested for DSI1 video-mode display only 178c2ecf20Sopenharmony_ci * currently, with most of the information necessary for DSI0 188c2ecf20Sopenharmony_ci * hopefully present. 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 228c2ecf20Sopenharmony_ci#include <linux/clk.h> 238c2ecf20Sopenharmony_ci#include <linux/completion.h> 248c2ecf20Sopenharmony_ci#include <linux/component.h> 258c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 268c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 278c2ecf20Sopenharmony_ci#include <linux/i2c.h> 288c2ecf20Sopenharmony_ci#include <linux/io.h> 298c2ecf20Sopenharmony_ci#include <linux/of_address.h> 308c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 318c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 348c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h> 358c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 368c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 378c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 388c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 398c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 408c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include "vc4_drv.h" 438c2ecf20Sopenharmony_ci#include "vc4_regs.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define DSI_CMD_FIFO_DEPTH 16 468c2ecf20Sopenharmony_ci#define DSI_PIX_FIFO_DEPTH 256 478c2ecf20Sopenharmony_ci#define DSI_PIX_FIFO_WIDTH 4 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define DSI0_CTRL 0x00 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Command packet control. */ 528c2ecf20Sopenharmony_ci#define DSI0_TXPKT1C 0x04 /* AKA PKTC */ 538c2ecf20Sopenharmony_ci#define DSI1_TXPKT1C 0x04 548c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_TRIG_CMD_MASK VC4_MASK(31, 24) 558c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_TRIG_CMD_SHIFT 24 568c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_REPEAT_MASK VC4_MASK(23, 10) 578c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_REPEAT_SHIFT 10 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_DISPLAY_NO_MASK VC4_MASK(9, 8) 608c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_DISPLAY_NO_SHIFT 8 618c2ecf20Sopenharmony_ci/* Short, trigger, BTA, or a long packet that fits all in CMDFIFO. */ 628c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_DISPLAY_NO_SHORT 0 638c2ecf20Sopenharmony_ci/* Primary display where cmdfifo provides part of the payload and 648c2ecf20Sopenharmony_ci * pixelvalve the rest. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_DISPLAY_NO_PRIMARY 1 678c2ecf20Sopenharmony_ci/* Secondary display where cmdfifo provides part of the payload and 688c2ecf20Sopenharmony_ci * pixfifo the rest. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_DISPLAY_NO_SECONDARY 2 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_TX_TIME_MASK VC4_MASK(7, 6) 738c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_TX_TIME_SHIFT 6 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_MASK VC4_MASK(5, 4) 768c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_SHIFT 4 778c2ecf20Sopenharmony_ci/* Command only. Uses TXPKT1H and DISPLAY_NO */ 788c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_TX 0 798c2ecf20Sopenharmony_ci/* Command with BTA for either ack or read data. */ 808c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_RX 1 818c2ecf20Sopenharmony_ci/* Trigger according to TRIG_CMD */ 828c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_TRIG 2 838c2ecf20Sopenharmony_ci/* BTA alone for getting error status after a command, or a TE trigger 848c2ecf20Sopenharmony_ci * without a previous command. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_BTA 3 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_MODE_LP BIT(3) 898c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_TYPE_LONG BIT(2) 908c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_TE_EN BIT(1) 918c2ecf20Sopenharmony_ci# define DSI_TXPKT1C_CMD_EN BIT(0) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* Command packet header. */ 948c2ecf20Sopenharmony_ci#define DSI0_TXPKT1H 0x08 /* AKA PKTH */ 958c2ecf20Sopenharmony_ci#define DSI1_TXPKT1H 0x08 968c2ecf20Sopenharmony_ci# define DSI_TXPKT1H_BC_CMDFIFO_MASK VC4_MASK(31, 24) 978c2ecf20Sopenharmony_ci# define DSI_TXPKT1H_BC_CMDFIFO_SHIFT 24 988c2ecf20Sopenharmony_ci# define DSI_TXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) 998c2ecf20Sopenharmony_ci# define DSI_TXPKT1H_BC_PARAM_SHIFT 8 1008c2ecf20Sopenharmony_ci# define DSI_TXPKT1H_BC_DT_MASK VC4_MASK(7, 0) 1018c2ecf20Sopenharmony_ci# define DSI_TXPKT1H_BC_DT_SHIFT 0 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define DSI0_RXPKT1H 0x0c /* AKA RX1_PKTH */ 1048c2ecf20Sopenharmony_ci#define DSI1_RXPKT1H 0x14 1058c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_CRC_ERR BIT(31) 1068c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_DET_ERR BIT(30) 1078c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_ECC_ERR BIT(29) 1088c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_COR_ERR BIT(28) 1098c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_INCOMP_PKT BIT(25) 1108c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_PKT_TYPE_LONG BIT(24) 1118c2ecf20Sopenharmony_ci/* Byte count if DSI_RXPKT1H_PKT_TYPE_LONG */ 1128c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) 1138c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_BC_PARAM_SHIFT 8 1148c2ecf20Sopenharmony_ci/* Short return bytes if !DSI_RXPKT1H_PKT_TYPE_LONG */ 1158c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_SHORT_1_MASK VC4_MASK(23, 16) 1168c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_SHORT_1_SHIFT 16 1178c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_SHORT_0_MASK VC4_MASK(15, 8) 1188c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_SHORT_0_SHIFT 8 1198c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_DT_LP_CMD_MASK VC4_MASK(7, 0) 1208c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_DT_LP_CMD_SHIFT 0 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci#define DSI0_RXPKT2H 0x10 /* AKA RX2_PKTH */ 1238c2ecf20Sopenharmony_ci#define DSI1_RXPKT2H 0x18 1248c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_DET_ERR BIT(30) 1258c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_ECC_ERR BIT(29) 1268c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_COR_ERR BIT(28) 1278c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_INCOMP_PKT BIT(25) 1288c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) 1298c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_BC_PARAM_SHIFT 8 1308c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_DT_MASK VC4_MASK(7, 0) 1318c2ecf20Sopenharmony_ci# define DSI_RXPKT1H_DT_SHIFT 0 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define DSI0_TXPKT_CMD_FIFO 0x14 /* AKA CMD_DATAF */ 1348c2ecf20Sopenharmony_ci#define DSI1_TXPKT_CMD_FIFO 0x1c 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define DSI0_DISP0_CTRL 0x18 1378c2ecf20Sopenharmony_ci# define DSI_DISP0_PIX_CLK_DIV_MASK VC4_MASK(21, 13) 1388c2ecf20Sopenharmony_ci# define DSI_DISP0_PIX_CLK_DIV_SHIFT 13 1398c2ecf20Sopenharmony_ci# define DSI_DISP0_LP_STOP_CTRL_MASK VC4_MASK(12, 11) 1408c2ecf20Sopenharmony_ci# define DSI_DISP0_LP_STOP_CTRL_SHIFT 11 1418c2ecf20Sopenharmony_ci# define DSI_DISP0_LP_STOP_DISABLE 0 1428c2ecf20Sopenharmony_ci# define DSI_DISP0_LP_STOP_PERLINE 1 1438c2ecf20Sopenharmony_ci# define DSI_DISP0_LP_STOP_PERFRAME 2 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* Transmit RGB pixels and null packets only during HACTIVE, instead 1468c2ecf20Sopenharmony_ci * of going to LP-STOP. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci# define DSI_DISP_HACTIVE_NULL BIT(10) 1498c2ecf20Sopenharmony_ci/* Transmit blanking packet only during vblank, instead of allowing LP-STOP. */ 1508c2ecf20Sopenharmony_ci# define DSI_DISP_VBLP_CTRL BIT(9) 1518c2ecf20Sopenharmony_ci/* Transmit blanking packet only during HFP, instead of allowing LP-STOP. */ 1528c2ecf20Sopenharmony_ci# define DSI_DISP_HFP_CTRL BIT(8) 1538c2ecf20Sopenharmony_ci/* Transmit blanking packet only during HBP, instead of allowing LP-STOP. */ 1548c2ecf20Sopenharmony_ci# define DSI_DISP_HBP_CTRL BIT(7) 1558c2ecf20Sopenharmony_ci# define DSI_DISP0_CHANNEL_MASK VC4_MASK(6, 5) 1568c2ecf20Sopenharmony_ci# define DSI_DISP0_CHANNEL_SHIFT 5 1578c2ecf20Sopenharmony_ci/* Enables end events for HSYNC/VSYNC, not just start events. */ 1588c2ecf20Sopenharmony_ci# define DSI_DISP0_ST_END BIT(4) 1598c2ecf20Sopenharmony_ci# define DSI_DISP0_PFORMAT_MASK VC4_MASK(3, 2) 1608c2ecf20Sopenharmony_ci# define DSI_DISP0_PFORMAT_SHIFT 2 1618c2ecf20Sopenharmony_ci# define DSI_PFORMAT_RGB565 0 1628c2ecf20Sopenharmony_ci# define DSI_PFORMAT_RGB666_PACKED 1 1638c2ecf20Sopenharmony_ci# define DSI_PFORMAT_RGB666 2 1648c2ecf20Sopenharmony_ci# define DSI_PFORMAT_RGB888 3 1658c2ecf20Sopenharmony_ci/* Default is VIDEO mode. */ 1668c2ecf20Sopenharmony_ci# define DSI_DISP0_COMMAND_MODE BIT(1) 1678c2ecf20Sopenharmony_ci# define DSI_DISP0_ENABLE BIT(0) 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci#define DSI0_DISP1_CTRL 0x1c 1708c2ecf20Sopenharmony_ci#define DSI1_DISP1_CTRL 0x2c 1718c2ecf20Sopenharmony_ci/* Format of the data written to TXPKT_PIX_FIFO. */ 1728c2ecf20Sopenharmony_ci# define DSI_DISP1_PFORMAT_MASK VC4_MASK(2, 1) 1738c2ecf20Sopenharmony_ci# define DSI_DISP1_PFORMAT_SHIFT 1 1748c2ecf20Sopenharmony_ci# define DSI_DISP1_PFORMAT_16BIT 0 1758c2ecf20Sopenharmony_ci# define DSI_DISP1_PFORMAT_24BIT 1 1768c2ecf20Sopenharmony_ci# define DSI_DISP1_PFORMAT_32BIT_LE 2 1778c2ecf20Sopenharmony_ci# define DSI_DISP1_PFORMAT_32BIT_BE 3 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* DISP1 is always command mode. */ 1808c2ecf20Sopenharmony_ci# define DSI_DISP1_ENABLE BIT(0) 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#define DSI0_TXPKT_PIX_FIFO 0x20 /* AKA PIX_FIFO */ 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci#define DSI0_INT_STAT 0x24 1858c2ecf20Sopenharmony_ci#define DSI0_INT_EN 0x28 1868c2ecf20Sopenharmony_ci# define DSI0_INT_FIFO_ERR BIT(25) 1878c2ecf20Sopenharmony_ci# define DSI0_INT_CMDC_DONE_MASK VC4_MASK(24, 23) 1888c2ecf20Sopenharmony_ci# define DSI0_INT_CMDC_DONE_SHIFT 23 1898c2ecf20Sopenharmony_ci# define DSI0_INT_CMDC_DONE_NO_REPEAT 1 1908c2ecf20Sopenharmony_ci# define DSI0_INT_CMDC_DONE_REPEAT 3 1918c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_DIR_RTF BIT(22) 1928c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_D1_ULPS BIT(21) 1938c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_D1_STOP BIT(20) 1948c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_RXLPDT BIT(19) 1958c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_RXTRIG BIT(18) 1968c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_D0_ULPS BIT(17) 1978c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_D0_LPDT BIT(16) 1988c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_D0_FTR BIT(15) 1998c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_D0_STOP BIT(14) 2008c2ecf20Sopenharmony_ci/* Signaled when the clock lane enters the given state. */ 2018c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_CLK_ULPS BIT(13) 2028c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_CLK_HS BIT(12) 2038c2ecf20Sopenharmony_ci# define DSI0_INT_PHY_CLK_FTR BIT(11) 2048c2ecf20Sopenharmony_ci/* Signaled on timeouts */ 2058c2ecf20Sopenharmony_ci# define DSI0_INT_PR_TO BIT(10) 2068c2ecf20Sopenharmony_ci# define DSI0_INT_TA_TO BIT(9) 2078c2ecf20Sopenharmony_ci# define DSI0_INT_LPRX_TO BIT(8) 2088c2ecf20Sopenharmony_ci# define DSI0_INT_HSTX_TO BIT(7) 2098c2ecf20Sopenharmony_ci/* Contention on a line when trying to drive the line low */ 2108c2ecf20Sopenharmony_ci# define DSI0_INT_ERR_CONT_LP1 BIT(6) 2118c2ecf20Sopenharmony_ci# define DSI0_INT_ERR_CONT_LP0 BIT(5) 2128c2ecf20Sopenharmony_ci/* Control error: incorrect line state sequence on data lane 0. */ 2138c2ecf20Sopenharmony_ci# define DSI0_INT_ERR_CONTROL BIT(4) 2148c2ecf20Sopenharmony_ci# define DSI0_INT_ERR_SYNC_ESC BIT(3) 2158c2ecf20Sopenharmony_ci# define DSI0_INT_RX2_PKT BIT(2) 2168c2ecf20Sopenharmony_ci# define DSI0_INT_RX1_PKT BIT(1) 2178c2ecf20Sopenharmony_ci# define DSI0_INT_CMD_PKT BIT(0) 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci#define DSI0_INTERRUPTS_ALWAYS_ENABLED (DSI0_INT_ERR_SYNC_ESC | \ 2208c2ecf20Sopenharmony_ci DSI0_INT_ERR_CONTROL | \ 2218c2ecf20Sopenharmony_ci DSI0_INT_ERR_CONT_LP0 | \ 2228c2ecf20Sopenharmony_ci DSI0_INT_ERR_CONT_LP1 | \ 2238c2ecf20Sopenharmony_ci DSI0_INT_HSTX_TO | \ 2248c2ecf20Sopenharmony_ci DSI0_INT_LPRX_TO | \ 2258c2ecf20Sopenharmony_ci DSI0_INT_TA_TO | \ 2268c2ecf20Sopenharmony_ci DSI0_INT_PR_TO) 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_D3_ULPS BIT(30) 2298c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_D3_STOP BIT(29) 2308c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_D2_ULPS BIT(28) 2318c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_D2_STOP BIT(27) 2328c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_D1_ULPS BIT(26) 2338c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_D1_STOP BIT(25) 2348c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_D0_ULPS BIT(24) 2358c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_D0_STOP BIT(23) 2368c2ecf20Sopenharmony_ci# define DSI1_INT_FIFO_ERR BIT(22) 2378c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_DIR_RTF BIT(21) 2388c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_RXLPDT BIT(20) 2398c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_RXTRIG BIT(19) 2408c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_D0_LPDT BIT(18) 2418c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_DIR_FTR BIT(17) 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* Signaled when the clock lane enters the given state. */ 2448c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_CLOCK_ULPS BIT(16) 2458c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_CLOCK_HS BIT(15) 2468c2ecf20Sopenharmony_ci# define DSI1_INT_PHY_CLOCK_STOP BIT(14) 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* Signaled on timeouts */ 2498c2ecf20Sopenharmony_ci# define DSI1_INT_PR_TO BIT(13) 2508c2ecf20Sopenharmony_ci# define DSI1_INT_TA_TO BIT(12) 2518c2ecf20Sopenharmony_ci# define DSI1_INT_LPRX_TO BIT(11) 2528c2ecf20Sopenharmony_ci# define DSI1_INT_HSTX_TO BIT(10) 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* Contention on a line when trying to drive the line low */ 2558c2ecf20Sopenharmony_ci# define DSI1_INT_ERR_CONT_LP1 BIT(9) 2568c2ecf20Sopenharmony_ci# define DSI1_INT_ERR_CONT_LP0 BIT(8) 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* Control error: incorrect line state sequence on data lane 0. */ 2598c2ecf20Sopenharmony_ci# define DSI1_INT_ERR_CONTROL BIT(7) 2608c2ecf20Sopenharmony_ci/* LPDT synchronization error (bits received not a multiple of 8. */ 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci# define DSI1_INT_ERR_SYNC_ESC BIT(6) 2638c2ecf20Sopenharmony_ci/* Signaled after receiving an error packet from the display in 2648c2ecf20Sopenharmony_ci * response to a read. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci# define DSI1_INT_RXPKT2 BIT(5) 2678c2ecf20Sopenharmony_ci/* Signaled after receiving a packet. The header and optional short 2688c2ecf20Sopenharmony_ci * response will be in RXPKT1H, and a long response will be in the 2698c2ecf20Sopenharmony_ci * RXPKT_FIFO. 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci# define DSI1_INT_RXPKT1 BIT(4) 2728c2ecf20Sopenharmony_ci# define DSI1_INT_TXPKT2_DONE BIT(3) 2738c2ecf20Sopenharmony_ci# define DSI1_INT_TXPKT2_END BIT(2) 2748c2ecf20Sopenharmony_ci/* Signaled after all repeats of TXPKT1 are transferred. */ 2758c2ecf20Sopenharmony_ci# define DSI1_INT_TXPKT1_DONE BIT(1) 2768c2ecf20Sopenharmony_ci/* Signaled after each TXPKT1 repeat is scheduled. */ 2778c2ecf20Sopenharmony_ci# define DSI1_INT_TXPKT1_END BIT(0) 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci#define DSI1_INTERRUPTS_ALWAYS_ENABLED (DSI1_INT_ERR_SYNC_ESC | \ 2808c2ecf20Sopenharmony_ci DSI1_INT_ERR_CONTROL | \ 2818c2ecf20Sopenharmony_ci DSI1_INT_ERR_CONT_LP0 | \ 2828c2ecf20Sopenharmony_ci DSI1_INT_ERR_CONT_LP1 | \ 2838c2ecf20Sopenharmony_ci DSI1_INT_HSTX_TO | \ 2848c2ecf20Sopenharmony_ci DSI1_INT_LPRX_TO | \ 2858c2ecf20Sopenharmony_ci DSI1_INT_TA_TO | \ 2868c2ecf20Sopenharmony_ci DSI1_INT_PR_TO) 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci#define DSI0_STAT 0x2c 2898c2ecf20Sopenharmony_ci#define DSI0_HSTX_TO_CNT 0x30 2908c2ecf20Sopenharmony_ci#define DSI0_LPRX_TO_CNT 0x34 2918c2ecf20Sopenharmony_ci#define DSI0_TA_TO_CNT 0x38 2928c2ecf20Sopenharmony_ci#define DSI0_PR_TO_CNT 0x3c 2938c2ecf20Sopenharmony_ci#define DSI0_PHYC 0x40 2948c2ecf20Sopenharmony_ci# define DSI1_PHYC_ESC_CLK_LPDT_MASK VC4_MASK(25, 20) 2958c2ecf20Sopenharmony_ci# define DSI1_PHYC_ESC_CLK_LPDT_SHIFT 20 2968c2ecf20Sopenharmony_ci# define DSI1_PHYC_HS_CLK_CONTINUOUS BIT(18) 2978c2ecf20Sopenharmony_ci# define DSI0_PHYC_ESC_CLK_LPDT_MASK VC4_MASK(17, 12) 2988c2ecf20Sopenharmony_ci# define DSI0_PHYC_ESC_CLK_LPDT_SHIFT 12 2998c2ecf20Sopenharmony_ci# define DSI1_PHYC_CLANE_ULPS BIT(17) 3008c2ecf20Sopenharmony_ci# define DSI1_PHYC_CLANE_ENABLE BIT(16) 3018c2ecf20Sopenharmony_ci# define DSI_PHYC_DLANE3_ULPS BIT(13) 3028c2ecf20Sopenharmony_ci# define DSI_PHYC_DLANE3_ENABLE BIT(12) 3038c2ecf20Sopenharmony_ci# define DSI0_PHYC_HS_CLK_CONTINUOUS BIT(10) 3048c2ecf20Sopenharmony_ci# define DSI0_PHYC_CLANE_ULPS BIT(9) 3058c2ecf20Sopenharmony_ci# define DSI_PHYC_DLANE2_ULPS BIT(9) 3068c2ecf20Sopenharmony_ci# define DSI0_PHYC_CLANE_ENABLE BIT(8) 3078c2ecf20Sopenharmony_ci# define DSI_PHYC_DLANE2_ENABLE BIT(8) 3088c2ecf20Sopenharmony_ci# define DSI_PHYC_DLANE1_ULPS BIT(5) 3098c2ecf20Sopenharmony_ci# define DSI_PHYC_DLANE1_ENABLE BIT(4) 3108c2ecf20Sopenharmony_ci# define DSI_PHYC_DLANE0_FORCE_STOP BIT(2) 3118c2ecf20Sopenharmony_ci# define DSI_PHYC_DLANE0_ULPS BIT(1) 3128c2ecf20Sopenharmony_ci# define DSI_PHYC_DLANE0_ENABLE BIT(0) 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci#define DSI0_HS_CLT0 0x44 3158c2ecf20Sopenharmony_ci#define DSI0_HS_CLT1 0x48 3168c2ecf20Sopenharmony_ci#define DSI0_HS_CLT2 0x4c 3178c2ecf20Sopenharmony_ci#define DSI0_HS_DLT3 0x50 3188c2ecf20Sopenharmony_ci#define DSI0_HS_DLT4 0x54 3198c2ecf20Sopenharmony_ci#define DSI0_HS_DLT5 0x58 3208c2ecf20Sopenharmony_ci#define DSI0_HS_DLT6 0x5c 3218c2ecf20Sopenharmony_ci#define DSI0_HS_DLT7 0x60 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci#define DSI0_PHY_AFEC0 0x64 3248c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_DDR2CLK_EN BIT(26) 3258c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_DDRCLK_EN BIT(25) 3268c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_LATCH_ULPS BIT(24) 3278c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE3_MASK VC4_MASK(31, 29) 3288c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE3_SHIFT 29 3298c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE2_MASK VC4_MASK(28, 26) 3308c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE2_SHIFT 26 3318c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE1_MASK VC4_MASK(27, 23) 3328c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE1_SHIFT 23 3338c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE0_MASK VC4_MASK(22, 20) 3348c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE0_SHIFT 20 3358c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_CLANE_MASK VC4_MASK(19, 17) 3368c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_CLANE_SHIFT 17 3378c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_DLANE1_MASK VC4_MASK(23, 20) 3388c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_DLANE1_SHIFT 20 3398c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_DLANE0_MASK VC4_MASK(19, 16) 3408c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_DLANE0_SHIFT 16 3418c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_CLANE_MASK VC4_MASK(15, 12) 3428c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_CLANE_SHIFT 12 3438c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_DDR2CLK_EN BIT(16) 3448c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_DDRCLK_EN BIT(15) 3458c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_LATCH_ULPS BIT(14) 3468c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_RESET BIT(13) 3478c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_PD BIT(12) 3488c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_RESET BIT(11) 3498c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_PD_BG BIT(11) 3508c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_PD BIT(10) 3518c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_PD_DLANE3 BIT(10) 3528c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_PD_BG BIT(9) 3538c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_PD_DLANE2 BIT(9) 3548c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC0_PD_DLANE1 BIT(8) 3558c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC0_PD_DLANE1 BIT(8) 3568c2ecf20Sopenharmony_ci# define DSI_PHY_AFEC0_PTATADJ_MASK VC4_MASK(7, 4) 3578c2ecf20Sopenharmony_ci# define DSI_PHY_AFEC0_PTATADJ_SHIFT 4 3588c2ecf20Sopenharmony_ci# define DSI_PHY_AFEC0_CTATADJ_MASK VC4_MASK(3, 0) 3598c2ecf20Sopenharmony_ci# define DSI_PHY_AFEC0_CTATADJ_SHIFT 0 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci#define DSI0_PHY_AFEC1 0x68 3628c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_DLANE1_MASK VC4_MASK(10, 8) 3638c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_DLANE1_SHIFT 8 3648c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_DLANE0_MASK VC4_MASK(6, 4) 3658c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_DLANE0_SHIFT 4 3668c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_CLANE_MASK VC4_MASK(2, 0) 3678c2ecf20Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_CLANE_SHIFT 0 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci#define DSI0_TST_SEL 0x6c 3708c2ecf20Sopenharmony_ci#define DSI0_TST_MON 0x70 3718c2ecf20Sopenharmony_ci#define DSI0_ID 0x74 3728c2ecf20Sopenharmony_ci# define DSI_ID_VALUE 0x00647369 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci#define DSI1_CTRL 0x00 3758c2ecf20Sopenharmony_ci# define DSI_CTRL_HS_CLKC_MASK VC4_MASK(15, 14) 3768c2ecf20Sopenharmony_ci# define DSI_CTRL_HS_CLKC_SHIFT 14 3778c2ecf20Sopenharmony_ci# define DSI_CTRL_HS_CLKC_BYTE 0 3788c2ecf20Sopenharmony_ci# define DSI_CTRL_HS_CLKC_DDR2 1 3798c2ecf20Sopenharmony_ci# define DSI_CTRL_HS_CLKC_DDR 2 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci# define DSI_CTRL_RX_LPDT_EOT_DISABLE BIT(13) 3828c2ecf20Sopenharmony_ci# define DSI_CTRL_LPDT_EOT_DISABLE BIT(12) 3838c2ecf20Sopenharmony_ci# define DSI_CTRL_HSDT_EOT_DISABLE BIT(11) 3848c2ecf20Sopenharmony_ci# define DSI_CTRL_SOFT_RESET_CFG BIT(10) 3858c2ecf20Sopenharmony_ci# define DSI_CTRL_CAL_BYTE BIT(9) 3868c2ecf20Sopenharmony_ci# define DSI_CTRL_INV_BYTE BIT(8) 3878c2ecf20Sopenharmony_ci# define DSI_CTRL_CLR_LDF BIT(7) 3888c2ecf20Sopenharmony_ci# define DSI0_CTRL_CLR_PBCF BIT(6) 3898c2ecf20Sopenharmony_ci# define DSI1_CTRL_CLR_RXF BIT(6) 3908c2ecf20Sopenharmony_ci# define DSI0_CTRL_CLR_CPBCF BIT(5) 3918c2ecf20Sopenharmony_ci# define DSI1_CTRL_CLR_PDF BIT(5) 3928c2ecf20Sopenharmony_ci# define DSI0_CTRL_CLR_PDF BIT(4) 3938c2ecf20Sopenharmony_ci# define DSI1_CTRL_CLR_CDF BIT(4) 3948c2ecf20Sopenharmony_ci# define DSI0_CTRL_CLR_CDF BIT(3) 3958c2ecf20Sopenharmony_ci# define DSI0_CTRL_CTRL2 BIT(2) 3968c2ecf20Sopenharmony_ci# define DSI1_CTRL_DISABLE_DISP_CRCC BIT(2) 3978c2ecf20Sopenharmony_ci# define DSI0_CTRL_CTRL1 BIT(1) 3988c2ecf20Sopenharmony_ci# define DSI1_CTRL_DISABLE_DISP_ECCC BIT(1) 3998c2ecf20Sopenharmony_ci# define DSI0_CTRL_CTRL0 BIT(0) 4008c2ecf20Sopenharmony_ci# define DSI1_CTRL_EN BIT(0) 4018c2ecf20Sopenharmony_ci# define DSI0_CTRL_RESET_FIFOS (DSI_CTRL_CLR_LDF | \ 4028c2ecf20Sopenharmony_ci DSI0_CTRL_CLR_PBCF | \ 4038c2ecf20Sopenharmony_ci DSI0_CTRL_CLR_CPBCF | \ 4048c2ecf20Sopenharmony_ci DSI0_CTRL_CLR_PDF | \ 4058c2ecf20Sopenharmony_ci DSI0_CTRL_CLR_CDF) 4068c2ecf20Sopenharmony_ci# define DSI1_CTRL_RESET_FIFOS (DSI_CTRL_CLR_LDF | \ 4078c2ecf20Sopenharmony_ci DSI1_CTRL_CLR_RXF | \ 4088c2ecf20Sopenharmony_ci DSI1_CTRL_CLR_PDF | \ 4098c2ecf20Sopenharmony_ci DSI1_CTRL_CLR_CDF) 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci#define DSI1_TXPKT2C 0x0c 4128c2ecf20Sopenharmony_ci#define DSI1_TXPKT2H 0x10 4138c2ecf20Sopenharmony_ci#define DSI1_TXPKT_PIX_FIFO 0x20 4148c2ecf20Sopenharmony_ci#define DSI1_RXPKT_FIFO 0x24 4158c2ecf20Sopenharmony_ci#define DSI1_DISP0_CTRL 0x28 4168c2ecf20Sopenharmony_ci#define DSI1_INT_STAT 0x30 4178c2ecf20Sopenharmony_ci#define DSI1_INT_EN 0x34 4188c2ecf20Sopenharmony_ci/* State reporting bits. These mostly behave like INT_STAT, where 4198c2ecf20Sopenharmony_ci * writing a 1 clears the bit. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_ci#define DSI1_STAT 0x38 4228c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_D3_ULPS BIT(31) 4238c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_D3_STOP BIT(30) 4248c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_D2_ULPS BIT(29) 4258c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_D2_STOP BIT(28) 4268c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_D1_ULPS BIT(27) 4278c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_D1_STOP BIT(26) 4288c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_D0_ULPS BIT(25) 4298c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_D0_STOP BIT(24) 4308c2ecf20Sopenharmony_ci# define DSI1_STAT_FIFO_ERR BIT(23) 4318c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_RXLPDT BIT(22) 4328c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_RXTRIG BIT(21) 4338c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_D0_LPDT BIT(20) 4348c2ecf20Sopenharmony_ci/* Set when in forward direction */ 4358c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_DIR BIT(19) 4368c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_CLOCK_ULPS BIT(18) 4378c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_CLOCK_HS BIT(17) 4388c2ecf20Sopenharmony_ci# define DSI1_STAT_PHY_CLOCK_STOP BIT(16) 4398c2ecf20Sopenharmony_ci# define DSI1_STAT_PR_TO BIT(15) 4408c2ecf20Sopenharmony_ci# define DSI1_STAT_TA_TO BIT(14) 4418c2ecf20Sopenharmony_ci# define DSI1_STAT_LPRX_TO BIT(13) 4428c2ecf20Sopenharmony_ci# define DSI1_STAT_HSTX_TO BIT(12) 4438c2ecf20Sopenharmony_ci# define DSI1_STAT_ERR_CONT_LP1 BIT(11) 4448c2ecf20Sopenharmony_ci# define DSI1_STAT_ERR_CONT_LP0 BIT(10) 4458c2ecf20Sopenharmony_ci# define DSI1_STAT_ERR_CONTROL BIT(9) 4468c2ecf20Sopenharmony_ci# define DSI1_STAT_ERR_SYNC_ESC BIT(8) 4478c2ecf20Sopenharmony_ci# define DSI1_STAT_RXPKT2 BIT(7) 4488c2ecf20Sopenharmony_ci# define DSI1_STAT_RXPKT1 BIT(6) 4498c2ecf20Sopenharmony_ci# define DSI1_STAT_TXPKT2_BUSY BIT(5) 4508c2ecf20Sopenharmony_ci# define DSI1_STAT_TXPKT2_DONE BIT(4) 4518c2ecf20Sopenharmony_ci# define DSI1_STAT_TXPKT2_END BIT(3) 4528c2ecf20Sopenharmony_ci# define DSI1_STAT_TXPKT1_BUSY BIT(2) 4538c2ecf20Sopenharmony_ci# define DSI1_STAT_TXPKT1_DONE BIT(1) 4548c2ecf20Sopenharmony_ci# define DSI1_STAT_TXPKT1_END BIT(0) 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci#define DSI1_HSTX_TO_CNT 0x3c 4578c2ecf20Sopenharmony_ci#define DSI1_LPRX_TO_CNT 0x40 4588c2ecf20Sopenharmony_ci#define DSI1_TA_TO_CNT 0x44 4598c2ecf20Sopenharmony_ci#define DSI1_PR_TO_CNT 0x48 4608c2ecf20Sopenharmony_ci#define DSI1_PHYC 0x4c 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci#define DSI1_HS_CLT0 0x50 4638c2ecf20Sopenharmony_ci# define DSI_HS_CLT0_CZERO_MASK VC4_MASK(26, 18) 4648c2ecf20Sopenharmony_ci# define DSI_HS_CLT0_CZERO_SHIFT 18 4658c2ecf20Sopenharmony_ci# define DSI_HS_CLT0_CPRE_MASK VC4_MASK(17, 9) 4668c2ecf20Sopenharmony_ci# define DSI_HS_CLT0_CPRE_SHIFT 9 4678c2ecf20Sopenharmony_ci# define DSI_HS_CLT0_CPREP_MASK VC4_MASK(8, 0) 4688c2ecf20Sopenharmony_ci# define DSI_HS_CLT0_CPREP_SHIFT 0 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci#define DSI1_HS_CLT1 0x54 4718c2ecf20Sopenharmony_ci# define DSI_HS_CLT1_CTRAIL_MASK VC4_MASK(17, 9) 4728c2ecf20Sopenharmony_ci# define DSI_HS_CLT1_CTRAIL_SHIFT 9 4738c2ecf20Sopenharmony_ci# define DSI_HS_CLT1_CPOST_MASK VC4_MASK(8, 0) 4748c2ecf20Sopenharmony_ci# define DSI_HS_CLT1_CPOST_SHIFT 0 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci#define DSI1_HS_CLT2 0x58 4778c2ecf20Sopenharmony_ci# define DSI_HS_CLT2_WUP_MASK VC4_MASK(23, 0) 4788c2ecf20Sopenharmony_ci# define DSI_HS_CLT2_WUP_SHIFT 0 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci#define DSI1_HS_DLT3 0x5c 4818c2ecf20Sopenharmony_ci# define DSI_HS_DLT3_EXIT_MASK VC4_MASK(26, 18) 4828c2ecf20Sopenharmony_ci# define DSI_HS_DLT3_EXIT_SHIFT 18 4838c2ecf20Sopenharmony_ci# define DSI_HS_DLT3_ZERO_MASK VC4_MASK(17, 9) 4848c2ecf20Sopenharmony_ci# define DSI_HS_DLT3_ZERO_SHIFT 9 4858c2ecf20Sopenharmony_ci# define DSI_HS_DLT3_PRE_MASK VC4_MASK(8, 0) 4868c2ecf20Sopenharmony_ci# define DSI_HS_DLT3_PRE_SHIFT 0 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci#define DSI1_HS_DLT4 0x60 4898c2ecf20Sopenharmony_ci# define DSI_HS_DLT4_ANLAT_MASK VC4_MASK(22, 18) 4908c2ecf20Sopenharmony_ci# define DSI_HS_DLT4_ANLAT_SHIFT 18 4918c2ecf20Sopenharmony_ci# define DSI_HS_DLT4_TRAIL_MASK VC4_MASK(17, 9) 4928c2ecf20Sopenharmony_ci# define DSI_HS_DLT4_TRAIL_SHIFT 9 4938c2ecf20Sopenharmony_ci# define DSI_HS_DLT4_LPX_MASK VC4_MASK(8, 0) 4948c2ecf20Sopenharmony_ci# define DSI_HS_DLT4_LPX_SHIFT 0 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci#define DSI1_HS_DLT5 0x64 4978c2ecf20Sopenharmony_ci# define DSI_HS_DLT5_INIT_MASK VC4_MASK(23, 0) 4988c2ecf20Sopenharmony_ci# define DSI_HS_DLT5_INIT_SHIFT 0 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci#define DSI1_HS_DLT6 0x68 5018c2ecf20Sopenharmony_ci# define DSI_HS_DLT6_TA_GET_MASK VC4_MASK(31, 24) 5028c2ecf20Sopenharmony_ci# define DSI_HS_DLT6_TA_GET_SHIFT 24 5038c2ecf20Sopenharmony_ci# define DSI_HS_DLT6_TA_SURE_MASK VC4_MASK(23, 16) 5048c2ecf20Sopenharmony_ci# define DSI_HS_DLT6_TA_SURE_SHIFT 16 5058c2ecf20Sopenharmony_ci# define DSI_HS_DLT6_TA_GO_MASK VC4_MASK(15, 8) 5068c2ecf20Sopenharmony_ci# define DSI_HS_DLT6_TA_GO_SHIFT 8 5078c2ecf20Sopenharmony_ci# define DSI_HS_DLT6_LP_LPX_MASK VC4_MASK(7, 0) 5088c2ecf20Sopenharmony_ci# define DSI_HS_DLT6_LP_LPX_SHIFT 0 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci#define DSI1_HS_DLT7 0x6c 5118c2ecf20Sopenharmony_ci# define DSI_HS_DLT7_LP_WUP_MASK VC4_MASK(23, 0) 5128c2ecf20Sopenharmony_ci# define DSI_HS_DLT7_LP_WUP_SHIFT 0 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci#define DSI1_PHY_AFEC0 0x70 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci#define DSI1_PHY_AFEC1 0x74 5178c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE3_MASK VC4_MASK(19, 16) 5188c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE3_SHIFT 16 5198c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE2_MASK VC4_MASK(15, 12) 5208c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE2_SHIFT 12 5218c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE1_MASK VC4_MASK(11, 8) 5228c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE1_SHIFT 8 5238c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE0_MASK VC4_MASK(7, 4) 5248c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE0_SHIFT 4 5258c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_CLANE_MASK VC4_MASK(3, 0) 5268c2ecf20Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_CLANE_SHIFT 0 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci#define DSI1_TST_SEL 0x78 5298c2ecf20Sopenharmony_ci#define DSI1_TST_MON 0x7c 5308c2ecf20Sopenharmony_ci#define DSI1_PHY_TST1 0x80 5318c2ecf20Sopenharmony_ci#define DSI1_PHY_TST2 0x84 5328c2ecf20Sopenharmony_ci#define DSI1_PHY_FIFO_STAT 0x88 5338c2ecf20Sopenharmony_ci/* Actually, all registers in the range that aren't otherwise claimed 5348c2ecf20Sopenharmony_ci * will return the ID. 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_ci#define DSI1_ID 0x8c 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistruct vc4_dsi_variant { 5398c2ecf20Sopenharmony_ci /* Whether we're on bcm2835's DSI0 or DSI1. */ 5408c2ecf20Sopenharmony_ci unsigned int port; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci bool broken_axi_workaround; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci const char *debugfs_name; 5458c2ecf20Sopenharmony_ci const struct debugfs_reg32 *regs; 5468c2ecf20Sopenharmony_ci size_t nregs; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci}; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci/* General DSI hardware state. */ 5518c2ecf20Sopenharmony_cistruct vc4_dsi { 5528c2ecf20Sopenharmony_ci struct platform_device *pdev; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci struct mipi_dsi_host dsi_host; 5558c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 5568c2ecf20Sopenharmony_ci struct drm_bridge *bridge; 5578c2ecf20Sopenharmony_ci struct list_head bridge_chain; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci void __iomem *regs; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci struct dma_chan *reg_dma_chan; 5628c2ecf20Sopenharmony_ci dma_addr_t reg_dma_paddr; 5638c2ecf20Sopenharmony_ci u32 *reg_dma_mem; 5648c2ecf20Sopenharmony_ci dma_addr_t reg_paddr; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci const struct vc4_dsi_variant *variant; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* DSI channel for the panel we're connected to. */ 5698c2ecf20Sopenharmony_ci u32 channel; 5708c2ecf20Sopenharmony_ci u32 lanes; 5718c2ecf20Sopenharmony_ci u32 format; 5728c2ecf20Sopenharmony_ci u32 divider; 5738c2ecf20Sopenharmony_ci u32 mode_flags; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* Input clock from CPRMAN to the digital PHY, for the DSI 5768c2ecf20Sopenharmony_ci * escape clock. 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_ci struct clk *escape_clock; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* Input clock to the analog PHY, used to generate the DSI bit 5818c2ecf20Sopenharmony_ci * clock. 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci struct clk *pll_phy_clock; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* HS Clocks generated within the DSI analog PHY. */ 5868c2ecf20Sopenharmony_ci struct clk_fixed_factor phy_clocks[3]; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci struct clk_hw_onecell_data *clk_onecell; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* Pixel clock output to the pixelvalve, generated from the HS 5918c2ecf20Sopenharmony_ci * clock. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_ci struct clk *pixel_clock; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci struct completion xfer_completion; 5968c2ecf20Sopenharmony_ci int xfer_result; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci struct debugfs_regset32 regset; 5998c2ecf20Sopenharmony_ci}; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci#define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host) 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic inline void 6048c2ecf20Sopenharmony_cidsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct dma_chan *chan = dsi->reg_dma_chan; 6078c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 6088c2ecf20Sopenharmony_ci dma_cookie_t cookie; 6098c2ecf20Sopenharmony_ci int ret; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* DSI0 should be able to write normally. */ 6128c2ecf20Sopenharmony_ci if (!chan) { 6138c2ecf20Sopenharmony_ci writel(val, dsi->regs + offset); 6148c2ecf20Sopenharmony_ci return; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci *dsi->reg_dma_mem = val; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci tx = chan->device->device_prep_dma_memcpy(chan, 6208c2ecf20Sopenharmony_ci dsi->reg_paddr + offset, 6218c2ecf20Sopenharmony_ci dsi->reg_dma_paddr, 6228c2ecf20Sopenharmony_ci 4, 0); 6238c2ecf20Sopenharmony_ci if (!tx) { 6248c2ecf20Sopenharmony_ci DRM_ERROR("Failed to set up DMA register write\n"); 6258c2ecf20Sopenharmony_ci return; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci cookie = tx->tx_submit(tx); 6298c2ecf20Sopenharmony_ci ret = dma_submit_error(cookie); 6308c2ecf20Sopenharmony_ci if (ret) { 6318c2ecf20Sopenharmony_ci DRM_ERROR("Failed to submit DMA: %d\n", ret); 6328c2ecf20Sopenharmony_ci return; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci ret = dma_sync_wait(chan, cookie); 6358c2ecf20Sopenharmony_ci if (ret) 6368c2ecf20Sopenharmony_ci DRM_ERROR("Failed to wait for DMA: %d\n", ret); 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci#define DSI_READ(offset) readl(dsi->regs + (offset)) 6408c2ecf20Sopenharmony_ci#define DSI_WRITE(offset, val) dsi_dma_workaround_write(dsi, offset, val) 6418c2ecf20Sopenharmony_ci#define DSI_PORT_READ(offset) \ 6428c2ecf20Sopenharmony_ci DSI_READ(dsi->variant->port ? DSI1_##offset : DSI0_##offset) 6438c2ecf20Sopenharmony_ci#define DSI_PORT_WRITE(offset, val) \ 6448c2ecf20Sopenharmony_ci DSI_WRITE(dsi->variant->port ? DSI1_##offset : DSI0_##offset, val) 6458c2ecf20Sopenharmony_ci#define DSI_PORT_BIT(bit) (dsi->variant->port ? DSI1_##bit : DSI0_##bit) 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/* VC4 DSI encoder KMS struct */ 6488c2ecf20Sopenharmony_cistruct vc4_dsi_encoder { 6498c2ecf20Sopenharmony_ci struct vc4_encoder base; 6508c2ecf20Sopenharmony_ci struct vc4_dsi *dsi; 6518c2ecf20Sopenharmony_ci}; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic inline struct vc4_dsi_encoder * 6548c2ecf20Sopenharmony_cito_vc4_dsi_encoder(struct drm_encoder *encoder) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci return container_of(encoder, struct vc4_dsi_encoder, base.base); 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic const struct debugfs_reg32 dsi0_regs[] = { 6608c2ecf20Sopenharmony_ci VC4_REG32(DSI0_CTRL), 6618c2ecf20Sopenharmony_ci VC4_REG32(DSI0_STAT), 6628c2ecf20Sopenharmony_ci VC4_REG32(DSI0_HSTX_TO_CNT), 6638c2ecf20Sopenharmony_ci VC4_REG32(DSI0_LPRX_TO_CNT), 6648c2ecf20Sopenharmony_ci VC4_REG32(DSI0_TA_TO_CNT), 6658c2ecf20Sopenharmony_ci VC4_REG32(DSI0_PR_TO_CNT), 6668c2ecf20Sopenharmony_ci VC4_REG32(DSI0_DISP0_CTRL), 6678c2ecf20Sopenharmony_ci VC4_REG32(DSI0_DISP1_CTRL), 6688c2ecf20Sopenharmony_ci VC4_REG32(DSI0_INT_STAT), 6698c2ecf20Sopenharmony_ci VC4_REG32(DSI0_INT_EN), 6708c2ecf20Sopenharmony_ci VC4_REG32(DSI0_PHYC), 6718c2ecf20Sopenharmony_ci VC4_REG32(DSI0_HS_CLT0), 6728c2ecf20Sopenharmony_ci VC4_REG32(DSI0_HS_CLT1), 6738c2ecf20Sopenharmony_ci VC4_REG32(DSI0_HS_CLT2), 6748c2ecf20Sopenharmony_ci VC4_REG32(DSI0_HS_DLT3), 6758c2ecf20Sopenharmony_ci VC4_REG32(DSI0_HS_DLT4), 6768c2ecf20Sopenharmony_ci VC4_REG32(DSI0_HS_DLT5), 6778c2ecf20Sopenharmony_ci VC4_REG32(DSI0_HS_DLT6), 6788c2ecf20Sopenharmony_ci VC4_REG32(DSI0_HS_DLT7), 6798c2ecf20Sopenharmony_ci VC4_REG32(DSI0_PHY_AFEC0), 6808c2ecf20Sopenharmony_ci VC4_REG32(DSI0_PHY_AFEC1), 6818c2ecf20Sopenharmony_ci VC4_REG32(DSI0_ID), 6828c2ecf20Sopenharmony_ci}; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic const struct debugfs_reg32 dsi1_regs[] = { 6858c2ecf20Sopenharmony_ci VC4_REG32(DSI1_CTRL), 6868c2ecf20Sopenharmony_ci VC4_REG32(DSI1_STAT), 6878c2ecf20Sopenharmony_ci VC4_REG32(DSI1_HSTX_TO_CNT), 6888c2ecf20Sopenharmony_ci VC4_REG32(DSI1_LPRX_TO_CNT), 6898c2ecf20Sopenharmony_ci VC4_REG32(DSI1_TA_TO_CNT), 6908c2ecf20Sopenharmony_ci VC4_REG32(DSI1_PR_TO_CNT), 6918c2ecf20Sopenharmony_ci VC4_REG32(DSI1_DISP0_CTRL), 6928c2ecf20Sopenharmony_ci VC4_REG32(DSI1_DISP1_CTRL), 6938c2ecf20Sopenharmony_ci VC4_REG32(DSI1_INT_STAT), 6948c2ecf20Sopenharmony_ci VC4_REG32(DSI1_INT_EN), 6958c2ecf20Sopenharmony_ci VC4_REG32(DSI1_PHYC), 6968c2ecf20Sopenharmony_ci VC4_REG32(DSI1_HS_CLT0), 6978c2ecf20Sopenharmony_ci VC4_REG32(DSI1_HS_CLT1), 6988c2ecf20Sopenharmony_ci VC4_REG32(DSI1_HS_CLT2), 6998c2ecf20Sopenharmony_ci VC4_REG32(DSI1_HS_DLT3), 7008c2ecf20Sopenharmony_ci VC4_REG32(DSI1_HS_DLT4), 7018c2ecf20Sopenharmony_ci VC4_REG32(DSI1_HS_DLT5), 7028c2ecf20Sopenharmony_ci VC4_REG32(DSI1_HS_DLT6), 7038c2ecf20Sopenharmony_ci VC4_REG32(DSI1_HS_DLT7), 7048c2ecf20Sopenharmony_ci VC4_REG32(DSI1_PHY_AFEC0), 7058c2ecf20Sopenharmony_ci VC4_REG32(DSI1_PHY_AFEC1), 7068c2ecf20Sopenharmony_ci VC4_REG32(DSI1_ID), 7078c2ecf20Sopenharmony_ci}; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic void vc4_dsi_latch_ulps(struct vc4_dsi *dsi, bool latch) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci u32 afec0 = DSI_PORT_READ(PHY_AFEC0); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (latch) 7148c2ecf20Sopenharmony_ci afec0 |= DSI_PORT_BIT(PHY_AFEC0_LATCH_ULPS); 7158c2ecf20Sopenharmony_ci else 7168c2ecf20Sopenharmony_ci afec0 &= ~DSI_PORT_BIT(PHY_AFEC0_LATCH_ULPS); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC0, afec0); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/* Enters or exits Ultra Low Power State. */ 7228c2ecf20Sopenharmony_cistatic void vc4_dsi_ulps(struct vc4_dsi *dsi, bool ulps) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci bool non_continuous = dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS; 7258c2ecf20Sopenharmony_ci u32 phyc_ulps = ((non_continuous ? DSI_PORT_BIT(PHYC_CLANE_ULPS) : 0) | 7268c2ecf20Sopenharmony_ci DSI_PHYC_DLANE0_ULPS | 7278c2ecf20Sopenharmony_ci (dsi->lanes > 1 ? DSI_PHYC_DLANE1_ULPS : 0) | 7288c2ecf20Sopenharmony_ci (dsi->lanes > 2 ? DSI_PHYC_DLANE2_ULPS : 0) | 7298c2ecf20Sopenharmony_ci (dsi->lanes > 3 ? DSI_PHYC_DLANE3_ULPS : 0)); 7308c2ecf20Sopenharmony_ci u32 stat_ulps = ((non_continuous ? DSI1_STAT_PHY_CLOCK_ULPS : 0) | 7318c2ecf20Sopenharmony_ci DSI1_STAT_PHY_D0_ULPS | 7328c2ecf20Sopenharmony_ci (dsi->lanes > 1 ? DSI1_STAT_PHY_D1_ULPS : 0) | 7338c2ecf20Sopenharmony_ci (dsi->lanes > 2 ? DSI1_STAT_PHY_D2_ULPS : 0) | 7348c2ecf20Sopenharmony_ci (dsi->lanes > 3 ? DSI1_STAT_PHY_D3_ULPS : 0)); 7358c2ecf20Sopenharmony_ci u32 stat_stop = ((non_continuous ? DSI1_STAT_PHY_CLOCK_STOP : 0) | 7368c2ecf20Sopenharmony_ci DSI1_STAT_PHY_D0_STOP | 7378c2ecf20Sopenharmony_ci (dsi->lanes > 1 ? DSI1_STAT_PHY_D1_STOP : 0) | 7388c2ecf20Sopenharmony_ci (dsi->lanes > 2 ? DSI1_STAT_PHY_D2_STOP : 0) | 7398c2ecf20Sopenharmony_ci (dsi->lanes > 3 ? DSI1_STAT_PHY_D3_STOP : 0)); 7408c2ecf20Sopenharmony_ci int ret; 7418c2ecf20Sopenharmony_ci bool ulps_currently_enabled = (DSI_PORT_READ(PHY_AFEC0) & 7428c2ecf20Sopenharmony_ci DSI_PORT_BIT(PHY_AFEC0_LATCH_ULPS)); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (ulps == ulps_currently_enabled) 7458c2ecf20Sopenharmony_ci return; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci DSI_PORT_WRITE(STAT, stat_ulps); 7488c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) | phyc_ulps); 7498c2ecf20Sopenharmony_ci ret = wait_for((DSI_PORT_READ(STAT) & stat_ulps) == stat_ulps, 200); 7508c2ecf20Sopenharmony_ci if (ret) { 7518c2ecf20Sopenharmony_ci dev_warn(&dsi->pdev->dev, 7528c2ecf20Sopenharmony_ci "Timeout waiting for DSI ULPS entry: STAT 0x%08x", 7538c2ecf20Sopenharmony_ci DSI_PORT_READ(STAT)); 7548c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); 7558c2ecf20Sopenharmony_ci vc4_dsi_latch_ulps(dsi, false); 7568c2ecf20Sopenharmony_ci return; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* The DSI module can't be disabled while the module is 7608c2ecf20Sopenharmony_ci * generating ULPS state. So, to be able to disable the 7618c2ecf20Sopenharmony_ci * module, we have the AFE latch the ULPS state and continue 7628c2ecf20Sopenharmony_ci * on to having the module enter STOP. 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci vc4_dsi_latch_ulps(dsi, ulps); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci DSI_PORT_WRITE(STAT, stat_stop); 7678c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); 7688c2ecf20Sopenharmony_ci ret = wait_for((DSI_PORT_READ(STAT) & stat_stop) == stat_stop, 200); 7698c2ecf20Sopenharmony_ci if (ret) { 7708c2ecf20Sopenharmony_ci dev_warn(&dsi->pdev->dev, 7718c2ecf20Sopenharmony_ci "Timeout waiting for DSI STOP entry: STAT 0x%08x", 7728c2ecf20Sopenharmony_ci DSI_PORT_READ(STAT)); 7738c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); 7748c2ecf20Sopenharmony_ci return; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cistatic u32 7798c2ecf20Sopenharmony_cidsi_hs_timing(u32 ui_ns, u32 ns, u32 ui) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci /* The HS timings have to be rounded up to a multiple of 8 7828c2ecf20Sopenharmony_ci * because we're using the byte clock. 7838c2ecf20Sopenharmony_ci */ 7848c2ecf20Sopenharmony_ci return roundup(ui + DIV_ROUND_UP(ns, ui_ns), 8); 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci/* ESC always runs at 100Mhz. */ 7888c2ecf20Sopenharmony_ci#define ESC_TIME_NS 10 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic u32 7918c2ecf20Sopenharmony_cidsi_esc_timing(u32 ns) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci return DIV_ROUND_UP(ns, ESC_TIME_NS); 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cistatic void vc4_dsi_encoder_disable(struct drm_encoder *encoder) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); 7998c2ecf20Sopenharmony_ci struct vc4_dsi *dsi = vc4_encoder->dsi; 8008c2ecf20Sopenharmony_ci struct device *dev = &dsi->pdev->dev; 8018c2ecf20Sopenharmony_ci struct drm_bridge *iter; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) { 8048c2ecf20Sopenharmony_ci if (iter->funcs->disable) 8058c2ecf20Sopenharmony_ci iter->funcs->disable(iter); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (iter == dsi->bridge) 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci vc4_dsi_ulps(dsi, true); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci list_for_each_entry_from(iter, &dsi->bridge_chain, chain_node) { 8148c2ecf20Sopenharmony_ci if (iter->funcs->post_disable) 8158c2ecf20Sopenharmony_ci iter->funcs->post_disable(iter); 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci clk_disable_unprepare(dsi->pll_phy_clock); 8198c2ecf20Sopenharmony_ci clk_disable_unprepare(dsi->escape_clock); 8208c2ecf20Sopenharmony_ci clk_disable_unprepare(dsi->pixel_clock); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci pm_runtime_put(dev); 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci/* Extends the mode's blank intervals to handle BCM2835's integer-only 8268c2ecf20Sopenharmony_ci * DSI PLL divider. 8278c2ecf20Sopenharmony_ci * 8288c2ecf20Sopenharmony_ci * On 2835, PLLD is set to 2Ghz, and may not be changed by the display 8298c2ecf20Sopenharmony_ci * driver since most peripherals are hanging off of the PLLD_PER 8308c2ecf20Sopenharmony_ci * divider. PLLD_DSI1, which drives our DSI bit clock (and therefore 8318c2ecf20Sopenharmony_ci * the pixel clock), only has an integer divider off of DSI. 8328c2ecf20Sopenharmony_ci * 8338c2ecf20Sopenharmony_ci * To get our panel mode to refresh at the expected 60Hz, we need to 8348c2ecf20Sopenharmony_ci * extend the horizontal blank time. This means we drive a 8358c2ecf20Sopenharmony_ci * higher-than-expected clock rate to the panel, but that's what the 8368c2ecf20Sopenharmony_ci * firmware does too. 8378c2ecf20Sopenharmony_ci */ 8388c2ecf20Sopenharmony_cistatic bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder, 8398c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 8408c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); 8438c2ecf20Sopenharmony_ci struct vc4_dsi *dsi = vc4_encoder->dsi; 8448c2ecf20Sopenharmony_ci struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock); 8458c2ecf20Sopenharmony_ci unsigned long parent_rate = clk_get_rate(phy_parent); 8468c2ecf20Sopenharmony_ci unsigned long pixel_clock_hz = mode->clock * 1000; 8478c2ecf20Sopenharmony_ci unsigned long pll_clock = pixel_clock_hz * dsi->divider; 8488c2ecf20Sopenharmony_ci int divider; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci /* Find what divider gets us a faster clock than the requested 8518c2ecf20Sopenharmony_ci * pixel clock. 8528c2ecf20Sopenharmony_ci */ 8538c2ecf20Sopenharmony_ci for (divider = 1; divider < 255; divider++) { 8548c2ecf20Sopenharmony_ci if (parent_rate / (divider + 1) < pll_clock) 8558c2ecf20Sopenharmony_ci break; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* Now that we've picked a PLL divider, calculate back to its 8598c2ecf20Sopenharmony_ci * pixel clock. 8608c2ecf20Sopenharmony_ci */ 8618c2ecf20Sopenharmony_ci pll_clock = parent_rate / divider; 8628c2ecf20Sopenharmony_ci pixel_clock_hz = pll_clock / dsi->divider; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci adjusted_mode->clock = pixel_clock_hz / 1000; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci /* Given the new pixel clock, adjust HFP to keep vrefresh the same. */ 8678c2ecf20Sopenharmony_ci adjusted_mode->htotal = adjusted_mode->clock * mode->htotal / 8688c2ecf20Sopenharmony_ci mode->clock; 8698c2ecf20Sopenharmony_ci adjusted_mode->hsync_end += adjusted_mode->htotal - mode->htotal; 8708c2ecf20Sopenharmony_ci adjusted_mode->hsync_start += adjusted_mode->htotal - mode->htotal; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return true; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic void vc4_dsi_encoder_enable(struct drm_encoder *encoder) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; 8788c2ecf20Sopenharmony_ci struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); 8798c2ecf20Sopenharmony_ci struct vc4_dsi *dsi = vc4_encoder->dsi; 8808c2ecf20Sopenharmony_ci struct device *dev = &dsi->pdev->dev; 8818c2ecf20Sopenharmony_ci bool debug_dump_regs = false; 8828c2ecf20Sopenharmony_ci struct drm_bridge *iter; 8838c2ecf20Sopenharmony_ci unsigned long hs_clock; 8848c2ecf20Sopenharmony_ci u32 ui_ns; 8858c2ecf20Sopenharmony_ci /* Minimum LP state duration in escape clock cycles. */ 8868c2ecf20Sopenharmony_ci u32 lpx = dsi_esc_timing(60); 8878c2ecf20Sopenharmony_ci unsigned long pixel_clock_hz = mode->clock * 1000; 8888c2ecf20Sopenharmony_ci unsigned long dsip_clock; 8898c2ecf20Sopenharmony_ci unsigned long phy_clock; 8908c2ecf20Sopenharmony_ci int ret; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 8938c2ecf20Sopenharmony_ci if (ret) { 8948c2ecf20Sopenharmony_ci DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi->variant->port); 8958c2ecf20Sopenharmony_ci return; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (debug_dump_regs) { 8998c2ecf20Sopenharmony_ci struct drm_printer p = drm_info_printer(&dsi->pdev->dev); 9008c2ecf20Sopenharmony_ci dev_info(&dsi->pdev->dev, "DSI regs before:\n"); 9018c2ecf20Sopenharmony_ci drm_print_regset32(&p, &dsi->regset); 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* Round up the clk_set_rate() request slightly, since 9058c2ecf20Sopenharmony_ci * PLLD_DSI1 is an integer divider and its rate selection will 9068c2ecf20Sopenharmony_ci * never round up. 9078c2ecf20Sopenharmony_ci */ 9088c2ecf20Sopenharmony_ci phy_clock = (pixel_clock_hz + 1000) * dsi->divider; 9098c2ecf20Sopenharmony_ci ret = clk_set_rate(dsi->pll_phy_clock, phy_clock); 9108c2ecf20Sopenharmony_ci if (ret) { 9118c2ecf20Sopenharmony_ci dev_err(&dsi->pdev->dev, 9128c2ecf20Sopenharmony_ci "Failed to set phy clock to %ld: %d\n", phy_clock, ret); 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* Reset the DSI and all its fifos. */ 9168c2ecf20Sopenharmony_ci DSI_PORT_WRITE(CTRL, 9178c2ecf20Sopenharmony_ci DSI_CTRL_SOFT_RESET_CFG | 9188c2ecf20Sopenharmony_ci DSI_PORT_BIT(CTRL_RESET_FIFOS)); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci DSI_PORT_WRITE(CTRL, 9218c2ecf20Sopenharmony_ci DSI_CTRL_HSDT_EOT_DISABLE | 9228c2ecf20Sopenharmony_ci DSI_CTRL_RX_LPDT_EOT_DISABLE); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* Clear all stat bits so we see what has happened during enable. */ 9258c2ecf20Sopenharmony_ci DSI_PORT_WRITE(STAT, DSI_PORT_READ(STAT)); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* Set AFE CTR00/CTR1 to release powerdown of analog. */ 9288c2ecf20Sopenharmony_ci if (dsi->variant->port == 0) { 9298c2ecf20Sopenharmony_ci u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) | 9308c2ecf20Sopenharmony_ci VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ)); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci if (dsi->lanes < 2) 9338c2ecf20Sopenharmony_ci afec0 |= DSI0_PHY_AFEC0_PD_DLANE1; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) 9368c2ecf20Sopenharmony_ci afec0 |= DSI0_PHY_AFEC0_RESET; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC0, afec0); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* AFEC reset hold time */ 9418c2ecf20Sopenharmony_ci mdelay(1); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC1, 9448c2ecf20Sopenharmony_ci VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE1) | 9458c2ecf20Sopenharmony_ci VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE0) | 9468c2ecf20Sopenharmony_ci VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_CLANE)); 9478c2ecf20Sopenharmony_ci } else { 9488c2ecf20Sopenharmony_ci u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) | 9498c2ecf20Sopenharmony_ci VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ) | 9508c2ecf20Sopenharmony_ci VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_CLANE) | 9518c2ecf20Sopenharmony_ci VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE0) | 9528c2ecf20Sopenharmony_ci VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE1) | 9538c2ecf20Sopenharmony_ci VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE2) | 9548c2ecf20Sopenharmony_ci VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE3)); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (dsi->lanes < 4) 9578c2ecf20Sopenharmony_ci afec0 |= DSI1_PHY_AFEC0_PD_DLANE3; 9588c2ecf20Sopenharmony_ci if (dsi->lanes < 3) 9598c2ecf20Sopenharmony_ci afec0 |= DSI1_PHY_AFEC0_PD_DLANE2; 9608c2ecf20Sopenharmony_ci if (dsi->lanes < 2) 9618c2ecf20Sopenharmony_ci afec0 |= DSI1_PHY_AFEC0_PD_DLANE1; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci afec0 |= DSI1_PHY_AFEC0_RESET; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC0, afec0); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC1, 0); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci /* AFEC reset hold time */ 9708c2ecf20Sopenharmony_ci mdelay(1); 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci ret = clk_prepare_enable(dsi->escape_clock); 9748c2ecf20Sopenharmony_ci if (ret) { 9758c2ecf20Sopenharmony_ci DRM_ERROR("Failed to turn on DSI escape clock: %d\n", ret); 9768c2ecf20Sopenharmony_ci return; 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci ret = clk_prepare_enable(dsi->pll_phy_clock); 9808c2ecf20Sopenharmony_ci if (ret) { 9818c2ecf20Sopenharmony_ci DRM_ERROR("Failed to turn on DSI PLL: %d\n", ret); 9828c2ecf20Sopenharmony_ci return; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci hs_clock = clk_get_rate(dsi->pll_phy_clock); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci /* Yes, we set the DSI0P/DSI1P pixel clock to the byte rate, 9888c2ecf20Sopenharmony_ci * not the pixel clock rate. DSIxP take from the APHY's byte, 9898c2ecf20Sopenharmony_ci * DDR2, or DDR4 clock (we use byte) and feed into the PV at 9908c2ecf20Sopenharmony_ci * that rate. Separately, a value derived from PIX_CLK_DIV 9918c2ecf20Sopenharmony_ci * and HS_CLKC is fed into the PV to divide down to the actual 9928c2ecf20Sopenharmony_ci * pixel clock for pushing pixels into DSI. 9938c2ecf20Sopenharmony_ci */ 9948c2ecf20Sopenharmony_ci dsip_clock = phy_clock / 8; 9958c2ecf20Sopenharmony_ci ret = clk_set_rate(dsi->pixel_clock, dsip_clock); 9968c2ecf20Sopenharmony_ci if (ret) { 9978c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set pixel clock to %ldHz: %d\n", 9988c2ecf20Sopenharmony_ci dsip_clock, ret); 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci ret = clk_prepare_enable(dsi->pixel_clock); 10028c2ecf20Sopenharmony_ci if (ret) { 10038c2ecf20Sopenharmony_ci DRM_ERROR("Failed to turn on DSI pixel clock: %d\n", ret); 10048c2ecf20Sopenharmony_ci return; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* How many ns one DSI unit interval is. Note that the clock 10088c2ecf20Sopenharmony_ci * is DDR, so there's an extra divide by 2. 10098c2ecf20Sopenharmony_ci */ 10108c2ecf20Sopenharmony_ci ui_ns = DIV_ROUND_UP(500000000, hs_clock); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci DSI_PORT_WRITE(HS_CLT0, 10138c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 262, 0), 10148c2ecf20Sopenharmony_ci DSI_HS_CLT0_CZERO) | 10158c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 0, 8), 10168c2ecf20Sopenharmony_ci DSI_HS_CLT0_CPRE) | 10178c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 38, 0), 10188c2ecf20Sopenharmony_ci DSI_HS_CLT0_CPREP)); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci DSI_PORT_WRITE(HS_CLT1, 10218c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 60, 0), 10228c2ecf20Sopenharmony_ci DSI_HS_CLT1_CTRAIL) | 10238c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 60, 52), 10248c2ecf20Sopenharmony_ci DSI_HS_CLT1_CPOST)); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci DSI_PORT_WRITE(HS_CLT2, 10278c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 1000000, 0), 10288c2ecf20Sopenharmony_ci DSI_HS_CLT2_WUP)); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci DSI_PORT_WRITE(HS_DLT3, 10318c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 100, 0), 10328c2ecf20Sopenharmony_ci DSI_HS_DLT3_EXIT) | 10338c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 105, 6), 10348c2ecf20Sopenharmony_ci DSI_HS_DLT3_ZERO) | 10358c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 40, 4), 10368c2ecf20Sopenharmony_ci DSI_HS_DLT3_PRE)); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci DSI_PORT_WRITE(HS_DLT4, 10398c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, lpx * ESC_TIME_NS, 0), 10408c2ecf20Sopenharmony_ci DSI_HS_DLT4_LPX) | 10418c2ecf20Sopenharmony_ci VC4_SET_FIELD(max(dsi_hs_timing(ui_ns, 0, 8), 10428c2ecf20Sopenharmony_ci dsi_hs_timing(ui_ns, 60, 4)), 10438c2ecf20Sopenharmony_ci DSI_HS_DLT4_TRAIL) | 10448c2ecf20Sopenharmony_ci VC4_SET_FIELD(0, DSI_HS_DLT4_ANLAT)); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* T_INIT is how long STOP is driven after power-up to 10478c2ecf20Sopenharmony_ci * indicate to the slave (also coming out of power-up) that 10488c2ecf20Sopenharmony_ci * master init is complete, and should be greater than the 10498c2ecf20Sopenharmony_ci * maximum of two value: T_INIT,MASTER and T_INIT,SLAVE. The 10508c2ecf20Sopenharmony_ci * D-PHY spec gives a minimum 100us for T_INIT,MASTER and 10518c2ecf20Sopenharmony_ci * T_INIT,SLAVE, while allowing protocols on top of it to give 10528c2ecf20Sopenharmony_ci * greater minimums. The vc4 firmware uses an extremely 10538c2ecf20Sopenharmony_ci * conservative 5ms, and we maintain that here. 10548c2ecf20Sopenharmony_ci */ 10558c2ecf20Sopenharmony_ci DSI_PORT_WRITE(HS_DLT5, VC4_SET_FIELD(dsi_hs_timing(ui_ns, 10568c2ecf20Sopenharmony_ci 5 * 1000 * 1000, 0), 10578c2ecf20Sopenharmony_ci DSI_HS_DLT5_INIT)); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci DSI_PORT_WRITE(HS_DLT6, 10608c2ecf20Sopenharmony_ci VC4_SET_FIELD(lpx * 5, DSI_HS_DLT6_TA_GET) | 10618c2ecf20Sopenharmony_ci VC4_SET_FIELD(lpx, DSI_HS_DLT6_TA_SURE) | 10628c2ecf20Sopenharmony_ci VC4_SET_FIELD(lpx * 4, DSI_HS_DLT6_TA_GO) | 10638c2ecf20Sopenharmony_ci VC4_SET_FIELD(lpx, DSI_HS_DLT6_LP_LPX)); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci DSI_PORT_WRITE(HS_DLT7, 10668c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi_esc_timing(1000000), 10678c2ecf20Sopenharmony_ci DSI_HS_DLT7_LP_WUP)); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHYC, 10708c2ecf20Sopenharmony_ci DSI_PHYC_DLANE0_ENABLE | 10718c2ecf20Sopenharmony_ci (dsi->lanes >= 2 ? DSI_PHYC_DLANE1_ENABLE : 0) | 10728c2ecf20Sopenharmony_ci (dsi->lanes >= 3 ? DSI_PHYC_DLANE2_ENABLE : 0) | 10738c2ecf20Sopenharmony_ci (dsi->lanes >= 4 ? DSI_PHYC_DLANE3_ENABLE : 0) | 10748c2ecf20Sopenharmony_ci DSI_PORT_BIT(PHYC_CLANE_ENABLE) | 10758c2ecf20Sopenharmony_ci ((dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 10768c2ecf20Sopenharmony_ci 0 : DSI_PORT_BIT(PHYC_HS_CLK_CONTINUOUS)) | 10778c2ecf20Sopenharmony_ci (dsi->variant->port == 0 ? 10788c2ecf20Sopenharmony_ci VC4_SET_FIELD(lpx - 1, DSI0_PHYC_ESC_CLK_LPDT) : 10798c2ecf20Sopenharmony_ci VC4_SET_FIELD(lpx - 1, DSI1_PHYC_ESC_CLK_LPDT))); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci DSI_PORT_WRITE(CTRL, 10828c2ecf20Sopenharmony_ci DSI_PORT_READ(CTRL) | 10838c2ecf20Sopenharmony_ci DSI_CTRL_CAL_BYTE); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* HS timeout in HS clock cycles: disabled. */ 10868c2ecf20Sopenharmony_ci DSI_PORT_WRITE(HSTX_TO_CNT, 0); 10878c2ecf20Sopenharmony_ci /* LP receive timeout in HS clocks. */ 10888c2ecf20Sopenharmony_ci DSI_PORT_WRITE(LPRX_TO_CNT, 0xffffff); 10898c2ecf20Sopenharmony_ci /* Bus turnaround timeout */ 10908c2ecf20Sopenharmony_ci DSI_PORT_WRITE(TA_TO_CNT, 100000); 10918c2ecf20Sopenharmony_ci /* Display reset sequence timeout */ 10928c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PR_TO_CNT, 100000); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci /* Set up DISP1 for transferring long command payloads through 10958c2ecf20Sopenharmony_ci * the pixfifo. 10968c2ecf20Sopenharmony_ci */ 10978c2ecf20Sopenharmony_ci DSI_PORT_WRITE(DISP1_CTRL, 10988c2ecf20Sopenharmony_ci VC4_SET_FIELD(DSI_DISP1_PFORMAT_32BIT_LE, 10998c2ecf20Sopenharmony_ci DSI_DISP1_PFORMAT) | 11008c2ecf20Sopenharmony_ci DSI_DISP1_ENABLE); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci /* Ungate the block. */ 11038c2ecf20Sopenharmony_ci if (dsi->variant->port == 0) 11048c2ecf20Sopenharmony_ci DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI0_CTRL_CTRL0); 11058c2ecf20Sopenharmony_ci else 11068c2ecf20Sopenharmony_ci DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI1_CTRL_EN); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci /* Bring AFE out of reset. */ 11098c2ecf20Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC0, 11108c2ecf20Sopenharmony_ci DSI_PORT_READ(PHY_AFEC0) & 11118c2ecf20Sopenharmony_ci ~DSI_PORT_BIT(PHY_AFEC0_RESET)); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci vc4_dsi_ulps(dsi, false); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) { 11168c2ecf20Sopenharmony_ci if (iter->funcs->pre_enable) 11178c2ecf20Sopenharmony_ci iter->funcs->pre_enable(iter); 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { 11218c2ecf20Sopenharmony_ci DSI_PORT_WRITE(DISP0_CTRL, 11228c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi->divider, 11238c2ecf20Sopenharmony_ci DSI_DISP0_PIX_CLK_DIV) | 11248c2ecf20Sopenharmony_ci VC4_SET_FIELD(dsi->format, DSI_DISP0_PFORMAT) | 11258c2ecf20Sopenharmony_ci VC4_SET_FIELD(DSI_DISP0_LP_STOP_PERFRAME, 11268c2ecf20Sopenharmony_ci DSI_DISP0_LP_STOP_CTRL) | 11278c2ecf20Sopenharmony_ci DSI_DISP0_ST_END | 11288c2ecf20Sopenharmony_ci DSI_DISP0_ENABLE); 11298c2ecf20Sopenharmony_ci } else { 11308c2ecf20Sopenharmony_ci DSI_PORT_WRITE(DISP0_CTRL, 11318c2ecf20Sopenharmony_ci DSI_DISP0_COMMAND_MODE | 11328c2ecf20Sopenharmony_ci DSI_DISP0_ENABLE); 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci list_for_each_entry(iter, &dsi->bridge_chain, chain_node) { 11368c2ecf20Sopenharmony_ci if (iter->funcs->enable) 11378c2ecf20Sopenharmony_ci iter->funcs->enable(iter); 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (debug_dump_regs) { 11418c2ecf20Sopenharmony_ci struct drm_printer p = drm_info_printer(&dsi->pdev->dev); 11428c2ecf20Sopenharmony_ci dev_info(&dsi->pdev->dev, "DSI regs after:\n"); 11438c2ecf20Sopenharmony_ci drm_print_regset32(&p, &dsi->regset); 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, 11488c2ecf20Sopenharmony_ci const struct mipi_dsi_msg *msg) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci struct vc4_dsi *dsi = host_to_dsi(host); 11518c2ecf20Sopenharmony_ci struct mipi_dsi_packet packet; 11528c2ecf20Sopenharmony_ci u32 pkth = 0, pktc = 0; 11538c2ecf20Sopenharmony_ci int i, ret; 11548c2ecf20Sopenharmony_ci bool is_long = mipi_dsi_packet_format_is_long(msg->type); 11558c2ecf20Sopenharmony_ci u32 cmd_fifo_len = 0, pix_fifo_len = 0; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci mipi_dsi_create_packet(&packet, msg); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci pkth |= VC4_SET_FIELD(packet.header[0], DSI_TXPKT1H_BC_DT); 11608c2ecf20Sopenharmony_ci pkth |= VC4_SET_FIELD(packet.header[1] | 11618c2ecf20Sopenharmony_ci (packet.header[2] << 8), 11628c2ecf20Sopenharmony_ci DSI_TXPKT1H_BC_PARAM); 11638c2ecf20Sopenharmony_ci if (is_long) { 11648c2ecf20Sopenharmony_ci /* Divide data across the various FIFOs we have available. 11658c2ecf20Sopenharmony_ci * The command FIFO takes byte-oriented data, but is of 11668c2ecf20Sopenharmony_ci * limited size. The pixel FIFO (never actually used for 11678c2ecf20Sopenharmony_ci * pixel data in reality) is word oriented, and substantially 11688c2ecf20Sopenharmony_ci * larger. So, we use the pixel FIFO for most of the data, 11698c2ecf20Sopenharmony_ci * sending the residual bytes in the command FIFO at the start. 11708c2ecf20Sopenharmony_ci * 11718c2ecf20Sopenharmony_ci * With this arrangement, the command FIFO will never get full. 11728c2ecf20Sopenharmony_ci */ 11738c2ecf20Sopenharmony_ci if (packet.payload_length <= 16) { 11748c2ecf20Sopenharmony_ci cmd_fifo_len = packet.payload_length; 11758c2ecf20Sopenharmony_ci pix_fifo_len = 0; 11768c2ecf20Sopenharmony_ci } else { 11778c2ecf20Sopenharmony_ci cmd_fifo_len = (packet.payload_length % 11788c2ecf20Sopenharmony_ci DSI_PIX_FIFO_WIDTH); 11798c2ecf20Sopenharmony_ci pix_fifo_len = ((packet.payload_length - cmd_fifo_len) / 11808c2ecf20Sopenharmony_ci DSI_PIX_FIFO_WIDTH); 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci WARN_ON_ONCE(pix_fifo_len >= DSI_PIX_FIFO_DEPTH); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci pkth |= VC4_SET_FIELD(cmd_fifo_len, DSI_TXPKT1H_BC_CMDFIFO); 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (msg->rx_len) { 11898c2ecf20Sopenharmony_ci pktc |= VC4_SET_FIELD(DSI_TXPKT1C_CMD_CTRL_RX, 11908c2ecf20Sopenharmony_ci DSI_TXPKT1C_CMD_CTRL); 11918c2ecf20Sopenharmony_ci } else { 11928c2ecf20Sopenharmony_ci pktc |= VC4_SET_FIELD(DSI_TXPKT1C_CMD_CTRL_TX, 11938c2ecf20Sopenharmony_ci DSI_TXPKT1C_CMD_CTRL); 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci for (i = 0; i < cmd_fifo_len; i++) 11978c2ecf20Sopenharmony_ci DSI_PORT_WRITE(TXPKT_CMD_FIFO, packet.payload[i]); 11988c2ecf20Sopenharmony_ci for (i = 0; i < pix_fifo_len; i++) { 11998c2ecf20Sopenharmony_ci const u8 *pix = packet.payload + cmd_fifo_len + i * 4; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci DSI_PORT_WRITE(TXPKT_PIX_FIFO, 12028c2ecf20Sopenharmony_ci pix[0] | 12038c2ecf20Sopenharmony_ci pix[1] << 8 | 12048c2ecf20Sopenharmony_ci pix[2] << 16 | 12058c2ecf20Sopenharmony_ci pix[3] << 24); 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci if (msg->flags & MIPI_DSI_MSG_USE_LPM) 12098c2ecf20Sopenharmony_ci pktc |= DSI_TXPKT1C_CMD_MODE_LP; 12108c2ecf20Sopenharmony_ci if (is_long) 12118c2ecf20Sopenharmony_ci pktc |= DSI_TXPKT1C_CMD_TYPE_LONG; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* Send one copy of the packet. Larger repeats are used for pixel 12148c2ecf20Sopenharmony_ci * data in command mode. 12158c2ecf20Sopenharmony_ci */ 12168c2ecf20Sopenharmony_ci pktc |= VC4_SET_FIELD(1, DSI_TXPKT1C_CMD_REPEAT); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci pktc |= DSI_TXPKT1C_CMD_EN; 12198c2ecf20Sopenharmony_ci if (pix_fifo_len) { 12208c2ecf20Sopenharmony_ci pktc |= VC4_SET_FIELD(DSI_TXPKT1C_DISPLAY_NO_SECONDARY, 12218c2ecf20Sopenharmony_ci DSI_TXPKT1C_DISPLAY_NO); 12228c2ecf20Sopenharmony_ci } else { 12238c2ecf20Sopenharmony_ci pktc |= VC4_SET_FIELD(DSI_TXPKT1C_DISPLAY_NO_SHORT, 12248c2ecf20Sopenharmony_ci DSI_TXPKT1C_DISPLAY_NO); 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* Enable the appropriate interrupt for the transfer completion. */ 12288c2ecf20Sopenharmony_ci dsi->xfer_result = 0; 12298c2ecf20Sopenharmony_ci reinit_completion(&dsi->xfer_completion); 12308c2ecf20Sopenharmony_ci if (dsi->variant->port == 0) { 12318c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_STAT, 12328c2ecf20Sopenharmony_ci DSI0_INT_CMDC_DONE_MASK | DSI1_INT_PHY_DIR_RTF); 12338c2ecf20Sopenharmony_ci if (msg->rx_len) { 12348c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_EN, (DSI0_INTERRUPTS_ALWAYS_ENABLED | 12358c2ecf20Sopenharmony_ci DSI0_INT_PHY_DIR_RTF)); 12368c2ecf20Sopenharmony_ci } else { 12378c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_EN, 12388c2ecf20Sopenharmony_ci (DSI0_INTERRUPTS_ALWAYS_ENABLED | 12398c2ecf20Sopenharmony_ci VC4_SET_FIELD(DSI0_INT_CMDC_DONE_NO_REPEAT, 12408c2ecf20Sopenharmony_ci DSI0_INT_CMDC_DONE))); 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci } else { 12438c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_STAT, 12448c2ecf20Sopenharmony_ci DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF); 12458c2ecf20Sopenharmony_ci if (msg->rx_len) { 12468c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | 12478c2ecf20Sopenharmony_ci DSI1_INT_PHY_DIR_RTF)); 12488c2ecf20Sopenharmony_ci } else { 12498c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | 12508c2ecf20Sopenharmony_ci DSI1_INT_TXPKT1_DONE)); 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci /* Send the packet. */ 12558c2ecf20Sopenharmony_ci DSI_PORT_WRITE(TXPKT1H, pkth); 12568c2ecf20Sopenharmony_ci DSI_PORT_WRITE(TXPKT1C, pktc); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&dsi->xfer_completion, 12598c2ecf20Sopenharmony_ci msecs_to_jiffies(1000))) { 12608c2ecf20Sopenharmony_ci dev_err(&dsi->pdev->dev, "transfer interrupt wait timeout"); 12618c2ecf20Sopenharmony_ci dev_err(&dsi->pdev->dev, "instat: 0x%08x\n", 12628c2ecf20Sopenharmony_ci DSI_PORT_READ(INT_STAT)); 12638c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 12648c2ecf20Sopenharmony_ci } else { 12658c2ecf20Sopenharmony_ci ret = dsi->xfer_result; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED)); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci if (ret) 12718c2ecf20Sopenharmony_ci goto reset_fifo_and_return; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci if (ret == 0 && msg->rx_len) { 12748c2ecf20Sopenharmony_ci u32 rxpkt1h = DSI_PORT_READ(RXPKT1H); 12758c2ecf20Sopenharmony_ci u8 *msg_rx = msg->rx_buf; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (rxpkt1h & DSI_RXPKT1H_PKT_TYPE_LONG) { 12788c2ecf20Sopenharmony_ci u32 rxlen = VC4_GET_FIELD(rxpkt1h, 12798c2ecf20Sopenharmony_ci DSI_RXPKT1H_BC_PARAM); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (rxlen != msg->rx_len) { 12828c2ecf20Sopenharmony_ci DRM_ERROR("DSI returned %db, expecting %db\n", 12838c2ecf20Sopenharmony_ci rxlen, (int)msg->rx_len); 12848c2ecf20Sopenharmony_ci ret = -ENXIO; 12858c2ecf20Sopenharmony_ci goto reset_fifo_and_return; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci for (i = 0; i < msg->rx_len; i++) 12898c2ecf20Sopenharmony_ci msg_rx[i] = DSI_READ(DSI1_RXPKT_FIFO); 12908c2ecf20Sopenharmony_ci } else { 12918c2ecf20Sopenharmony_ci /* FINISHME: Handle AWER */ 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci msg_rx[0] = VC4_GET_FIELD(rxpkt1h, 12948c2ecf20Sopenharmony_ci DSI_RXPKT1H_SHORT_0); 12958c2ecf20Sopenharmony_ci if (msg->rx_len > 1) { 12968c2ecf20Sopenharmony_ci msg_rx[1] = VC4_GET_FIELD(rxpkt1h, 12978c2ecf20Sopenharmony_ci DSI_RXPKT1H_SHORT_1); 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci return ret; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cireset_fifo_and_return: 13058c2ecf20Sopenharmony_ci DRM_ERROR("DSI transfer failed, resetting: %d\n", ret); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci DSI_PORT_WRITE(TXPKT1C, DSI_PORT_READ(TXPKT1C) & ~DSI_TXPKT1C_CMD_EN); 13088c2ecf20Sopenharmony_ci udelay(1); 13098c2ecf20Sopenharmony_ci DSI_PORT_WRITE(CTRL, 13108c2ecf20Sopenharmony_ci DSI_PORT_READ(CTRL) | 13118c2ecf20Sopenharmony_ci DSI_PORT_BIT(CTRL_RESET_FIFOS)); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci DSI_PORT_WRITE(TXPKT1C, 0); 13148c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED)); 13158c2ecf20Sopenharmony_ci return ret; 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_cistatic int vc4_dsi_host_attach(struct mipi_dsi_host *host, 13198c2ecf20Sopenharmony_ci struct mipi_dsi_device *device) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci struct vc4_dsi *dsi = host_to_dsi(host); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci dsi->lanes = device->lanes; 13248c2ecf20Sopenharmony_ci dsi->channel = device->channel; 13258c2ecf20Sopenharmony_ci dsi->mode_flags = device->mode_flags; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci switch (device->format) { 13288c2ecf20Sopenharmony_ci case MIPI_DSI_FMT_RGB888: 13298c2ecf20Sopenharmony_ci dsi->format = DSI_PFORMAT_RGB888; 13308c2ecf20Sopenharmony_ci dsi->divider = 24 / dsi->lanes; 13318c2ecf20Sopenharmony_ci break; 13328c2ecf20Sopenharmony_ci case MIPI_DSI_FMT_RGB666: 13338c2ecf20Sopenharmony_ci dsi->format = DSI_PFORMAT_RGB666; 13348c2ecf20Sopenharmony_ci dsi->divider = 24 / dsi->lanes; 13358c2ecf20Sopenharmony_ci break; 13368c2ecf20Sopenharmony_ci case MIPI_DSI_FMT_RGB666_PACKED: 13378c2ecf20Sopenharmony_ci dsi->format = DSI_PFORMAT_RGB666_PACKED; 13388c2ecf20Sopenharmony_ci dsi->divider = 18 / dsi->lanes; 13398c2ecf20Sopenharmony_ci break; 13408c2ecf20Sopenharmony_ci case MIPI_DSI_FMT_RGB565: 13418c2ecf20Sopenharmony_ci dsi->format = DSI_PFORMAT_RGB565; 13428c2ecf20Sopenharmony_ci dsi->divider = 16 / dsi->lanes; 13438c2ecf20Sopenharmony_ci break; 13448c2ecf20Sopenharmony_ci default: 13458c2ecf20Sopenharmony_ci dev_err(&dsi->pdev->dev, "Unknown DSI format: %d.\n", 13468c2ecf20Sopenharmony_ci dsi->format); 13478c2ecf20Sopenharmony_ci return 0; 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) { 13518c2ecf20Sopenharmony_ci dev_err(&dsi->pdev->dev, 13528c2ecf20Sopenharmony_ci "Only VIDEO mode panels supported currently.\n"); 13538c2ecf20Sopenharmony_ci return 0; 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci return 0; 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic int vc4_dsi_host_detach(struct mipi_dsi_host *host, 13608c2ecf20Sopenharmony_ci struct mipi_dsi_device *device) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci return 0; 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_cistatic const struct mipi_dsi_host_ops vc4_dsi_host_ops = { 13668c2ecf20Sopenharmony_ci .attach = vc4_dsi_host_attach, 13678c2ecf20Sopenharmony_ci .detach = vc4_dsi_host_detach, 13688c2ecf20Sopenharmony_ci .transfer = vc4_dsi_host_transfer, 13698c2ecf20Sopenharmony_ci}; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = { 13728c2ecf20Sopenharmony_ci .disable = vc4_dsi_encoder_disable, 13738c2ecf20Sopenharmony_ci .enable = vc4_dsi_encoder_enable, 13748c2ecf20Sopenharmony_ci .mode_fixup = vc4_dsi_encoder_mode_fixup, 13758c2ecf20Sopenharmony_ci}; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_cistatic const struct vc4_dsi_variant bcm2835_dsi1_variant = { 13788c2ecf20Sopenharmony_ci .port = 1, 13798c2ecf20Sopenharmony_ci .broken_axi_workaround = true, 13808c2ecf20Sopenharmony_ci .debugfs_name = "dsi1_regs", 13818c2ecf20Sopenharmony_ci .regs = dsi1_regs, 13828c2ecf20Sopenharmony_ci .nregs = ARRAY_SIZE(dsi1_regs), 13838c2ecf20Sopenharmony_ci}; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_cistatic const struct of_device_id vc4_dsi_dt_match[] = { 13868c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm2835-dsi1", &bcm2835_dsi1_variant }, 13878c2ecf20Sopenharmony_ci {} 13888c2ecf20Sopenharmony_ci}; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic void dsi_handle_error(struct vc4_dsi *dsi, 13918c2ecf20Sopenharmony_ci irqreturn_t *ret, u32 stat, u32 bit, 13928c2ecf20Sopenharmony_ci const char *type) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci if (!(stat & bit)) 13958c2ecf20Sopenharmony_ci return; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci DRM_ERROR("DSI%d: %s error\n", dsi->variant->port, type); 13988c2ecf20Sopenharmony_ci *ret = IRQ_HANDLED; 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci/* 14028c2ecf20Sopenharmony_ci * Initial handler for port 1 where we need the reg_dma workaround. 14038c2ecf20Sopenharmony_ci * The register DMA writes sleep, so we can't do it in the top half. 14048c2ecf20Sopenharmony_ci * Instead we use IRQF_ONESHOT so that the IRQ gets disabled in the 14058c2ecf20Sopenharmony_ci * parent interrupt contrller until our interrupt thread is done. 14068c2ecf20Sopenharmony_ci */ 14078c2ecf20Sopenharmony_cistatic irqreturn_t vc4_dsi_irq_defer_to_thread_handler(int irq, void *data) 14088c2ecf20Sopenharmony_ci{ 14098c2ecf20Sopenharmony_ci struct vc4_dsi *dsi = data; 14108c2ecf20Sopenharmony_ci u32 stat = DSI_PORT_READ(INT_STAT); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci if (!stat) 14138c2ecf20Sopenharmony_ci return IRQ_NONE; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci/* 14198c2ecf20Sopenharmony_ci * Normal IRQ handler for port 0, or the threaded IRQ handler for port 14208c2ecf20Sopenharmony_ci * 1 where we need the reg_dma workaround. 14218c2ecf20Sopenharmony_ci */ 14228c2ecf20Sopenharmony_cistatic irqreturn_t vc4_dsi_irq_handler(int irq, void *data) 14238c2ecf20Sopenharmony_ci{ 14248c2ecf20Sopenharmony_ci struct vc4_dsi *dsi = data; 14258c2ecf20Sopenharmony_ci u32 stat = DSI_PORT_READ(INT_STAT); 14268c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_STAT, stat); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 14318c2ecf20Sopenharmony_ci DSI_PORT_BIT(INT_ERR_SYNC_ESC), "LPDT sync"); 14328c2ecf20Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 14338c2ecf20Sopenharmony_ci DSI_PORT_BIT(INT_ERR_CONTROL), "data lane 0 sequence"); 14348c2ecf20Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 14358c2ecf20Sopenharmony_ci DSI_PORT_BIT(INT_ERR_CONT_LP0), "LP0 contention"); 14368c2ecf20Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 14378c2ecf20Sopenharmony_ci DSI_PORT_BIT(INT_ERR_CONT_LP1), "LP1 contention"); 14388c2ecf20Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 14398c2ecf20Sopenharmony_ci DSI_PORT_BIT(INT_HSTX_TO), "HSTX timeout"); 14408c2ecf20Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 14418c2ecf20Sopenharmony_ci DSI_PORT_BIT(INT_LPRX_TO), "LPRX timeout"); 14428c2ecf20Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 14438c2ecf20Sopenharmony_ci DSI_PORT_BIT(INT_TA_TO), "turnaround timeout"); 14448c2ecf20Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 14458c2ecf20Sopenharmony_ci DSI_PORT_BIT(INT_PR_TO), "peripheral reset timeout"); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (stat & ((dsi->variant->port ? DSI1_INT_TXPKT1_DONE : 14488c2ecf20Sopenharmony_ci DSI0_INT_CMDC_DONE_MASK) | 14498c2ecf20Sopenharmony_ci DSI_PORT_BIT(INT_PHY_DIR_RTF))) { 14508c2ecf20Sopenharmony_ci complete(&dsi->xfer_completion); 14518c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 14528c2ecf20Sopenharmony_ci } else if (stat & DSI_PORT_BIT(INT_HSTX_TO)) { 14538c2ecf20Sopenharmony_ci complete(&dsi->xfer_completion); 14548c2ecf20Sopenharmony_ci dsi->xfer_result = -ETIMEDOUT; 14558c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci return ret; 14598c2ecf20Sopenharmony_ci} 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci/** 14628c2ecf20Sopenharmony_ci * vc4_dsi_init_phy_clocks - Exposes clocks generated by the analog 14638c2ecf20Sopenharmony_ci * PHY that are consumed by CPRMAN (clk-bcm2835.c). 14648c2ecf20Sopenharmony_ci * @dsi: DSI encoder 14658c2ecf20Sopenharmony_ci */ 14668c2ecf20Sopenharmony_cistatic int 14678c2ecf20Sopenharmony_civc4_dsi_init_phy_clocks(struct vc4_dsi *dsi) 14688c2ecf20Sopenharmony_ci{ 14698c2ecf20Sopenharmony_ci struct device *dev = &dsi->pdev->dev; 14708c2ecf20Sopenharmony_ci const char *parent_name = __clk_get_name(dsi->pll_phy_clock); 14718c2ecf20Sopenharmony_ci static const struct { 14728c2ecf20Sopenharmony_ci const char *name; 14738c2ecf20Sopenharmony_ci int div; 14748c2ecf20Sopenharmony_ci } phy_clocks[] = { 14758c2ecf20Sopenharmony_ci { "byte", 8 }, 14768c2ecf20Sopenharmony_ci { "ddr2", 4 }, 14778c2ecf20Sopenharmony_ci { "ddr", 2 }, 14788c2ecf20Sopenharmony_ci }; 14798c2ecf20Sopenharmony_ci int i; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci dsi->clk_onecell = devm_kzalloc(dev, 14828c2ecf20Sopenharmony_ci sizeof(*dsi->clk_onecell) + 14838c2ecf20Sopenharmony_ci ARRAY_SIZE(phy_clocks) * 14848c2ecf20Sopenharmony_ci sizeof(struct clk_hw *), 14858c2ecf20Sopenharmony_ci GFP_KERNEL); 14868c2ecf20Sopenharmony_ci if (!dsi->clk_onecell) 14878c2ecf20Sopenharmony_ci return -ENOMEM; 14888c2ecf20Sopenharmony_ci dsi->clk_onecell->num = ARRAY_SIZE(phy_clocks); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(phy_clocks); i++) { 14918c2ecf20Sopenharmony_ci struct clk_fixed_factor *fix = &dsi->phy_clocks[i]; 14928c2ecf20Sopenharmony_ci struct clk_init_data init; 14938c2ecf20Sopenharmony_ci char clk_name[16]; 14948c2ecf20Sopenharmony_ci int ret; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci snprintf(clk_name, sizeof(clk_name), 14978c2ecf20Sopenharmony_ci "dsi%u_%s", dsi->variant->port, phy_clocks[i].name); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci /* We just use core fixed factor clock ops for the PHY 15008c2ecf20Sopenharmony_ci * clocks. The clocks are actually gated by the 15018c2ecf20Sopenharmony_ci * PHY_AFEC0_DDRCLK_EN bits, which we should be 15028c2ecf20Sopenharmony_ci * setting if we use the DDR/DDR2 clocks. However, 15038c2ecf20Sopenharmony_ci * vc4_dsi_encoder_enable() is setting up both AFEC0, 15048c2ecf20Sopenharmony_ci * setting both our parent DSI PLL's rate and this 15058c2ecf20Sopenharmony_ci * clock's rate, so it knows if DDR/DDR2 are going to 15068c2ecf20Sopenharmony_ci * be used and could enable the gates itself. 15078c2ecf20Sopenharmony_ci */ 15088c2ecf20Sopenharmony_ci fix->mult = 1; 15098c2ecf20Sopenharmony_ci fix->div = phy_clocks[i].div; 15108c2ecf20Sopenharmony_ci fix->hw.init = &init; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci memset(&init, 0, sizeof(init)); 15138c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 15148c2ecf20Sopenharmony_ci init.num_parents = 1; 15158c2ecf20Sopenharmony_ci init.name = clk_name; 15168c2ecf20Sopenharmony_ci init.ops = &clk_fixed_factor_ops; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(dev, &fix->hw); 15198c2ecf20Sopenharmony_ci if (ret) 15208c2ecf20Sopenharmony_ci return ret; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci dsi->clk_onecell->hws[i] = &fix->hw; 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci return of_clk_add_hw_provider(dev->of_node, 15268c2ecf20Sopenharmony_ci of_clk_hw_onecell_get, 15278c2ecf20Sopenharmony_ci dsi->clk_onecell); 15288c2ecf20Sopenharmony_ci} 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_cistatic int vc4_dsi_bind(struct device *dev, struct device *master, void *data) 15318c2ecf20Sopenharmony_ci{ 15328c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 15338c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(master); 15348c2ecf20Sopenharmony_ci struct vc4_dsi *dsi = dev_get_drvdata(dev); 15358c2ecf20Sopenharmony_ci struct vc4_dsi_encoder *vc4_dsi_encoder; 15368c2ecf20Sopenharmony_ci struct drm_panel *panel; 15378c2ecf20Sopenharmony_ci const struct of_device_id *match; 15388c2ecf20Sopenharmony_ci dma_cap_mask_t dma_mask; 15398c2ecf20Sopenharmony_ci int ret; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci match = of_match_device(vc4_dsi_dt_match, dev); 15428c2ecf20Sopenharmony_ci if (!match) 15438c2ecf20Sopenharmony_ci return -ENODEV; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci dsi->variant = match->data; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci vc4_dsi_encoder = devm_kzalloc(dev, sizeof(*vc4_dsi_encoder), 15488c2ecf20Sopenharmony_ci GFP_KERNEL); 15498c2ecf20Sopenharmony_ci if (!vc4_dsi_encoder) 15508c2ecf20Sopenharmony_ci return -ENOMEM; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dsi->bridge_chain); 15538c2ecf20Sopenharmony_ci vc4_dsi_encoder->base.type = dsi->variant->port ? 15548c2ecf20Sopenharmony_ci VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0; 15558c2ecf20Sopenharmony_ci vc4_dsi_encoder->dsi = dsi; 15568c2ecf20Sopenharmony_ci dsi->encoder = &vc4_dsi_encoder->base.base; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci dsi->regs = vc4_ioremap_regs(pdev, 0); 15598c2ecf20Sopenharmony_ci if (IS_ERR(dsi->regs)) 15608c2ecf20Sopenharmony_ci return PTR_ERR(dsi->regs); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci dsi->regset.base = dsi->regs; 15638c2ecf20Sopenharmony_ci dsi->regset.regs = dsi->variant->regs; 15648c2ecf20Sopenharmony_ci dsi->regset.nregs = dsi->variant->nregs; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci if (DSI_PORT_READ(ID) != DSI_ID_VALUE) { 15678c2ecf20Sopenharmony_ci dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n", 15688c2ecf20Sopenharmony_ci DSI_PORT_READ(ID), DSI_ID_VALUE); 15698c2ecf20Sopenharmony_ci return -ENODEV; 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci /* DSI1 has a broken AXI slave that doesn't respond to writes 15738c2ecf20Sopenharmony_ci * from the ARM. It does handle writes from the DMA engine, 15748c2ecf20Sopenharmony_ci * so set up a channel for talking to it. 15758c2ecf20Sopenharmony_ci */ 15768c2ecf20Sopenharmony_ci if (dsi->variant->broken_axi_workaround) { 15778c2ecf20Sopenharmony_ci dsi->reg_dma_mem = dma_alloc_coherent(dev, 4, 15788c2ecf20Sopenharmony_ci &dsi->reg_dma_paddr, 15798c2ecf20Sopenharmony_ci GFP_KERNEL); 15808c2ecf20Sopenharmony_ci if (!dsi->reg_dma_mem) { 15818c2ecf20Sopenharmony_ci DRM_ERROR("Failed to get DMA memory\n"); 15828c2ecf20Sopenharmony_ci return -ENOMEM; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci dma_cap_zero(dma_mask); 15868c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dma_mask); 15878c2ecf20Sopenharmony_ci dsi->reg_dma_chan = dma_request_chan_by_mask(&dma_mask); 15888c2ecf20Sopenharmony_ci if (IS_ERR(dsi->reg_dma_chan)) { 15898c2ecf20Sopenharmony_ci ret = PTR_ERR(dsi->reg_dma_chan); 15908c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 15918c2ecf20Sopenharmony_ci DRM_ERROR("Failed to get DMA channel: %d\n", 15928c2ecf20Sopenharmony_ci ret); 15938c2ecf20Sopenharmony_ci return ret; 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci /* Get the physical address of the device's registers. The 15978c2ecf20Sopenharmony_ci * struct resource for the regs gives us the bus address 15988c2ecf20Sopenharmony_ci * instead. 15998c2ecf20Sopenharmony_ci */ 16008c2ecf20Sopenharmony_ci dsi->reg_paddr = be32_to_cpup(of_get_address(dev->of_node, 16018c2ecf20Sopenharmony_ci 0, NULL, NULL)); 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci init_completion(&dsi->xfer_completion); 16058c2ecf20Sopenharmony_ci /* At startup enable error-reporting interrupts and nothing else. */ 16068c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); 16078c2ecf20Sopenharmony_ci /* Clear any existing interrupt state. */ 16088c2ecf20Sopenharmony_ci DSI_PORT_WRITE(INT_STAT, DSI_PORT_READ(INT_STAT)); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci if (dsi->reg_dma_mem) 16118c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), 16128c2ecf20Sopenharmony_ci vc4_dsi_irq_defer_to_thread_handler, 16138c2ecf20Sopenharmony_ci vc4_dsi_irq_handler, 16148c2ecf20Sopenharmony_ci IRQF_ONESHOT, 16158c2ecf20Sopenharmony_ci "vc4 dsi", dsi); 16168c2ecf20Sopenharmony_ci else 16178c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, platform_get_irq(pdev, 0), 16188c2ecf20Sopenharmony_ci vc4_dsi_irq_handler, 0, "vc4 dsi", dsi); 16198c2ecf20Sopenharmony_ci if (ret) { 16208c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 16218c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get interrupt: %d\n", ret); 16228c2ecf20Sopenharmony_ci return ret; 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci dsi->escape_clock = devm_clk_get(dev, "escape"); 16268c2ecf20Sopenharmony_ci if (IS_ERR(dsi->escape_clock)) { 16278c2ecf20Sopenharmony_ci ret = PTR_ERR(dsi->escape_clock); 16288c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 16298c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get escape clock: %d\n", ret); 16308c2ecf20Sopenharmony_ci return ret; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci dsi->pll_phy_clock = devm_clk_get(dev, "phy"); 16348c2ecf20Sopenharmony_ci if (IS_ERR(dsi->pll_phy_clock)) { 16358c2ecf20Sopenharmony_ci ret = PTR_ERR(dsi->pll_phy_clock); 16368c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 16378c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get phy clock: %d\n", ret); 16388c2ecf20Sopenharmony_ci return ret; 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci dsi->pixel_clock = devm_clk_get(dev, "pixel"); 16428c2ecf20Sopenharmony_ci if (IS_ERR(dsi->pixel_clock)) { 16438c2ecf20Sopenharmony_ci ret = PTR_ERR(dsi->pixel_clock); 16448c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 16458c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get pixel clock: %d\n", ret); 16468c2ecf20Sopenharmony_ci return ret; 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, 16508c2ecf20Sopenharmony_ci &panel, &dsi->bridge); 16518c2ecf20Sopenharmony_ci if (ret) { 16528c2ecf20Sopenharmony_ci /* If the bridge or panel pointed by dev->of_node is not 16538c2ecf20Sopenharmony_ci * enabled, just return 0 here so that we don't prevent the DRM 16548c2ecf20Sopenharmony_ci * dev from being registered. Of course that means the DSI 16558c2ecf20Sopenharmony_ci * encoder won't be exposed, but that's not a problem since 16568c2ecf20Sopenharmony_ci * nothing is connected to it. 16578c2ecf20Sopenharmony_ci */ 16588c2ecf20Sopenharmony_ci if (ret == -ENODEV) 16598c2ecf20Sopenharmony_ci return 0; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci return ret; 16628c2ecf20Sopenharmony_ci } 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci if (panel) { 16658c2ecf20Sopenharmony_ci dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel, 16668c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 16678c2ecf20Sopenharmony_ci if (IS_ERR(dsi->bridge)) 16688c2ecf20Sopenharmony_ci return PTR_ERR(dsi->bridge); 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci /* The esc clock rate is supposed to always be 100Mhz. */ 16728c2ecf20Sopenharmony_ci ret = clk_set_rate(dsi->escape_clock, 100 * 1000000); 16738c2ecf20Sopenharmony_ci if (ret) { 16748c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set esc clock: %d\n", ret); 16758c2ecf20Sopenharmony_ci return ret; 16768c2ecf20Sopenharmony_ci } 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci ret = vc4_dsi_init_phy_clocks(dsi); 16798c2ecf20Sopenharmony_ci if (ret) 16808c2ecf20Sopenharmony_ci return ret; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci drm_simple_encoder_init(drm, dsi->encoder, DRM_MODE_ENCODER_DSI); 16838c2ecf20Sopenharmony_ci drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL, 0); 16868c2ecf20Sopenharmony_ci if (ret) { 16878c2ecf20Sopenharmony_ci dev_err(dev, "bridge attach failed: %d\n", ret); 16888c2ecf20Sopenharmony_ci return ret; 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci /* Disable the atomic helper calls into the bridge. We 16918c2ecf20Sopenharmony_ci * manually call the bridge pre_enable / enable / etc. calls 16928c2ecf20Sopenharmony_ci * from our driver, since we need to sequence them within the 16938c2ecf20Sopenharmony_ci * encoder's enable/disable paths. 16948c2ecf20Sopenharmony_ci */ 16958c2ecf20Sopenharmony_ci list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain); 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset); 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci return 0; 17028c2ecf20Sopenharmony_ci} 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_cistatic void vc4_dsi_unbind(struct device *dev, struct device *master, 17058c2ecf20Sopenharmony_ci void *data) 17068c2ecf20Sopenharmony_ci{ 17078c2ecf20Sopenharmony_ci struct vc4_dsi *dsi = dev_get_drvdata(dev); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci if (dsi->bridge) 17108c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci /* 17138c2ecf20Sopenharmony_ci * Restore the bridge_chain so the bridge detach procedure can happen 17148c2ecf20Sopenharmony_ci * normally. 17158c2ecf20Sopenharmony_ci */ 17168c2ecf20Sopenharmony_ci list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain); 17178c2ecf20Sopenharmony_ci drm_encoder_cleanup(dsi->encoder); 17188c2ecf20Sopenharmony_ci} 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_cistatic const struct component_ops vc4_dsi_ops = { 17218c2ecf20Sopenharmony_ci .bind = vc4_dsi_bind, 17228c2ecf20Sopenharmony_ci .unbind = vc4_dsi_unbind, 17238c2ecf20Sopenharmony_ci}; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_cistatic int vc4_dsi_dev_probe(struct platform_device *pdev) 17268c2ecf20Sopenharmony_ci{ 17278c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 17288c2ecf20Sopenharmony_ci struct vc4_dsi *dsi; 17298c2ecf20Sopenharmony_ci int ret; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); 17328c2ecf20Sopenharmony_ci if (!dsi) 17338c2ecf20Sopenharmony_ci return -ENOMEM; 17348c2ecf20Sopenharmony_ci dev_set_drvdata(dev, dsi); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci dsi->pdev = pdev; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci /* Note, the initialization sequence for DSI and panels is 17398c2ecf20Sopenharmony_ci * tricky. The component bind above won't get past its 17408c2ecf20Sopenharmony_ci * -EPROBE_DEFER until the panel/bridge probes. The 17418c2ecf20Sopenharmony_ci * panel/bridge will return -EPROBE_DEFER until it has a 17428c2ecf20Sopenharmony_ci * mipi_dsi_host to register its device to. So, we register 17438c2ecf20Sopenharmony_ci * the host during pdev probe time, so vc4 as a whole can then 17448c2ecf20Sopenharmony_ci * -EPROBE_DEFER its component bind process until the panel 17458c2ecf20Sopenharmony_ci * successfully attaches. 17468c2ecf20Sopenharmony_ci */ 17478c2ecf20Sopenharmony_ci dsi->dsi_host.ops = &vc4_dsi_host_ops; 17488c2ecf20Sopenharmony_ci dsi->dsi_host.dev = dev; 17498c2ecf20Sopenharmony_ci mipi_dsi_host_register(&dsi->dsi_host); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci ret = component_add(&pdev->dev, &vc4_dsi_ops); 17528c2ecf20Sopenharmony_ci if (ret) { 17538c2ecf20Sopenharmony_ci mipi_dsi_host_unregister(&dsi->dsi_host); 17548c2ecf20Sopenharmony_ci return ret; 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci return 0; 17588c2ecf20Sopenharmony_ci} 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_cistatic int vc4_dsi_dev_remove(struct platform_device *pdev) 17618c2ecf20Sopenharmony_ci{ 17628c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 17638c2ecf20Sopenharmony_ci struct vc4_dsi *dsi = dev_get_drvdata(dev); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci component_del(&pdev->dev, &vc4_dsi_ops); 17668c2ecf20Sopenharmony_ci mipi_dsi_host_unregister(&dsi->dsi_host); 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci return 0; 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_cistruct platform_driver vc4_dsi_driver = { 17728c2ecf20Sopenharmony_ci .probe = vc4_dsi_dev_probe, 17738c2ecf20Sopenharmony_ci .remove = vc4_dsi_dev_remove, 17748c2ecf20Sopenharmony_ci .driver = { 17758c2ecf20Sopenharmony_ci .name = "vc4_dsi", 17768c2ecf20Sopenharmony_ci .of_match_table = vc4_dsi_dt_match, 17778c2ecf20Sopenharmony_ci }, 17788c2ecf20Sopenharmony_ci}; 1779