162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Broadcom 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/** 762306a36Sopenharmony_ci * DOC: VC4 DSI0/DSI1 module 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * BCM2835 contains two DSI modules, DSI0 and DSI1. DSI0 is a 1062306a36Sopenharmony_ci * single-lane DSI controller, while DSI1 is a more modern 4-lane DSI 1162306a36Sopenharmony_ci * controller. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Most Raspberry Pi boards expose DSI1 as their "DISPLAY" connector, 1462306a36Sopenharmony_ci * while the compute module brings both DSI0 and DSI1 out. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * This driver has been tested for DSI1 video-mode display only 1762306a36Sopenharmony_ci * currently, with most of the information necessary for DSI0 1862306a36Sopenharmony_ci * hopefully present. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/clk-provider.h> 2262306a36Sopenharmony_ci#include <linux/clk.h> 2362306a36Sopenharmony_ci#include <linux/completion.h> 2462306a36Sopenharmony_ci#include <linux/component.h> 2562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2662306a36Sopenharmony_ci#include <linux/dmaengine.h> 2762306a36Sopenharmony_ci#include <linux/io.h> 2862306a36Sopenharmony_ci#include <linux/of.h> 2962306a36Sopenharmony_ci#include <linux/of_address.h> 3062306a36Sopenharmony_ci#include <linux/platform_device.h> 3162306a36Sopenharmony_ci#include <linux/pm_runtime.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 3462306a36Sopenharmony_ci#include <drm/drm_bridge.h> 3562306a36Sopenharmony_ci#include <drm/drm_edid.h> 3662306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 3762306a36Sopenharmony_ci#include <drm/drm_of.h> 3862306a36Sopenharmony_ci#include <drm/drm_panel.h> 3962306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 4062306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include "vc4_drv.h" 4362306a36Sopenharmony_ci#include "vc4_regs.h" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define DSI_CMD_FIFO_DEPTH 16 4662306a36Sopenharmony_ci#define DSI_PIX_FIFO_DEPTH 256 4762306a36Sopenharmony_ci#define DSI_PIX_FIFO_WIDTH 4 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define DSI0_CTRL 0x00 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Command packet control. */ 5262306a36Sopenharmony_ci#define DSI0_TXPKT1C 0x04 /* AKA PKTC */ 5362306a36Sopenharmony_ci#define DSI1_TXPKT1C 0x04 5462306a36Sopenharmony_ci# define DSI_TXPKT1C_TRIG_CMD_MASK VC4_MASK(31, 24) 5562306a36Sopenharmony_ci# define DSI_TXPKT1C_TRIG_CMD_SHIFT 24 5662306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_REPEAT_MASK VC4_MASK(23, 10) 5762306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_REPEAT_SHIFT 10 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci# define DSI_TXPKT1C_DISPLAY_NO_MASK VC4_MASK(9, 8) 6062306a36Sopenharmony_ci# define DSI_TXPKT1C_DISPLAY_NO_SHIFT 8 6162306a36Sopenharmony_ci/* Short, trigger, BTA, or a long packet that fits all in CMDFIFO. */ 6262306a36Sopenharmony_ci# define DSI_TXPKT1C_DISPLAY_NO_SHORT 0 6362306a36Sopenharmony_ci/* Primary display where cmdfifo provides part of the payload and 6462306a36Sopenharmony_ci * pixelvalve the rest. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci# define DSI_TXPKT1C_DISPLAY_NO_PRIMARY 1 6762306a36Sopenharmony_ci/* Secondary display where cmdfifo provides part of the payload and 6862306a36Sopenharmony_ci * pixfifo the rest. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci# define DSI_TXPKT1C_DISPLAY_NO_SECONDARY 2 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_TX_TIME_MASK VC4_MASK(7, 6) 7362306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_TX_TIME_SHIFT 6 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_MASK VC4_MASK(5, 4) 7662306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_SHIFT 4 7762306a36Sopenharmony_ci/* Command only. Uses TXPKT1H and DISPLAY_NO */ 7862306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_TX 0 7962306a36Sopenharmony_ci/* Command with BTA for either ack or read data. */ 8062306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_RX 1 8162306a36Sopenharmony_ci/* Trigger according to TRIG_CMD */ 8262306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_TRIG 2 8362306a36Sopenharmony_ci/* BTA alone for getting error status after a command, or a TE trigger 8462306a36Sopenharmony_ci * without a previous command. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_CTRL_BTA 3 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_MODE_LP BIT(3) 8962306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_TYPE_LONG BIT(2) 9062306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_TE_EN BIT(1) 9162306a36Sopenharmony_ci# define DSI_TXPKT1C_CMD_EN BIT(0) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* Command packet header. */ 9462306a36Sopenharmony_ci#define DSI0_TXPKT1H 0x08 /* AKA PKTH */ 9562306a36Sopenharmony_ci#define DSI1_TXPKT1H 0x08 9662306a36Sopenharmony_ci# define DSI_TXPKT1H_BC_CMDFIFO_MASK VC4_MASK(31, 24) 9762306a36Sopenharmony_ci# define DSI_TXPKT1H_BC_CMDFIFO_SHIFT 24 9862306a36Sopenharmony_ci# define DSI_TXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) 9962306a36Sopenharmony_ci# define DSI_TXPKT1H_BC_PARAM_SHIFT 8 10062306a36Sopenharmony_ci# define DSI_TXPKT1H_BC_DT_MASK VC4_MASK(7, 0) 10162306a36Sopenharmony_ci# define DSI_TXPKT1H_BC_DT_SHIFT 0 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define DSI0_RXPKT1H 0x0c /* AKA RX1_PKTH */ 10462306a36Sopenharmony_ci#define DSI1_RXPKT1H 0x14 10562306a36Sopenharmony_ci# define DSI_RXPKT1H_CRC_ERR BIT(31) 10662306a36Sopenharmony_ci# define DSI_RXPKT1H_DET_ERR BIT(30) 10762306a36Sopenharmony_ci# define DSI_RXPKT1H_ECC_ERR BIT(29) 10862306a36Sopenharmony_ci# define DSI_RXPKT1H_COR_ERR BIT(28) 10962306a36Sopenharmony_ci# define DSI_RXPKT1H_INCOMP_PKT BIT(25) 11062306a36Sopenharmony_ci# define DSI_RXPKT1H_PKT_TYPE_LONG BIT(24) 11162306a36Sopenharmony_ci/* Byte count if DSI_RXPKT1H_PKT_TYPE_LONG */ 11262306a36Sopenharmony_ci# define DSI_RXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) 11362306a36Sopenharmony_ci# define DSI_RXPKT1H_BC_PARAM_SHIFT 8 11462306a36Sopenharmony_ci/* Short return bytes if !DSI_RXPKT1H_PKT_TYPE_LONG */ 11562306a36Sopenharmony_ci# define DSI_RXPKT1H_SHORT_1_MASK VC4_MASK(23, 16) 11662306a36Sopenharmony_ci# define DSI_RXPKT1H_SHORT_1_SHIFT 16 11762306a36Sopenharmony_ci# define DSI_RXPKT1H_SHORT_0_MASK VC4_MASK(15, 8) 11862306a36Sopenharmony_ci# define DSI_RXPKT1H_SHORT_0_SHIFT 8 11962306a36Sopenharmony_ci# define DSI_RXPKT1H_DT_LP_CMD_MASK VC4_MASK(7, 0) 12062306a36Sopenharmony_ci# define DSI_RXPKT1H_DT_LP_CMD_SHIFT 0 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define DSI0_RXPKT2H 0x10 /* AKA RX2_PKTH */ 12362306a36Sopenharmony_ci#define DSI1_RXPKT2H 0x18 12462306a36Sopenharmony_ci# define DSI_RXPKT1H_DET_ERR BIT(30) 12562306a36Sopenharmony_ci# define DSI_RXPKT1H_ECC_ERR BIT(29) 12662306a36Sopenharmony_ci# define DSI_RXPKT1H_COR_ERR BIT(28) 12762306a36Sopenharmony_ci# define DSI_RXPKT1H_INCOMP_PKT BIT(25) 12862306a36Sopenharmony_ci# define DSI_RXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) 12962306a36Sopenharmony_ci# define DSI_RXPKT1H_BC_PARAM_SHIFT 8 13062306a36Sopenharmony_ci# define DSI_RXPKT1H_DT_MASK VC4_MASK(7, 0) 13162306a36Sopenharmony_ci# define DSI_RXPKT1H_DT_SHIFT 0 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#define DSI0_TXPKT_CMD_FIFO 0x14 /* AKA CMD_DATAF */ 13462306a36Sopenharmony_ci#define DSI1_TXPKT_CMD_FIFO 0x1c 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define DSI0_DISP0_CTRL 0x18 13762306a36Sopenharmony_ci# define DSI_DISP0_PIX_CLK_DIV_MASK VC4_MASK(21, 13) 13862306a36Sopenharmony_ci# define DSI_DISP0_PIX_CLK_DIV_SHIFT 13 13962306a36Sopenharmony_ci# define DSI_DISP0_LP_STOP_CTRL_MASK VC4_MASK(12, 11) 14062306a36Sopenharmony_ci# define DSI_DISP0_LP_STOP_CTRL_SHIFT 11 14162306a36Sopenharmony_ci# define DSI_DISP0_LP_STOP_DISABLE 0 14262306a36Sopenharmony_ci# define DSI_DISP0_LP_STOP_PERLINE 1 14362306a36Sopenharmony_ci# define DSI_DISP0_LP_STOP_PERFRAME 2 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* Transmit RGB pixels and null packets only during HACTIVE, instead 14662306a36Sopenharmony_ci * of going to LP-STOP. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci# define DSI_DISP_HACTIVE_NULL BIT(10) 14962306a36Sopenharmony_ci/* Transmit blanking packet only during vblank, instead of allowing LP-STOP. */ 15062306a36Sopenharmony_ci# define DSI_DISP_VBLP_CTRL BIT(9) 15162306a36Sopenharmony_ci/* Transmit blanking packet only during HFP, instead of allowing LP-STOP. */ 15262306a36Sopenharmony_ci# define DSI_DISP_HFP_CTRL BIT(8) 15362306a36Sopenharmony_ci/* Transmit blanking packet only during HBP, instead of allowing LP-STOP. */ 15462306a36Sopenharmony_ci# define DSI_DISP_HBP_CTRL BIT(7) 15562306a36Sopenharmony_ci# define DSI_DISP0_CHANNEL_MASK VC4_MASK(6, 5) 15662306a36Sopenharmony_ci# define DSI_DISP0_CHANNEL_SHIFT 5 15762306a36Sopenharmony_ci/* Enables end events for HSYNC/VSYNC, not just start events. */ 15862306a36Sopenharmony_ci# define DSI_DISP0_ST_END BIT(4) 15962306a36Sopenharmony_ci# define DSI_DISP0_PFORMAT_MASK VC4_MASK(3, 2) 16062306a36Sopenharmony_ci# define DSI_DISP0_PFORMAT_SHIFT 2 16162306a36Sopenharmony_ci# define DSI_PFORMAT_RGB565 0 16262306a36Sopenharmony_ci# define DSI_PFORMAT_RGB666_PACKED 1 16362306a36Sopenharmony_ci# define DSI_PFORMAT_RGB666 2 16462306a36Sopenharmony_ci# define DSI_PFORMAT_RGB888 3 16562306a36Sopenharmony_ci/* Default is VIDEO mode. */ 16662306a36Sopenharmony_ci# define DSI_DISP0_COMMAND_MODE BIT(1) 16762306a36Sopenharmony_ci# define DSI_DISP0_ENABLE BIT(0) 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#define DSI0_DISP1_CTRL 0x1c 17062306a36Sopenharmony_ci#define DSI1_DISP1_CTRL 0x2c 17162306a36Sopenharmony_ci/* Format of the data written to TXPKT_PIX_FIFO. */ 17262306a36Sopenharmony_ci# define DSI_DISP1_PFORMAT_MASK VC4_MASK(2, 1) 17362306a36Sopenharmony_ci# define DSI_DISP1_PFORMAT_SHIFT 1 17462306a36Sopenharmony_ci# define DSI_DISP1_PFORMAT_16BIT 0 17562306a36Sopenharmony_ci# define DSI_DISP1_PFORMAT_24BIT 1 17662306a36Sopenharmony_ci# define DSI_DISP1_PFORMAT_32BIT_LE 2 17762306a36Sopenharmony_ci# define DSI_DISP1_PFORMAT_32BIT_BE 3 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* DISP1 is always command mode. */ 18062306a36Sopenharmony_ci# define DSI_DISP1_ENABLE BIT(0) 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci#define DSI0_TXPKT_PIX_FIFO 0x20 /* AKA PIX_FIFO */ 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci#define DSI0_INT_STAT 0x24 18562306a36Sopenharmony_ci#define DSI0_INT_EN 0x28 18662306a36Sopenharmony_ci# define DSI0_INT_FIFO_ERR BIT(25) 18762306a36Sopenharmony_ci# define DSI0_INT_CMDC_DONE_MASK VC4_MASK(24, 23) 18862306a36Sopenharmony_ci# define DSI0_INT_CMDC_DONE_SHIFT 23 18962306a36Sopenharmony_ci# define DSI0_INT_CMDC_DONE_NO_REPEAT 1 19062306a36Sopenharmony_ci# define DSI0_INT_CMDC_DONE_REPEAT 3 19162306a36Sopenharmony_ci# define DSI0_INT_PHY_DIR_RTF BIT(22) 19262306a36Sopenharmony_ci# define DSI0_INT_PHY_D1_ULPS BIT(21) 19362306a36Sopenharmony_ci# define DSI0_INT_PHY_D1_STOP BIT(20) 19462306a36Sopenharmony_ci# define DSI0_INT_PHY_RXLPDT BIT(19) 19562306a36Sopenharmony_ci# define DSI0_INT_PHY_RXTRIG BIT(18) 19662306a36Sopenharmony_ci# define DSI0_INT_PHY_D0_ULPS BIT(17) 19762306a36Sopenharmony_ci# define DSI0_INT_PHY_D0_LPDT BIT(16) 19862306a36Sopenharmony_ci# define DSI0_INT_PHY_D0_FTR BIT(15) 19962306a36Sopenharmony_ci# define DSI0_INT_PHY_D0_STOP BIT(14) 20062306a36Sopenharmony_ci/* Signaled when the clock lane enters the given state. */ 20162306a36Sopenharmony_ci# define DSI0_INT_PHY_CLK_ULPS BIT(13) 20262306a36Sopenharmony_ci# define DSI0_INT_PHY_CLK_HS BIT(12) 20362306a36Sopenharmony_ci# define DSI0_INT_PHY_CLK_FTR BIT(11) 20462306a36Sopenharmony_ci/* Signaled on timeouts */ 20562306a36Sopenharmony_ci# define DSI0_INT_PR_TO BIT(10) 20662306a36Sopenharmony_ci# define DSI0_INT_TA_TO BIT(9) 20762306a36Sopenharmony_ci# define DSI0_INT_LPRX_TO BIT(8) 20862306a36Sopenharmony_ci# define DSI0_INT_HSTX_TO BIT(7) 20962306a36Sopenharmony_ci/* Contention on a line when trying to drive the line low */ 21062306a36Sopenharmony_ci# define DSI0_INT_ERR_CONT_LP1 BIT(6) 21162306a36Sopenharmony_ci# define DSI0_INT_ERR_CONT_LP0 BIT(5) 21262306a36Sopenharmony_ci/* Control error: incorrect line state sequence on data lane 0. */ 21362306a36Sopenharmony_ci# define DSI0_INT_ERR_CONTROL BIT(4) 21462306a36Sopenharmony_ci# define DSI0_INT_ERR_SYNC_ESC BIT(3) 21562306a36Sopenharmony_ci# define DSI0_INT_RX2_PKT BIT(2) 21662306a36Sopenharmony_ci# define DSI0_INT_RX1_PKT BIT(1) 21762306a36Sopenharmony_ci# define DSI0_INT_CMD_PKT BIT(0) 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci#define DSI0_INTERRUPTS_ALWAYS_ENABLED (DSI0_INT_ERR_SYNC_ESC | \ 22062306a36Sopenharmony_ci DSI0_INT_ERR_CONTROL | \ 22162306a36Sopenharmony_ci DSI0_INT_ERR_CONT_LP0 | \ 22262306a36Sopenharmony_ci DSI0_INT_ERR_CONT_LP1 | \ 22362306a36Sopenharmony_ci DSI0_INT_HSTX_TO | \ 22462306a36Sopenharmony_ci DSI0_INT_LPRX_TO | \ 22562306a36Sopenharmony_ci DSI0_INT_TA_TO | \ 22662306a36Sopenharmony_ci DSI0_INT_PR_TO) 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci# define DSI1_INT_PHY_D3_ULPS BIT(30) 22962306a36Sopenharmony_ci# define DSI1_INT_PHY_D3_STOP BIT(29) 23062306a36Sopenharmony_ci# define DSI1_INT_PHY_D2_ULPS BIT(28) 23162306a36Sopenharmony_ci# define DSI1_INT_PHY_D2_STOP BIT(27) 23262306a36Sopenharmony_ci# define DSI1_INT_PHY_D1_ULPS BIT(26) 23362306a36Sopenharmony_ci# define DSI1_INT_PHY_D1_STOP BIT(25) 23462306a36Sopenharmony_ci# define DSI1_INT_PHY_D0_ULPS BIT(24) 23562306a36Sopenharmony_ci# define DSI1_INT_PHY_D0_STOP BIT(23) 23662306a36Sopenharmony_ci# define DSI1_INT_FIFO_ERR BIT(22) 23762306a36Sopenharmony_ci# define DSI1_INT_PHY_DIR_RTF BIT(21) 23862306a36Sopenharmony_ci# define DSI1_INT_PHY_RXLPDT BIT(20) 23962306a36Sopenharmony_ci# define DSI1_INT_PHY_RXTRIG BIT(19) 24062306a36Sopenharmony_ci# define DSI1_INT_PHY_D0_LPDT BIT(18) 24162306a36Sopenharmony_ci# define DSI1_INT_PHY_DIR_FTR BIT(17) 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* Signaled when the clock lane enters the given state. */ 24462306a36Sopenharmony_ci# define DSI1_INT_PHY_CLOCK_ULPS BIT(16) 24562306a36Sopenharmony_ci# define DSI1_INT_PHY_CLOCK_HS BIT(15) 24662306a36Sopenharmony_ci# define DSI1_INT_PHY_CLOCK_STOP BIT(14) 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* Signaled on timeouts */ 24962306a36Sopenharmony_ci# define DSI1_INT_PR_TO BIT(13) 25062306a36Sopenharmony_ci# define DSI1_INT_TA_TO BIT(12) 25162306a36Sopenharmony_ci# define DSI1_INT_LPRX_TO BIT(11) 25262306a36Sopenharmony_ci# define DSI1_INT_HSTX_TO BIT(10) 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* Contention on a line when trying to drive the line low */ 25562306a36Sopenharmony_ci# define DSI1_INT_ERR_CONT_LP1 BIT(9) 25662306a36Sopenharmony_ci# define DSI1_INT_ERR_CONT_LP0 BIT(8) 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* Control error: incorrect line state sequence on data lane 0. */ 25962306a36Sopenharmony_ci# define DSI1_INT_ERR_CONTROL BIT(7) 26062306a36Sopenharmony_ci/* LPDT synchronization error (bits received not a multiple of 8. */ 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci# define DSI1_INT_ERR_SYNC_ESC BIT(6) 26362306a36Sopenharmony_ci/* Signaled after receiving an error packet from the display in 26462306a36Sopenharmony_ci * response to a read. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci# define DSI1_INT_RXPKT2 BIT(5) 26762306a36Sopenharmony_ci/* Signaled after receiving a packet. The header and optional short 26862306a36Sopenharmony_ci * response will be in RXPKT1H, and a long response will be in the 26962306a36Sopenharmony_ci * RXPKT_FIFO. 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_ci# define DSI1_INT_RXPKT1 BIT(4) 27262306a36Sopenharmony_ci# define DSI1_INT_TXPKT2_DONE BIT(3) 27362306a36Sopenharmony_ci# define DSI1_INT_TXPKT2_END BIT(2) 27462306a36Sopenharmony_ci/* Signaled after all repeats of TXPKT1 are transferred. */ 27562306a36Sopenharmony_ci# define DSI1_INT_TXPKT1_DONE BIT(1) 27662306a36Sopenharmony_ci/* Signaled after each TXPKT1 repeat is scheduled. */ 27762306a36Sopenharmony_ci# define DSI1_INT_TXPKT1_END BIT(0) 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci#define DSI1_INTERRUPTS_ALWAYS_ENABLED (DSI1_INT_ERR_SYNC_ESC | \ 28062306a36Sopenharmony_ci DSI1_INT_ERR_CONTROL | \ 28162306a36Sopenharmony_ci DSI1_INT_ERR_CONT_LP0 | \ 28262306a36Sopenharmony_ci DSI1_INT_ERR_CONT_LP1 | \ 28362306a36Sopenharmony_ci DSI1_INT_HSTX_TO | \ 28462306a36Sopenharmony_ci DSI1_INT_LPRX_TO | \ 28562306a36Sopenharmony_ci DSI1_INT_TA_TO | \ 28662306a36Sopenharmony_ci DSI1_INT_PR_TO) 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci#define DSI0_STAT 0x2c 28962306a36Sopenharmony_ci#define DSI0_HSTX_TO_CNT 0x30 29062306a36Sopenharmony_ci#define DSI0_LPRX_TO_CNT 0x34 29162306a36Sopenharmony_ci#define DSI0_TA_TO_CNT 0x38 29262306a36Sopenharmony_ci#define DSI0_PR_TO_CNT 0x3c 29362306a36Sopenharmony_ci#define DSI0_PHYC 0x40 29462306a36Sopenharmony_ci# define DSI1_PHYC_ESC_CLK_LPDT_MASK VC4_MASK(25, 20) 29562306a36Sopenharmony_ci# define DSI1_PHYC_ESC_CLK_LPDT_SHIFT 20 29662306a36Sopenharmony_ci# define DSI1_PHYC_HS_CLK_CONTINUOUS BIT(18) 29762306a36Sopenharmony_ci# define DSI0_PHYC_ESC_CLK_LPDT_MASK VC4_MASK(17, 12) 29862306a36Sopenharmony_ci# define DSI0_PHYC_ESC_CLK_LPDT_SHIFT 12 29962306a36Sopenharmony_ci# define DSI1_PHYC_CLANE_ULPS BIT(17) 30062306a36Sopenharmony_ci# define DSI1_PHYC_CLANE_ENABLE BIT(16) 30162306a36Sopenharmony_ci# define DSI_PHYC_DLANE3_ULPS BIT(13) 30262306a36Sopenharmony_ci# define DSI_PHYC_DLANE3_ENABLE BIT(12) 30362306a36Sopenharmony_ci# define DSI0_PHYC_HS_CLK_CONTINUOUS BIT(10) 30462306a36Sopenharmony_ci# define DSI0_PHYC_CLANE_ULPS BIT(9) 30562306a36Sopenharmony_ci# define DSI_PHYC_DLANE2_ULPS BIT(9) 30662306a36Sopenharmony_ci# define DSI0_PHYC_CLANE_ENABLE BIT(8) 30762306a36Sopenharmony_ci# define DSI_PHYC_DLANE2_ENABLE BIT(8) 30862306a36Sopenharmony_ci# define DSI_PHYC_DLANE1_ULPS BIT(5) 30962306a36Sopenharmony_ci# define DSI_PHYC_DLANE1_ENABLE BIT(4) 31062306a36Sopenharmony_ci# define DSI_PHYC_DLANE0_FORCE_STOP BIT(2) 31162306a36Sopenharmony_ci# define DSI_PHYC_DLANE0_ULPS BIT(1) 31262306a36Sopenharmony_ci# define DSI_PHYC_DLANE0_ENABLE BIT(0) 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci#define DSI0_HS_CLT0 0x44 31562306a36Sopenharmony_ci#define DSI0_HS_CLT1 0x48 31662306a36Sopenharmony_ci#define DSI0_HS_CLT2 0x4c 31762306a36Sopenharmony_ci#define DSI0_HS_DLT3 0x50 31862306a36Sopenharmony_ci#define DSI0_HS_DLT4 0x54 31962306a36Sopenharmony_ci#define DSI0_HS_DLT5 0x58 32062306a36Sopenharmony_ci#define DSI0_HS_DLT6 0x5c 32162306a36Sopenharmony_ci#define DSI0_HS_DLT7 0x60 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci#define DSI0_PHY_AFEC0 0x64 32462306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_DDR2CLK_EN BIT(26) 32562306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_DDRCLK_EN BIT(25) 32662306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_LATCH_ULPS BIT(24) 32762306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE3_MASK VC4_MASK(31, 29) 32862306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE3_SHIFT 29 32962306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE2_MASK VC4_MASK(28, 26) 33062306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE2_SHIFT 26 33162306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE1_MASK VC4_MASK(27, 23) 33262306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE1_SHIFT 23 33362306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE0_MASK VC4_MASK(22, 20) 33462306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_DLANE0_SHIFT 20 33562306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_CLANE_MASK VC4_MASK(19, 17) 33662306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_IDR_CLANE_SHIFT 17 33762306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_DLANE1_MASK VC4_MASK(23, 20) 33862306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_DLANE1_SHIFT 20 33962306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_DLANE0_MASK VC4_MASK(19, 16) 34062306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_DLANE0_SHIFT 16 34162306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_CLANE_MASK VC4_MASK(15, 12) 34262306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_ACTRL_CLANE_SHIFT 12 34362306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_DDR2CLK_EN BIT(16) 34462306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_DDRCLK_EN BIT(15) 34562306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_LATCH_ULPS BIT(14) 34662306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_RESET BIT(13) 34762306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_PD BIT(12) 34862306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_RESET BIT(11) 34962306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_PD_BG BIT(11) 35062306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_PD BIT(10) 35162306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_PD_DLANE1 BIT(10) 35262306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_PD_BG BIT(9) 35362306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_PD_DLANE2 BIT(9) 35462306a36Sopenharmony_ci# define DSI0_PHY_AFEC0_PD_DLANE1 BIT(8) 35562306a36Sopenharmony_ci# define DSI1_PHY_AFEC0_PD_DLANE3 BIT(8) 35662306a36Sopenharmony_ci# define DSI_PHY_AFEC0_PTATADJ_MASK VC4_MASK(7, 4) 35762306a36Sopenharmony_ci# define DSI_PHY_AFEC0_PTATADJ_SHIFT 4 35862306a36Sopenharmony_ci# define DSI_PHY_AFEC0_CTATADJ_MASK VC4_MASK(3, 0) 35962306a36Sopenharmony_ci# define DSI_PHY_AFEC0_CTATADJ_SHIFT 0 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci#define DSI0_PHY_AFEC1 0x68 36262306a36Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_DLANE1_MASK VC4_MASK(10, 8) 36362306a36Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_DLANE1_SHIFT 8 36462306a36Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_DLANE0_MASK VC4_MASK(6, 4) 36562306a36Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_DLANE0_SHIFT 4 36662306a36Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_CLANE_MASK VC4_MASK(2, 0) 36762306a36Sopenharmony_ci# define DSI0_PHY_AFEC1_IDR_CLANE_SHIFT 0 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci#define DSI0_TST_SEL 0x6c 37062306a36Sopenharmony_ci#define DSI0_TST_MON 0x70 37162306a36Sopenharmony_ci#define DSI0_ID 0x74 37262306a36Sopenharmony_ci# define DSI_ID_VALUE 0x00647369 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci#define DSI1_CTRL 0x00 37562306a36Sopenharmony_ci# define DSI_CTRL_HS_CLKC_MASK VC4_MASK(15, 14) 37662306a36Sopenharmony_ci# define DSI_CTRL_HS_CLKC_SHIFT 14 37762306a36Sopenharmony_ci# define DSI_CTRL_HS_CLKC_BYTE 0 37862306a36Sopenharmony_ci# define DSI_CTRL_HS_CLKC_DDR2 1 37962306a36Sopenharmony_ci# define DSI_CTRL_HS_CLKC_DDR 2 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci# define DSI_CTRL_RX_LPDT_EOT_DISABLE BIT(13) 38262306a36Sopenharmony_ci# define DSI_CTRL_LPDT_EOT_DISABLE BIT(12) 38362306a36Sopenharmony_ci# define DSI_CTRL_HSDT_EOT_DISABLE BIT(11) 38462306a36Sopenharmony_ci# define DSI_CTRL_SOFT_RESET_CFG BIT(10) 38562306a36Sopenharmony_ci# define DSI_CTRL_CAL_BYTE BIT(9) 38662306a36Sopenharmony_ci# define DSI_CTRL_INV_BYTE BIT(8) 38762306a36Sopenharmony_ci# define DSI_CTRL_CLR_LDF BIT(7) 38862306a36Sopenharmony_ci# define DSI0_CTRL_CLR_PBCF BIT(6) 38962306a36Sopenharmony_ci# define DSI1_CTRL_CLR_RXF BIT(6) 39062306a36Sopenharmony_ci# define DSI0_CTRL_CLR_CPBCF BIT(5) 39162306a36Sopenharmony_ci# define DSI1_CTRL_CLR_PDF BIT(5) 39262306a36Sopenharmony_ci# define DSI0_CTRL_CLR_PDF BIT(4) 39362306a36Sopenharmony_ci# define DSI1_CTRL_CLR_CDF BIT(4) 39462306a36Sopenharmony_ci# define DSI0_CTRL_CLR_CDF BIT(3) 39562306a36Sopenharmony_ci# define DSI0_CTRL_CTRL2 BIT(2) 39662306a36Sopenharmony_ci# define DSI1_CTRL_DISABLE_DISP_CRCC BIT(2) 39762306a36Sopenharmony_ci# define DSI0_CTRL_CTRL1 BIT(1) 39862306a36Sopenharmony_ci# define DSI1_CTRL_DISABLE_DISP_ECCC BIT(1) 39962306a36Sopenharmony_ci# define DSI0_CTRL_CTRL0 BIT(0) 40062306a36Sopenharmony_ci# define DSI1_CTRL_EN BIT(0) 40162306a36Sopenharmony_ci# define DSI0_CTRL_RESET_FIFOS (DSI_CTRL_CLR_LDF | \ 40262306a36Sopenharmony_ci DSI0_CTRL_CLR_PBCF | \ 40362306a36Sopenharmony_ci DSI0_CTRL_CLR_CPBCF | \ 40462306a36Sopenharmony_ci DSI0_CTRL_CLR_PDF | \ 40562306a36Sopenharmony_ci DSI0_CTRL_CLR_CDF) 40662306a36Sopenharmony_ci# define DSI1_CTRL_RESET_FIFOS (DSI_CTRL_CLR_LDF | \ 40762306a36Sopenharmony_ci DSI1_CTRL_CLR_RXF | \ 40862306a36Sopenharmony_ci DSI1_CTRL_CLR_PDF | \ 40962306a36Sopenharmony_ci DSI1_CTRL_CLR_CDF) 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci#define DSI1_TXPKT2C 0x0c 41262306a36Sopenharmony_ci#define DSI1_TXPKT2H 0x10 41362306a36Sopenharmony_ci#define DSI1_TXPKT_PIX_FIFO 0x20 41462306a36Sopenharmony_ci#define DSI1_RXPKT_FIFO 0x24 41562306a36Sopenharmony_ci#define DSI1_DISP0_CTRL 0x28 41662306a36Sopenharmony_ci#define DSI1_INT_STAT 0x30 41762306a36Sopenharmony_ci#define DSI1_INT_EN 0x34 41862306a36Sopenharmony_ci/* State reporting bits. These mostly behave like INT_STAT, where 41962306a36Sopenharmony_ci * writing a 1 clears the bit. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci#define DSI1_STAT 0x38 42262306a36Sopenharmony_ci# define DSI1_STAT_PHY_D3_ULPS BIT(31) 42362306a36Sopenharmony_ci# define DSI1_STAT_PHY_D3_STOP BIT(30) 42462306a36Sopenharmony_ci# define DSI1_STAT_PHY_D2_ULPS BIT(29) 42562306a36Sopenharmony_ci# define DSI1_STAT_PHY_D2_STOP BIT(28) 42662306a36Sopenharmony_ci# define DSI1_STAT_PHY_D1_ULPS BIT(27) 42762306a36Sopenharmony_ci# define DSI1_STAT_PHY_D1_STOP BIT(26) 42862306a36Sopenharmony_ci# define DSI1_STAT_PHY_D0_ULPS BIT(25) 42962306a36Sopenharmony_ci# define DSI1_STAT_PHY_D0_STOP BIT(24) 43062306a36Sopenharmony_ci# define DSI1_STAT_FIFO_ERR BIT(23) 43162306a36Sopenharmony_ci# define DSI1_STAT_PHY_RXLPDT BIT(22) 43262306a36Sopenharmony_ci# define DSI1_STAT_PHY_RXTRIG BIT(21) 43362306a36Sopenharmony_ci# define DSI1_STAT_PHY_D0_LPDT BIT(20) 43462306a36Sopenharmony_ci/* Set when in forward direction */ 43562306a36Sopenharmony_ci# define DSI1_STAT_PHY_DIR BIT(19) 43662306a36Sopenharmony_ci# define DSI1_STAT_PHY_CLOCK_ULPS BIT(18) 43762306a36Sopenharmony_ci# define DSI1_STAT_PHY_CLOCK_HS BIT(17) 43862306a36Sopenharmony_ci# define DSI1_STAT_PHY_CLOCK_STOP BIT(16) 43962306a36Sopenharmony_ci# define DSI1_STAT_PR_TO BIT(15) 44062306a36Sopenharmony_ci# define DSI1_STAT_TA_TO BIT(14) 44162306a36Sopenharmony_ci# define DSI1_STAT_LPRX_TO BIT(13) 44262306a36Sopenharmony_ci# define DSI1_STAT_HSTX_TO BIT(12) 44362306a36Sopenharmony_ci# define DSI1_STAT_ERR_CONT_LP1 BIT(11) 44462306a36Sopenharmony_ci# define DSI1_STAT_ERR_CONT_LP0 BIT(10) 44562306a36Sopenharmony_ci# define DSI1_STAT_ERR_CONTROL BIT(9) 44662306a36Sopenharmony_ci# define DSI1_STAT_ERR_SYNC_ESC BIT(8) 44762306a36Sopenharmony_ci# define DSI1_STAT_RXPKT2 BIT(7) 44862306a36Sopenharmony_ci# define DSI1_STAT_RXPKT1 BIT(6) 44962306a36Sopenharmony_ci# define DSI1_STAT_TXPKT2_BUSY BIT(5) 45062306a36Sopenharmony_ci# define DSI1_STAT_TXPKT2_DONE BIT(4) 45162306a36Sopenharmony_ci# define DSI1_STAT_TXPKT2_END BIT(3) 45262306a36Sopenharmony_ci# define DSI1_STAT_TXPKT1_BUSY BIT(2) 45362306a36Sopenharmony_ci# define DSI1_STAT_TXPKT1_DONE BIT(1) 45462306a36Sopenharmony_ci# define DSI1_STAT_TXPKT1_END BIT(0) 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci#define DSI1_HSTX_TO_CNT 0x3c 45762306a36Sopenharmony_ci#define DSI1_LPRX_TO_CNT 0x40 45862306a36Sopenharmony_ci#define DSI1_TA_TO_CNT 0x44 45962306a36Sopenharmony_ci#define DSI1_PR_TO_CNT 0x48 46062306a36Sopenharmony_ci#define DSI1_PHYC 0x4c 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci#define DSI1_HS_CLT0 0x50 46362306a36Sopenharmony_ci# define DSI_HS_CLT0_CZERO_MASK VC4_MASK(26, 18) 46462306a36Sopenharmony_ci# define DSI_HS_CLT0_CZERO_SHIFT 18 46562306a36Sopenharmony_ci# define DSI_HS_CLT0_CPRE_MASK VC4_MASK(17, 9) 46662306a36Sopenharmony_ci# define DSI_HS_CLT0_CPRE_SHIFT 9 46762306a36Sopenharmony_ci# define DSI_HS_CLT0_CPREP_MASK VC4_MASK(8, 0) 46862306a36Sopenharmony_ci# define DSI_HS_CLT0_CPREP_SHIFT 0 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci#define DSI1_HS_CLT1 0x54 47162306a36Sopenharmony_ci# define DSI_HS_CLT1_CTRAIL_MASK VC4_MASK(17, 9) 47262306a36Sopenharmony_ci# define DSI_HS_CLT1_CTRAIL_SHIFT 9 47362306a36Sopenharmony_ci# define DSI_HS_CLT1_CPOST_MASK VC4_MASK(8, 0) 47462306a36Sopenharmony_ci# define DSI_HS_CLT1_CPOST_SHIFT 0 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci#define DSI1_HS_CLT2 0x58 47762306a36Sopenharmony_ci# define DSI_HS_CLT2_WUP_MASK VC4_MASK(23, 0) 47862306a36Sopenharmony_ci# define DSI_HS_CLT2_WUP_SHIFT 0 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci#define DSI1_HS_DLT3 0x5c 48162306a36Sopenharmony_ci# define DSI_HS_DLT3_EXIT_MASK VC4_MASK(26, 18) 48262306a36Sopenharmony_ci# define DSI_HS_DLT3_EXIT_SHIFT 18 48362306a36Sopenharmony_ci# define DSI_HS_DLT3_ZERO_MASK VC4_MASK(17, 9) 48462306a36Sopenharmony_ci# define DSI_HS_DLT3_ZERO_SHIFT 9 48562306a36Sopenharmony_ci# define DSI_HS_DLT3_PRE_MASK VC4_MASK(8, 0) 48662306a36Sopenharmony_ci# define DSI_HS_DLT3_PRE_SHIFT 0 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci#define DSI1_HS_DLT4 0x60 48962306a36Sopenharmony_ci# define DSI_HS_DLT4_ANLAT_MASK VC4_MASK(22, 18) 49062306a36Sopenharmony_ci# define DSI_HS_DLT4_ANLAT_SHIFT 18 49162306a36Sopenharmony_ci# define DSI_HS_DLT4_TRAIL_MASK VC4_MASK(17, 9) 49262306a36Sopenharmony_ci# define DSI_HS_DLT4_TRAIL_SHIFT 9 49362306a36Sopenharmony_ci# define DSI_HS_DLT4_LPX_MASK VC4_MASK(8, 0) 49462306a36Sopenharmony_ci# define DSI_HS_DLT4_LPX_SHIFT 0 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci#define DSI1_HS_DLT5 0x64 49762306a36Sopenharmony_ci# define DSI_HS_DLT5_INIT_MASK VC4_MASK(23, 0) 49862306a36Sopenharmony_ci# define DSI_HS_DLT5_INIT_SHIFT 0 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci#define DSI1_HS_DLT6 0x68 50162306a36Sopenharmony_ci# define DSI_HS_DLT6_TA_GET_MASK VC4_MASK(31, 24) 50262306a36Sopenharmony_ci# define DSI_HS_DLT6_TA_GET_SHIFT 24 50362306a36Sopenharmony_ci# define DSI_HS_DLT6_TA_SURE_MASK VC4_MASK(23, 16) 50462306a36Sopenharmony_ci# define DSI_HS_DLT6_TA_SURE_SHIFT 16 50562306a36Sopenharmony_ci# define DSI_HS_DLT6_TA_GO_MASK VC4_MASK(15, 8) 50662306a36Sopenharmony_ci# define DSI_HS_DLT6_TA_GO_SHIFT 8 50762306a36Sopenharmony_ci# define DSI_HS_DLT6_LP_LPX_MASK VC4_MASK(7, 0) 50862306a36Sopenharmony_ci# define DSI_HS_DLT6_LP_LPX_SHIFT 0 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci#define DSI1_HS_DLT7 0x6c 51162306a36Sopenharmony_ci# define DSI_HS_DLT7_LP_WUP_MASK VC4_MASK(23, 0) 51262306a36Sopenharmony_ci# define DSI_HS_DLT7_LP_WUP_SHIFT 0 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci#define DSI1_PHY_AFEC0 0x70 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci#define DSI1_PHY_AFEC1 0x74 51762306a36Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE3_MASK VC4_MASK(19, 16) 51862306a36Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE3_SHIFT 16 51962306a36Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE2_MASK VC4_MASK(15, 12) 52062306a36Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE2_SHIFT 12 52162306a36Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE1_MASK VC4_MASK(11, 8) 52262306a36Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE1_SHIFT 8 52362306a36Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE0_MASK VC4_MASK(7, 4) 52462306a36Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_DLANE0_SHIFT 4 52562306a36Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_CLANE_MASK VC4_MASK(3, 0) 52662306a36Sopenharmony_ci# define DSI1_PHY_AFEC1_ACTRL_CLANE_SHIFT 0 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci#define DSI1_TST_SEL 0x78 52962306a36Sopenharmony_ci#define DSI1_TST_MON 0x7c 53062306a36Sopenharmony_ci#define DSI1_PHY_TST1 0x80 53162306a36Sopenharmony_ci#define DSI1_PHY_TST2 0x84 53262306a36Sopenharmony_ci#define DSI1_PHY_FIFO_STAT 0x88 53362306a36Sopenharmony_ci/* Actually, all registers in the range that aren't otherwise claimed 53462306a36Sopenharmony_ci * will return the ID. 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_ci#define DSI1_ID 0x8c 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistruct vc4_dsi_variant { 53962306a36Sopenharmony_ci /* Whether we're on bcm2835's DSI0 or DSI1. */ 54062306a36Sopenharmony_ci unsigned int port; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci bool broken_axi_workaround; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci const char *debugfs_name; 54562306a36Sopenharmony_ci const struct debugfs_reg32 *regs; 54662306a36Sopenharmony_ci size_t nregs; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci}; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci/* General DSI hardware state. */ 55162306a36Sopenharmony_cistruct vc4_dsi { 55262306a36Sopenharmony_ci struct vc4_encoder encoder; 55362306a36Sopenharmony_ci struct mipi_dsi_host dsi_host; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci struct kref kref; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci struct platform_device *pdev; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci struct drm_bridge *out_bridge; 56062306a36Sopenharmony_ci struct drm_bridge bridge; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci void __iomem *regs; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci struct dma_chan *reg_dma_chan; 56562306a36Sopenharmony_ci dma_addr_t reg_dma_paddr; 56662306a36Sopenharmony_ci u32 *reg_dma_mem; 56762306a36Sopenharmony_ci dma_addr_t reg_paddr; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci const struct vc4_dsi_variant *variant; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* DSI channel for the panel we're connected to. */ 57262306a36Sopenharmony_ci u32 channel; 57362306a36Sopenharmony_ci u32 lanes; 57462306a36Sopenharmony_ci u32 format; 57562306a36Sopenharmony_ci u32 divider; 57662306a36Sopenharmony_ci u32 mode_flags; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* Input clock from CPRMAN to the digital PHY, for the DSI 57962306a36Sopenharmony_ci * escape clock. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci struct clk *escape_clock; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* Input clock to the analog PHY, used to generate the DSI bit 58462306a36Sopenharmony_ci * clock. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_ci struct clk *pll_phy_clock; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* HS Clocks generated within the DSI analog PHY. */ 58962306a36Sopenharmony_ci struct clk_fixed_factor phy_clocks[3]; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci struct clk_hw_onecell_data *clk_onecell; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Pixel clock output to the pixelvalve, generated from the HS 59462306a36Sopenharmony_ci * clock. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci struct clk *pixel_clock; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci struct completion xfer_completion; 59962306a36Sopenharmony_ci int xfer_result; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci struct debugfs_regset32 regset; 60262306a36Sopenharmony_ci}; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci#define host_to_dsi(host) \ 60562306a36Sopenharmony_ci container_of_const(host, struct vc4_dsi, dsi_host) 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci#define to_vc4_dsi(_encoder) \ 60862306a36Sopenharmony_ci container_of_const(_encoder, struct vc4_dsi, encoder.base) 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci#define bridge_to_vc4_dsi(_bridge) \ 61162306a36Sopenharmony_ci container_of_const(_bridge, struct vc4_dsi, bridge) 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic inline void 61462306a36Sopenharmony_cidsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct dma_chan *chan = dsi->reg_dma_chan; 61762306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx; 61862306a36Sopenharmony_ci dma_cookie_t cookie; 61962306a36Sopenharmony_ci int ret; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci kunit_fail_current_test("Accessing a register in a unit test!\n"); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* DSI0 should be able to write normally. */ 62462306a36Sopenharmony_ci if (!chan) { 62562306a36Sopenharmony_ci writel(val, dsi->regs + offset); 62662306a36Sopenharmony_ci return; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci *dsi->reg_dma_mem = val; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci tx = chan->device->device_prep_dma_memcpy(chan, 63262306a36Sopenharmony_ci dsi->reg_paddr + offset, 63362306a36Sopenharmony_ci dsi->reg_dma_paddr, 63462306a36Sopenharmony_ci 4, 0); 63562306a36Sopenharmony_ci if (!tx) { 63662306a36Sopenharmony_ci DRM_ERROR("Failed to set up DMA register write\n"); 63762306a36Sopenharmony_ci return; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci cookie = tx->tx_submit(tx); 64162306a36Sopenharmony_ci ret = dma_submit_error(cookie); 64262306a36Sopenharmony_ci if (ret) { 64362306a36Sopenharmony_ci DRM_ERROR("Failed to submit DMA: %d\n", ret); 64462306a36Sopenharmony_ci return; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci ret = dma_sync_wait(chan, cookie); 64762306a36Sopenharmony_ci if (ret) 64862306a36Sopenharmony_ci DRM_ERROR("Failed to wait for DMA: %d\n", ret); 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci#define DSI_READ(offset) \ 65262306a36Sopenharmony_ci ({ \ 65362306a36Sopenharmony_ci kunit_fail_current_test("Accessing a register in a unit test!\n"); \ 65462306a36Sopenharmony_ci readl(dsi->regs + (offset)); \ 65562306a36Sopenharmony_ci }) 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci#define DSI_WRITE(offset, val) dsi_dma_workaround_write(dsi, offset, val) 65862306a36Sopenharmony_ci#define DSI_PORT_READ(offset) \ 65962306a36Sopenharmony_ci DSI_READ(dsi->variant->port ? DSI1_##offset : DSI0_##offset) 66062306a36Sopenharmony_ci#define DSI_PORT_WRITE(offset, val) \ 66162306a36Sopenharmony_ci DSI_WRITE(dsi->variant->port ? DSI1_##offset : DSI0_##offset, val) 66262306a36Sopenharmony_ci#define DSI_PORT_BIT(bit) (dsi->variant->port ? DSI1_##bit : DSI0_##bit) 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic const struct debugfs_reg32 dsi0_regs[] = { 66562306a36Sopenharmony_ci VC4_REG32(DSI0_CTRL), 66662306a36Sopenharmony_ci VC4_REG32(DSI0_STAT), 66762306a36Sopenharmony_ci VC4_REG32(DSI0_HSTX_TO_CNT), 66862306a36Sopenharmony_ci VC4_REG32(DSI0_LPRX_TO_CNT), 66962306a36Sopenharmony_ci VC4_REG32(DSI0_TA_TO_CNT), 67062306a36Sopenharmony_ci VC4_REG32(DSI0_PR_TO_CNT), 67162306a36Sopenharmony_ci VC4_REG32(DSI0_DISP0_CTRL), 67262306a36Sopenharmony_ci VC4_REG32(DSI0_DISP1_CTRL), 67362306a36Sopenharmony_ci VC4_REG32(DSI0_INT_STAT), 67462306a36Sopenharmony_ci VC4_REG32(DSI0_INT_EN), 67562306a36Sopenharmony_ci VC4_REG32(DSI0_PHYC), 67662306a36Sopenharmony_ci VC4_REG32(DSI0_HS_CLT0), 67762306a36Sopenharmony_ci VC4_REG32(DSI0_HS_CLT1), 67862306a36Sopenharmony_ci VC4_REG32(DSI0_HS_CLT2), 67962306a36Sopenharmony_ci VC4_REG32(DSI0_HS_DLT3), 68062306a36Sopenharmony_ci VC4_REG32(DSI0_HS_DLT4), 68162306a36Sopenharmony_ci VC4_REG32(DSI0_HS_DLT5), 68262306a36Sopenharmony_ci VC4_REG32(DSI0_HS_DLT6), 68362306a36Sopenharmony_ci VC4_REG32(DSI0_HS_DLT7), 68462306a36Sopenharmony_ci VC4_REG32(DSI0_PHY_AFEC0), 68562306a36Sopenharmony_ci VC4_REG32(DSI0_PHY_AFEC1), 68662306a36Sopenharmony_ci VC4_REG32(DSI0_ID), 68762306a36Sopenharmony_ci}; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic const struct debugfs_reg32 dsi1_regs[] = { 69062306a36Sopenharmony_ci VC4_REG32(DSI1_CTRL), 69162306a36Sopenharmony_ci VC4_REG32(DSI1_STAT), 69262306a36Sopenharmony_ci VC4_REG32(DSI1_HSTX_TO_CNT), 69362306a36Sopenharmony_ci VC4_REG32(DSI1_LPRX_TO_CNT), 69462306a36Sopenharmony_ci VC4_REG32(DSI1_TA_TO_CNT), 69562306a36Sopenharmony_ci VC4_REG32(DSI1_PR_TO_CNT), 69662306a36Sopenharmony_ci VC4_REG32(DSI1_DISP0_CTRL), 69762306a36Sopenharmony_ci VC4_REG32(DSI1_DISP1_CTRL), 69862306a36Sopenharmony_ci VC4_REG32(DSI1_INT_STAT), 69962306a36Sopenharmony_ci VC4_REG32(DSI1_INT_EN), 70062306a36Sopenharmony_ci VC4_REG32(DSI1_PHYC), 70162306a36Sopenharmony_ci VC4_REG32(DSI1_HS_CLT0), 70262306a36Sopenharmony_ci VC4_REG32(DSI1_HS_CLT1), 70362306a36Sopenharmony_ci VC4_REG32(DSI1_HS_CLT2), 70462306a36Sopenharmony_ci VC4_REG32(DSI1_HS_DLT3), 70562306a36Sopenharmony_ci VC4_REG32(DSI1_HS_DLT4), 70662306a36Sopenharmony_ci VC4_REG32(DSI1_HS_DLT5), 70762306a36Sopenharmony_ci VC4_REG32(DSI1_HS_DLT6), 70862306a36Sopenharmony_ci VC4_REG32(DSI1_HS_DLT7), 70962306a36Sopenharmony_ci VC4_REG32(DSI1_PHY_AFEC0), 71062306a36Sopenharmony_ci VC4_REG32(DSI1_PHY_AFEC1), 71162306a36Sopenharmony_ci VC4_REG32(DSI1_ID), 71262306a36Sopenharmony_ci}; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic void vc4_dsi_latch_ulps(struct vc4_dsi *dsi, bool latch) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci u32 afec0 = DSI_PORT_READ(PHY_AFEC0); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (latch) 71962306a36Sopenharmony_ci afec0 |= DSI_PORT_BIT(PHY_AFEC0_LATCH_ULPS); 72062306a36Sopenharmony_ci else 72162306a36Sopenharmony_ci afec0 &= ~DSI_PORT_BIT(PHY_AFEC0_LATCH_ULPS); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC0, afec0); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci/* Enters or exits Ultra Low Power State. */ 72762306a36Sopenharmony_cistatic void vc4_dsi_ulps(struct vc4_dsi *dsi, bool ulps) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci bool non_continuous = dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS; 73062306a36Sopenharmony_ci u32 phyc_ulps = ((non_continuous ? DSI_PORT_BIT(PHYC_CLANE_ULPS) : 0) | 73162306a36Sopenharmony_ci DSI_PHYC_DLANE0_ULPS | 73262306a36Sopenharmony_ci (dsi->lanes > 1 ? DSI_PHYC_DLANE1_ULPS : 0) | 73362306a36Sopenharmony_ci (dsi->lanes > 2 ? DSI_PHYC_DLANE2_ULPS : 0) | 73462306a36Sopenharmony_ci (dsi->lanes > 3 ? DSI_PHYC_DLANE3_ULPS : 0)); 73562306a36Sopenharmony_ci u32 stat_ulps = ((non_continuous ? DSI1_STAT_PHY_CLOCK_ULPS : 0) | 73662306a36Sopenharmony_ci DSI1_STAT_PHY_D0_ULPS | 73762306a36Sopenharmony_ci (dsi->lanes > 1 ? DSI1_STAT_PHY_D1_ULPS : 0) | 73862306a36Sopenharmony_ci (dsi->lanes > 2 ? DSI1_STAT_PHY_D2_ULPS : 0) | 73962306a36Sopenharmony_ci (dsi->lanes > 3 ? DSI1_STAT_PHY_D3_ULPS : 0)); 74062306a36Sopenharmony_ci u32 stat_stop = ((non_continuous ? DSI1_STAT_PHY_CLOCK_STOP : 0) | 74162306a36Sopenharmony_ci DSI1_STAT_PHY_D0_STOP | 74262306a36Sopenharmony_ci (dsi->lanes > 1 ? DSI1_STAT_PHY_D1_STOP : 0) | 74362306a36Sopenharmony_ci (dsi->lanes > 2 ? DSI1_STAT_PHY_D2_STOP : 0) | 74462306a36Sopenharmony_ci (dsi->lanes > 3 ? DSI1_STAT_PHY_D3_STOP : 0)); 74562306a36Sopenharmony_ci int ret; 74662306a36Sopenharmony_ci bool ulps_currently_enabled = (DSI_PORT_READ(PHY_AFEC0) & 74762306a36Sopenharmony_ci DSI_PORT_BIT(PHY_AFEC0_LATCH_ULPS)); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (ulps == ulps_currently_enabled) 75062306a36Sopenharmony_ci return; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci DSI_PORT_WRITE(STAT, stat_ulps); 75362306a36Sopenharmony_ci DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) | phyc_ulps); 75462306a36Sopenharmony_ci ret = wait_for((DSI_PORT_READ(STAT) & stat_ulps) == stat_ulps, 200); 75562306a36Sopenharmony_ci if (ret) { 75662306a36Sopenharmony_ci dev_warn(&dsi->pdev->dev, 75762306a36Sopenharmony_ci "Timeout waiting for DSI ULPS entry: STAT 0x%08x", 75862306a36Sopenharmony_ci DSI_PORT_READ(STAT)); 75962306a36Sopenharmony_ci DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); 76062306a36Sopenharmony_ci vc4_dsi_latch_ulps(dsi, false); 76162306a36Sopenharmony_ci return; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* The DSI module can't be disabled while the module is 76562306a36Sopenharmony_ci * generating ULPS state. So, to be able to disable the 76662306a36Sopenharmony_ci * module, we have the AFE latch the ULPS state and continue 76762306a36Sopenharmony_ci * on to having the module enter STOP. 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_ci vc4_dsi_latch_ulps(dsi, ulps); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci DSI_PORT_WRITE(STAT, stat_stop); 77262306a36Sopenharmony_ci DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); 77362306a36Sopenharmony_ci ret = wait_for((DSI_PORT_READ(STAT) & stat_stop) == stat_stop, 200); 77462306a36Sopenharmony_ci if (ret) { 77562306a36Sopenharmony_ci dev_warn(&dsi->pdev->dev, 77662306a36Sopenharmony_ci "Timeout waiting for DSI STOP entry: STAT 0x%08x", 77762306a36Sopenharmony_ci DSI_PORT_READ(STAT)); 77862306a36Sopenharmony_ci DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); 77962306a36Sopenharmony_ci return; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic u32 78462306a36Sopenharmony_cidsi_hs_timing(u32 ui_ns, u32 ns, u32 ui) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci /* The HS timings have to be rounded up to a multiple of 8 78762306a36Sopenharmony_ci * because we're using the byte clock. 78862306a36Sopenharmony_ci */ 78962306a36Sopenharmony_ci return roundup(ui + DIV_ROUND_UP(ns, ui_ns), 8); 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci/* ESC always runs at 100Mhz. */ 79362306a36Sopenharmony_ci#define ESC_TIME_NS 10 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic u32 79662306a36Sopenharmony_cidsi_esc_timing(u32 ns) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci return DIV_ROUND_UP(ns, ESC_TIME_NS); 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic void vc4_dsi_bridge_disable(struct drm_bridge *bridge, 80262306a36Sopenharmony_ci struct drm_bridge_state *state) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); 80562306a36Sopenharmony_ci u32 disp0_ctrl; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci disp0_ctrl = DSI_PORT_READ(DISP0_CTRL); 80862306a36Sopenharmony_ci disp0_ctrl &= ~DSI_DISP0_ENABLE; 80962306a36Sopenharmony_ci DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl); 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge, 81362306a36Sopenharmony_ci struct drm_bridge_state *state) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); 81662306a36Sopenharmony_ci struct device *dev = &dsi->pdev->dev; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci clk_disable_unprepare(dsi->pll_phy_clock); 81962306a36Sopenharmony_ci clk_disable_unprepare(dsi->escape_clock); 82062306a36Sopenharmony_ci clk_disable_unprepare(dsi->pixel_clock); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci pm_runtime_put(dev); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci/* Extends the mode's blank intervals to handle BCM2835's integer-only 82662306a36Sopenharmony_ci * DSI PLL divider. 82762306a36Sopenharmony_ci * 82862306a36Sopenharmony_ci * On 2835, PLLD is set to 2Ghz, and may not be changed by the display 82962306a36Sopenharmony_ci * driver since most peripherals are hanging off of the PLLD_PER 83062306a36Sopenharmony_ci * divider. PLLD_DSI1, which drives our DSI bit clock (and therefore 83162306a36Sopenharmony_ci * the pixel clock), only has an integer divider off of DSI. 83262306a36Sopenharmony_ci * 83362306a36Sopenharmony_ci * To get our panel mode to refresh at the expected 60Hz, we need to 83462306a36Sopenharmony_ci * extend the horizontal blank time. This means we drive a 83562306a36Sopenharmony_ci * higher-than-expected clock rate to the panel, but that's what the 83662306a36Sopenharmony_ci * firmware does too. 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_cistatic bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge, 83962306a36Sopenharmony_ci const struct drm_display_mode *mode, 84062306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); 84362306a36Sopenharmony_ci struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock); 84462306a36Sopenharmony_ci unsigned long parent_rate = clk_get_rate(phy_parent); 84562306a36Sopenharmony_ci unsigned long pixel_clock_hz = mode->clock * 1000; 84662306a36Sopenharmony_ci unsigned long pll_clock = pixel_clock_hz * dsi->divider; 84762306a36Sopenharmony_ci int divider; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* Find what divider gets us a faster clock than the requested 85062306a36Sopenharmony_ci * pixel clock. 85162306a36Sopenharmony_ci */ 85262306a36Sopenharmony_ci for (divider = 1; divider < 255; divider++) { 85362306a36Sopenharmony_ci if (parent_rate / (divider + 1) < pll_clock) 85462306a36Sopenharmony_ci break; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* Now that we've picked a PLL divider, calculate back to its 85862306a36Sopenharmony_ci * pixel clock. 85962306a36Sopenharmony_ci */ 86062306a36Sopenharmony_ci pll_clock = parent_rate / divider; 86162306a36Sopenharmony_ci pixel_clock_hz = pll_clock / dsi->divider; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci adjusted_mode->clock = pixel_clock_hz / 1000; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* Given the new pixel clock, adjust HFP to keep vrefresh the same. */ 86662306a36Sopenharmony_ci adjusted_mode->htotal = adjusted_mode->clock * mode->htotal / 86762306a36Sopenharmony_ci mode->clock; 86862306a36Sopenharmony_ci adjusted_mode->hsync_end += adjusted_mode->htotal - mode->htotal; 86962306a36Sopenharmony_ci adjusted_mode->hsync_start += adjusted_mode->htotal - mode->htotal; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci return true; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge, 87562306a36Sopenharmony_ci struct drm_bridge_state *old_state) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct drm_atomic_state *state = old_state->base.state; 87862306a36Sopenharmony_ci struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); 87962306a36Sopenharmony_ci const struct drm_crtc_state *crtc_state; 88062306a36Sopenharmony_ci struct device *dev = &dsi->pdev->dev; 88162306a36Sopenharmony_ci const struct drm_display_mode *mode; 88262306a36Sopenharmony_ci struct drm_connector *connector; 88362306a36Sopenharmony_ci bool debug_dump_regs = false; 88462306a36Sopenharmony_ci unsigned long hs_clock; 88562306a36Sopenharmony_ci struct drm_crtc *crtc; 88662306a36Sopenharmony_ci u32 ui_ns; 88762306a36Sopenharmony_ci /* Minimum LP state duration in escape clock cycles. */ 88862306a36Sopenharmony_ci u32 lpx = dsi_esc_timing(60); 88962306a36Sopenharmony_ci unsigned long pixel_clock_hz; 89062306a36Sopenharmony_ci unsigned long dsip_clock; 89162306a36Sopenharmony_ci unsigned long phy_clock; 89262306a36Sopenharmony_ci int ret; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 89562306a36Sopenharmony_ci if (ret) { 89662306a36Sopenharmony_ci DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi->variant->port); 89762306a36Sopenharmony_ci return; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (debug_dump_regs) { 90162306a36Sopenharmony_ci struct drm_printer p = drm_info_printer(&dsi->pdev->dev); 90262306a36Sopenharmony_ci dev_info(&dsi->pdev->dev, "DSI regs before:\n"); 90362306a36Sopenharmony_ci drm_print_regset32(&p, &dsi->regset); 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* 90762306a36Sopenharmony_ci * Retrieve the CRTC adjusted mode. This requires a little dance to go 90862306a36Sopenharmony_ci * from the bridge to the encoder, to the connector and to the CRTC. 90962306a36Sopenharmony_ci */ 91062306a36Sopenharmony_ci connector = drm_atomic_get_new_connector_for_encoder(state, 91162306a36Sopenharmony_ci bridge->encoder); 91262306a36Sopenharmony_ci crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; 91362306a36Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 91462306a36Sopenharmony_ci mode = &crtc_state->adjusted_mode; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci pixel_clock_hz = mode->clock * 1000; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* Round up the clk_set_rate() request slightly, since 91962306a36Sopenharmony_ci * PLLD_DSI1 is an integer divider and its rate selection will 92062306a36Sopenharmony_ci * never round up. 92162306a36Sopenharmony_ci */ 92262306a36Sopenharmony_ci phy_clock = (pixel_clock_hz + 1000) * dsi->divider; 92362306a36Sopenharmony_ci ret = clk_set_rate(dsi->pll_phy_clock, phy_clock); 92462306a36Sopenharmony_ci if (ret) { 92562306a36Sopenharmony_ci dev_err(&dsi->pdev->dev, 92662306a36Sopenharmony_ci "Failed to set phy clock to %ld: %d\n", phy_clock, ret); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* Reset the DSI and all its fifos. */ 93062306a36Sopenharmony_ci DSI_PORT_WRITE(CTRL, 93162306a36Sopenharmony_ci DSI_CTRL_SOFT_RESET_CFG | 93262306a36Sopenharmony_ci DSI_PORT_BIT(CTRL_RESET_FIFOS)); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci DSI_PORT_WRITE(CTRL, 93562306a36Sopenharmony_ci DSI_CTRL_HSDT_EOT_DISABLE | 93662306a36Sopenharmony_ci DSI_CTRL_RX_LPDT_EOT_DISABLE); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci /* Clear all stat bits so we see what has happened during enable. */ 93962306a36Sopenharmony_ci DSI_PORT_WRITE(STAT, DSI_PORT_READ(STAT)); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* Set AFE CTR00/CTR1 to release powerdown of analog. */ 94262306a36Sopenharmony_ci if (dsi->variant->port == 0) { 94362306a36Sopenharmony_ci u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) | 94462306a36Sopenharmony_ci VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ)); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (dsi->lanes < 2) 94762306a36Sopenharmony_ci afec0 |= DSI0_PHY_AFEC0_PD_DLANE1; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) 95062306a36Sopenharmony_ci afec0 |= DSI0_PHY_AFEC0_RESET; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC0, afec0); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* AFEC reset hold time */ 95562306a36Sopenharmony_ci mdelay(1); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC1, 95862306a36Sopenharmony_ci VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE1) | 95962306a36Sopenharmony_ci VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE0) | 96062306a36Sopenharmony_ci VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_CLANE)); 96162306a36Sopenharmony_ci } else { 96262306a36Sopenharmony_ci u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) | 96362306a36Sopenharmony_ci VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ) | 96462306a36Sopenharmony_ci VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_CLANE) | 96562306a36Sopenharmony_ci VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE0) | 96662306a36Sopenharmony_ci VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE1) | 96762306a36Sopenharmony_ci VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE2) | 96862306a36Sopenharmony_ci VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE3)); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (dsi->lanes < 4) 97162306a36Sopenharmony_ci afec0 |= DSI1_PHY_AFEC0_PD_DLANE3; 97262306a36Sopenharmony_ci if (dsi->lanes < 3) 97362306a36Sopenharmony_ci afec0 |= DSI1_PHY_AFEC0_PD_DLANE2; 97462306a36Sopenharmony_ci if (dsi->lanes < 2) 97562306a36Sopenharmony_ci afec0 |= DSI1_PHY_AFEC0_PD_DLANE1; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci afec0 |= DSI1_PHY_AFEC0_RESET; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC0, afec0); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC1, 0); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* AFEC reset hold time */ 98462306a36Sopenharmony_ci mdelay(1); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci ret = clk_prepare_enable(dsi->escape_clock); 98862306a36Sopenharmony_ci if (ret) { 98962306a36Sopenharmony_ci DRM_ERROR("Failed to turn on DSI escape clock: %d\n", ret); 99062306a36Sopenharmony_ci return; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci ret = clk_prepare_enable(dsi->pll_phy_clock); 99462306a36Sopenharmony_ci if (ret) { 99562306a36Sopenharmony_ci DRM_ERROR("Failed to turn on DSI PLL: %d\n", ret); 99662306a36Sopenharmony_ci return; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci hs_clock = clk_get_rate(dsi->pll_phy_clock); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci /* Yes, we set the DSI0P/DSI1P pixel clock to the byte rate, 100262306a36Sopenharmony_ci * not the pixel clock rate. DSIxP take from the APHY's byte, 100362306a36Sopenharmony_ci * DDR2, or DDR4 clock (we use byte) and feed into the PV at 100462306a36Sopenharmony_ci * that rate. Separately, a value derived from PIX_CLK_DIV 100562306a36Sopenharmony_ci * and HS_CLKC is fed into the PV to divide down to the actual 100662306a36Sopenharmony_ci * pixel clock for pushing pixels into DSI. 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_ci dsip_clock = phy_clock / 8; 100962306a36Sopenharmony_ci ret = clk_set_rate(dsi->pixel_clock, dsip_clock); 101062306a36Sopenharmony_ci if (ret) { 101162306a36Sopenharmony_ci dev_err(dev, "Failed to set pixel clock to %ldHz: %d\n", 101262306a36Sopenharmony_ci dsip_clock, ret); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci ret = clk_prepare_enable(dsi->pixel_clock); 101662306a36Sopenharmony_ci if (ret) { 101762306a36Sopenharmony_ci DRM_ERROR("Failed to turn on DSI pixel clock: %d\n", ret); 101862306a36Sopenharmony_ci return; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* How many ns one DSI unit interval is. Note that the clock 102262306a36Sopenharmony_ci * is DDR, so there's an extra divide by 2. 102362306a36Sopenharmony_ci */ 102462306a36Sopenharmony_ci ui_ns = DIV_ROUND_UP(500000000, hs_clock); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci DSI_PORT_WRITE(HS_CLT0, 102762306a36Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 262, 0), 102862306a36Sopenharmony_ci DSI_HS_CLT0_CZERO) | 102962306a36Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 0, 8), 103062306a36Sopenharmony_ci DSI_HS_CLT0_CPRE) | 103162306a36Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 38, 0), 103262306a36Sopenharmony_ci DSI_HS_CLT0_CPREP)); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci DSI_PORT_WRITE(HS_CLT1, 103562306a36Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 60, 0), 103662306a36Sopenharmony_ci DSI_HS_CLT1_CTRAIL) | 103762306a36Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 60, 52), 103862306a36Sopenharmony_ci DSI_HS_CLT1_CPOST)); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci DSI_PORT_WRITE(HS_CLT2, 104162306a36Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 1000000, 0), 104262306a36Sopenharmony_ci DSI_HS_CLT2_WUP)); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci DSI_PORT_WRITE(HS_DLT3, 104562306a36Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 100, 0), 104662306a36Sopenharmony_ci DSI_HS_DLT3_EXIT) | 104762306a36Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 105, 6), 104862306a36Sopenharmony_ci DSI_HS_DLT3_ZERO) | 104962306a36Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, 40, 4), 105062306a36Sopenharmony_ci DSI_HS_DLT3_PRE)); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci DSI_PORT_WRITE(HS_DLT4, 105362306a36Sopenharmony_ci VC4_SET_FIELD(dsi_hs_timing(ui_ns, lpx * ESC_TIME_NS, 0), 105462306a36Sopenharmony_ci DSI_HS_DLT4_LPX) | 105562306a36Sopenharmony_ci VC4_SET_FIELD(max(dsi_hs_timing(ui_ns, 0, 8), 105662306a36Sopenharmony_ci dsi_hs_timing(ui_ns, 60, 4)), 105762306a36Sopenharmony_ci DSI_HS_DLT4_TRAIL) | 105862306a36Sopenharmony_ci VC4_SET_FIELD(0, DSI_HS_DLT4_ANLAT)); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* T_INIT is how long STOP is driven after power-up to 106162306a36Sopenharmony_ci * indicate to the slave (also coming out of power-up) that 106262306a36Sopenharmony_ci * master init is complete, and should be greater than the 106362306a36Sopenharmony_ci * maximum of two value: T_INIT,MASTER and T_INIT,SLAVE. The 106462306a36Sopenharmony_ci * D-PHY spec gives a minimum 100us for T_INIT,MASTER and 106562306a36Sopenharmony_ci * T_INIT,SLAVE, while allowing protocols on top of it to give 106662306a36Sopenharmony_ci * greater minimums. The vc4 firmware uses an extremely 106762306a36Sopenharmony_ci * conservative 5ms, and we maintain that here. 106862306a36Sopenharmony_ci */ 106962306a36Sopenharmony_ci DSI_PORT_WRITE(HS_DLT5, VC4_SET_FIELD(dsi_hs_timing(ui_ns, 107062306a36Sopenharmony_ci 5 * 1000 * 1000, 0), 107162306a36Sopenharmony_ci DSI_HS_DLT5_INIT)); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci DSI_PORT_WRITE(HS_DLT6, 107462306a36Sopenharmony_ci VC4_SET_FIELD(lpx * 5, DSI_HS_DLT6_TA_GET) | 107562306a36Sopenharmony_ci VC4_SET_FIELD(lpx, DSI_HS_DLT6_TA_SURE) | 107662306a36Sopenharmony_ci VC4_SET_FIELD(lpx * 4, DSI_HS_DLT6_TA_GO) | 107762306a36Sopenharmony_ci VC4_SET_FIELD(lpx, DSI_HS_DLT6_LP_LPX)); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci DSI_PORT_WRITE(HS_DLT7, 108062306a36Sopenharmony_ci VC4_SET_FIELD(dsi_esc_timing(1000000), 108162306a36Sopenharmony_ci DSI_HS_DLT7_LP_WUP)); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci DSI_PORT_WRITE(PHYC, 108462306a36Sopenharmony_ci DSI_PHYC_DLANE0_ENABLE | 108562306a36Sopenharmony_ci (dsi->lanes >= 2 ? DSI_PHYC_DLANE1_ENABLE : 0) | 108662306a36Sopenharmony_ci (dsi->lanes >= 3 ? DSI_PHYC_DLANE2_ENABLE : 0) | 108762306a36Sopenharmony_ci (dsi->lanes >= 4 ? DSI_PHYC_DLANE3_ENABLE : 0) | 108862306a36Sopenharmony_ci DSI_PORT_BIT(PHYC_CLANE_ENABLE) | 108962306a36Sopenharmony_ci ((dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 109062306a36Sopenharmony_ci 0 : DSI_PORT_BIT(PHYC_HS_CLK_CONTINUOUS)) | 109162306a36Sopenharmony_ci (dsi->variant->port == 0 ? 109262306a36Sopenharmony_ci VC4_SET_FIELD(lpx - 1, DSI0_PHYC_ESC_CLK_LPDT) : 109362306a36Sopenharmony_ci VC4_SET_FIELD(lpx - 1, DSI1_PHYC_ESC_CLK_LPDT))); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci DSI_PORT_WRITE(CTRL, 109662306a36Sopenharmony_ci DSI_PORT_READ(CTRL) | 109762306a36Sopenharmony_ci DSI_CTRL_CAL_BYTE); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci /* HS timeout in HS clock cycles: disabled. */ 110062306a36Sopenharmony_ci DSI_PORT_WRITE(HSTX_TO_CNT, 0); 110162306a36Sopenharmony_ci /* LP receive timeout in HS clocks. */ 110262306a36Sopenharmony_ci DSI_PORT_WRITE(LPRX_TO_CNT, 0xffffff); 110362306a36Sopenharmony_ci /* Bus turnaround timeout */ 110462306a36Sopenharmony_ci DSI_PORT_WRITE(TA_TO_CNT, 100000); 110562306a36Sopenharmony_ci /* Display reset sequence timeout */ 110662306a36Sopenharmony_ci DSI_PORT_WRITE(PR_TO_CNT, 100000); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci /* Set up DISP1 for transferring long command payloads through 110962306a36Sopenharmony_ci * the pixfifo. 111062306a36Sopenharmony_ci */ 111162306a36Sopenharmony_ci DSI_PORT_WRITE(DISP1_CTRL, 111262306a36Sopenharmony_ci VC4_SET_FIELD(DSI_DISP1_PFORMAT_32BIT_LE, 111362306a36Sopenharmony_ci DSI_DISP1_PFORMAT) | 111462306a36Sopenharmony_ci DSI_DISP1_ENABLE); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci /* Ungate the block. */ 111762306a36Sopenharmony_ci if (dsi->variant->port == 0) 111862306a36Sopenharmony_ci DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI0_CTRL_CTRL0); 111962306a36Sopenharmony_ci else 112062306a36Sopenharmony_ci DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI1_CTRL_EN); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* Bring AFE out of reset. */ 112362306a36Sopenharmony_ci DSI_PORT_WRITE(PHY_AFEC0, 112462306a36Sopenharmony_ci DSI_PORT_READ(PHY_AFEC0) & 112562306a36Sopenharmony_ci ~DSI_PORT_BIT(PHY_AFEC0_RESET)); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci vc4_dsi_ulps(dsi, false); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { 113062306a36Sopenharmony_ci DSI_PORT_WRITE(DISP0_CTRL, 113162306a36Sopenharmony_ci VC4_SET_FIELD(dsi->divider, 113262306a36Sopenharmony_ci DSI_DISP0_PIX_CLK_DIV) | 113362306a36Sopenharmony_ci VC4_SET_FIELD(dsi->format, DSI_DISP0_PFORMAT) | 113462306a36Sopenharmony_ci VC4_SET_FIELD(DSI_DISP0_LP_STOP_PERFRAME, 113562306a36Sopenharmony_ci DSI_DISP0_LP_STOP_CTRL) | 113662306a36Sopenharmony_ci DSI_DISP0_ST_END); 113762306a36Sopenharmony_ci } else { 113862306a36Sopenharmony_ci DSI_PORT_WRITE(DISP0_CTRL, 113962306a36Sopenharmony_ci DSI_DISP0_COMMAND_MODE); 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic void vc4_dsi_bridge_enable(struct drm_bridge *bridge, 114462306a36Sopenharmony_ci struct drm_bridge_state *old_state) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); 114762306a36Sopenharmony_ci bool debug_dump_regs = false; 114862306a36Sopenharmony_ci u32 disp0_ctrl; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci disp0_ctrl = DSI_PORT_READ(DISP0_CTRL); 115162306a36Sopenharmony_ci disp0_ctrl |= DSI_DISP0_ENABLE; 115262306a36Sopenharmony_ci DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (debug_dump_regs) { 115562306a36Sopenharmony_ci struct drm_printer p = drm_info_printer(&dsi->pdev->dev); 115662306a36Sopenharmony_ci dev_info(&dsi->pdev->dev, "DSI regs after:\n"); 115762306a36Sopenharmony_ci drm_print_regset32(&p, &dsi->regset); 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci} 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic int vc4_dsi_bridge_attach(struct drm_bridge *bridge, 116262306a36Sopenharmony_ci enum drm_bridge_attach_flags flags) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* Attach the panel or bridge to the dsi bridge */ 116762306a36Sopenharmony_ci return drm_bridge_attach(bridge->encoder, dsi->out_bridge, 116862306a36Sopenharmony_ci &dsi->bridge, flags); 116962306a36Sopenharmony_ci} 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_cistatic ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, 117262306a36Sopenharmony_ci const struct mipi_dsi_msg *msg) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci struct vc4_dsi *dsi = host_to_dsi(host); 117562306a36Sopenharmony_ci struct mipi_dsi_packet packet; 117662306a36Sopenharmony_ci u32 pkth = 0, pktc = 0; 117762306a36Sopenharmony_ci int i, ret; 117862306a36Sopenharmony_ci bool is_long = mipi_dsi_packet_format_is_long(msg->type); 117962306a36Sopenharmony_ci u32 cmd_fifo_len = 0, pix_fifo_len = 0; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci mipi_dsi_create_packet(&packet, msg); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci pkth |= VC4_SET_FIELD(packet.header[0], DSI_TXPKT1H_BC_DT); 118462306a36Sopenharmony_ci pkth |= VC4_SET_FIELD(packet.header[1] | 118562306a36Sopenharmony_ci (packet.header[2] << 8), 118662306a36Sopenharmony_ci DSI_TXPKT1H_BC_PARAM); 118762306a36Sopenharmony_ci if (is_long) { 118862306a36Sopenharmony_ci /* Divide data across the various FIFOs we have available. 118962306a36Sopenharmony_ci * The command FIFO takes byte-oriented data, but is of 119062306a36Sopenharmony_ci * limited size. The pixel FIFO (never actually used for 119162306a36Sopenharmony_ci * pixel data in reality) is word oriented, and substantially 119262306a36Sopenharmony_ci * larger. So, we use the pixel FIFO for most of the data, 119362306a36Sopenharmony_ci * sending the residual bytes in the command FIFO at the start. 119462306a36Sopenharmony_ci * 119562306a36Sopenharmony_ci * With this arrangement, the command FIFO will never get full. 119662306a36Sopenharmony_ci */ 119762306a36Sopenharmony_ci if (packet.payload_length <= 16) { 119862306a36Sopenharmony_ci cmd_fifo_len = packet.payload_length; 119962306a36Sopenharmony_ci pix_fifo_len = 0; 120062306a36Sopenharmony_ci } else { 120162306a36Sopenharmony_ci cmd_fifo_len = (packet.payload_length % 120262306a36Sopenharmony_ci DSI_PIX_FIFO_WIDTH); 120362306a36Sopenharmony_ci pix_fifo_len = ((packet.payload_length - cmd_fifo_len) / 120462306a36Sopenharmony_ci DSI_PIX_FIFO_WIDTH); 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci WARN_ON_ONCE(pix_fifo_len >= DSI_PIX_FIFO_DEPTH); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci pkth |= VC4_SET_FIELD(cmd_fifo_len, DSI_TXPKT1H_BC_CMDFIFO); 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (msg->rx_len) { 121362306a36Sopenharmony_ci pktc |= VC4_SET_FIELD(DSI_TXPKT1C_CMD_CTRL_RX, 121462306a36Sopenharmony_ci DSI_TXPKT1C_CMD_CTRL); 121562306a36Sopenharmony_ci } else { 121662306a36Sopenharmony_ci pktc |= VC4_SET_FIELD(DSI_TXPKT1C_CMD_CTRL_TX, 121762306a36Sopenharmony_ci DSI_TXPKT1C_CMD_CTRL); 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci for (i = 0; i < cmd_fifo_len; i++) 122162306a36Sopenharmony_ci DSI_PORT_WRITE(TXPKT_CMD_FIFO, packet.payload[i]); 122262306a36Sopenharmony_ci for (i = 0; i < pix_fifo_len; i++) { 122362306a36Sopenharmony_ci const u8 *pix = packet.payload + cmd_fifo_len + i * 4; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci DSI_PORT_WRITE(TXPKT_PIX_FIFO, 122662306a36Sopenharmony_ci pix[0] | 122762306a36Sopenharmony_ci pix[1] << 8 | 122862306a36Sopenharmony_ci pix[2] << 16 | 122962306a36Sopenharmony_ci pix[3] << 24); 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (msg->flags & MIPI_DSI_MSG_USE_LPM) 123362306a36Sopenharmony_ci pktc |= DSI_TXPKT1C_CMD_MODE_LP; 123462306a36Sopenharmony_ci if (is_long) 123562306a36Sopenharmony_ci pktc |= DSI_TXPKT1C_CMD_TYPE_LONG; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* Send one copy of the packet. Larger repeats are used for pixel 123862306a36Sopenharmony_ci * data in command mode. 123962306a36Sopenharmony_ci */ 124062306a36Sopenharmony_ci pktc |= VC4_SET_FIELD(1, DSI_TXPKT1C_CMD_REPEAT); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci pktc |= DSI_TXPKT1C_CMD_EN; 124362306a36Sopenharmony_ci if (pix_fifo_len) { 124462306a36Sopenharmony_ci pktc |= VC4_SET_FIELD(DSI_TXPKT1C_DISPLAY_NO_SECONDARY, 124562306a36Sopenharmony_ci DSI_TXPKT1C_DISPLAY_NO); 124662306a36Sopenharmony_ci } else { 124762306a36Sopenharmony_ci pktc |= VC4_SET_FIELD(DSI_TXPKT1C_DISPLAY_NO_SHORT, 124862306a36Sopenharmony_ci DSI_TXPKT1C_DISPLAY_NO); 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci /* Enable the appropriate interrupt for the transfer completion. */ 125262306a36Sopenharmony_ci dsi->xfer_result = 0; 125362306a36Sopenharmony_ci reinit_completion(&dsi->xfer_completion); 125462306a36Sopenharmony_ci if (dsi->variant->port == 0) { 125562306a36Sopenharmony_ci DSI_PORT_WRITE(INT_STAT, 125662306a36Sopenharmony_ci DSI0_INT_CMDC_DONE_MASK | DSI1_INT_PHY_DIR_RTF); 125762306a36Sopenharmony_ci if (msg->rx_len) { 125862306a36Sopenharmony_ci DSI_PORT_WRITE(INT_EN, (DSI0_INTERRUPTS_ALWAYS_ENABLED | 125962306a36Sopenharmony_ci DSI0_INT_PHY_DIR_RTF)); 126062306a36Sopenharmony_ci } else { 126162306a36Sopenharmony_ci DSI_PORT_WRITE(INT_EN, 126262306a36Sopenharmony_ci (DSI0_INTERRUPTS_ALWAYS_ENABLED | 126362306a36Sopenharmony_ci VC4_SET_FIELD(DSI0_INT_CMDC_DONE_NO_REPEAT, 126462306a36Sopenharmony_ci DSI0_INT_CMDC_DONE))); 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci } else { 126762306a36Sopenharmony_ci DSI_PORT_WRITE(INT_STAT, 126862306a36Sopenharmony_ci DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF); 126962306a36Sopenharmony_ci if (msg->rx_len) { 127062306a36Sopenharmony_ci DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | 127162306a36Sopenharmony_ci DSI1_INT_PHY_DIR_RTF)); 127262306a36Sopenharmony_ci } else { 127362306a36Sopenharmony_ci DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | 127462306a36Sopenharmony_ci DSI1_INT_TXPKT1_DONE)); 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci /* Send the packet. */ 127962306a36Sopenharmony_ci DSI_PORT_WRITE(TXPKT1H, pkth); 128062306a36Sopenharmony_ci DSI_PORT_WRITE(TXPKT1C, pktc); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci if (!wait_for_completion_timeout(&dsi->xfer_completion, 128362306a36Sopenharmony_ci msecs_to_jiffies(1000))) { 128462306a36Sopenharmony_ci dev_err(&dsi->pdev->dev, "transfer interrupt wait timeout"); 128562306a36Sopenharmony_ci dev_err(&dsi->pdev->dev, "instat: 0x%08x\n", 128662306a36Sopenharmony_ci DSI_PORT_READ(INT_STAT)); 128762306a36Sopenharmony_ci ret = -ETIMEDOUT; 128862306a36Sopenharmony_ci } else { 128962306a36Sopenharmony_ci ret = dsi->xfer_result; 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED)); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci if (ret) 129562306a36Sopenharmony_ci goto reset_fifo_and_return; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (ret == 0 && msg->rx_len) { 129862306a36Sopenharmony_ci u32 rxpkt1h = DSI_PORT_READ(RXPKT1H); 129962306a36Sopenharmony_ci u8 *msg_rx = msg->rx_buf; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (rxpkt1h & DSI_RXPKT1H_PKT_TYPE_LONG) { 130262306a36Sopenharmony_ci u32 rxlen = VC4_GET_FIELD(rxpkt1h, 130362306a36Sopenharmony_ci DSI_RXPKT1H_BC_PARAM); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci if (rxlen != msg->rx_len) { 130662306a36Sopenharmony_ci DRM_ERROR("DSI returned %db, expecting %db\n", 130762306a36Sopenharmony_ci rxlen, (int)msg->rx_len); 130862306a36Sopenharmony_ci ret = -ENXIO; 130962306a36Sopenharmony_ci goto reset_fifo_and_return; 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci for (i = 0; i < msg->rx_len; i++) 131362306a36Sopenharmony_ci msg_rx[i] = DSI_READ(DSI1_RXPKT_FIFO); 131462306a36Sopenharmony_ci } else { 131562306a36Sopenharmony_ci /* FINISHME: Handle AWER */ 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci msg_rx[0] = VC4_GET_FIELD(rxpkt1h, 131862306a36Sopenharmony_ci DSI_RXPKT1H_SHORT_0); 131962306a36Sopenharmony_ci if (msg->rx_len > 1) { 132062306a36Sopenharmony_ci msg_rx[1] = VC4_GET_FIELD(rxpkt1h, 132162306a36Sopenharmony_ci DSI_RXPKT1H_SHORT_1); 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci return ret; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_cireset_fifo_and_return: 132962306a36Sopenharmony_ci DRM_ERROR("DSI transfer failed, resetting: %d\n", ret); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci DSI_PORT_WRITE(TXPKT1C, DSI_PORT_READ(TXPKT1C) & ~DSI_TXPKT1C_CMD_EN); 133262306a36Sopenharmony_ci udelay(1); 133362306a36Sopenharmony_ci DSI_PORT_WRITE(CTRL, 133462306a36Sopenharmony_ci DSI_PORT_READ(CTRL) | 133562306a36Sopenharmony_ci DSI_PORT_BIT(CTRL_RESET_FIFOS)); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci DSI_PORT_WRITE(TXPKT1C, 0); 133862306a36Sopenharmony_ci DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED)); 133962306a36Sopenharmony_ci return ret; 134062306a36Sopenharmony_ci} 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_cistatic const struct component_ops vc4_dsi_ops; 134362306a36Sopenharmony_cistatic int vc4_dsi_host_attach(struct mipi_dsi_host *host, 134462306a36Sopenharmony_ci struct mipi_dsi_device *device) 134562306a36Sopenharmony_ci{ 134662306a36Sopenharmony_ci struct vc4_dsi *dsi = host_to_dsi(host); 134762306a36Sopenharmony_ci int ret; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci dsi->lanes = device->lanes; 135062306a36Sopenharmony_ci dsi->channel = device->channel; 135162306a36Sopenharmony_ci dsi->mode_flags = device->mode_flags; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci switch (device->format) { 135462306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB888: 135562306a36Sopenharmony_ci dsi->format = DSI_PFORMAT_RGB888; 135662306a36Sopenharmony_ci dsi->divider = 24 / dsi->lanes; 135762306a36Sopenharmony_ci break; 135862306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB666: 135962306a36Sopenharmony_ci dsi->format = DSI_PFORMAT_RGB666; 136062306a36Sopenharmony_ci dsi->divider = 24 / dsi->lanes; 136162306a36Sopenharmony_ci break; 136262306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB666_PACKED: 136362306a36Sopenharmony_ci dsi->format = DSI_PFORMAT_RGB666_PACKED; 136462306a36Sopenharmony_ci dsi->divider = 18 / dsi->lanes; 136562306a36Sopenharmony_ci break; 136662306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB565: 136762306a36Sopenharmony_ci dsi->format = DSI_PFORMAT_RGB565; 136862306a36Sopenharmony_ci dsi->divider = 16 / dsi->lanes; 136962306a36Sopenharmony_ci break; 137062306a36Sopenharmony_ci default: 137162306a36Sopenharmony_ci dev_err(&dsi->pdev->dev, "Unknown DSI format: %d.\n", 137262306a36Sopenharmony_ci dsi->format); 137362306a36Sopenharmony_ci return 0; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) { 137762306a36Sopenharmony_ci dev_err(&dsi->pdev->dev, 137862306a36Sopenharmony_ci "Only VIDEO mode panels supported currently.\n"); 137962306a36Sopenharmony_ci return 0; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci drm_bridge_add(&dsi->bridge); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci ret = component_add(&dsi->pdev->dev, &vc4_dsi_ops); 138562306a36Sopenharmony_ci if (ret) { 138662306a36Sopenharmony_ci drm_bridge_remove(&dsi->bridge); 138762306a36Sopenharmony_ci return ret; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci return 0; 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic int vc4_dsi_host_detach(struct mipi_dsi_host *host, 139462306a36Sopenharmony_ci struct mipi_dsi_device *device) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci struct vc4_dsi *dsi = host_to_dsi(host); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci component_del(&dsi->pdev->dev, &vc4_dsi_ops); 139962306a36Sopenharmony_ci drm_bridge_remove(&dsi->bridge); 140062306a36Sopenharmony_ci return 0; 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_cistatic const struct mipi_dsi_host_ops vc4_dsi_host_ops = { 140462306a36Sopenharmony_ci .attach = vc4_dsi_host_attach, 140562306a36Sopenharmony_ci .detach = vc4_dsi_host_detach, 140662306a36Sopenharmony_ci .transfer = vc4_dsi_host_transfer, 140762306a36Sopenharmony_ci}; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_cistatic const struct drm_bridge_funcs vc4_dsi_bridge_funcs = { 141062306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, 141162306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, 141262306a36Sopenharmony_ci .atomic_reset = drm_atomic_helper_bridge_reset, 141362306a36Sopenharmony_ci .atomic_pre_enable = vc4_dsi_bridge_pre_enable, 141462306a36Sopenharmony_ci .atomic_enable = vc4_dsi_bridge_enable, 141562306a36Sopenharmony_ci .atomic_disable = vc4_dsi_bridge_disable, 141662306a36Sopenharmony_ci .atomic_post_disable = vc4_dsi_bridge_post_disable, 141762306a36Sopenharmony_ci .attach = vc4_dsi_bridge_attach, 141862306a36Sopenharmony_ci .mode_fixup = vc4_dsi_bridge_mode_fixup, 141962306a36Sopenharmony_ci}; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_cistatic int vc4_dsi_late_register(struct drm_encoder *encoder) 142262306a36Sopenharmony_ci{ 142362306a36Sopenharmony_ci struct drm_device *drm = encoder->dev; 142462306a36Sopenharmony_ci struct vc4_dsi *dsi = to_vc4_dsi(encoder); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset); 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci return 0; 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_cistatic const struct drm_encoder_funcs vc4_dsi_encoder_funcs = { 143262306a36Sopenharmony_ci .late_register = vc4_dsi_late_register, 143362306a36Sopenharmony_ci}; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_cistatic const struct vc4_dsi_variant bcm2711_dsi1_variant = { 143662306a36Sopenharmony_ci .port = 1, 143762306a36Sopenharmony_ci .debugfs_name = "dsi1_regs", 143862306a36Sopenharmony_ci .regs = dsi1_regs, 143962306a36Sopenharmony_ci .nregs = ARRAY_SIZE(dsi1_regs), 144062306a36Sopenharmony_ci}; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_cistatic const struct vc4_dsi_variant bcm2835_dsi0_variant = { 144362306a36Sopenharmony_ci .port = 0, 144462306a36Sopenharmony_ci .debugfs_name = "dsi0_regs", 144562306a36Sopenharmony_ci .regs = dsi0_regs, 144662306a36Sopenharmony_ci .nregs = ARRAY_SIZE(dsi0_regs), 144762306a36Sopenharmony_ci}; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_cistatic const struct vc4_dsi_variant bcm2835_dsi1_variant = { 145062306a36Sopenharmony_ci .port = 1, 145162306a36Sopenharmony_ci .broken_axi_workaround = true, 145262306a36Sopenharmony_ci .debugfs_name = "dsi1_regs", 145362306a36Sopenharmony_ci .regs = dsi1_regs, 145462306a36Sopenharmony_ci .nregs = ARRAY_SIZE(dsi1_regs), 145562306a36Sopenharmony_ci}; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic const struct of_device_id vc4_dsi_dt_match[] = { 145862306a36Sopenharmony_ci { .compatible = "brcm,bcm2711-dsi1", &bcm2711_dsi1_variant }, 145962306a36Sopenharmony_ci { .compatible = "brcm,bcm2835-dsi0", &bcm2835_dsi0_variant }, 146062306a36Sopenharmony_ci { .compatible = "brcm,bcm2835-dsi1", &bcm2835_dsi1_variant }, 146162306a36Sopenharmony_ci {} 146262306a36Sopenharmony_ci}; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_cistatic void dsi_handle_error(struct vc4_dsi *dsi, 146562306a36Sopenharmony_ci irqreturn_t *ret, u32 stat, u32 bit, 146662306a36Sopenharmony_ci const char *type) 146762306a36Sopenharmony_ci{ 146862306a36Sopenharmony_ci if (!(stat & bit)) 146962306a36Sopenharmony_ci return; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci DRM_ERROR("DSI%d: %s error\n", dsi->variant->port, type); 147262306a36Sopenharmony_ci *ret = IRQ_HANDLED; 147362306a36Sopenharmony_ci} 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci/* 147662306a36Sopenharmony_ci * Initial handler for port 1 where we need the reg_dma workaround. 147762306a36Sopenharmony_ci * The register DMA writes sleep, so we can't do it in the top half. 147862306a36Sopenharmony_ci * Instead we use IRQF_ONESHOT so that the IRQ gets disabled in the 147962306a36Sopenharmony_ci * parent interrupt contrller until our interrupt thread is done. 148062306a36Sopenharmony_ci */ 148162306a36Sopenharmony_cistatic irqreturn_t vc4_dsi_irq_defer_to_thread_handler(int irq, void *data) 148262306a36Sopenharmony_ci{ 148362306a36Sopenharmony_ci struct vc4_dsi *dsi = data; 148462306a36Sopenharmony_ci u32 stat = DSI_PORT_READ(INT_STAT); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (!stat) 148762306a36Sopenharmony_ci return IRQ_NONE; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci/* 149362306a36Sopenharmony_ci * Normal IRQ handler for port 0, or the threaded IRQ handler for port 149462306a36Sopenharmony_ci * 1 where we need the reg_dma workaround. 149562306a36Sopenharmony_ci */ 149662306a36Sopenharmony_cistatic irqreturn_t vc4_dsi_irq_handler(int irq, void *data) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci struct vc4_dsi *dsi = data; 149962306a36Sopenharmony_ci u32 stat = DSI_PORT_READ(INT_STAT); 150062306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci DSI_PORT_WRITE(INT_STAT, stat); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 150562306a36Sopenharmony_ci DSI_PORT_BIT(INT_ERR_SYNC_ESC), "LPDT sync"); 150662306a36Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 150762306a36Sopenharmony_ci DSI_PORT_BIT(INT_ERR_CONTROL), "data lane 0 sequence"); 150862306a36Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 150962306a36Sopenharmony_ci DSI_PORT_BIT(INT_ERR_CONT_LP0), "LP0 contention"); 151062306a36Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 151162306a36Sopenharmony_ci DSI_PORT_BIT(INT_ERR_CONT_LP1), "LP1 contention"); 151262306a36Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 151362306a36Sopenharmony_ci DSI_PORT_BIT(INT_HSTX_TO), "HSTX timeout"); 151462306a36Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 151562306a36Sopenharmony_ci DSI_PORT_BIT(INT_LPRX_TO), "LPRX timeout"); 151662306a36Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 151762306a36Sopenharmony_ci DSI_PORT_BIT(INT_TA_TO), "turnaround timeout"); 151862306a36Sopenharmony_ci dsi_handle_error(dsi, &ret, stat, 151962306a36Sopenharmony_ci DSI_PORT_BIT(INT_PR_TO), "peripheral reset timeout"); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci if (stat & ((dsi->variant->port ? DSI1_INT_TXPKT1_DONE : 152262306a36Sopenharmony_ci DSI0_INT_CMDC_DONE_MASK) | 152362306a36Sopenharmony_ci DSI_PORT_BIT(INT_PHY_DIR_RTF))) { 152462306a36Sopenharmony_ci complete(&dsi->xfer_completion); 152562306a36Sopenharmony_ci ret = IRQ_HANDLED; 152662306a36Sopenharmony_ci } else if (stat & DSI_PORT_BIT(INT_HSTX_TO)) { 152762306a36Sopenharmony_ci complete(&dsi->xfer_completion); 152862306a36Sopenharmony_ci dsi->xfer_result = -ETIMEDOUT; 152962306a36Sopenharmony_ci ret = IRQ_HANDLED; 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci return ret; 153362306a36Sopenharmony_ci} 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci/** 153662306a36Sopenharmony_ci * vc4_dsi_init_phy_clocks - Exposes clocks generated by the analog 153762306a36Sopenharmony_ci * PHY that are consumed by CPRMAN (clk-bcm2835.c). 153862306a36Sopenharmony_ci * @dsi: DSI encoder 153962306a36Sopenharmony_ci */ 154062306a36Sopenharmony_cistatic int 154162306a36Sopenharmony_civc4_dsi_init_phy_clocks(struct vc4_dsi *dsi) 154262306a36Sopenharmony_ci{ 154362306a36Sopenharmony_ci struct device *dev = &dsi->pdev->dev; 154462306a36Sopenharmony_ci const char *parent_name = __clk_get_name(dsi->pll_phy_clock); 154562306a36Sopenharmony_ci static const struct { 154662306a36Sopenharmony_ci const char *name; 154762306a36Sopenharmony_ci int div; 154862306a36Sopenharmony_ci } phy_clocks[] = { 154962306a36Sopenharmony_ci { "byte", 8 }, 155062306a36Sopenharmony_ci { "ddr2", 4 }, 155162306a36Sopenharmony_ci { "ddr", 2 }, 155262306a36Sopenharmony_ci }; 155362306a36Sopenharmony_ci int i; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci dsi->clk_onecell = devm_kzalloc(dev, 155662306a36Sopenharmony_ci sizeof(*dsi->clk_onecell) + 155762306a36Sopenharmony_ci ARRAY_SIZE(phy_clocks) * 155862306a36Sopenharmony_ci sizeof(struct clk_hw *), 155962306a36Sopenharmony_ci GFP_KERNEL); 156062306a36Sopenharmony_ci if (!dsi->clk_onecell) 156162306a36Sopenharmony_ci return -ENOMEM; 156262306a36Sopenharmony_ci dsi->clk_onecell->num = ARRAY_SIZE(phy_clocks); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(phy_clocks); i++) { 156562306a36Sopenharmony_ci struct clk_fixed_factor *fix = &dsi->phy_clocks[i]; 156662306a36Sopenharmony_ci struct clk_init_data init; 156762306a36Sopenharmony_ci char clk_name[16]; 156862306a36Sopenharmony_ci int ret; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci snprintf(clk_name, sizeof(clk_name), 157162306a36Sopenharmony_ci "dsi%u_%s", dsi->variant->port, phy_clocks[i].name); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci /* We just use core fixed factor clock ops for the PHY 157462306a36Sopenharmony_ci * clocks. The clocks are actually gated by the 157562306a36Sopenharmony_ci * PHY_AFEC0_DDRCLK_EN bits, which we should be 157662306a36Sopenharmony_ci * setting if we use the DDR/DDR2 clocks. However, 157762306a36Sopenharmony_ci * vc4_dsi_encoder_enable() is setting up both AFEC0, 157862306a36Sopenharmony_ci * setting both our parent DSI PLL's rate and this 157962306a36Sopenharmony_ci * clock's rate, so it knows if DDR/DDR2 are going to 158062306a36Sopenharmony_ci * be used and could enable the gates itself. 158162306a36Sopenharmony_ci */ 158262306a36Sopenharmony_ci fix->mult = 1; 158362306a36Sopenharmony_ci fix->div = phy_clocks[i].div; 158462306a36Sopenharmony_ci fix->hw.init = &init; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 158762306a36Sopenharmony_ci init.parent_names = &parent_name; 158862306a36Sopenharmony_ci init.num_parents = 1; 158962306a36Sopenharmony_ci init.name = clk_name; 159062306a36Sopenharmony_ci init.ops = &clk_fixed_factor_ops; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &fix->hw); 159362306a36Sopenharmony_ci if (ret) 159462306a36Sopenharmony_ci return ret; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci dsi->clk_onecell->hws[i] = &fix->hw; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci return of_clk_add_hw_provider(dev->of_node, 160062306a36Sopenharmony_ci of_clk_hw_onecell_get, 160162306a36Sopenharmony_ci dsi->clk_onecell); 160262306a36Sopenharmony_ci} 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_cistatic void vc4_dsi_dma_mem_release(void *ptr) 160562306a36Sopenharmony_ci{ 160662306a36Sopenharmony_ci struct vc4_dsi *dsi = ptr; 160762306a36Sopenharmony_ci struct device *dev = &dsi->pdev->dev; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci dma_free_coherent(dev, 4, dsi->reg_dma_mem, dsi->reg_dma_paddr); 161062306a36Sopenharmony_ci dsi->reg_dma_mem = NULL; 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cistatic void vc4_dsi_dma_chan_release(void *ptr) 161462306a36Sopenharmony_ci{ 161562306a36Sopenharmony_ci struct vc4_dsi *dsi = ptr; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci dma_release_channel(dsi->reg_dma_chan); 161862306a36Sopenharmony_ci dsi->reg_dma_chan = NULL; 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_cistatic void vc4_dsi_release(struct kref *kref) 162262306a36Sopenharmony_ci{ 162362306a36Sopenharmony_ci struct vc4_dsi *dsi = 162462306a36Sopenharmony_ci container_of(kref, struct vc4_dsi, kref); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci kfree(dsi); 162762306a36Sopenharmony_ci} 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_cistatic void vc4_dsi_get(struct vc4_dsi *dsi) 163062306a36Sopenharmony_ci{ 163162306a36Sopenharmony_ci kref_get(&dsi->kref); 163262306a36Sopenharmony_ci} 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_cistatic void vc4_dsi_put(struct vc4_dsi *dsi) 163562306a36Sopenharmony_ci{ 163662306a36Sopenharmony_ci kref_put(&dsi->kref, &vc4_dsi_release); 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cistatic void vc4_dsi_release_action(struct drm_device *drm, void *ptr) 164062306a36Sopenharmony_ci{ 164162306a36Sopenharmony_ci struct vc4_dsi *dsi = ptr; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci vc4_dsi_put(dsi); 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_cistatic int vc4_dsi_bind(struct device *dev, struct device *master, void *data) 164762306a36Sopenharmony_ci{ 164862306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 164962306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(master); 165062306a36Sopenharmony_ci struct vc4_dsi *dsi = dev_get_drvdata(dev); 165162306a36Sopenharmony_ci struct drm_encoder *encoder = &dsi->encoder.base; 165262306a36Sopenharmony_ci int ret; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci vc4_dsi_get(dsi); 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci ret = drmm_add_action_or_reset(drm, vc4_dsi_release_action, dsi); 165762306a36Sopenharmony_ci if (ret) 165862306a36Sopenharmony_ci return ret; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci dsi->variant = of_device_get_match_data(dev); 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci dsi->encoder.type = dsi->variant->port ? 166362306a36Sopenharmony_ci VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci dsi->regs = vc4_ioremap_regs(pdev, 0); 166662306a36Sopenharmony_ci if (IS_ERR(dsi->regs)) 166762306a36Sopenharmony_ci return PTR_ERR(dsi->regs); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci dsi->regset.base = dsi->regs; 167062306a36Sopenharmony_ci dsi->regset.regs = dsi->variant->regs; 167162306a36Sopenharmony_ci dsi->regset.nregs = dsi->variant->nregs; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci if (DSI_PORT_READ(ID) != DSI_ID_VALUE) { 167462306a36Sopenharmony_ci dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n", 167562306a36Sopenharmony_ci DSI_PORT_READ(ID), DSI_ID_VALUE); 167662306a36Sopenharmony_ci return -ENODEV; 167762306a36Sopenharmony_ci } 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci /* DSI1 on BCM2835/6/7 has a broken AXI slave that doesn't respond to 168062306a36Sopenharmony_ci * writes from the ARM. It does handle writes from the DMA engine, 168162306a36Sopenharmony_ci * so set up a channel for talking to it. 168262306a36Sopenharmony_ci */ 168362306a36Sopenharmony_ci if (dsi->variant->broken_axi_workaround) { 168462306a36Sopenharmony_ci dma_cap_mask_t dma_mask; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci dsi->reg_dma_mem = dma_alloc_coherent(dev, 4, 168762306a36Sopenharmony_ci &dsi->reg_dma_paddr, 168862306a36Sopenharmony_ci GFP_KERNEL); 168962306a36Sopenharmony_ci if (!dsi->reg_dma_mem) { 169062306a36Sopenharmony_ci DRM_ERROR("Failed to get DMA memory\n"); 169162306a36Sopenharmony_ci return -ENOMEM; 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, vc4_dsi_dma_mem_release, dsi); 169562306a36Sopenharmony_ci if (ret) 169662306a36Sopenharmony_ci return ret; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci dma_cap_zero(dma_mask); 169962306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dma_mask); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci dsi->reg_dma_chan = dma_request_chan_by_mask(&dma_mask); 170262306a36Sopenharmony_ci if (IS_ERR(dsi->reg_dma_chan)) { 170362306a36Sopenharmony_ci ret = PTR_ERR(dsi->reg_dma_chan); 170462306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 170562306a36Sopenharmony_ci DRM_ERROR("Failed to get DMA channel: %d\n", 170662306a36Sopenharmony_ci ret); 170762306a36Sopenharmony_ci return ret; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, vc4_dsi_dma_chan_release, dsi); 171162306a36Sopenharmony_ci if (ret) 171262306a36Sopenharmony_ci return ret; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci /* Get the physical address of the device's registers. The 171562306a36Sopenharmony_ci * struct resource for the regs gives us the bus address 171662306a36Sopenharmony_ci * instead. 171762306a36Sopenharmony_ci */ 171862306a36Sopenharmony_ci dsi->reg_paddr = be32_to_cpup(of_get_address(dev->of_node, 171962306a36Sopenharmony_ci 0, NULL, NULL)); 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci init_completion(&dsi->xfer_completion); 172362306a36Sopenharmony_ci /* At startup enable error-reporting interrupts and nothing else. */ 172462306a36Sopenharmony_ci DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); 172562306a36Sopenharmony_ci /* Clear any existing interrupt state. */ 172662306a36Sopenharmony_ci DSI_PORT_WRITE(INT_STAT, DSI_PORT_READ(INT_STAT)); 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci if (dsi->reg_dma_mem) 172962306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), 173062306a36Sopenharmony_ci vc4_dsi_irq_defer_to_thread_handler, 173162306a36Sopenharmony_ci vc4_dsi_irq_handler, 173262306a36Sopenharmony_ci IRQF_ONESHOT, 173362306a36Sopenharmony_ci "vc4 dsi", dsi); 173462306a36Sopenharmony_ci else 173562306a36Sopenharmony_ci ret = devm_request_irq(dev, platform_get_irq(pdev, 0), 173662306a36Sopenharmony_ci vc4_dsi_irq_handler, 0, "vc4 dsi", dsi); 173762306a36Sopenharmony_ci if (ret) { 173862306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 173962306a36Sopenharmony_ci dev_err(dev, "Failed to get interrupt: %d\n", ret); 174062306a36Sopenharmony_ci return ret; 174162306a36Sopenharmony_ci } 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci dsi->escape_clock = devm_clk_get(dev, "escape"); 174462306a36Sopenharmony_ci if (IS_ERR(dsi->escape_clock)) { 174562306a36Sopenharmony_ci ret = PTR_ERR(dsi->escape_clock); 174662306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 174762306a36Sopenharmony_ci dev_err(dev, "Failed to get escape clock: %d\n", ret); 174862306a36Sopenharmony_ci return ret; 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci dsi->pll_phy_clock = devm_clk_get(dev, "phy"); 175262306a36Sopenharmony_ci if (IS_ERR(dsi->pll_phy_clock)) { 175362306a36Sopenharmony_ci ret = PTR_ERR(dsi->pll_phy_clock); 175462306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 175562306a36Sopenharmony_ci dev_err(dev, "Failed to get phy clock: %d\n", ret); 175662306a36Sopenharmony_ci return ret; 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci dsi->pixel_clock = devm_clk_get(dev, "pixel"); 176062306a36Sopenharmony_ci if (IS_ERR(dsi->pixel_clock)) { 176162306a36Sopenharmony_ci ret = PTR_ERR(dsi->pixel_clock); 176262306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 176362306a36Sopenharmony_ci dev_err(dev, "Failed to get pixel clock: %d\n", ret); 176462306a36Sopenharmony_ci return ret; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci dsi->out_bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0); 176862306a36Sopenharmony_ci if (IS_ERR(dsi->out_bridge)) 176962306a36Sopenharmony_ci return PTR_ERR(dsi->out_bridge); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci /* The esc clock rate is supposed to always be 100Mhz. */ 177262306a36Sopenharmony_ci ret = clk_set_rate(dsi->escape_clock, 100 * 1000000); 177362306a36Sopenharmony_ci if (ret) { 177462306a36Sopenharmony_ci dev_err(dev, "Failed to set esc clock: %d\n", ret); 177562306a36Sopenharmony_ci return ret; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci ret = vc4_dsi_init_phy_clocks(dsi); 177962306a36Sopenharmony_ci if (ret) 178062306a36Sopenharmony_ci return ret; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci ret = drmm_encoder_init(drm, encoder, 178362306a36Sopenharmony_ci &vc4_dsi_encoder_funcs, 178462306a36Sopenharmony_ci DRM_MODE_ENCODER_DSI, 178562306a36Sopenharmony_ci NULL); 178662306a36Sopenharmony_ci if (ret) 178762306a36Sopenharmony_ci return ret; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci ret = devm_pm_runtime_enable(dev); 179062306a36Sopenharmony_ci if (ret) 179162306a36Sopenharmony_ci return ret; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, 0); 179462306a36Sopenharmony_ci if (ret) 179562306a36Sopenharmony_ci return ret; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci return 0; 179862306a36Sopenharmony_ci} 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_cistatic const struct component_ops vc4_dsi_ops = { 180162306a36Sopenharmony_ci .bind = vc4_dsi_bind, 180262306a36Sopenharmony_ci}; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_cistatic int vc4_dsi_dev_probe(struct platform_device *pdev) 180562306a36Sopenharmony_ci{ 180662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 180762306a36Sopenharmony_ci struct vc4_dsi *dsi; 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci dsi = kzalloc(sizeof(*dsi), GFP_KERNEL); 181062306a36Sopenharmony_ci if (!dsi) 181162306a36Sopenharmony_ci return -ENOMEM; 181262306a36Sopenharmony_ci dev_set_drvdata(dev, dsi); 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci kref_init(&dsi->kref); 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci dsi->pdev = pdev; 181762306a36Sopenharmony_ci dsi->bridge.funcs = &vc4_dsi_bridge_funcs; 181862306a36Sopenharmony_ci#ifdef CONFIG_OF 181962306a36Sopenharmony_ci dsi->bridge.of_node = dev->of_node; 182062306a36Sopenharmony_ci#endif 182162306a36Sopenharmony_ci dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; 182262306a36Sopenharmony_ci dsi->dsi_host.ops = &vc4_dsi_host_ops; 182362306a36Sopenharmony_ci dsi->dsi_host.dev = dev; 182462306a36Sopenharmony_ci mipi_dsi_host_register(&dsi->dsi_host); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci return 0; 182762306a36Sopenharmony_ci} 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_cistatic void vc4_dsi_dev_remove(struct platform_device *pdev) 183062306a36Sopenharmony_ci{ 183162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 183262306a36Sopenharmony_ci struct vc4_dsi *dsi = dev_get_drvdata(dev); 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci mipi_dsi_host_unregister(&dsi->dsi_host); 183562306a36Sopenharmony_ci vc4_dsi_put(dsi); 183662306a36Sopenharmony_ci} 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_cistruct platform_driver vc4_dsi_driver = { 183962306a36Sopenharmony_ci .probe = vc4_dsi_dev_probe, 184062306a36Sopenharmony_ci .remove_new = vc4_dsi_dev_remove, 184162306a36Sopenharmony_ci .driver = { 184262306a36Sopenharmony_ci .name = "vc4_dsi", 184362306a36Sopenharmony_ci .of_match_table = vc4_dsi_dt_match, 184462306a36Sopenharmony_ci }, 184562306a36Sopenharmony_ci}; 1846