From 4e93e94c7c8ee50bba395ac09d9a23cab685fd2c Mon Sep 17 00:00:00 2001 From: Vinod Polimera Date: Fri, 25 Feb 2022 13:21:42 +0530 Subject: [PATCH] qualcomm/sc7280: Add support for edp and mdp driver - Add support for edp aux read and write. - Update edp panel properties based on edid read. - Configure edp controller and edp phy. Panel details: Manufacturer: SHP Model 1523 Serial Number 0 Made week 53 of 2020 EDID version: 1.4 Digital display 8 bits per primary color channel DisplayPort interface Maximum image size: 31 cm x 17 cm Gamma: 220% Check DPMS levels Supported color formats: RGB 4:4:4 Default (sRGB) color space is primary color space First detailed timing is preferred timing Supports GTF timings within operating range Established timings supported: Standard timings supported: Detailed timings Hex of detail: 5a8780a070384d403020350035ae10000018 Detailed mode (IN HEX): Clock 346500 KHz, 135 mm x ae mm 0780 07b0 07d0 0820 hborder 0 0438 043b 0440 0485 vborder 0 -hsync -vsync Did detailed timing Hex of detail: 653880a070384d403020350035ae10000018 Detailed mode (IN HEX): Clock 144370 KHz, 135 mm x ae mm 0780 07b0 07d0 0820 hborder 0 0438 043b 0440 0485 vborder 0 -hsync -vsync Hex of detail: 000000fd003090a7a7230100000000000000 Monitor ranges (bare limits): 48-144Hz V, 167-167kHz H, max dotclock 350MHz Hex of detail: 000000fc004c513134304d314a5734390a20 Monitor name: LQ140M1JW49 Changes in V2: - Remove Misc delays in edp code. - Move mdss soc code to disp.c - Update EDID read using I2C write & read. Changes in V3: - Remove unrelated delays. - Misc changes. BUG=b:182963902,b:216687885 TEST=Validated on qualcomm sc7280 development board. Monitor name: LQ140M1JW49 Signed-off-by: Vinod Polimera Change-Id: If89abb76028766b19450e756889a5d7776106f95 Reviewed-on: https://review.coreboot.org/c/coreboot/+/61342 Tested-by: build bot (Jenkins) Reviewed-by: Shelley Chen --- src/soc/qualcomm/sc7280/Kconfig | 3 + src/soc/qualcomm/sc7280/Makefile.inc | 5 + src/soc/qualcomm/sc7280/display/disp.c | 60 + src/soc/qualcomm/sc7280/display/edp_aux.c | 229 +++ src/soc/qualcomm/sc7280/display/edp_ctrl.c | 1451 +++++++++++++++++ src/soc/qualcomm/sc7280/display/edp_phy_7nm.c | 327 ++++ .../sc7280/include/soc/display/edp_aux.h | 59 + .../sc7280/include/soc/display/edp_ctrl.h | 11 + .../sc7280/include/soc/display/edp_phy.h | 14 + .../sc7280/include/soc/display/edp_reg.h | 328 ++++ .../sc7280/include/soc/display/mdssreg.h | 529 ++++++ 11 files changed, 3016 insertions(+) create mode 100644 src/soc/qualcomm/sc7280/display/disp.c create mode 100644 src/soc/qualcomm/sc7280/display/edp_aux.c create mode 100644 src/soc/qualcomm/sc7280/display/edp_ctrl.c create mode 100644 src/soc/qualcomm/sc7280/display/edp_phy_7nm.c create mode 100644 src/soc/qualcomm/sc7280/include/soc/display/edp_aux.h create mode 100644 src/soc/qualcomm/sc7280/include/soc/display/edp_ctrl.h create mode 100644 src/soc/qualcomm/sc7280/include/soc/display/edp_phy.h create mode 100644 src/soc/qualcomm/sc7280/include/soc/display/edp_reg.h create mode 100644 src/soc/qualcomm/sc7280/include/soc/display/mdssreg.h diff --git a/src/soc/qualcomm/sc7280/Kconfig b/src/soc/qualcomm/sc7280/Kconfig index 42f4f2c654..ccba8ff9b5 100644 --- a/src/soc/qualcomm/sc7280/Kconfig +++ b/src/soc/qualcomm/sc7280/Kconfig @@ -14,6 +14,9 @@ config SOC_QUALCOMM_SC7280 select SOC_QUALCOMM_COMMON select CACHE_MRC_SETTINGS select HAS_RECOVERY_MRC_CACHE + select MAINBOARD_HAS_NATIVE_VGA_INIT + select MAINBOARD_FORCE_NATIVE_VGA_INIT + select HAVE_LINEAR_FRAMEBUFFER select COMPRESS_BOOTBLOCK select HAVE_UART_SPECIAL select PCI diff --git a/src/soc/qualcomm/sc7280/Makefile.inc b/src/soc/qualcomm/sc7280/Makefile.inc index 403ac7a8aa..5691ff3180 100644 --- a/src/soc/qualcomm/sc7280/Makefile.inc +++ b/src/soc/qualcomm/sc7280/Makefile.inc @@ -46,6 +46,11 @@ ramstage-y += ../common/usb/snps_usb_phy.c ramstage-y += ../common/usb/qmpv4_usb_phy.c ramstage-y += ../common/aop_load_reset.c ramstage-y += cpucp_load_reset.c +ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += display/edp_aux.c +ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += display/edp_ctrl.c +ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += display/edp_phy_7nm.c +ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += ../common/display/mdss.c +ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += display/disp.c ramstage-$(CONFIG_PCI) += ../common/pcie_common.c ramstage-$(CONFIG_PCI) += pcie.c diff --git a/src/soc/qualcomm/sc7280/display/disp.c b/src/soc/qualcomm/sc7280/display/disp.c new file mode 100644 index 0000000000..28356f247f --- /dev/null +++ b/src/soc/qualcomm/sc7280/display/disp.c @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include + +void enable_mdss_clk(void) +{ + mdss_clock_enable(GCC_DISP_AHB); + + // enable gdsc before enabling clocks. + clock_enable_gdsc(MDSS_CORE_GDSC); + + mdss_clock_enable(GCC_DISP_HF_AXI); + mdss_clock_enable(GCC_DISP_SF_AXI); + mdss_clock_enable(MDSS_CLK_AHB); + mdss_clock_configure(MDSS_CLK_MDP, 400 * MHz, 0, 0, 0, 0, 0); + mdss_clock_enable(MDSS_CLK_MDP); + mdss_clock_configure(MDSS_CLK_VSYNC, 0, 0, 0, 0, 0, 0); + mdss_clock_enable(MDSS_CLK_VSYNC); +} + +void mdss_intf_tg_setup(struct edid *edid) +{ + uint32_t hsync_period, vsync_period; + uint32_t active_vstart, active_vend, active_hctl; + uint32_t display_hctl, hsync_ctl, display_vstart, display_vend; + + hsync_period = edid->mode.ha + edid->mode.hbl; + vsync_period = edid->mode.va + edid->mode.vbl; + display_vstart = edid->mode.vbl * hsync_period + edid->mode.hbl; + display_vend = (vsync_period * hsync_period) - 1; + hsync_ctl = (hsync_period << 16) | edid->mode.hspw; + display_hctl = edid->mode.hbl | (hsync_period - 1) << 16; + active_vstart = edid->mode.vbl * hsync_period; + active_vend = display_vend; + active_hctl = display_hctl; + + write32(&mdp_intf->intf_active_v_start_f0, active_vstart); + write32(&mdp_intf->intf_active_v_end_f0, active_vend); + write32(&mdp_intf->intf_active_hctl, active_hctl); + write32(&mdp_intf->display_data_hctl, display_hctl); + write32(&mdp_intf->intf_hsync_ctl, hsync_ctl); + write32(&mdp_intf->intf_vysnc_period_f0, vsync_period * hsync_period); + write32(&mdp_intf->intf_vysnc_pulse_width_f0, edid->mode.vspw * hsync_period); + write32(&mdp_intf->intf_disp_hctl, display_hctl); + write32(&mdp_intf->intf_disp_v_start_f0, display_vstart); + write32(&mdp_intf->intf_disp_v_end_f0, display_vend); + write32(&mdp_intf->intf_underflow_color, 0x00); +} + +void mdss_ctrl_config(void) +{ + /* Select vigo pipe active */ + write32(&mdp_ctl->ctl_fetch_pipe_active, FETCH_PIPE_VIG0_ACTIVE); + + /* PPB0 to INTF1 */ + write32(&mdp_ctl->ctl_intf_active, INTF_ACTIVE_5); + +} diff --git a/src/soc/qualcomm/sc7280/display/edp_aux.c b/src/soc/qualcomm/sc7280/display/edp_aux.c new file mode 100644 index 0000000000..4e5f7da124 --- /dev/null +++ b/src/soc/qualcomm/sc7280/display/edp_aux.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AUX_CMD_FIFO_LEN 144 +#define AUX_CMD_NATIVE_MAX 16 +#define AUX_CMD_I2C_MAX 128 +#define AUX_INTR_I2C_DONE BIT(0) +#define AUX_INTR_WRONG_ADDR BIT(1) +#define AUX_INTR_CONSECUTIVE_TIMEOUT BIT(2) +#define AUX_INTR_CONSECUTIVE_NACK_DEFER BIT(3) +#define AUX_INTR_WRONG_RD_DATA_CNT BIT(4) +#define AUX_INTR_NACK_I2C BIT(5) +#define AUX_INTR_DEFER_I2C BIT(6) +#define AUX_INTR_DPPHY_AUX_ERR BIT(7) +#define EDP_AUX_INTERRUPT (AUX_INTR_I2C_DONE | AUX_INTR_WRONG_ADDR | \ + AUX_INTR_CONSECUTIVE_TIMEOUT | \ + AUX_INTR_CONSECUTIVE_NACK_DEFER | \ + AUX_INTR_WRONG_RD_DATA_CNT | AUX_INTR_DEFER_I2C | \ + AUX_INTR_NACK_I2C | AUX_INTR_DPPHY_AUX_ERR) + +static void edp_wait_for_aux_done(void) +{ + u32 intr_status = 0; + + if (!wait_ms(100, read32(&edp_auxclk->status) & EDP_AUX_INTERRUPT)) { + printk(BIOS_ERR, "ERROR: AUX SEND not acknowledged\n"); + return; + } + + intr_status = read32(&edp_auxclk->status); + if (!(intr_status & AUX_INTR_I2C_DONE)) { + printk(BIOS_ERR, "ERROR: AUX command failed, status = %#x\n", intr_status); + return; + } + + write32(&edp_ahbclk->interrupt_status, 0x0); +} + +static int edp_msg_fifo_tx(unsigned int address, u8 request, void *buffer, size_t size) +{ + u32 data[4]; + u32 reg, len; + bool native = (request == DP_AUX_NATIVE_WRITE) || (request == DP_AUX_NATIVE_READ); + bool read = (request == DP_AUX_I2C_READ) || (request == DP_AUX_NATIVE_READ); + u8 *msgdata = buffer; + int i; + + if (read) + len = 4; + else + len = size + 4; + + /* + * cmd fifo only has depth of 144 bytes + */ + if (len > AUX_CMD_FIFO_LEN) + return -1; + + /* Pack cmd and write to HW */ + data[0] = (address >> 16) & 0xf; /* addr[19:16] */ + if (read) + data[0] |= AUX_CMD_READ; /* R/W */ + + data[1] = (address >> 8) & 0xff; /* addr[15:8] */ + data[2] = address & 0xff; /* addr[7:0] */ + data[3] = (size - 1) & 0xff; /* len[7:0] */ + + for (i = 0; i < len; i++) { + reg = (i < 4) ? data[i] : msgdata[i - 4]; + reg = EDP_AUX_DATA_DATA(reg); /* index = 0, write */ + if (i == 0) + reg |= EDP_AUX_DATA_INDEX_WRITE; + write32(&edp_auxclk->aux_data, reg); + } + + /* clear old aux transaction control */ + write32(&edp_auxclk->aux_trans_ctrl, 0); + reg = RX_STOP_ERR | RX_DEC_ERR | RX_SYNC_ERR | RX_ALIGN_ERR | TX_REQ_ERR; + write32(&edp_phy->aux_interrupt_clr, reg); + write32(&edp_phy->aux_interrupt_clr, reg | GLOBE_REQ_CLR); + write32(&edp_phy->aux_interrupt_clr, 0x0); + + reg = 0; /* Transaction number is always 1 */ + if (!native) /* i2c */ + reg |= EDP_AUX_TRANS_CTRL_I2C | EDP_AUX_TRANS_CTRL_NO_SEND_ADDR | + EDP_AUX_TRANS_CTRL_NO_SEND_STOP; + + reg |= EDP_AUX_TRANS_CTRL_GO; + write32(&edp_auxclk->aux_trans_ctrl, reg); + edp_wait_for_aux_done(); + + return 0; +} + +static int edp_msg_fifo_rx(void *buffer, size_t size) +{ + u32 data; + u8 *dp; + int i; + u32 len = size; + + clrbits32(&edp_auxclk->aux_trans_ctrl, EDP_AUX_TRANS_CTRL_GO); + write32(&edp_auxclk->aux_data, + EDP_AUX_DATA_INDEX_WRITE | EDP_AUX_DATA_READ); /* index = 0 */ + + dp = buffer; + + /* discard first byte */ + data = read32(&edp_auxclk->aux_data); + for (i = 0; i < len; i++) { + data = read32(&edp_auxclk->aux_data); + dp[i] = (u8)((data >> 8) & 0xff); + } + + return 0; +} + +ssize_t edp_aux_transfer(unsigned int address, u8 request, void *buffer, size_t size) +{ + ssize_t ret; + bool native = (request == DP_AUX_NATIVE_WRITE) || (request == DP_AUX_NATIVE_READ); + bool read = (request == DP_AUX_I2C_READ) || (request == DP_AUX_NATIVE_READ); + + /* Ignore address only message */ + if ((size == 0) || (buffer == NULL)) { + printk(BIOS_ERR, "%s: invalid size or buffer\n", __func__); + return size; + } + + /* msg sanity check */ + if ((native && (size > AUX_CMD_NATIVE_MAX)) || + (size > AUX_CMD_I2C_MAX)) { + printk(BIOS_ERR, "%s: invalid msg: size(%zu), request(%x)\n", + __func__, size, request); + return -1; + } + + ret = edp_msg_fifo_tx(address, request, buffer, size); + if (ret < 0) { + printk(BIOS_ERR, "edp aux transfer tx failed\n"); + return ret; + } + + if (read) { + ret = edp_msg_fifo_rx(buffer, size); + if (ret < 0) { + printk(BIOS_ERR, "edp aux transfer rx failed\n"); + return ret; + } + } + + /* Return requested size for success or retry */ + ret = size; + + return ret; +} + +int edp_read_edid(struct edid *out) +{ + int err; + u8 edid[EDID_LENGTH * 2]; + int edid_size = EDID_LENGTH; + + uint8_t reg_addr = 0; + err = edp_aux_transfer(EDID_I2C_ADDR, DP_AUX_I2C_WRITE, ®_addr, 1); + if (err > 0) + err = edp_aux_transfer(EDID_I2C_ADDR, DP_AUX_I2C_READ, edid, EDID_LENGTH); + + if (err < EDID_LENGTH) { + printk(BIOS_ERR, "ERROR: Failed to read EDID. :%d\n", err); + return err; + } + + if (edid[EDID_EXTENSION_FLAG]) { + printk(BIOS_ERR, " read EDID ext block.\n"); + edid_size += EDID_LENGTH; + reg_addr = EDID_LENGTH; + err = edp_aux_transfer(EDID_I2C_ADDR, DP_AUX_I2C_WRITE, ®_addr, 1); + if (err > 0) + err = edp_aux_transfer(EDID_I2C_ADDR, DP_AUX_I2C_READ, + &edid[EDID_LENGTH], EDID_LENGTH); + + if (err < EDID_LENGTH) { + printk(BIOS_ERR, "Failed to read EDID ext block.\n"); + return err; + } + } + + if (decode_edid(edid, edid_size, out) != EDID_CONFORMANT) { + printk(BIOS_ERR, "ERROR: Failed to decode EDID.\n"); + return CB_ERR; + } + + + return CB_SUCCESS; +} + +void edp_aux_ctrl(int enable) +{ + u32 data; + data = read32(&edp_auxclk->aux_ctrl); + + if (!enable) { + data &= ~EDP_AUX_CTRL_ENABLE; + write32(&edp_auxclk->aux_ctrl, data); + return; + } + + data |= EDP_AUX_CTRL_RESET; + write32(&edp_auxclk->aux_ctrl, data); + + data &= ~EDP_AUX_CTRL_RESET; + write32(&edp_auxclk->aux_ctrl, data); + + write32(&edp_auxclk->timeout_count, 0xffff); + write32(&edp_auxclk->aux_limits, 0xffff); + + data |= EDP_AUX_CTRL_ENABLE; + write32(&edp_auxclk->aux_ctrl, data); +} diff --git a/src/soc/qualcomm/sc7280/display/edp_ctrl.c b/src/soc/qualcomm/sc7280/display/edp_ctrl.c new file mode 100644 index 0000000000..0bc1fe4964 --- /dev/null +++ b/src/soc/qualcomm/sc7280/display/edp_ctrl.c @@ -0,0 +1,1451 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DPCD_LINK_VOLTAGE_MAX 4 +#define DPCD_LINK_PRE_EMPHASIS_MAX 4 +#define MAX_LINK_TRAINING_LOOP 5 + +/* DP_TX Registers */ +#define MAX_16BITS_VALUE ((1 << 16) - 1) /* 16 bits value */ +#define EDP_INTR_AUX_I2C_DONE BIT(3) +#define EDP_INTR_WRONG_ADDR BIT(6) +#define EDP_INTR_TIMEOUT BIT(9) +#define EDP_INTR_NACK_DEFER BIT(12) +#define EDP_INTR_WRONG_DATA_CNT BIT(15) +#define EDP_INTR_I2C_NACK BIT(18) +#define EDP_INTR_I2C_DEFER BIT(21) +#define EDP_INTR_PLL_UNLOCKED BIT(24) +#define EDP_INTR_AUX_ERROR BIT(27) +#define EDP_INTR_READY_FOR_VIDEO BIT(0) +#define EDP_INTR_IDLE_PATTERN_SENT BIT(3) +#define EDP_INTR_FRAME_END BIT(6) +#define EDP_INTR_CRC_UPDATED BIT(9) +#define EDP_INTR_SST_FIFO_UNDERFLOW BIT(28) +#define REG_EDP_DP_HPD_CTRL (0x00000000) +#define EDP_DP_HPD_CTRL_HPD_EN (0x00000001) +#define EDP_DP_HPD_PLUG_INT_ACK (0x00000001) +#define EDP_DP_IRQ_HPD_INT_ACK (0x00000002) +#define EDP_DP_HPD_REPLUG_INT_ACK (0x00000004) +#define EDP_DP_HPD_UNPLUG_INT_ACK (0x00000008) +#define EDP_DP_HPD_STATE_STATUS_BITS_MASK (0x0000000F) +#define EDP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1C) +#define EDP_DP_HPD_PLUG_INT_MASK (0x00000001) +#define EDP_DP_IRQ_HPD_INT_MASK (0x00000002) +#define EDP_DP_HPD_REPLUG_INT_MASK (0x00000004) +#define EDP_DP_HPD_UNPLUG_INT_MASK (0x00000008) +#define EDP_DP_HPD_INT_MASK (EDP_DP_HPD_PLUG_INT_MASK | \ + EDP_DP_IRQ_HPD_INT_MASK | \ + EDP_DP_HPD_REPLUG_INT_MASK | \ + EDP_DP_HPD_UNPLUG_INT_MASK) +#define EDP_DP_HPD_STATE_STATUS_CONNECTED (0x40000000) +#define EDP_DP_HPD_STATE_STATUS_PENDING (0x20000000) +#define EDP_DP_HPD_STATE_STATUS_DISCONNECTED (0x00000000) +#define EDP_DP_HPD_STATE_STATUS_MASK (0xE0000000) +#define EDP_DP_HPD_REFTIMER_ENABLE (1 << 16) +#define EDP_DP_HPD_EVENT_TIME_0_VAL (0x3E800FA) +#define EDP_DP_HPD_EVENT_TIME_1_VAL (0x1F407D0) +#define EDP_INTERRUPT_TRANS_NUM (0x000000A0) +#define EDP_MAINLINK_CTRL_ENABLE (0x00000001) +#define EDP_MAINLINK_CTRL_RESET (0x00000002) +#define EDP_MAINLINK_CTRL_SW_BYPASS_SCRAMBLER (0x00000010) +#define EDP_MAINLINK_FB_BOUNDARY_SEL (0x02000000) +#define EDP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK (0x00000001) +#define EDP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN (0x00000002) +#define EDP_CONFIGURATION_CTRL_P_INTERLACED (0x00000004) +#define EDP_CONFIGURATION_CTRL_INTERLACED_BTF (0x00000008) +#define EDP_CONFIGURATION_CTRL_NUM_OF_LANES (0x00000010) +#define EDP_CONFIGURATION_CTRL_ENHANCED_FRAMING (0x00000040) +#define EDP_CONFIGURATION_CTRL_SEND_VSC (0x00000080) +#define EDP_CONFIGURATION_CTRL_BPC (0x00000100) +#define EDP_CONFIGURATION_CTRL_ASSR (0x00000400) +#define EDP_CONFIGURATION_CTRL_RGB_YUV (0x00000800) +#define EDP_CONFIGURATION_CTRL_LSCLK_DIV (0x00002000) +#define EDP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT (0x04) +#define EDP_CONFIGURATION_CTRL_BPC_SHIFT (0x08) +#define EDP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT (0x0D) +#define EDP_TOTAL_HOR_VER_HORIZ__MASK (0x0000FFFF) +#define EDP_TOTAL_HOR_VER_HORIZ__SHIFT (0) +#define DP_EDP_CONFIGURATION_CAP 0x00d /* XXX 1.2? */ +#define DP_ALTERNATE_SCRAMBLER_RESET_CAP (1 << 0) +#define DP_FRAMING_CHANGE_CAP (1 << 1) +#define DP_DPCD_DISPLAY_CONTROL_CAPABLE (1 << 3) /* edp v1.2 or higher */ +#define EDP_MISC0_SYNCHRONOUS_CLK (0x00000001) +#define EDP_MISC0_COLORIMETRY_CFG_SHIFT (0x00000001) +#define EDP_MISC0_TEST_BITS_DEPTH_SHIFT (0x00000005) +#define LANE0_MAPPING_SHIFT (0x00000000) +#define LANE1_MAPPING_SHIFT (0x00000002) +#define LANE2_MAPPING_SHIFT (0x00000004) +#define LANE3_MAPPING_SHIFT (0x00000006) +#define EDP_MAINLINK_READY_FOR_VIDEO (0x00000001) +#define EDP_MAINLINK_READY_TRAIN_PATTERN_1_READY (0x00000008) +#define EDP_MAINLINK_SAFE_TO_EXIT_LEVEL_2 (0x00000002) +#define EDP_LINK_BW_MAX DP_LINK_BW_5_4 +#define DP_RECEIVER_CAP_SIZE 0xf +#define DP_LINK_STATUS_SIZE 6 +#define DP_TRAINING_AUX_RD_MASK 0x7F /* DP 1.3 */ + +/* AUX CH addresses */ +/* DPCD */ +#define DP_DPCD_REV 0x000 +#define DP_DPCD_REV_10 0x10 +#define DP_DPCD_REV_11 0x11 +#define DP_DPCD_REV_12 0x12 +#define DP_DPCD_REV_13 0x13 +#define DP_DPCD_REV_14 0x14 +#define DP_SET_POWER 0x600 +#define DP_SET_POWER_D0 0x1 +#define DP_SET_POWER_D3 0x2 +#define DP_SET_POWER_MASK 0x3 +#define DP_MAX_LINK_RATE 0x001 +#define DP_MAX_LANE_COUNT 0x002 +#define DP_MAX_LANE_COUNT_MASK 0x1f +#define DP_TPS3_SUPPORTED (1 << 6) +#define DP_ENHANCED_FRAME_CAP (1 << 7) +#define DP_MAX_DOWNSPREAD 0x003 +#define DP_NO_AUX_HANDSHAKE_LINK_TRAINING (1 << 6) +#define DP_NORP 0x004 +#define DP_DOWNSTREAMPORT_PRESENT 0x005 +#define DP_DWN_STRM_PORT_PRESENT (1 << 0) +#define DP_DWN_STRM_PORT_TYPE_MASK 0x06 +#define DP_FORMAT_CONVERSION (1 << 3) +#define DP_MAIN_LINK_CHANNEL_CODING 0x006 +#define DP_EDP_CONFIGURATION_CAP 0x00d +#define DP_TRAINING_AUX_RD_INTERVAL 0x00e + +/* link configuration */ +#define DP_LINK_BW_SET 0x100 +#define DP_LINK_RATE_TABLE 0x00 /* eDP 1.4 */ +#define DP_LINK_BW_1_62 0x06 +#define DP_LINK_BW_2_7 0x0a +#define DP_LINK_BW_5_4 0x14 /* 1.2 */ +#define DP_LINK_BW_8_1 0x1e /* 1.4 */ +#define DP_LANE_COUNT_SET 0x101 +#define DP_LANE_COUNT_MASK 0x0f +#define DP_LANE_COUNT_ENHANCED_FRAME_EN (1 << 7) +#define DP_TRAINING_PATTERN_SET 0x102 +#define DP_TRAINING_PATTERN_DISABLE 0 +#define DP_TRAINING_PATTERN_1 1 +#define DP_TRAINING_PATTERN_2 2 +#define DP_TRAINING_PATTERN_3 3 +#define DP_TRAINING_PATTERN_MASK 0x3 +#define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2) +#define DP_LINK_QUAL_PATTERN_D10_2 (1 << 2) +#define DP_LINK_QUAL_PATTERN_ERROR_RATE (2 << 2) +#define DP_LINK_QUAL_PATTERN_PRBS7 (3 << 2) +#define DP_LINK_QUAL_PATTERN_MASK (3 << 2) +#define DP_RECOVERED_CLOCK_OUT_EN (1 << 4) +#define DP_LINK_SCRAMBLING_DISABLE (1 << 5) +#define DP_EDP_CONFIGURATION_SET 0x10a +#define DP_ALTERNATE_SCRAMBLER_RESET_ENABLE (1 << 0) +#define DP_FRAMING_CHANGE_ENABLE (1 << 1) +#define DP_PANEL_SELF_TEST_ENABLE (1 << 7) +#define DP_SYMBOL_ERROR_COUNT_BOTH (0 << 6) +#define DP_SYMBOL_ERROR_COUNT_DISPARITY (1 << 6) +#define DP_SYMBOL_ERROR_COUNT_SYMBOL (2 << 6) +#define DP_SYMBOL_ERROR_COUNT_MASK (3 << 6) +#define DP_TRAINING_LANE0_SET 0x103 +#define DP_TRAINING_LANE1_SET 0x104 +#define DP_TRAINING_LANE2_SET 0x105 +#define DP_TRAINING_LANE3_SET 0x106 + +#define DP_TRAIN_VOLTAGE_SWING_MASK 0x3 +#define DP_TRAIN_VOLTAGE_SWING_SHIFT 0 +#define DP_TRAIN_MAX_SWING_REACHED (1 << 2) +#define DP_TRAIN_VOLTAGE_SWING_400 (0 << 0) +#define DP_TRAIN_VOLTAGE_SWING_600 (1 << 0) +#define DP_TRAIN_VOLTAGE_SWING_800 (2 << 0) +#define DP_TRAIN_VOLTAGE_SWING_1200 (3 << 0) +#define DP_TRAIN_PRE_EMPHASIS_MASK (3 << 3) +#define DP_TRAIN_PRE_EMPHASIS_0 (0 << 3) +#define DP_TRAIN_PRE_EMPHASIS_3_5 (1 << 3) +#define DP_TRAIN_PRE_EMPHASIS_6 (2 << 3) +#define DP_TRAIN_PRE_EMPHASIS_9_5 (3 << 3) +#define DP_TRAIN_PRE_EMPHASIS_SHIFT 3 +#define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5) +#define DP_DOWNSPREAD_CTRL 0x107 +#define DP_SPREAD_AMP_0_5 (1 << 4) +#define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108 +#define DP_SET_ANSI_8B10B (1 << 0) +#define DP_LANE0_1_STATUS 0x202 +#define DP_LANE2_3_STATUS 0x203 +#define DP_LANE_CR_DONE (1 << 0) +#define DP_LANE_CHANNEL_EQ_DONE (1 << 1) +#define DP_LANE_SYMBOL_LOCKED (1 << 2) +#define DP_CHANNEL_EQ_BITS (DP_LANE_CR_DONE | \ + DP_LANE_CHANNEL_EQ_DONE | \ + DP_LANE_SYMBOL_LOCKED) +#define DP_LANE_ALIGN_STATUS_UPDATED 0x204 +#define DP_INTERLANE_ALIGN_DONE (1 << 0) +#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6) +#define DP_LINK_STATUS_UPDATED (1 << 7) +#define DP_ADJUST_REQUEST_LANE0_1 0x206 +#define DP_ADJUST_REQUEST_LANE2_3 0x207 +#define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK 0x03 +#define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0 +#define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK 0x0c +#define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT 2 +#define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK 0x30 +#define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4 +#define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0 +#define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6 +#define DP_TEST_REQUEST 0x218 +#define DP_TEST_LINK_TRAINING (1 << 0) +#define DP_TEST_LINK_PATTERN (1 << 1) +#define DP_TEST_LINK_EDID_READ (1 << 2) +#define DP_TEST_LINK_PHY_TEST_PATTERN (1 << 3) /* DPCD >= 1.1 */ +#define DP_TEST_LINK_RATE 0x219 +#define DP_LINK_RATE_162 (0x6) +#define DP_LINK_RATE_27 (0xa) + +#define DP_TEST_LANE_COUNT 0x220 +#define DP_TEST_PATTERN 0x221 +#define DP_TEST_RESPONSE 0x260 +#define DP_TEST_ACK (1 << 0) +#define DP_TEST_NAK (1 << 1) +#define DP_TEST_EDID_CHECKSUM_WRITE (1 << 2) + +#define DP_SET_POWER 0x600 +#define DP_SET_POWER_D0 0x1 +#define DP_SET_POWER_D3 0x2 + +/* Link training return value */ +#define EDP_TRAIN_FAIL -1 +#define EDP_TRAIN_SUCCESS 0 +#define EDP_TRAIN_RECONFIG 1 + +#define EDP_INTERRUPT_STATUS_ACK_SHIFT 1 +#define EDP_INTERRUPT_STATUS_MASK_SHIFT 2 + +#define EDP_INTERRUPT_STATUS1 \ + (EDP_INTR_AUX_I2C_DONE| \ + EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \ + EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \ + EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \ + EDP_INTR_PLL_UNLOCKED | EDP_INTR_AUX_ERROR) + +#define EDP_INTERRUPT_STATUS1_ACK \ + (EDP_INTERRUPT_STATUS1 << EDP_INTERRUPT_STATUS_ACK_SHIFT) +#define EDP_INTERRUPT_STATUS1_MASK \ + (EDP_INTERRUPT_STATUS1 << EDP_INTERRUPT_STATUS_MASK_SHIFT) + +#define EDP_INTERRUPT_STATUS2 \ + (EDP_INTR_READY_FOR_VIDEO | EDP_INTR_IDLE_PATTERN_SENT | \ + EDP_INTR_FRAME_END | EDP_INTR_CRC_UPDATED | EDP_INTR_SST_FIFO_UNDERFLOW) + +#define EDP_INTERRUPT_STATUS2_ACK \ + (EDP_INTERRUPT_STATUS2 << EDP_INTERRUPT_STATUS_ACK_SHIFT) +#define EDP_INTERRUPT_STATUS2_MASK \ + (EDP_INTERRUPT_STATUS2 << EDP_INTERRUPT_STATUS_MASK_SHIFT) + +enum edp_color_depth { + EDP_6BIT = 0, + EDP_8BIT = 1, + EDP_10BIT = 2, + EDP_12BIT = 3, + EDP_16BIT = 4, +}; + +struct tu_algo_data { + int64_t lclk_fp; + int64_t lclk; + int64_t pclk; + int64_t pclk_fp; + int64_t lwidth; + int64_t lwidth_fp; + int64_t hbp_relative_to_pclk; + int64_t hbp_relative_to_pclk_fp; + int nlanes; + int bpp; + int async_en; + int bpc; + int delay_start_link_extra_pclk; + int extra_buffer_margin; + int64_t ratio_fp; + int64_t original_ratio_fp; + int64_t err_fp; + int64_t old_err_fp; + int64_t new_err_fp; + int tu_size; + int tu_size_desired; + int tu_size_minus1; + int valid_boundary_link; + int64_t resulting_valid_fp; + int64_t total_valid_fp; + int64_t effective_valid_fp; + int64_t effective_valid_recorded_fp; + int num_tus; + int num_tus_per_lane; + int paired_tus; + int remainder_tus; + int remainder_tus_upper; + int remainder_tus_lower; + int extra_bytes; + int filler_size; + int delay_start_link; + int extra_pclk_cycles; + int extra_pclk_cycles_in_link_clk; + int64_t ratio_by_tu_fp; + int64_t average_valid2_fp; + int new_valid_boundary_link; + int remainder_symbols_exist; + int n_symbols; + int64_t n_remainder_symbols_per_lane_fp; + int64_t last_partial_tu_fp; + int64_t TU_ratio_err_fp; + int n_tus_incl_last_incomplete_tu; + int extra_pclk_cycles_tmp; + int extra_pclk_cycles_in_link_clk_tmp; + int extra_required_bytes_new_tmp; + int filler_size_tmp; + int lower_filler_size_tmp; + int delay_start_link_tmp; + bool boundary_moderation_en; + int boundary_mod_lower_err; + int upper_boundary_count; + int lower_boundary_count; + int i_upper_boundary_count; + int i_lower_boundary_count; + int valid_lower_boundary_link; + int even_distribution_BF; + int even_distribution_legacy; + int even_distribution; + int min_hblank_violated; + int64_t delay_start_time_fp; + int64_t hbp_time_fp; + int64_t hactive_time_fp; + int64_t diff_abs_fp; + int64_t ratio; +}; + +struct edp_ctrl { + /* Link status */ + uint32_t link_rate_khz; + uint8_t link_rate; + uint32_t lane_cnt; + uint8_t v_level; + uint8_t p_level; + + /* Timing status */ + uint32_t pixel_rate; /* in kHz */ + uint32_t color_depth; +}; + +struct edp_ctrl_tu { + uint32_t tu_size_minus1; /* Desired TU Size */ + uint32_t valid_boundary_link; /* Upper valid boundary */ + uint32_t delay_start_link; /* # of clock cycles to delay */ + bool boundary_moderation_en; /* Enable boundary Moderation? */ + uint32_t valid_lower_boundary_link; /* Valid lower boundary link */ + uint32_t upper_boundary_count; /* Upper boundary Count */ + uint32_t lower_boundary_count; /* Lower boundary Count */ +}; + +static uint8_t dp_get_lane_status(const uint8_t link_status[DP_LINK_STATUS_SIZE], int lane) +{ + int i = DP_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + uint8_t l = link_status[i - DP_LANE0_1_STATUS]; + return (l >> s) & 0xf; +} + + +static uint8_t edp_get_adjust_request_voltage( + const uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : + DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); + uint8_t l = link_status[i - DP_LANE0_1_STATUS]; + + return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; +} + +static uint8_t edp_get_adjust_request_pre_emphasis( + const uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : + DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); + uint8_t l = link_status[i - DP_LANE0_1_STATUS]; + + return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; +} + +static void edp_link_train_clock_recovery_delay(const uint8_t dpcd[DP_RECEIVER_CAP_SIZE]) +{ + int rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & DP_TRAINING_AUX_RD_MASK; + + if (rd_interval > 4) + printk(BIOS_ERR, "AUX interval %d, out of range (max 4)\n", rd_interval); + + /* + * The DPCD stores the AUX read interval in units of 4 ms. + * for DP v1.4 and above, clock recovery should use 100 us for AUX read intervals. + */ + if (rd_interval == 0 || dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) + udelay(100); + else + mdelay(rd_interval * 4); +} + +static bool edp_clock_recovery_ok(const uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane_count) +{ + int lane; + uint8_t lane_status; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = dp_get_lane_status(link_status, lane); + if ((lane_status & DP_LANE_CR_DONE) == 0) { + printk(BIOS_ERR, "clock recovery ok failed : %x\n", lane_status); + return false; + } + } + return true; +} + +static void edp_link_train_channel_eq_delay(const uint8_t dpcd[DP_RECEIVER_CAP_SIZE]) +{ + int rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & + DP_TRAINING_AUX_RD_MASK; + + if (rd_interval > 4) + printk(BIOS_INFO, "AUX interval %d, out of range (max 4)\n", + rd_interval); + + /* + * The DPCD stores the AUX read interval in units of 4 ms. + * if the TRAINING_AUX_RD_INTERVAL field is 0, the channel equalization + * should use 400 us AUX read intervals. + */ + if (rd_interval == 0) + udelay(400); + else + mdelay(rd_interval * 4); +} + +static bool edp_channel_eq_ok(const uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) +{ + uint8_t lane_align; + uint8_t lane_status; + int lane; + + lane_align = link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS]; + if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) + return false; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = dp_get_lane_status(link_status, lane); + if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) + return false; + } + return true; +} + +static void edp_ctrl_irq_enable(int enable) +{ + if (enable) { + write32(&edp_ahbclk->interrupt_status, EDP_INTERRUPT_STATUS1_MASK); + write32(&edp_ahbclk->interrupt_status2, + EDP_INTERRUPT_STATUS2_MASK); + } else { + write32(&edp_ahbclk->interrupt_status, + EDP_INTERRUPT_STATUS1_ACK); + write32(&edp_ahbclk->interrupt_status2, + EDP_INTERRUPT_STATUS2_ACK); + } +} + +static void edp_config_ctrl(struct edp_ctrl *ctrl, uint8_t *dpcd) +{ + uint32_t config = 0, depth = 0; + + /* Default-> LSCLK DIV: 1/4 LCLK */ + config |= (2 << EDP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT); + + /* Scrambler reset enable */ + if (dpcd[DP_EDP_CONFIGURATION_CAP] & DP_ALTERNATE_SCRAMBLER_RESET_CAP) + config |= EDP_CONFIGURATION_CTRL_ASSR; + + if (ctrl->color_depth == 8) + depth = EDP_8BIT; + else if (ctrl->color_depth == 10) + depth = EDP_10BIT; + else if (ctrl->color_depth == 12) + depth = EDP_12BIT; + else if (ctrl->color_depth == 16) + depth = EDP_16BIT; + config |= depth << EDP_CONFIGURATION_CTRL_BPC_SHIFT; + + /* Num of Lanes */ + config |= ((ctrl->lane_cnt - 1) + << EDP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT); + + if (dpcd[DP_DPCD_REV] >= 0x11 && + (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) + config |= EDP_CONFIGURATION_CTRL_ENHANCED_FRAMING; + + config |= EDP_CONFIGURATION_CTRL_P_INTERLACED; /* progressive video */ + + /* sync clock & static Mvid */ + config |= EDP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN; + config |= EDP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK; + + write32(&edp_lclk->configuration_ctrl, config); +} + +static void edp_state_ctrl(uint32_t state) +{ + write32(&edp_lclk->state_ctrl, state); +} + +static int edp_lane_set_write(uint8_t voltage_level, uint8_t pre_emphasis_level) +{ + int i; + uint8_t buf[4]; + + if (voltage_level >= DPCD_LINK_VOLTAGE_MAX) + voltage_level |= 0x04; + + if (pre_emphasis_level >= DPCD_LINK_PRE_EMPHASIS_MAX) + pre_emphasis_level |= 0x20; + + for (i = 0; i < 4; i++) + buf[i] = voltage_level | pre_emphasis_level; + + if (edp_aux_transfer(DP_TRAINING_LANE0_SET, DP_AUX_NATIVE_WRITE, buf, 2) < 2) { + printk(BIOS_ERR, "%s: Set sw/pe to panel failed\n", __func__); + return -1; + } + + return 0; +} + +static int edp_train_pattern_set_write(uint8_t pattern) +{ + uint8_t p = pattern; + + printk(BIOS_INFO, "pattern=%x\n", p); + if (edp_aux_transfer(DP_TRAINING_PATTERN_SET, DP_AUX_NATIVE_WRITE, &p, 1) < 1) { + printk(BIOS_ERR, "%s: Set training pattern to panel failed\n", __func__); + return -1; + } + + return 0; +} + +static void edp_sink_train_set_adjust(struct edp_ctrl *ctrl, + const uint8_t *link_status) +{ + int i; + uint8_t max = 0; + uint8_t data; + + /* use the max level across lanes */ + for (i = 0; i < ctrl->lane_cnt; i++) { + data = edp_get_adjust_request_voltage(link_status, i); + printk(BIOS_INFO, "lane=%d req_voltage_swing=0x%x\n", i, data); + if (max < data) + max = data; + } + + ctrl->v_level = max >> DP_TRAIN_VOLTAGE_SWING_SHIFT; + + /* use the max level across lanes */ + max = 0; + for (i = 0; i < ctrl->lane_cnt; i++) { + data = edp_get_adjust_request_pre_emphasis(link_status, i); + printk(BIOS_INFO, "lane=%d req_pre_emphasis=0x%x\n", i, data); + if (max < data) + max = data; + } + + ctrl->p_level = max >> DP_TRAIN_PRE_EMPHASIS_SHIFT; + printk(BIOS_INFO, "v_level=%d, p_level=%d\n", ctrl->v_level, ctrl->p_level); +} + +static void edp_host_train_set(uint32_t train) +{ + int cnt = 10; + uint32_t data = 0; + uint32_t shift = train - 1; + + printk(BIOS_INFO, "train=%d", train); + + edp_state_ctrl(SW_LINK_TRAINING_PATTERN1 << shift); + while (--cnt) { + data = read32(&edp_lclk->mainlink_ready); + if (data & (EDP_MAINLINK_READY_TRAIN_PATTERN_1_READY << shift)) + break; + } + + if (cnt == 0) + printk(BIOS_INFO, "%s: set link_train=%u failed\n", __func__, data); +} + +static int edp_voltage_pre_emphasis_set(struct edp_ctrl *ctrl) +{ + printk(BIOS_INFO, "v=%d p=%d\n", ctrl->v_level, ctrl->p_level); + + edp_phy_config(ctrl->v_level, ctrl->p_level); + return edp_lane_set_write(ctrl->v_level, ctrl->p_level); +} + +static int edp_start_link_train_1(struct edp_ctrl *ctrl, uint8_t *dpcd) +{ + uint8_t link_status[DP_LINK_STATUS_SIZE]; + uint8_t old_v_level; + int tries; + int ret, rlen; + + edp_state_ctrl(0); + edp_host_train_set(DP_TRAINING_PATTERN_1); + + ret = edp_train_pattern_set_write(DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE); + if (ret) + return ret; + + ret = edp_voltage_pre_emphasis_set(ctrl); + if (ret) + return ret; + + tries = 0; + old_v_level = ctrl->v_level; + while (1) { + edp_link_train_clock_recovery_delay(dpcd); + + rlen = edp_aux_transfer(DP_LANE0_1_STATUS, DP_AUX_NATIVE_READ, + &link_status, DP_LINK_STATUS_SIZE); + if (rlen < DP_LINK_STATUS_SIZE) { + printk(BIOS_ERR, "%s: read link status failed\n", __func__); + return -1; + } + + if (edp_clock_recovery_ok(link_status, ctrl->lane_cnt)) { + ret = 0; + break; + } + + if (ctrl->v_level == DPCD_LINK_VOLTAGE_MAX) { + ret = -1; + break; + } + + if (old_v_level != ctrl->v_level) { + tries++; + if (tries >= 5) { + ret = -1; + break; + } + } else { + tries = 0; + old_v_level = ctrl->v_level; + } + + edp_sink_train_set_adjust(ctrl, link_status); + ret = edp_voltage_pre_emphasis_set(ctrl); + if (ret) + return ret; + } + + return 0; +} + +static int edp_start_link_train_2(struct edp_ctrl *ctrl, uint8_t *dpcd) +{ + uint8_t link_status[DP_LINK_STATUS_SIZE]; + int tries = 0; + int ret, rlen; + + edp_host_train_set(DP_TRAINING_PATTERN_2); + ret = edp_voltage_pre_emphasis_set(ctrl); + if (ret) + return ret; + + ret = edp_train_pattern_set_write(DP_TRAINING_PATTERN_2 | DP_RECOVERED_CLOCK_OUT_EN); + if (ret) + return ret; + + while (1) { + edp_link_train_channel_eq_delay(dpcd); + + rlen = edp_aux_transfer(DP_LANE0_1_STATUS, DP_AUX_NATIVE_READ, + &link_status, DP_LINK_STATUS_SIZE); + if (rlen < DP_LINK_STATUS_SIZE) { + printk(BIOS_ERR, "%s: read link status failed\n", __func__); + return -1; + } + + if (edp_channel_eq_ok(link_status, ctrl->lane_cnt)) { + ret = 0; + break; + } + + tries++; + if (tries > 10) { + ret = -1; + break; + } + + edp_sink_train_set_adjust(ctrl, link_status); + ret = edp_voltage_pre_emphasis_set(ctrl); + if (ret) + return ret; + } + + return ret; +} + +static int edp_link_rate_down_shift(struct edp_ctrl *ctrl, uint8_t *dpcd) +{ + int ret = 0; + int link_rate = ctrl->link_rate_khz; + + switch (link_rate) { + case 810000: + link_rate = 540000; + break; + case 540000: + link_rate = 270000; + break; + case 270000: + link_rate = 162000; + break; + case 162000: + default: + ret = -1; + break; + } + + if (!ret) { + ctrl->link_rate_khz = link_rate; + ctrl->link_rate = link_rate / 27000; + printk(BIOS_INFO, "new rate=0x%x\n", ctrl->link_rate_khz); +} + + return ret; +} + +static bool edp_clock_recovery_reduced_lanes( + const uint8_t link_status[DP_LINK_STATUS_SIZE], uint32_t lane_cnt) +{ + int reduced_lanes = 0; + + if (lane_cnt <= 1) + return -1; + + reduced_lanes = lane_cnt >> 1; + + return edp_clock_recovery_ok(link_status, reduced_lanes); +} + +static int edp_link_lane_down_shift(struct edp_ctrl *ctrl, uint8_t *dpcd) +{ + + if (ctrl->lane_cnt <= 1) + return -1; + + ctrl->lane_cnt = ctrl->lane_cnt >> 1; + ctrl->link_rate_khz = dpcd[DP_MAX_LINK_RATE] * 27000; + ctrl->link_rate = dpcd[DP_MAX_LINK_RATE]; + ctrl->p_level = 0; + ctrl->v_level = 0; + + return 0; +} + +static int edp_clear_training_pattern(uint8_t *dpcd) +{ + int ret; + + ret = edp_train_pattern_set_write(0); + edp_link_train_channel_eq_delay(dpcd); + + return ret; +} + +static int edp_do_link_train(struct edp_ctrl *ctrl, uint8_t *dpcd) +{ + uint8_t values[2], edp_config = 0; + int ret; + int rlen; + uint8_t link_status[DP_LINK_STATUS_SIZE]; + + /* + * Set the current link rate and lane cnt to panel. They may have been + * adjusted and the values are different from them in DPCD CAP + */ + + values[0] = ctrl->link_rate; + values[1] = ctrl->lane_cnt; + + if (dpcd[DP_DPCD_REV] >= 0x11 && + (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) + values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + + if (edp_aux_transfer(DP_LINK_BW_SET, DP_AUX_NATIVE_WRITE, &values[0], 1) < 0) + return EDP_TRAIN_FAIL; + + edp_aux_transfer(DP_LANE_COUNT_SET, DP_AUX_NATIVE_WRITE, &values[1], 1); + ctrl->v_level = 0; /* start from default level */ + ctrl->p_level = 0; + + values[0] = 0x10; + + if (dpcd[DP_MAX_DOWNSPREAD] & 1) + values[0] = DP_SPREAD_AMP_0_5; + + values[1] = 1; + edp_aux_transfer(DP_DOWNSPREAD_CTRL, DP_AUX_NATIVE_WRITE, &values[0], 1); + edp_aux_transfer(DP_MAIN_LINK_CHANNEL_CODING_SET, DP_AUX_NATIVE_WRITE, &values[1], 1); + + ret = edp_start_link_train_1(ctrl, dpcd); + if (ret < 0) { + rlen = edp_aux_transfer(DP_LANE0_1_STATUS, DP_AUX_NATIVE_READ, + &link_status, DP_LINK_STATUS_SIZE); + if (rlen < DP_LINK_STATUS_SIZE) { + printk(BIOS_ERR, "%s: read link status failed\n", __func__); + return -1; + } + + ret = edp_link_rate_down_shift(ctrl, dpcd); + if (!ret) { + printk(BIOS_ERR, "link reconfig\n"); + ret = EDP_TRAIN_RECONFIG; + } else if (ret < 0) { + if (edp_clock_recovery_reduced_lanes(link_status, + ctrl->lane_cnt) == 0) { + if (edp_link_lane_down_shift(ctrl, dpcd) < 0) { + printk(BIOS_ERR, "%s: Training 1 failed\n", __func__); + ret = EDP_TRAIN_FAIL; + } else { + printk(BIOS_ERR, "link reconfig\n"); + ret = EDP_TRAIN_RECONFIG; + } + } else { + printk(BIOS_ERR, "%s: Training 1 failed\n", __func__); + ret = EDP_TRAIN_FAIL; + } + } + edp_clear_training_pattern(dpcd); + return ret; + } + + printk(BIOS_INFO, "Training 1 completed successfully\n"); + edp_state_ctrl(0); + if (edp_clear_training_pattern(dpcd)) + return EDP_TRAIN_FAIL; + + ret = edp_start_link_train_2(ctrl, dpcd); + if (ret < 0) { + rlen = edp_aux_transfer(DP_LANE0_1_STATUS, DP_AUX_NATIVE_READ, + &link_status, DP_LINK_STATUS_SIZE); + if (rlen < DP_LINK_STATUS_SIZE) { + printk(BIOS_ERR, "%s: read link status failed\n", __func__); + return -1; + } + + if (edp_clock_recovery_ok(link_status, ctrl->lane_cnt)) { + if (edp_link_rate_down_shift(ctrl, dpcd) == 0) { + printk(BIOS_ERR, "link reconfig\n"); + ret = EDP_TRAIN_RECONFIG; + } else { + printk(BIOS_ERR, "%s: Training 2 failed\n", __func__); + ret = EDP_TRAIN_FAIL; + } + } else { + if (edp_link_lane_down_shift(ctrl, dpcd) < 0) { + printk(BIOS_ERR, "%s: Training 1 failed\n", __func__); + ret = EDP_TRAIN_FAIL; + } else { + printk(BIOS_ERR, "link reconfig\n"); + ret = EDP_TRAIN_RECONFIG; + } + } + + edp_clear_training_pattern(dpcd); + return ret; + } + + printk(BIOS_INFO, "Training 2 completed successfully\n"); + edp_config = DP_ALTERNATE_SCRAMBLER_RESET_ENABLE; + edp_aux_transfer(DP_EDP_CONFIGURATION_SET, DP_AUX_NATIVE_WRITE, &edp_config, 1); + + return ret; +} + +static void edp_ctrl_config_misc(struct edp_ctrl *ctrl) +{ + uint32_t misc_val; + enum edp_color_depth depth = EDP_8BIT; + + misc_val = read32(&edp_lclk->misc1_misc0); + if (ctrl->color_depth == 8) + depth = EDP_8BIT; + else if (ctrl->color_depth == 10) + depth = EDP_10BIT; + else if (ctrl->color_depth == 12) + depth = EDP_12BIT; + else if (ctrl->color_depth == 16) + depth = EDP_16BIT; + + /* clear bpp bits */ + misc_val &= ~(0x07 << EDP_MISC0_TEST_BITS_DEPTH_SHIFT); + misc_val |= depth << EDP_MISC0_TEST_BITS_DEPTH_SHIFT; + + /* Configure clock to synchronous mode */ + misc_val |= EDP_MISC0_SYNCHRONOUS_CLK; + write32(&edp_lclk->misc1_misc0, misc_val); +} + +static void edp_ctrl_pixel_clock_dividers(struct edp_ctrl *ctrl, + uint32_t *pixel_m, uint32_t *pixel_n) +{ + uint32_t pixel_div = 0, dispcc_input_rate; + unsigned long den, num; + uint8_t rate = ctrl->link_rate; + uint32_t stream_rate_khz = ctrl->pixel_rate; + + if (rate == DP_LINK_BW_8_1) + pixel_div = 6; + else if (rate == DP_LINK_BW_1_62 || rate == DP_LINK_BW_2_7) + pixel_div = 2; + else if (rate == DP_LINK_BW_5_4) + pixel_div = 4; + else + printk(BIOS_ERR, "Invalid pixel mux divider\n"); + + dispcc_input_rate = (ctrl->link_rate_khz * 10) / pixel_div; + + rational_best_approximation(dispcc_input_rate, stream_rate_khz, + (unsigned long)(1 << 16) - 1, + (unsigned long)(1 << 16) - 1, &den, &num); + *pixel_m = num; + *pixel_n = den; +} + +static void edp_ctrl_config_msa(struct edp_ctrl *ctrl) +{ + uint32_t pixel_m, pixel_n; + uint32_t mvid, nvid; + u32 const nvid_fixed = 0x8000; + uint8_t rate = ctrl->link_rate; + + edp_ctrl_pixel_clock_dividers(ctrl, &pixel_m, &pixel_n); + pixel_n = ~(pixel_n - pixel_m); + pixel_n = pixel_n & 0xFFFF; + mvid = (pixel_m & 0xFFFF) * 5; + nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF); + + if (nvid < nvid_fixed) { + u32 temp; + + temp = (nvid_fixed / nvid) * nvid; + mvid = (nvid_fixed / nvid) * mvid; + nvid = temp; + } + + if (rate == DP_LINK_BW_5_4) + nvid *= 2; + + if (rate == DP_LINK_BW_8_1) + nvid *= 3; + + printk(BIOS_INFO, "mvid=0x%x, nvid=0x%x\n", mvid, nvid); + write32(&edp_lclk->software_mvid, mvid); + write32(&edp_lclk->software_nvid, nvid); + write32(&edp_p0clk->dsc_dto, 0x0); +} + +static void tu_valid_boundary_calc(struct tu_algo_data *tu) +{ + int64_t f = 100000; + + tu->new_valid_boundary_link = DIV_ROUND_UP(tu->tu_size * tu->ratio_fp, f); + tu->average_valid2_fp = ((tu->i_upper_boundary_count * tu->new_valid_boundary_link + + tu->i_lower_boundary_count * + (tu->new_valid_boundary_link - 1)) * f) / + (tu->i_upper_boundary_count + tu->i_lower_boundary_count); + tu->num_tus = ((tu->bpp * tu->lwidth_fp) / 8) / tu->average_valid2_fp; + tu->n_remainder_symbols_per_lane_fp = ((tu->n_symbols * f) - + (tu->num_tus * tu->average_valid2_fp)) / + tu->nlanes; + tu->last_partial_tu_fp = (tu->n_remainder_symbols_per_lane_fp / tu->tu_size); + if (tu->n_remainder_symbols_per_lane_fp != 0) + tu->remainder_symbols_exist = 1; + else + tu->remainder_symbols_exist = 0; + + tu->num_tus_per_lane = tu->num_tus / tu->nlanes; + tu->paired_tus = (int)((tu->num_tus_per_lane) / (tu->i_upper_boundary_count + + tu->i_lower_boundary_count)); + tu->remainder_tus = tu->num_tus_per_lane - tu->paired_tus * + (tu->i_upper_boundary_count + tu->i_lower_boundary_count); + if ((tu->remainder_tus - tu->i_upper_boundary_count) > 0) { + tu->remainder_tus_upper = tu->i_upper_boundary_count; + tu->remainder_tus_lower = tu->remainder_tus - tu->i_upper_boundary_count; + } else { + tu->remainder_tus_upper = tu->remainder_tus; + tu->remainder_tus_lower = 0; + } + + tu->total_valid_fp = (tu->paired_tus * (tu->i_upper_boundary_count * + tu->new_valid_boundary_link + tu->i_lower_boundary_count * + (tu->new_valid_boundary_link - 1)) + (tu->remainder_tus_upper * + tu->new_valid_boundary_link) + (tu->remainder_tus_lower * + (tu->new_valid_boundary_link - 1))) * f; + if (tu->remainder_symbols_exist) { + tu->effective_valid_fp = (((tu->total_valid_fp + + tu->n_remainder_symbols_per_lane_fp) * f) / + (tu->num_tus_per_lane * f + tu->last_partial_tu_fp)); + } else { + tu->effective_valid_fp = (tu->total_valid_fp / tu->num_tus_per_lane); + } + + tu->new_err_fp = tu->effective_valid_fp - (tu->ratio_fp * tu->tu_size); + tu->old_err_fp = tu->average_valid2_fp - (tu->ratio_fp * tu->tu_size); + tu->even_distribution = tu->num_tus % tu->nlanes == 0 ? 1 : 0; + tu->n_tus_incl_last_incomplete_tu = DIV_ROUND_UP((tu->bpp * tu->lwidth_fp / 8), + tu->average_valid2_fp); + tu->extra_required_bytes_new_tmp = DIV_ROUND_UP((tu->average_valid2_fp - (tu->tu_size * + tu->original_ratio_fp)) * + tu->n_tus_incl_last_incomplete_tu, f); + tu->extra_required_bytes_new_tmp += DIV_ROUND_UP(((tu->i_upper_boundary_count * + tu->nlanes) * + (tu->new_valid_boundary_link * f - + tu->tu_size * tu->original_ratio_fp)), + f); + tu->extra_pclk_cycles_tmp = DIV_ROUND_UP(8 * tu->extra_required_bytes_new_tmp, + tu->bpp); + tu->extra_pclk_cycles_in_link_clk_tmp = DIV_ROUND_UP(tu->extra_pclk_cycles_tmp * + tu->lclk_fp, tu->pclk_fp); + tu->filler_size_tmp = tu->tu_size - tu->new_valid_boundary_link; + tu->lower_filler_size_tmp = tu->filler_size_tmp + 1; + tu->delay_start_link_tmp = tu->extra_pclk_cycles_in_link_clk_tmp + + tu->lower_filler_size_tmp + tu->extra_buffer_margin; + tu->delay_start_time_fp = tu->delay_start_link_tmp * f / tu->lclk; + if (((tu->even_distribution == 1) || + ((tu->even_distribution_BF == 0) && + (tu->even_distribution_legacy == 0))) && + tu->old_err_fp >= 0 && tu->new_err_fp >= 0 && + (tu->new_err_fp < tu->err_fp) && + ((tu->new_err_fp < tu->diff_abs_fp) || + (tu->min_hblank_violated == 1)) && + (tu->new_valid_boundary_link - 1) > 0 && + (tu->hbp_time_fp > tu->delay_start_time_fp) && + (tu->delay_start_link_tmp <= 1023)) { + tu->upper_boundary_count = tu->i_upper_boundary_count; + tu->lower_boundary_count = tu->i_lower_boundary_count; + tu->err_fp = tu->new_err_fp; + tu->boundary_moderation_en = true; + tu->tu_size_desired = tu->tu_size; + tu->valid_boundary_link = tu->new_valid_boundary_link; + tu->effective_valid_recorded_fp = tu->effective_valid_fp; + tu->even_distribution_BF = 1; + tu->delay_start_link = tu->delay_start_link_tmp; + } else if (tu->boundary_mod_lower_err == 0) { + if (tu->new_err_fp < tu->diff_abs_fp) + tu->boundary_mod_lower_err = 1; + } +} + +static void edp_ctrl_calc_tu(struct edp_ctrl *ctrl, struct edid *edid, + struct edp_ctrl_tu *tu_table) +{ + struct tu_algo_data *tu = NULL; + int64_t f = 100000; + int64_t LCLK_FAST_SKEW_fp = (6 * f) / 1000; /* 0.0006 */ + uint8_t DP_BRUTE_FORCE = 1; + int64_t BRUTE_FORCE_THRESHOLD_fp = (1 * f) / 10; /* 0.1 */ + int RATIO_SCALE_NUM = 1001; + int RATIO_SCALE_DEN = 1000; + int HBLANK_MARGIN = 4; + int EXTRA_PIXCLK_CYCLE_DELAY = 4; + int64_t temp = 0; + int64_t temp_fp = 0; + uint32_t async_en = 0; + + tu = malloc(sizeof(*tu)); + memset(tu, 0, sizeof(*tu)); + + tu->lclk_fp = ctrl->link_rate_khz * f; + tu->lclk = ctrl->link_rate_khz; + tu->pclk = edid->mode.pixel_clock; + tu->pclk_fp = edid->mode.pixel_clock * f; + tu->nlanes = ctrl->lane_cnt; + tu->bpp = edid->panel_bits_per_pixel; + tu->lwidth = edid->mode.ha; + tu->lwidth_fp = tu->lwidth * f; + tu->hbp_relative_to_pclk = edid->mode.hbl; + tu->hbp_relative_to_pclk_fp = tu->hbp_relative_to_pclk * f; + tu->err_fp = 1000 * f; + tu->extra_buffer_margin = DIV_ROUND_UP(tu->lclk * 4, tu->pclk); + tu->ratio_fp = ((int64_t)(tu->pclk_fp * tu->bpp) / 8) / (tu->nlanes * tu->lclk); + tu->original_ratio_fp = tu->ratio_fp; + tu->err_fp = 1000 * f; + if (((tu->lwidth % tu->nlanes) != 0) && (tu->ratio_fp < f)) { + tu->ratio_fp = (tu->ratio_fp * RATIO_SCALE_NUM) / RATIO_SCALE_DEN; + tu->ratio_fp = tu->ratio_fp < f ? tu->ratio_fp : f; + } + + tu->err_fp = 1000 * f; + if (tu->ratio_fp > f) + tu->ratio_fp = f; + + for (tu->tu_size = 32; tu->tu_size <= 64; tu->tu_size++) { + tu->new_err_fp = (tu->ratio_fp * tu->tu_size) - + (((tu->ratio_fp * tu->tu_size) / f) * f); + if (tu->new_err_fp > 0) + tu->new_err_fp = f - tu->new_err_fp; + + if (tu->new_err_fp < tu->err_fp) { + tu->err_fp = tu->new_err_fp; + tu->tu_size_desired = tu->tu_size; + } + } + + tu->tu_size_minus1 = tu->tu_size_desired - 1; + tu->valid_boundary_link = DIV_ROUND_UP(tu->tu_size_desired * tu->ratio_fp, f); + tu->num_tus = ((tu->bpp * tu->lwidth) / 8) / tu->valid_boundary_link; + tu->even_distribution_legacy = tu->num_tus % tu->nlanes == 0 ? 1 : 0; + tu->extra_bytes = DIV_ROUND_UP(((tu->num_tus + 1) * (tu->valid_boundary_link * f - + tu->original_ratio_fp * tu->tu_size_desired)), f); + tu->extra_pclk_cycles = DIV_ROUND_UP(tu->extra_bytes * 8, tu->bpp); + tu->extra_pclk_cycles_in_link_clk = DIV_ROUND_UP(tu->extra_pclk_cycles * tu->lclk, + tu->pclk); + tu->filler_size = tu->tu_size_desired - tu->valid_boundary_link; + tu->ratio_by_tu_fp = tu->ratio_fp * tu->tu_size_desired; + tu->delay_start_link = tu->extra_pclk_cycles_in_link_clk + tu->filler_size + + tu->extra_buffer_margin; + tu->resulting_valid_fp = tu->valid_boundary_link * f; + tu->TU_ratio_err_fp = (tu->resulting_valid_fp / tu->tu_size_desired) - + tu->original_ratio_fp; + tu->hbp_time_fp = (tu->hbp_relative_to_pclk_fp - HBLANK_MARGIN * f) / tu->pclk; + tu->delay_start_time_fp = (tu->delay_start_link * f) / tu->lclk; + if (tu->hbp_time_fp < tu->delay_start_time_fp) + tu->min_hblank_violated = 1; + + tu->hactive_time_fp = (tu->lwidth * f) / tu->pclk; + if (tu->hactive_time_fp < tu->delay_start_time_fp) + tu->min_hblank_violated = 1; + + tu->delay_start_time_fp = 0; + + /* brute force */ + tu->delay_start_link_extra_pclk = EXTRA_PIXCLK_CYCLE_DELAY; + tu->diff_abs_fp = tu->resulting_valid_fp - tu->ratio_by_tu_fp; + if (tu->diff_abs_fp < 0) + tu->diff_abs_fp = tu->diff_abs_fp * -1; + + tu->boundary_mod_lower_err = 0; + if ((tu->diff_abs_fp != 0 && + ((tu->diff_abs_fp > BRUTE_FORCE_THRESHOLD_fp) || + (tu->even_distribution_legacy == 0) || + (DP_BRUTE_FORCE == 1))) || + (tu->min_hblank_violated == 1)) { + do { + tu->err_fp = 1000 * f; + tu->extra_buffer_margin = DIV_ROUND_UP(tu->lclk_fp * + tu->delay_start_link_extra_pclk, + tu->pclk_fp); + tu->n_symbols = DIV_ROUND_UP(tu->bpp * tu->lwidth, 8); + for (tu->tu_size = 32; tu->tu_size <= 64; tu->tu_size++) { + for (tu->i_upper_boundary_count = 1; + tu->i_upper_boundary_count <= 15; + tu->i_upper_boundary_count++) { + for (tu->i_lower_boundary_count = 1; + tu->i_lower_boundary_count <= 15; + tu->i_lower_boundary_count++) { + tu_valid_boundary_calc(tu); + } + } + } + tu->delay_start_link_extra_pclk--; + } while (tu->boundary_moderation_en != true && + tu->boundary_mod_lower_err == 1 && + tu->delay_start_link_extra_pclk != 0); + + if (tu->boundary_moderation_en == true) { + tu->resulting_valid_fp = f * (tu->upper_boundary_count * + tu->valid_boundary_link + + tu->lower_boundary_count * + (tu->valid_boundary_link - 1)); + tu->resulting_valid_fp /= (tu->upper_boundary_count + + tu->lower_boundary_count); + tu->ratio_by_tu_fp = tu->original_ratio_fp * tu->tu_size_desired; + tu->valid_lower_boundary_link = tu->valid_boundary_link - 1; + tu->num_tus = ((tu->bpp / 8) * tu->lwidth_fp) / tu->resulting_valid_fp; + tu->tu_size_minus1 = tu->tu_size_desired - 1; + tu->even_distribution_BF = 1; + tu->TU_ratio_err_fp = ((tu->tu_size_desired * f / + tu->resulting_valid_fp) * f); + tu->TU_ratio_err_fp -= tu->original_ratio_fp; + } + } + + temp_fp = LCLK_FAST_SKEW_fp * tu->lwidth; + temp = DIV_ROUND_UP(temp_fp, f); + temp_fp = ((tu->bpp * f / 8) / (tu->nlanes * tu->original_ratio_fp)) * f * temp; + temp = temp_fp / f; + + if (async_en) + tu->delay_start_link += (int)temp; + tu->delay_start_time_fp = (tu->delay_start_link * f) / tu->lclk; + + /* OUTPUTS */ + tu_table->valid_boundary_link = tu->valid_boundary_link; + tu_table->delay_start_link = tu->delay_start_link; + tu_table->boundary_moderation_en = tu->boundary_moderation_en; + tu_table->valid_lower_boundary_link = tu->valid_lower_boundary_link; + tu_table->upper_boundary_count = tu->upper_boundary_count; + tu_table->lower_boundary_count = tu->lower_boundary_count; + tu_table->tu_size_minus1 = tu->tu_size_minus1; + + printk(BIOS_INFO, "TU: valid_boundary_link: %d\n", + tu_table->valid_boundary_link); + printk(BIOS_INFO, "TU: delay_start_link: %d\n", + tu_table->delay_start_link); + printk(BIOS_INFO, "TU: boundary_moderation_en: %d\n", + tu_table->boundary_moderation_en); + printk(BIOS_INFO, "TU: valid_lower_boundary_link: %d\n", + tu_table->valid_lower_boundary_link); + printk(BIOS_INFO, "TU: upper_boundary_count: %d\n", + tu_table->upper_boundary_count); + printk(BIOS_INFO, "TU: lower_boundary_count: %d\n", + tu_table->lower_boundary_count); + printk(BIOS_INFO, "TU: tu_size_minus1: %d\n", tu_table->tu_size_minus1); +} + +static void edp_ctrl_config_TU(struct edp_ctrl *ctrl, struct edid *edid) +{ + struct edp_ctrl_tu tu_config; + + edp_ctrl_calc_tu(ctrl, edid, &tu_config); + write32(&edp_lclk->valid_boundary, tu_config.delay_start_link << 16 | + tu_config.valid_boundary_link); + write32(&edp_lclk->tu, tu_config.tu_size_minus1); + write32(&edp_lclk->valid_boundary2, tu_config.boundary_moderation_en | + tu_config.valid_lower_boundary_link << 1| + tu_config.upper_boundary_count << 16 | + tu_config.lower_boundary_count << 20); +} + +static void edp_ctrl_timing_cfg(struct edid *edid) +{ + uint32_t hpolarity; + uint32_t vpolarity; + + hpolarity = (edid->mode.phsync == '+'); + vpolarity = (edid->mode.pvsync == '+'); + + /* Configure eDP timing to HW */ + write32(&edp_lclk->total_hor_ver, + (edid->mode.ha + edid->mode.hbl) | + (((edid->mode.va + edid->mode.vbl) << 16) & 0xffff0000)); + + write32(&edp_lclk->start_hor_ver_from_sync, + (edid->mode.hbl - edid->mode.hso) | + (((edid->mode.vbl - edid->mode.vso) << 16) & 0xffff0000)); + + write32(&edp_lclk->hysnc_vsync_width_polarity, edid->mode.hspw | + ((hpolarity << 15) & 0x8000) | ((edid->mode.vspw << 16) & + 0x7fff0000) | ((vpolarity << 31) & 0x80000000)); + + write32(&edp_lclk->active_hor_ver, + (edid->mode.ha) | + ((edid->mode.va << 16) & 0xffff0000)); + +} + +static void edp_mainlink_ctrl(int enable) +{ + uint32_t data = 0; + + write32(&edp_lclk->mainlink_ctrl, 0x2); + if (enable) + data |= (0x1); + + write32(&edp_lclk->mainlink_ctrl, data); +} + +static void edp_ctrl_phy_enable(int enable) +{ + if (enable) { + write32(&edp_ahbclk->phy_ctrl, 0x4 | 0x1); + write32(&edp_ahbclk->phy_ctrl, 0x0); + edp_phy_enable(); + } +} + +static void edp_ctrl_phy_aux_enable(int enable) +{ + if (enable) { + /* regulators are enabled in QCLIB */ + edp_ctrl_phy_enable(1); + edp_aux_ctrl(1); + } else { + edp_aux_ctrl(0); + } +} + +static void edp_ctrl_link_enable(struct edp_ctrl *ctrl, + struct edid *edid, uint8_t *dpcd, int enable) +{ + int ret = 0; + uint32_t m = 0, n = 0; + + if (enable) { + edp_phy_power_on(ctrl->link_rate_khz); + edp_phy_vm_pe_init(); + mdss_clock_configure(MDSS_CLK_EDP_LINK, 0, 1, 0, 0, 0, 0); + ret = mdss_clock_enable(MDSS_CLK_EDP_LINK); + if (ret != 0) + printk(BIOS_ERR, "failed to enable link clk"); + + mdss_clock_configure(MDSS_CLK_EDP_LINK_INTF, 0, 1, 0, 0, 0, 0); + ret = mdss_clock_enable(MDSS_CLK_EDP_LINK_INTF); + if (ret != 0) + printk(BIOS_ERR, "failed to enable link intf clk"); + + edp_ctrl_pixel_clock_dividers(ctrl, &m, &n); + mdss_clock_configure(MDSS_CLK_EDP_PIXEL, 0, 2, 0, m, n, n); + ret = mdss_clock_enable(MDSS_CLK_EDP_PIXEL); + if (ret != 0) + printk(BIOS_ERR, "failed to enable pixel clk"); + + edp_mainlink_ctrl(1); + } else { + edp_mainlink_ctrl(0); + } +} + +static int edp_ctrl_training(struct edp_ctrl *ctrl, struct edid *edid, uint8_t *dpcd) +{ + int ret; + + /* Do link training only when power is on */ + ret = edp_do_link_train(ctrl, dpcd); + while (ret == EDP_TRAIN_RECONFIG) { + /* Re-configure main link */ + edp_ctrl_irq_enable(0); + edp_ctrl_link_enable(ctrl, edid, dpcd, 0); + edp_ctrl_phy_enable(1); + edp_ctrl_irq_enable(1); + edp_ctrl_link_enable(ctrl, edid, dpcd, 1); + ret = edp_do_link_train(ctrl, dpcd); + } + + return ret; +} + +static void edp_ctrl_on(struct edp_ctrl *ctrl, struct edid *edid, uint8_t *dpcd) +{ + uint8_t value; + int ret; + + /* + * By default, use the maximum link rate and minimum lane count, + * so that we can do rate down shift during link training. + */ + ctrl->link_rate_khz = dpcd[DP_MAX_LINK_RATE] * 27000; + ctrl->link_rate = dpcd[DP_MAX_LINK_RATE]; + ctrl->lane_cnt = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; + ctrl->color_depth = edid->panel_bits_per_color; + ctrl->pixel_rate = edid->mode.pixel_clock; + + /* DP_SET_POWER register is only available on DPCD v1.1 and later */ + if (dpcd[DP_DPCD_REV] >= 0x11) { + ret = edp_aux_transfer(DP_SET_POWER, DP_AUX_NATIVE_READ, &value, 1); + if (ret < 0) + printk(BIOS_ERR, "ERROR: edp native read failure\n"); + + value &= ~DP_SET_POWER_MASK; + value |= DP_SET_POWER_D0; + + ret = edp_aux_transfer(DP_SET_POWER, DP_AUX_NATIVE_WRITE, &value, 1); + if (ret < 0) + printk(BIOS_ERR, "ERROR: edp native read failure\n"); + + /* + * According to the DP 1.1 specification, a "Sink Device must + * exit the power saving state within 1 ms" (Section 2.5.3.1, + * Table 5-52, "Sink Control Field" (register 0x600). + */ + udelay(1000); + } + + edp_ctrl_irq_enable(1); + edp_ctrl_link_enable(ctrl, edid, dpcd, 1); + /* Start link training */ + ret = edp_ctrl_training(ctrl, edid, dpcd); + if (ret != EDP_TRAIN_SUCCESS) + printk(BIOS_ERR, "ERROR: edp training failure\n"); + + edp_train_pattern_set_write(0); + + write32(&edp_lclk->mainlink_ctrl, 0x2000000); + write32(&edp_lclk->mainlink_ctrl, 0x2000002); + write32(&edp_lclk->mainlink_ctrl, 0x2000000); + write32(&edp_lclk->mainlink_ctrl, 0x2000001); + edp_config_ctrl(ctrl, dpcd); + edp_ctrl_config_misc(ctrl); + edp_ctrl_timing_cfg(edid); + edp_ctrl_config_msa(ctrl); + edp_ctrl_config_TU(ctrl, edid); + edp_state_ctrl(SW_SEND_VIDEO); + edp_ctrl_irq_enable(0); +} + +static bool edp_ctrl_panel_connected(uint8_t *dpcd) +{ + /* enable aux clk */ + mdss_clock_configure(MDSS_CLK_EDP_AUX, 0, 0, 0, 0, 0, 0); + mdss_clock_enable(MDSS_CLK_EDP_AUX); + edp_ctrl_phy_aux_enable(1); + edp_ctrl_irq_enable(1); + if (edp_aux_transfer(DP_DPCD_REV, DP_AUX_NATIVE_READ, dpcd, + DP_RECEIVER_CAP_SIZE) < DP_RECEIVER_CAP_SIZE) { + printk(BIOS_ERR, "%s: AUX channel is NOT ready\n", __func__); + + } else { + return true; + } + + return false; +} + +enum cb_err edp_ctrl_init(struct edid *edid) +{ + uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; + struct edp_ctrl ctrl; + + memset(&ctrl, 0, sizeof(struct edp_ctrl)); + mdss_clock_enable(GCC_EDP_CLKREF_EN); + + if (edp_ctrl_panel_connected(dpcd) && edp_read_edid(edid) == 0) { + edp_ctrl_on(&ctrl, edid, dpcd); + return CB_SUCCESS; + } + + edp_ctrl_irq_enable(0); + + return CB_ERR; +} diff --git a/src/soc/qualcomm/sc7280/display/edp_phy_7nm.c b/src/soc/qualcomm/sc7280/display/edp_phy_7nm.c new file mode 100644 index 0000000000..ab7f04b4b5 --- /dev/null +++ b/src/soc/qualcomm/sc7280/display/edp_phy_7nm.c @@ -0,0 +1,327 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void edp_phy_ssc_en(bool en) +{ + if (en) { + write32(&edp_phy_pll->qserdes_com_ssc_en_center, 0x01); + write32(&edp_phy_pll->qserdes_com_ssc_adj_per1, 0x00); + write32(&edp_phy_pll->qserdes_com_ssc_per1, 0x36); + write32(&edp_phy_pll->qserdes_com_ssc_per2, 0x01); + write32(&edp_phy_pll->qserdes_com_ssc_step_size1_mode0, 0x5c); + write32(&edp_phy_pll->qserdes_com_ssc_step_size2_mode0, 0x08); + } else { + write32(&edp_phy_pll->qserdes_com_ssc_en_center, 0x00); + } +} + +int edp_phy_enable(void) +{ + write32(&edp_phy->pd_ctl, 0x7D); + write32(&edp_phy_pll->qserdes_com_bias_en_clkbuflr_en, 0x17); + write32(&edp_phy->aux_cfg[1], 0x13); + write32(&edp_phy->aux_cfg[2], 0x24); + write32(&edp_phy->aux_cfg[3], 0x00); + write32(&edp_phy->aux_cfg[4], 0x0a); + write32(&edp_phy->aux_cfg[5], 0x26); + write32(&edp_phy->aux_cfg[6], 0x0a); + write32(&edp_phy->aux_cfg[7], 0x03); + write32(&edp_phy->aux_cfg[8], 0x37); + write32(&edp_phy->aux_cfg[9], 0x03); + write32(&edp_phy->aux_interrupt_mask, 0x1f); + write32(&edp_phy->mode, 0xFC); + + if (!wait_us(1000, read32(&edp_phy_pll->qserdes_com_cmn_status) & BIT(7))) + printk(BIOS_ERR, "%s: refgen not ready : 0x%x\n", __func__, + read32(&edp_phy_pll->qserdes_com_cmn_status)); + + write32(&edp_phy_lane_tx0->tx_ldo_config, 0x01); + write32(&edp_phy_lane_tx1->tx_ldo_config, 0x01); + write32(&edp_phy_lane_tx0->tx_lane_mode1, 0x00); + write32(&edp_phy_lane_tx1->tx_lane_mode1, 0x00); + + return 0; +} + +static const u8 edp_hbr2_pre_emphasis[4][4] = { + {0x0c, 0x15, 0x19, 0x1e}, /* pe0, 0 db */ + {0x08, 0x15, 0x19, 0xFF}, /* pe1, 3.5 db */ + {0x0e, 0x14, 0xFF, 0xFF}, /* pe2, 6.0 db */ + {0x0d, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */ +}; + +static const u8 edp_hbr2_voltage_swing[4][4] = { + {0xb, 0x11, 0x17, 0x1c}, /* sw0, 0.4v */ + {0x10, 0x19, 0x1f, 0xFF}, /* sw1, 0.6 v */ + {0x19, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */ + {0x1f, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ +}; + +void edp_phy_vm_pe_init(void) +{ + write32(&edp_phy_lane_tx0->tx_drv_lvl, edp_hbr2_voltage_swing[0][0]); + write32(&edp_phy_lane_tx0->tx_emp_post1_lvl, + edp_hbr2_pre_emphasis[0][0]); + write32(&edp_phy_lane_tx1->tx_drv_lvl, edp_hbr2_voltage_swing[0][0]); + write32(&edp_phy_lane_tx1->tx_emp_post1_lvl, + edp_hbr2_pre_emphasis[0][0]); + + write32(&edp_phy_lane_tx0->tx_highz_drvr_en, 4); + write32(&edp_phy_lane_tx0->tx_transceiver_bias_en, 3); + write32(&edp_phy_lane_tx1->tx_highz_drvr_en, 7); + write32(&edp_phy_lane_tx1->tx_transceiver_bias_en, 0); + write32(&edp_phy->cfg1, 3); +} + +void edp_phy_config(u8 v_level, u8 p_level) +{ + write32(&edp_phy_lane_tx0->tx_drv_lvl, + edp_hbr2_voltage_swing[v_level][p_level]); + write32(&edp_phy_lane_tx0->tx_emp_post1_lvl, + edp_hbr2_pre_emphasis[v_level][p_level]); + write32(&edp_phy_lane_tx1->tx_drv_lvl, + edp_hbr2_voltage_swing[v_level][p_level]); + write32(&edp_phy_lane_tx1->tx_emp_post1_lvl, + edp_hbr2_pre_emphasis[v_level][p_level]); +} + +static void edp_phy_pll_vco_init(uint32_t link_rate) +{ + edp_phy_ssc_en(true); + write32(&edp_phy_pll->qserdes_com_svs_mode_clk_sel, 0x01); + write32(&edp_phy_pll->qserdes_com_sysclk_en_sel, 0x0b); + write32(&edp_phy_pll->qserdes_com_sys_clk_ctrl, 0x02); + write32(&edp_phy_pll->qserdes_com_clk_enable1, 0x0c); + write32(&edp_phy_pll->qserdes_com_sysclk_buf_enable, 0x06); + write32(&edp_phy_pll->qserdes_com_clk_sel, 0x30); + write32(&edp_phy_pll->qserdes_com_pll_ivco, 0x07); + write32(&edp_phy_pll->qserdes_com_lock_cmp_en, 0x04); + write32(&edp_phy_pll->qserdes_com_pll_cctrl_mode0, 0x36); + write32(&edp_phy_pll->qserdes_com_pll_rctrl_mode0, 0x16); + write32(&edp_phy_pll->qserdes_com_cp_ctrl_mode0, 0x06); + write32(&edp_phy_pll->qserdes_com_div_frac_start1_mode0, 0x00); + write32(&edp_phy_pll->qserdes_com_cmn_config, 0x02); + write32(&edp_phy_pll->qserdes_com_integloop_gain0_mode0, 0x3f); + write32(&edp_phy_pll->qserdes_com_integloop_gain1_mode0, 0x00); + write32(&edp_phy_pll->qserdes_com_vco_tune_map, 0x00); + write32(&edp_phy_pll->qserdes_com_bg_timer, 0x0a); + write32(&edp_phy_pll->qserdes_com_coreclk_div_mode0, 0x14); + write32(&edp_phy_pll->qserdes_com_vco_tune_ctrl, 0x00); + write32(&edp_phy_pll->qserdes_com_bias_en_clkbuflr_en, 0x17); + write32(&edp_phy_pll->qserdes_com_core_clk_en, 0x0f); + + switch (link_rate) { + case 162000: + write32(&edp_phy_pll->qserdes_com_hsclk_sel, 0x05); + write32(&edp_phy_pll->qserdes_com_dec_start_mode0, 0x69); + write32(&edp_phy_pll->qserdes_com_div_frac_start2_mode0, 0x80); + write32(&edp_phy_pll->qserdes_com_div_frac_start3_mode0, 0x07); + write32(&edp_phy_pll->qserdes_com_lock_cmp1_mode0, 0x6f); + write32(&edp_phy_pll->qserdes_com_lock_cmp2_mode0, 0x08); + write32(&edp_phy_pll->qserdes_com_vco_tune1_mode0, 0xa0); + write32(&edp_phy_pll->qserdes_com_vco_tune2_mode0, 0x03); + break; + case 216000: + write32(&edp_phy_pll->qserdes_com_hsclk_sel, 0x04); + write32(&edp_phy_pll->qserdes_com_dec_start_mode0, 0x70); + write32(&edp_phy_pll->qserdes_com_div_frac_start2_mode0, 0x00); + write32(&edp_phy_pll->qserdes_com_div_frac_start3_mode0, 0x08); + write32(&edp_phy_pll->qserdes_com_lock_cmp1_mode0, 0x3f); + write32(&edp_phy_pll->qserdes_com_lock_cmp2_mode0, 0x0b); + write32(&edp_phy_pll->qserdes_com_vco_tune1_mode0, 0x34); + write32(&edp_phy_pll->qserdes_com_vco_tune2_mode0, 0x03); + break; + case 243000: + write32(&edp_phy_pll->qserdes_com_hsclk_sel, 0x04); + write32(&edp_phy_pll->qserdes_com_dec_start_mode0, 0x7e); + write32(&edp_phy_pll->qserdes_com_div_frac_start2_mode0, 0x00); + write32(&edp_phy_pll->qserdes_com_div_frac_start3_mode0, 0x09); + write32(&edp_phy_pll->qserdes_com_lock_cmp1_mode0, 0xa7); + write32(&edp_phy_pll->qserdes_com_lock_cmp2_mode0, 0x0c); + write32(&edp_phy_pll->qserdes_com_vco_tune1_mode0, 0x5c); + write32(&edp_phy_pll->qserdes_com_vco_tune2_mode0, 0x02); + break; + case 270000: + write32(&edp_phy_pll->qserdes_com_hsclk_sel, 0x03); + write32(&edp_phy_pll->qserdes_com_dec_start_mode0, 0x69); + write32(&edp_phy_pll->qserdes_com_div_frac_start2_mode0, 0x80); + write32(&edp_phy_pll->qserdes_com_div_frac_start3_mode0, 0x07); + write32(&edp_phy_pll->qserdes_com_lock_cmp1_mode0, 0x0f); + write32(&edp_phy_pll->qserdes_com_lock_cmp2_mode0, 0x0e); + write32(&edp_phy_pll->qserdes_com_vco_tune1_mode0, 0xa0); + write32(&edp_phy_pll->qserdes_com_vco_tune2_mode0, 0x03); + break; + case 324000: + write32(&edp_phy_pll->qserdes_com_hsclk_sel, 0x03); + write32(&edp_phy_pll->qserdes_com_dec_start_mode0, 0x7e); + write32(&edp_phy_pll->qserdes_com_div_frac_start2_mode0, 0x00); + write32(&edp_phy_pll->qserdes_com_div_frac_start3_mode0, 0x09); + write32(&edp_phy_pll->qserdes_com_lock_cmp1_mode0, 0xdf); + write32(&edp_phy_pll->qserdes_com_lock_cmp2_mode0, 0x10); + write32(&edp_phy_pll->qserdes_com_vco_tune1_mode0, 0x5c); + write32(&edp_phy_pll->qserdes_com_vco_tune2_mode0, 0x02); + break; + case 432000: + write32(&edp_phy_pll->qserdes_com_hsclk_sel, 0x01); + write32(&edp_phy_pll->qserdes_com_dec_start_mode0, 0x70); + write32(&edp_phy_pll->qserdes_com_div_frac_start2_mode0, 0x00); + write32(&edp_phy_pll->qserdes_com_div_frac_start3_mode0, 0x08); + write32(&edp_phy_pll->qserdes_com_lock_cmp1_mode0, 0x7f); + write32(&edp_phy_pll->qserdes_com_lock_cmp2_mode0, 0x16); + write32(&edp_phy_pll->qserdes_com_vco_tune1_mode0, 0x34); + write32(&edp_phy_pll->qserdes_com_vco_tune2_mode0, 0x03); + break; + case 540000: + write32(&edp_phy_pll->qserdes_com_hsclk_sel, 0x01); + write32(&edp_phy_pll->qserdes_com_dec_start_mode0, 0x8c); + write32(&edp_phy_pll->qserdes_com_div_frac_start2_mode0, 0x00); + write32(&edp_phy_pll->qserdes_com_div_frac_start3_mode0, 0x0a); + write32(&edp_phy_pll->qserdes_com_lock_cmp1_mode0, 0x1f); + write32(&edp_phy_pll->qserdes_com_lock_cmp2_mode0, 0x1c); + write32(&edp_phy_pll->qserdes_com_vco_tune1_mode0, 0x84); + write32(&edp_phy_pll->qserdes_com_vco_tune2_mode0, 0x01); + break; + case 594000: + write32(&edp_phy_pll->qserdes_com_hsclk_sel, 0x01); + write32(&edp_phy_pll->qserdes_com_dec_start_mode0, 0x9a); + write32(&edp_phy_pll->qserdes_com_div_frac_start2_mode0, 0x00); + write32(&edp_phy_pll->qserdes_com_div_frac_start3_mode0, 0x0b); + write32(&edp_phy_pll->qserdes_com_lock_cmp1_mode0, 0xef); + write32(&edp_phy_pll->qserdes_com_lock_cmp2_mode0, 0x1e); + write32(&edp_phy_pll->qserdes_com_vco_tune1_mode0, 0xac); + write32(&edp_phy_pll->qserdes_com_vco_tune2_mode0, 0x00); + break; + case 810000: + write32(&edp_phy_pll->qserdes_com_hsclk_sel, 0x00); + write32(&edp_phy_pll->qserdes_com_dec_start_mode0, 0x69); + write32(&edp_phy_pll->qserdes_com_div_frac_start2_mode0, 0x80); + write32(&edp_phy_pll->qserdes_com_div_frac_start3_mode0, 0x07); + write32(&edp_phy_pll->qserdes_com_lock_cmp1_mode0, 0x2f); + write32(&edp_phy_pll->qserdes_com_lock_cmp2_mode0, 0x2a); + write32(&edp_phy_pll->qserdes_com_vco_tune1_mode0, 0xa0); + write32(&edp_phy_pll->qserdes_com_vco_tune2_mode0, 0x03); + break; + default: + printk(BIOS_ERR, "%s: Invalid link rate. rate = %u\n", __func__, + link_rate); + break; + } +} + +static void edp_phy_lanes_init(void) +{ + write32(&edp_phy_lane_tx0->tx_transceiver_bias_en, 0x03); + write32(&edp_phy_lane_tx0->tx_clk_buf_enable, 0x0f); + write32(&edp_phy_lane_tx0->tx_reset_tsync_en, 0x03); + write32(&edp_phy_lane_tx0->tx_tran_drvr_emp_en, 0x01); + write32(&edp_phy_lane_tx0->tx_tx_band, 0x4); + + write32(&edp_phy_lane_tx1->tx_transceiver_bias_en, 0x03); + write32(&edp_phy_lane_tx1->tx_clk_buf_enable, 0x0f); + write32(&edp_phy_lane_tx1->tx_reset_tsync_en, 0x03); + write32(&edp_phy_lane_tx1->tx_tran_drvr_emp_en, 0x01); + write32(&edp_phy_lane_tx1->tx_tx_band, 0x4); +} + +static void edp_lanes_configure(void) +{ + write32(&edp_phy_lane_tx0->tx_highz_drvr_en, 0x1f); + write32(&edp_phy_lane_tx0->tx_highz_drvr_en, 0x04); + write32(&edp_phy_lane_tx0->tx_tx_pol_inv, 0x00); + + write32(&edp_phy_lane_tx1->tx_highz_drvr_en, 0x1f); + write32(&edp_phy_lane_tx1->tx_highz_drvr_en, 0x04); + write32(&edp_phy_lane_tx1->tx_tx_pol_inv, 0x00); + + write32(&edp_phy_lane_tx1->tx_highz_drvr_en, 0x04); + write32(&edp_phy_lane_tx1->tx_tx_pol_inv, 0x00); + + write32(&edp_phy_lane_tx0->tx_drv_lvl_offset, 0x10); + write32(&edp_phy_lane_tx1->tx_drv_lvl_offset, 0x10); + + write32(&edp_phy_lane_tx0->tx_rescode_lane_offset_tx0, 0x11); + write32(&edp_phy_lane_tx0->tx_rescode_lane_offset_tx1, 0x11); + + write32(&edp_phy_lane_tx1->tx_rescode_lane_offset_tx0, 0x11); + write32(&edp_phy_lane_tx1->tx_rescode_lane_offset_tx1, 0x11); +} + +static int edp_phy_pll_vco_configure(uint32_t link_rate) +{ + u32 phy_vco_div = 0; + + switch (link_rate) { + case 162000: + phy_vco_div = 2; + break; + case 216000: + case 243000: + case 270000: + phy_vco_div = 1; + break; + case 324000: + case 432000: + case 540000: + phy_vco_div = 2; + break; + case 594000: + case 810000: + phy_vco_div = 0; + break; + default: + printk(BIOS_ERR, "%s: Invalid link rate. rate = %u\n", __func__, + link_rate); + break; + } + + write32(&edp_phy->vco_div, phy_vco_div); + write32(&edp_phy->cfg, 0x01); + write32(&edp_phy->cfg, 0x05); + write32(&edp_phy->cfg, 0x01); + write32(&edp_phy->cfg, 0x09); + write32(&edp_phy_pll->qserdes_com_resetsm_cntrl, 0x20); + if (!wait_us(10000, read32(&edp_phy_pll->qserdes_com_c_ready_status) & BIT(0))) { + printk(BIOS_ERR, "%s: PLL not locked. Status\n", __func__); + return -1; + } + + write32(&edp_phy->cfg, 0x19); + edp_lanes_configure(); + edp_phy_vm_pe_init(); + if (!wait_us(10000, read32(&edp_phy->status) & BIT(1))) { + printk(BIOS_ERR, "%s: PHY not ready. Status\n", __func__); + return -1; + } + + write32(&edp_phy->cfg, 0x18); + write32(&edp_phy->cfg, 0x19); + if (!wait_us(10000, read32(&edp_phy_pll->qserdes_com_c_ready_status) & BIT(0))) { + printk(BIOS_ERR, "%s: PLL not locked. Status\n", __func__); + return -1; + } + + return 0; +} + +int edp_phy_power_on(uint32_t link_rate) +{ + int ret = 0; + edp_phy_pll_vco_init(link_rate); + + write32(&edp_phy->tx0_tx1_lane_ctl, 0x5); + write32(&edp_phy->tx2_tx3_lane_ctl, 0x5); + edp_phy_lanes_init(); + ret = edp_phy_pll_vco_configure(link_rate); + + return ret; +} diff --git a/src/soc/qualcomm/sc7280/include/soc/display/edp_aux.h b/src/soc/qualcomm/sc7280/include/soc/display/edp_aux.h new file mode 100644 index 0000000000..df5923d580 --- /dev/null +++ b/src/soc/qualcomm/sc7280/include/soc/display/edp_aux.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _EDP_AUX_H +#define _EDP_AUX_H + +#include + +#define DP_AUX_I2C_WRITE 0x0 +#define DP_AUX_I2C_READ 0x1 +#define DP_AUX_I2C_STATUS 0x2 +#define DP_AUX_I2C_MOT 0x4 +#define DP_AUX_NATIVE_WRITE 0x8 +#define DP_AUX_NATIVE_READ 0x9 +#define REG_EDP_AUX_CTRL (0x00000030) +#define EDP_AUX_CTRL_ENABLE (0x00000001) +#define EDP_AUX_CTRL_RESET (0x00000002) + +#define REG_EDP_AUX_DATA (0x00000034) +#define EDP_AUX_DATA_READ (0x00000001) +#define EDP_AUX_DATA_DATA__MASK (0x0000ff00) +#define EDP_AUX_DATA_DATA__SHIFT (8) + +#define EDP_AUX_DATA_INDEX__MASK (0x00ff0000) +#define EDP_AUX_DATA_INDEX__SHIFT (16) + +#define EDP_AUX_DATA_INDEX_WRITE (0x80000000) + +#define REG_EDP_AUX_TRANS_CTRL (0x00000038) +#define EDP_AUX_TRANS_CTRL_I2C (0x00000100) +#define EDP_AUX_TRANS_CTRL_GO (0x00000200) +#define EDP_AUX_TRANS_CTRL_NO_SEND_ADDR (0x00000400) +#define EDP_AUX_TRANS_CTRL_NO_SEND_STOP (0x00000800) + +#define REG_EDP_TIMEOUT_COUNT (0x0000003C) +#define REG_EDP_AUX_LIMITS (0x00000040) +#define REG_EDP_AUX_STATUS (0x00000044) +#define AUX_CMD_READ (BIT(4)) + +enum { + EDID_LENGTH = 128, + EDID_I2C_ADDR = 0x50, + EDID_EXTENSION_FLAG = 0x7e, +}; + +static inline uint32_t EDP_AUX_DATA_DATA(uint32_t val) +{ + return ((val) << EDP_AUX_DATA_DATA__SHIFT) & EDP_AUX_DATA_DATA__MASK; +} + +static inline uint32_t EDP_AUX_DATA_INDEX(uint32_t val) +{ + return ((val) << EDP_AUX_DATA_INDEX__SHIFT) & EDP_AUX_DATA_INDEX__MASK; +} + +void edp_aux_ctrl(int enable); +int edp_read_edid(struct edid *out); +ssize_t edp_aux_transfer(unsigned int address, u8 request, void *buffer, size_t size); + +#endif diff --git a/src/soc/qualcomm/sc7280/include/soc/display/edp_ctrl.h b/src/soc/qualcomm/sc7280/include/soc/display/edp_ctrl.h new file mode 100644 index 0000000000..fa6e1720b7 --- /dev/null +++ b/src/soc/qualcomm/sc7280/include/soc/display/edp_ctrl.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _EDP_CTRL_H +#define _EDP_CTRL_H + +#include +#include + +enum cb_err edp_ctrl_init(struct edid *edid); + +#endif diff --git a/src/soc/qualcomm/sc7280/include/soc/display/edp_phy.h b/src/soc/qualcomm/sc7280/include/soc/display/edp_phy.h new file mode 100644 index 0000000000..84932f0f79 --- /dev/null +++ b/src/soc/qualcomm/sc7280/include/soc/display/edp_phy.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _EDP_PHY_H +#define _EDP_PHY_H + +#include +#include + +void edp_phy_config(u8 v_level, u8 p_level); +void edp_phy_vm_pe_init(void); +int edp_phy_enable(void); +int edp_phy_power_on(uint32_t link_rate); + +#endif diff --git a/src/soc/qualcomm/sc7280/include/soc/display/edp_reg.h b/src/soc/qualcomm/sc7280/include/soc/display/edp_reg.h new file mode 100644 index 0000000000..1bdf4b17cb --- /dev/null +++ b/src/soc/qualcomm/sc7280/include/soc/display/edp_reg.h @@ -0,0 +1,328 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _EDP_REG_H_ +#define _EDP_REG_H_ + +#include +#include + +struct edp_ahbclk_regs { + uint32_t hw_version; + uint32_t reserved0[3]; + uint32_t sw_reset; + uint32_t phy_ctrl; + uint32_t clk_ctrl; + uint32_t clk_active; + uint32_t interrupt_status; + uint32_t interrupt_status2; + uint32_t interrupt_status3; +}; + +check_member(edp_ahbclk_regs, sw_reset, 0x10); + +struct edp_auxclk_regs { + uint32_t hpd_ctrl; + uint32_t hpd_int_status; + uint32_t hpd_int_ack; + uint32_t hpd_int_mask; + uint32_t reserved0[2]; + uint32_t hpd_reftimer; + uint32_t hpd_event_time0; + uint32_t hpd_event_time1; + uint32_t reserved1[3]; + uint32_t aux_ctrl; + uint32_t aux_data; + uint32_t aux_trans_ctrl; + uint32_t timeout_count; + uint32_t aux_limits; + uint32_t status; + uint32_t reserved2[22]; + uint32_t interrupt_trans_num; +}; +check_member(edp_auxclk_regs, hpd_reftimer, 0x18); +check_member(edp_auxclk_regs, aux_ctrl, 0x30); +check_member(edp_auxclk_regs, interrupt_trans_num, 0xa0); + +struct edp_lclk_regs { + uint32_t mainlink_ctrl; + uint32_t state_ctrl; + uint32_t configuration_ctrl; + uint32_t top_bot_interlaced_num_of_lanes; + uint32_t software_mvid; + uint32_t reserved0; + uint32_t software_nvid; + uint32_t total_hor_ver; + uint32_t start_hor_ver_from_sync; + uint32_t hysnc_vsync_width_polarity; + uint32_t active_hor_ver; + uint32_t misc1_misc0; + uint32_t valid_boundary; + uint32_t valid_boundary2; + uint32_t logcial2physical_lane_mapping; + uint32_t reserved1; + uint32_t mainlink_ready; + uint32_t mainlink_levels; + uint32_t mainlink_levels2; + uint32_t tu; +}; + +struct edp_p0clk_regs { + uint32_t bist_enable; + uint32_t reserved0[3]; + uint32_t timing_engine_en; + uint32_t intf_config; + uint32_t hsync_ctl; + uint32_t vsync_period_f0; + uint32_t vsync_period_f1; + uint32_t vsync_pulse_width_f0; + uint32_t vsync_pulse_width_f1; + uint32_t display_v_start_f0; + uint32_t display_v_start_f1; + uint32_t display_v_end_f0; + uint32_t display_v_end_f1; + uint32_t active_v_start_f0; + uint32_t active_v_start_f1; + uint32_t active_v_end_f0; + uint32_t active_v_end_f1; + uint32_t display_hctl; + uint32_t active_hctl; + uint32_t hsync_skew; + uint32_t polarity_ctl; + uint32_t reserved1; + uint32_t tpg_main_control; + uint32_t tpg_video_config; + uint32_t tpg_component_limits; + uint32_t tpg_rectangle; + uint32_t tpg_initial_value; + uint32_t tpg_color_changing_frames; + uint32_t tpg_rgb_mapping; + uint32_t dsc_dto; +}; + +check_member(edp_p0clk_regs, dsc_dto, 0x7c); + +struct edp_phy_regs { + uint32_t revision_id0; + uint32_t revision_id1; + uint32_t revision_id2; + uint32_t revision_id3; + uint32_t cfg; + uint32_t cfg1; + uint32_t cfg2; + uint32_t pd_ctl; + uint32_t mode; + uint32_t aux_cfg[13]; + uint32_t aux_interrupt_mask; + uint32_t aux_interrupt_clr; + uint32_t aux_bist_cfg; + uint32_t aux_bist_prbs_seed; + uint32_t aux_bist_prbs_poly; + uint32_t aux_tx_prog_pat_16b_lsb; + uint32_t aux_tx_prog_pat_16b_msb; + uint32_t vco_div; + uint32_t tsync_ovrd; + uint32_t tx0_tx1_lane_ctl; + uint32_t tx0_tx1_bist_cfg[4]; + uint32_t tx0_tx1_prbs_seed_byte0; + uint32_t tx0_tx1_prbs_seed_byte1; + uint32_t tx0_tx1_bist_pattern0; + uint32_t tx0_tx1_bist_pattern1; + uint32_t tx2_tx3_lane_ctl; + uint32_t tx2_tx3_bist_cfg[4]; + uint32_t tx2_tx3_prbs_seed_byte0; + uint32_t tx2_tx3_prbs_seed_byte1; + uint32_t tx2_tx3_bist_pattern0; + uint32_t tx2_tx3_bist_pattern1; + uint32_t misr_ctl; + uint32_t debug_bus_sel; + uint32_t spare[4]; + uint32_t aux_interrupt_status; + uint32_t status; +}; + +struct edp_phy_lane_regs { + uint32_t tx_clk_buf_enable; + uint32_t tx_emp_post1_lvl; + uint32_t tx_post2_emph; + uint32_t tx_boost_lvl_up_dn; + uint32_t tx_idle_lvl_large_amp; + uint32_t tx_drv_lvl; + uint32_t tx_drv_lvl_offset; + uint32_t tx_reset_tsync_en; + uint32_t tx_pre_emph; + uint32_t tx_interface_select; + uint32_t tx_tx_band; + uint32_t tx_slew_cntl; + uint32_t tx_lpb0_cfg[3]; + uint32_t tx_rescode_lane_tx; + uint32_t tx_rescode_lane_tx1; + uint32_t tx_rescode_lane_offset_tx0; + uint32_t tx_rescode_lane_offset_tx1; + uint32_t tx_serdes_byp_en_out; + uint32_t tx_dbg_bus_sel; + uint32_t tx_transceiver_bias_en; + uint32_t tx_highz_drvr_en; + uint32_t tx_tx_pol_inv; + uint32_t tx_parrate_rec_detect_idle_en; + uint32_t tx_lane_mode1; + uint32_t tx_lane_mode2; + uint32_t tx_atb_sel1; + uint32_t tx_atb_sel2; + uint32_t tx_reset_gen_muxes; + uint32_t tx_tran_drvr_emp_en; + uint32_t tx_vmode_ctrl1; + uint32_t tx_lane_dig_config; + uint32_t tx_ldo_config; + uint32_t tx_dig_bkup_ctrl; +}; + +struct edp_phy_pll_regs { + uint32_t qserdes_com_atb_sel1; + uint32_t qserdes_com_atb_sel2; + uint32_t qserdes_com_freq_update; + uint32_t qserdes_com_bg_timer; + uint32_t qserdes_com_ssc_en_center; + uint32_t qserdes_com_ssc_adj_per1; + uint32_t qserdes_com_ssc_adj_per2; + uint32_t qserdes_com_ssc_per1; + uint32_t qserdes_com_ssc_per2; + uint32_t qserdes_com_ssc_step_size1_mode0; + uint32_t qserdes_com_ssc_step_size2_mode0; + uint32_t qserdes_com_ssc_step_size3_mode0; + uint32_t qserdes_com_ssc_step_size1_mode1; + uint32_t qserdes_com_ssc_step_size2_mode1; + uint32_t qserdes_com_ssc_step_size3_mode1; + uint32_t qserdes_com_post_div; + uint32_t qserdes_com_post_div_mux; + uint32_t qserdes_com_bias_en_clkbuflr_en; + uint32_t qserdes_com_clk_enable1; + uint32_t qserdes_com_sys_clk_ctrl; + uint32_t qserdes_com_sysclk_buf_enable; + uint32_t qserdes_com_pll_en; + uint32_t qserdes_com_pll_ivco; + uint32_t qserdes_com_cmn_iterim; + uint32_t qserdes_com_cmn_iptrim; + uint32_t qserdes_com_ep_clk_detect_ctrl; + uint32_t qserdes_com_sysclk_det_comp_status; + uint32_t qserdes_com_clk_ep_div_mode0; + uint32_t qserdes_com_clk_ep_div_mode1; + uint32_t qserdes_com_cp_ctrl_mode0; + uint32_t qserdes_com_cp_ctrl_mode1; + uint32_t qserdes_com_pll_rctrl_mode0; + uint32_t qserdes_com_pll_rctrl_mode1; + uint32_t qserdes_com_pll_cctrl_mode0; + uint32_t qserdes_com_pll_cctrl_mode1; + uint32_t qserdes_com_pll_cntrl; + uint32_t qserdes_com_bias_en_ctrl_by_psm; + uint32_t qserdes_com_sysclk_en_sel; + uint32_t qserdes_com_cml_sysclk_sel; + uint32_t qserdes_com_resetsm_cntrl; + uint32_t qserdes_com_resetsm_cntrl2; + uint32_t qserdes_com_lock_cmp_en; + uint32_t qserdes_com_lock_cmp_cfg; + uint32_t qserdes_com_lock_cmp1_mode0; + uint32_t qserdes_com_lock_cmp2_mode0; + uint32_t qserdes_com_lock_cmp1_mode1; + uint32_t qserdes_com_lock_cmp2_mode1; + uint32_t qserdes_com_dec_start_mode0; + uint32_t qserdes_com_dec_start_msb_mode0; + uint32_t qserdes_com_dec_start_mode1; + uint32_t qserdes_com_dec_start_msb_mode1; + uint32_t qserdes_com_div_frac_start1_mode0; + uint32_t qserdes_com_div_frac_start2_mode0; + uint32_t qserdes_com_div_frac_start3_mode0; + uint32_t qserdes_com_div_frac_start1_mode1; + uint32_t qserdes_com_div_frac_start2_mode1; + uint32_t qserdes_com_div_frac_start3_mode1; + uint32_t qserdes_com_integloop_initval; + uint32_t qserdes_com_integloop_en; + uint32_t qserdes_com_integloop_gain0_mode0; + uint32_t qserdes_com_integloop_gain1_mode0; + uint32_t qserdes_com_integloop_gain0_mode1; + uint32_t qserdes_com_integloop_gain1_mode1; + uint32_t qserdes_com_integloop_p_path_gain0; + uint32_t qserdes_com_integloop_p_path_gain1; + uint32_t qserdes_com_vcoval_deadman_ctrl; + uint32_t qserdes_com_vco_tune_ctrl; + uint32_t qserdes_com_vco_tune_map; + uint32_t qserdes_com_vco_tune1_mode0; + uint32_t qserdes_com_vco_tune2_mode0; + uint32_t qserdes_com_vco_tune1_mode1; + uint32_t qserdes_com_vco_tune2_mode1; + uint32_t qserdes_com_vco_tune_initval1; + uint32_t qserdes_com_vco_tune_initval2; + uint32_t qserdes_com_vco_tune_minval1; + uint32_t qserdes_com_vco_tune_minval2; + uint32_t qserdes_com_vco_tune_maxval1; + uint32_t qserdes_com_vco_tune_maxval2; + uint32_t qserdes_com_vco_tune_timer1; + uint32_t qserdes_com_vco_tune_timer2; + uint32_t qserdes_com_cmn_status; + uint32_t qserdes_com_reset_sm_status; + uint32_t qserdes_com_restrim_code_status; + uint32_t qserdes_com_pllcal_code1_status; + uint32_t qserdes_com_pllcal_code2_status; + uint32_t qserdes_com_clk_sel; + uint32_t qserdes_com_hsclk_sel; + uint32_t qserdes_com_hsclk_hs_switch_sel; + uint32_t qserdes_com_integloop_bincode_status; + uint32_t qserdes_com_pll_analog; + uint32_t qserdes_com_coreclk_div_mode0; + uint32_t qserdes_com_coreclk_div_mode1; + uint32_t qserdes_com_sw_reset; + uint32_t qserdes_com_core_clk_en; + uint32_t qserdes_com_c_ready_status; + uint32_t qserdes_com_cmn_config; + uint32_t qserdes_com_cmn_rate_override; + uint32_t qserdes_com_svs_mode_clk_sel; +}; + +/* EDP_STATE_CTRL */ +enum { + SW_LINK_TRAINING_PATTERN1 = BIT(0), + SW_LINK_TRAINING_PATTERN2 = BIT(1), + SW_LINK_TRAINING_PATTERN3 = BIT(2), + SW_LINK_TRAINING_PATTERN4 = BIT(3), + SW_LINK_SYMBOL_ERROR_RATE_MEASUREMENT = BIT(4), + SW_LINK_PRBS7 = BIT(5), + SW_LINK_TEST_CUSTOM_80BIT_PATTERN = BIT(6), + SW_SEND_VIDEO = BIT(7), + SW_PUSH_IDLE = BIT(8), +}; + +/* EDP_PHY_AUX_INTERRUPT_CLEAR */ +enum { + RX_STOP_ERR = BIT(0), + RX_DEC_ERR = BIT(1), + RX_SYNC_ERR = BIT(2), + RX_ALIGN_ERR = BIT(3), + TX_REQ_ERR = BIT(4), + GLOBE_REQ_CLR = BIT(5), +}; + +enum { + EDP_CTRL_BASE = 0xAEA0000, + DP_EDP_PHY_BASE = 0xAEC0000, +}; + +enum { + EDP_AHBCLK_BASE = EDP_CTRL_BASE, + EDP_AUXCLK_BASE = EDP_CTRL_BASE + 0x200, + EDP_LCLK_BASE = EDP_CTRL_BASE + 0x400, + EDP_P0CLK_BASE = EDP_CTRL_BASE + 0x1000, + EDP_PHY_BASE = DP_EDP_PHY_BASE + 0x2A00, + EDP_PHY_LANE_TX0_BASE = DP_EDP_PHY_BASE + 0x2200, + EDP_PHY_LANE_TX1_BASE = DP_EDP_PHY_BASE + 0x2600, + EDP_PHY_PLL_BASE = DP_EDP_PHY_BASE + 0x2000, +}; + +static struct edp_ahbclk_regs *const edp_ahbclk = (void *)EDP_AHBCLK_BASE; +static struct edp_auxclk_regs *const edp_auxclk = (void *)EDP_AUXCLK_BASE; +static struct edp_lclk_regs *const edp_lclk = (void *)EDP_LCLK_BASE; +static struct edp_p0clk_regs *const edp_p0clk = (void *)EDP_P0CLK_BASE; +static struct edp_phy_regs *const edp_phy = (void *)EDP_PHY_BASE; +static struct edp_phy_lane_regs *const edp_phy_lane_tx0 = (void *)EDP_PHY_LANE_TX0_BASE; +static struct edp_phy_lane_regs *const edp_phy_lane_tx1 = (void *)EDP_PHY_LANE_TX1_BASE; +static struct edp_phy_pll_regs *const edp_phy_pll = (void *)EDP_PHY_PLL_BASE; + +#endif diff --git a/src/soc/qualcomm/sc7280/include/soc/display/mdssreg.h b/src/soc/qualcomm/sc7280/include/soc/display/mdssreg.h new file mode 100644 index 0000000000..40968f2229 --- /dev/null +++ b/src/soc/qualcomm/sc7280/include/soc/display/mdssreg.h @@ -0,0 +1,529 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _SOC_DISPLAY_MDSS_REG_H_ +#define _SOC_DISPLAY_MDSS_REG_H_ + +#include +#include +#include + +#define INTF_FLUSH INTF_FLUSH_5 + +struct dsi_regs { + uint32_t hw_version; + uint32_t ctrl; + uint32_t reserved0[2]; + uint32_t video_mode_ctrl; + uint32_t reserved1[4]; + uint32_t video_mode_active_h; + uint32_t video_mode_active_v; + uint32_t video_mode_active_total; + uint32_t video_mode_active_hsync; + uint32_t video_mode_active_vsync; + uint32_t video_mode_active_vsync_vpos; + uint32_t cmd_mode_dma_ctrl; + uint32_t cmd_mode_mdp_ctrl; + uint32_t cmd_mode_mdp_dcs_cmd_ctrl; + uint32_t dma_cmd_offset; + uint32_t dma_cmd_length; + uint32_t reserved2[2]; + uint32_t cmd_mode_mdp_stream0_ctrl; + uint32_t cmd_mode_mdp_stream0_total; + uint32_t cmd_mode_mdp_stream1_ctrl; + uint32_t cmd_mode_mdp_stream1_total; + uint32_t reserved4[7]; + uint32_t trig_ctrl; + uint32_t reserved5[2]; + uint32_t cmd_mode_dma_sw_trigger; + uint32_t reserved6[3]; + uint32_t misr_cmd_ctrl; + uint32_t misr_video_ctrl; + uint32_t lane_status; + uint32_t lane_ctrl; + uint32_t reserved7[3]; + uint32_t hs_timer_ctrl; + uint32_t timeout_status; + uint32_t clkout_timing_ctrl; + uint32_t eot_packet; + uint32_t eot_packet_ctrl; + uint32_t reserved8[15]; + uint32_t err_int_mask0; + uint32_t int_ctrl; + uint32_t iobist_ctrl; + uint32_t soft_reset; + uint32_t clk_ctrl; + uint32_t reserved9[15]; + uint32_t test_pattern_gen_ctrl; + uint32_t reserved10[7]; + uint32_t test_pattern_gen_cmd_dma_init_val; + uint32_t reserved11[14]; + uint32_t cmd_mode_mdp_ctrl2; + uint32_t reserved12[12]; + uint32_t tpg_dma_fifo_reset; + uint32_t reserved13[44]; + uint32_t video_compression_mode_ctrl; + uint32_t video_compression_mode_ctrl2; + uint32_t cmd_compression_mode_ctrl; + uint32_t cmd_compression_mode_ctrl2; + uint32_t cmd_compression_mode_ctrl3; +}; + +check_member(dsi_regs, video_mode_active_h, 0x24); +check_member(dsi_regs, cmd_mode_mdp_stream0_ctrl, 0x58); +check_member(dsi_regs, trig_ctrl, 0x84); +check_member(dsi_regs, cmd_mode_dma_sw_trigger, 0x90); +check_member(dsi_regs, misr_cmd_ctrl, 0xA0); +check_member(dsi_regs, hs_timer_ctrl, 0xBC); +check_member(dsi_regs, err_int_mask0, 0x10C); +check_member(dsi_regs, test_pattern_gen_ctrl, 0x15c); +check_member(dsi_regs, test_pattern_gen_cmd_dma_init_val, 0x17c); +check_member(dsi_regs, cmd_mode_mdp_ctrl2, 0x1B8); +check_member(dsi_regs, tpg_dma_fifo_reset, 0x1EC); +check_member(dsi_regs, video_compression_mode_ctrl, 0x2A0); + +struct dsi_phy_regs { + uint32_t phy_cmn_revision_id0; + uint32_t reserved0[3]; + uint32_t phy_cmn_clk_cfg0; + uint32_t phy_cmn_clk_cfg1; + uint32_t phy_cmn_glbl_ctrl; + uint32_t phy_cmn_rbuf_ctrl; + uint32_t phy_cmn_vreg_ctrl; + uint32_t phy_cmn_ctrl0; + uint32_t phy_cmn_ctrl1; + uint32_t phy_cmn_ctrl2; + uint32_t phy_cmn_lane_cfg0; + uint32_t phy_cmn_lane_cfg1; + uint32_t phy_cmn_pll_ctrl; + uint32_t reserved1[23]; + uint32_t phy_cmn_dsi_lane_ctrl0; + uint32_t reserved2[4]; + uint32_t phy_cmn_timing_ctrl[12]; + uint32_t reserved3[4]; + uint32_t phy_cmn_phy_status; + uint32_t reserved4[68]; + struct { + uint32_t dln0_cfg[4]; + uint32_t dln0_test_datapath; + uint32_t dln0_pin_swap; + uint32_t dln0_hstx_str_ctrl; + uint32_t dln0_offset_top_ctrl; + uint32_t dln0_offset_bot_ctrl; + uint32_t dln0_lptx_str_ctrl; + uint32_t dln0_lprx_ctrl; + uint32_t dln0_tx_dctrl; + uint32_t reserved5[20]; + } phy_ln_regs[5]; +}; + +check_member(dsi_phy_regs, phy_cmn_clk_cfg0, 0x10); +check_member(dsi_phy_regs, phy_cmn_dsi_lane_ctrl0, 0x98); +check_member(dsi_phy_regs, phy_cmn_timing_ctrl[0], 0xAC); +check_member(dsi_phy_regs, phy_cmn_phy_status, 0xEC); +check_member(dsi_phy_regs, phy_ln_regs[0], 0x200); +check_member(dsi_phy_regs, phy_ln_regs[1], 0x280); +check_member(dsi_phy_regs, phy_ln_regs[2], 0x300); +check_member(dsi_phy_regs, phy_ln_regs[3], 0x380); +check_member(dsi_phy_regs, phy_ln_regs[4], 0x400); + +struct dsi_phy_pll_qlink_regs { + uint32_t pll_analog_ctrls_one; + uint32_t pll_analog_ctrls_two; + uint32_t pll_int_loop_settings; + uint32_t pll_int_loop_settings_two; + uint32_t pll_analog_ctrls_three; + uint32_t pll_analog_ctrls_four; + uint32_t pll_int_loop_ctrls; + uint32_t pll_dsm_divider; + uint32_t pll_feedback_divider; + uint32_t pll_system_muxes; + uint32_t pll_freq_update_ctrl_overrides; + uint32_t pll_cmode; + uint32_t pll_cal_settings; + uint32_t pll_band_sel_cal_timer_low; + uint32_t pll_band_sel_cal_timer_high; + uint32_t pll_band_sel_cal_settings; + uint32_t pll_band_sel_min; + uint32_t pll_band_sel_max; + uint32_t pll_band_sel_pfilt; + uint32_t pll_band_sel_ifilt; + uint32_t pll_band_sel_cal_settings_two; + uint32_t pll_band_sel_cal_settings_three; + uint32_t pll_band_sel_cal_settings_four; + uint32_t pll_band_sel_icode_high; + uint32_t pll_band_sel_icode_low; + uint32_t pll_freq_detect_settings_one; + uint32_t pll_freq_detect_thresh; + uint32_t pll_freq_det_refclk_high; + uint32_t pll_freq_det_refclk_low; + uint32_t pll_freq_det_pllclk_high; + uint32_t pll_freq_det_pllclk_low; + uint32_t pll_pfilt; + uint32_t pll_ifilt; + uint32_t pll_pll_gain; + uint32_t pll_icode_low; + uint32_t pll_icode_high; + uint32_t pll_lockdet; + uint32_t pll_outdiv; + uint32_t pll_fastlock_ctrl; + uint32_t pll_pass_out_override_one; + uint32_t pll_pass_out_override_two; + uint32_t pll_core_override; + uint32_t pll_core_input_override; + uint32_t pll_rate_change; + uint32_t pll_digital_timers; + uint32_t pll_digital_timers_two; + uint32_t pll_decimal_div_start; + uint32_t pll_frac_div_start_low; + uint32_t pll_frac_div_start_mid; + uint32_t pll_frac_div_start_high; + uint32_t pll_dec_frac_muxes; + uint32_t pll_decimal_div_start_1; + uint32_t pll_frac_div_start_low1; + uint32_t pll_frac_div_start_mid1; + uint32_t pll_frac_div_start_high1; + uint32_t reserve0[4]; + uint32_t pll_mash_ctrl; + uint32_t reserved1[6]; + uint32_t pll_ssc_mux_ctrl; + uint32_t pll_ssc_stepsize_low1; + uint32_t pll_ssc_stepsize_high1; + uint32_t pll_ssc_div_per_low_1; + uint32_t pll_ssc_div_per_high_1; + uint32_t pll_ssc_adjper_low_1; + uint32_t pll_ssc_adjper_high_1; + uint32_t reserved2[6]; + uint32_t pll_ssc_ctrl; + uint32_t pll_outdiv_rate; + uint32_t pll_lockdet_rate[2]; + uint32_t pll_prop_gain_rate[2]; + uint32_t pll_band_set_rate[2]; + uint32_t pll_gain_ifilt_band[2]; + uint32_t pll_fl_int_gain_pfilt_band[2]; + uint32_t pll_pll_fastlock_en_band; + uint32_t reserved9[3]; + uint32_t pll_freq_tune_accum_init_mux; + uint32_t pll_lock_override; + uint32_t pll_lock_delay; + uint32_t pll_lock_min_delay; + uint32_t pll_clock_inverters; + uint32_t pll_spare_and_jpc_overrides; + uint32_t pll_bias_ctrl_1; + uint32_t pll_bias_ctrl_2; + uint32_t pll_alog_obsv_bus_ctrl_1; + uint32_t pll_common_status_one; +}; +check_member(dsi_phy_pll_qlink_regs, pll_mash_ctrl, 0xEC); +check_member(dsi_phy_pll_qlink_regs, pll_ssc_mux_ctrl, 0x108); +check_member(dsi_phy_pll_qlink_regs, pll_ssc_ctrl, 0x13C); +check_member(dsi_phy_pll_qlink_regs, pll_freq_tune_accum_init_mux, 0x17C); + +struct mdp_intf_regs { + uint32_t timing_eng_enable; + uint32_t intf_config; + uint32_t intf_hsync_ctl; + uint32_t intf_vysnc_period_f0; + uint32_t intf_vysnc_period_f1; + uint32_t intf_vysnc_pulse_width_f0; + uint32_t intf_vysnc_pulse_width_f1; + uint32_t intf_disp_v_start_f0; + uint32_t intf_disp_v_start_f1; + uint32_t intf_disp_v_end_f0; + uint32_t intf_disp_v_end_f1; + uint32_t intf_active_v_start_f0; + uint32_t intf_active_v_start_f1; + uint32_t intf_active_v_end_f0; + uint32_t intf_active_v_end_f1; + uint32_t intf_disp_hctl; + uint32_t intf_active_hctl; + uint32_t intf_border_color; + uint32_t intf_underflow_color; + uint32_t hsync_skew; + uint32_t polarity_ctl; + uint32_t test_ctl; + uint32_t tp_color0; + uint32_t tp_color1; + uint32_t intf_config2; + uint32_t display_data_hctl; + uint32_t reserved0[10]; + uint32_t intf_panel_format; + uint32_t reserved1[55]; + uint32_t intf_prof_fetch_start; + uint32_t reserved2[58]; + uint32_t intf_mux; +}; + +check_member(mdp_intf_regs, intf_panel_format, 0x90); +check_member(mdp_intf_regs, intf_prof_fetch_start, 0x170); +check_member(mdp_intf_regs, intf_mux, 0x25C); + +struct mdp_ctl_regs { + uint32_t ctl_layer0; + uint32_t ctl_layer1; + uint32_t reserved0[3]; + uint32_t ctl_top; + uint32_t ctl_flush; + uint32_t ctl_start; + uint32_t reserved1[53]; + uint32_t ctl_intf_active; + uint32_t ctl_cdm_active; + uint32_t ctl_fetch_pipe_active; + uint32_t reserved2[4]; + uint32_t ctl_intf_flush; +}; + +check_member(mdp_ctl_regs, ctl_top, 0x14); +check_member(mdp_ctl_regs, ctl_intf_active, 0xF4); +check_member(mdp_ctl_regs, ctl_intf_flush, 0x110); + +struct mdp_layer_mixer_regs { + uint32_t layer_op_mode; + uint32_t layer_out_size; + uint32_t layer_border_color_0; + uint32_t layer_border_color_1; + uint32_t reserved0[4]; + struct { + uint32_t layer_blend_op; + uint32_t layer_blend_const_alpha; + uint32_t layer_blend_fg_color_fill_color0; + uint32_t layer_blend_fg_color_fill_color1; + uint32_t layer_blend_fg_fill_size; + uint32_t layer_blend_fg_fill_xy; + } layer_blend[6]; +}; + +struct mdp_sspp_regs { + uint32_t sspp_src_size; + uint32_t sspp_src_img_size; + uint32_t sspp_src_xy; + uint32_t sspp_out_size; + uint32_t sspp_out_xy; + uint32_t sspp_src0; + uint32_t sspp_src1; + uint32_t sspp_src2; + uint32_t sspp_src3; + uint32_t sspp_src_ystride0; + uint32_t sspp_src_ystride1; + uint32_t sspp_tile_frame_size; + uint32_t sspp_src_format; + uint32_t sspp_src_unpack_pattern; + uint32_t sspp_src_op_mode; + uint32_t reserved0[51]; + uint32_t sspp_sw_pic_ext_c0_req_pixels; + uint32_t reserved1[3]; + uint32_t sspp_sw_pic_ext_c1c2_req_pixels; + uint32_t reserved2[3]; + uint32_t sspp_sw_pic_ext_c3_req_pixels; +}; + +check_member(mdp_sspp_regs, sspp_sw_pic_ext_c0_req_pixels, 0x108); +check_member(mdp_sspp_regs, sspp_sw_pic_ext_c1c2_req_pixels, 0x118); +check_member(mdp_sspp_regs, sspp_sw_pic_ext_c3_req_pixels, 0x128); + +struct mdss_hw_regs { + uint32_t hw_version; +}; + +struct vbif_rt_regs { + uint32_t reserved0[88]; + uint32_t vbif_out_axi_amemtype_conf0; + uint32_t vbif_out_axi_amemtype_conf1; + uint32_t reserved1[250]; + struct { + uint32_t vbif_xinl_qos_rp_remap; + uint32_t vbif_xinh_qos_rp_remap; + } qos_rp_remap[8]; + struct { + uint32_t vbif_xinl_qos_lvl_remap; + uint32_t vbif_xinh_qos_lvl_remap; + } qos_lvl_remap[8]; +}; + +check_member(vbif_rt_regs, vbif_out_axi_amemtype_conf0, 0x160); +check_member(vbif_rt_regs, qos_rp_remap[0], 0x550); + +enum { + MDSS_BASE = 0xAE00000, +}; + +enum { + MDP_0_CTL_BASE = MDSS_BASE + 0x16000, + MDP_VP_0_SSPP_BASE = MDSS_BASE + 0x5000, + MDP_VP_0_LAYER_MIXER_BASE = MDSS_BASE + 0x45000, + MDP_5_INTF_BASE = MDSS_BASE + 0x3A000, + MDP_VBIF_RT_BASE = MDSS_BASE + 0xB0000, + DSI0_CTL_BASE = MDSS_BASE + 0x94000, + DSI0_PHY_BASE = MDSS_BASE + 0x94400, + DSI0_PHY_DLN0_BASE = MDSS_BASE + 0x94600, + DSI0_PHY_DLN1_BASE = MDSS_BASE + 0x94680, + DSI0_PHY_DLN2_BASE = MDSS_BASE + 0x94700, + DSI0_PHY_DLN3_BASE = MDSS_BASE + 0x94780, + DSI0_PHY_CLKLN_BASE = MDSS_BASE + 0x94800, + DSI0_PHY_PLL_QLINK_COM = MDSS_BASE + 0x94a00, +}; + +/* DSI_0_CLK_CTRL */ +enum { + INTF = BIT(31), + PERIPH = BIT(30), + CWB = BIT(28), + ROT = BIT(27), + CDM_0 = BIT(26), + DMA_3 = BIT(25), + DMA_2 = BIT(24), + MERGE_3D = BIT(23), + DSC = BIT(22), + DSPP_3 = BIT(21), + LAYER_MIXER_5 = BIT(20), + DSPP_PA_LUTV_3 = BIT(19), + VIG_3 = BIT(18), + CTL = BIT(17), + WB = BIT(16), + DSPP_2 = BIT(15), + DSPP_1 = BIT(14), + DSPP_0 = BIT(13), + DMA_1 = BIT(12), + DMA_0 = BIT(11), + LAYER_MIXER_4 = BIT(10), + LAYER_MIXER_3 = BIT(9), + LAYER_MIXER_2 = BIT(8), + LAYER_MIXER_1 = BIT(7), + LAYER_MIXER_0 = BIT(6), + DSPP_PA_LUTV_2 = BIT(5), + DSPP_PA_LUTV_1 = BIT(4), + DSPP_PA_LUTV_0 = BIT(3), + VIG_2 = BIT(2), + VIG_1 = BIT(1), + VIG_0 = BIT(0), +}; + +enum { + DSI_AHBS_HCLK_ON = BIT(0), + DSI_AHBM_SCLK_ON = BIT(1), + DSI_PCLK_ON = BIT(2), + DSI_DSICLK_ON = BIT(3), + DSI_BYTECLK_ON = BIT(4), + DSI_ESCCLK_ON = BIT(5), + DSI_FORCE_ON_DYN_AHBS_HCLK = BIT(8), + DSI_FORCE_ON_DYN_AHBM_HCLK = BIT(9), + DSI_FORCE_ON_DYN_DSICLK = BIT(10), + DSI_FORCE_ON_DYN_BYTECLK = BIT(11), + DSI_AHBS_HCLK_HYSTERISIS1_CTRL = (3 << 11), + DSI_AHBM_HCLK_HYSTERISIS1_CTRL = (3 << 13), + DSI_DSICLK_HYSTERISIS1_CTRL = (3 << 15), + DSI_FORCE_ON_DYN_PCLK = BIT(20), + DSI_FORCE_ON_LANE_LAYER_TG_BYTECLK = BIT(21), + DSI_DMA_CLK_STOP = BIT(22), +}; + +/* DSI_0_INT_CTRL */ +enum { + DSI_CMD_MODE_DMA_DONE_AK = BIT(0), + DSI_CMD_MODE_DMA_DONE_STAT = BIT(0), + DSI_CMD_MODE_DMA_DONE_MASK = BIT(1), + DSI_CMD_MODE_MDP_DONE_AK = BIT(8), + DSI_CMD_MODE_MDP_DONE_STAT = BIT(8), + DSI_CMD_MODE_MDP_DONE_MASK = BIT(9), + DSI_CMD_MDP_STREAM0_DONE_AK = BIT(10), + DSI_CMD_MDP_STREAM0_DONE_STAT = BIT(10), + DSI_CMD_MDP_STREAM0_DONE_MASK = BIT(11), + DSI_VIDEO_MODE_DONE_AK = BIT(16), + DSI_VIDEO_MODE_DONE_STAT = BIT(16), + DSI_VIDEO_MODE_DONE_MASK = BIT(17), + DSI_BTA_DONE_AK = BIT(20), + DSI_BTA_DONE_STAT = BIT(20), + DSI_BTA_DONE_MASK = BIT(21), + DSI_ERROR_AK = BIT(24), + DSI_ERROR_STAT = BIT(24), + DSI_ERROR_MASK = BIT(25), + DSI_DYNAMIC_BLANKING_DMA_DONE_AK = BIT(26), + DSI_DYNAMIC_BLANKING_DMA_DONE_STAT = BIT(26), + DSI_DYNAMIC_BLANKING_DMA_DONE_MASK = BIT(27), + DSI_DYNAMIC_REFRESH_DONE_AK = BIT(28), + DSI_DYNAMIC_REFRESH_DONE_STAT = BIT(28), + DSI_DYNAMIC_REFRESH_DONE_MASK = BIT(29), + DSI_DESKEW_DONE_AK = BIT(30), + DSI_DESKEW_DONE_STAT = BIT(30), + DSI_DESKEW_DONE_MASK = BIT(31), +}; + +/* DSI_0_COMMAND_MODE_MDP_DCS_CMD_CTRL */ +enum { + WR_MEM_START = 255, + WR_MEM_CONTINUE = 255 << 8, + INSERT_DCS_COMMAND = BIT(16), +}; + +/* DSI_0_COMMAND_MODE_DMA_CTRL */ +enum { + PACKET_TYPE = BIT(24), + POWER_MODE = BIT(26), + EMBEDDED_MODE = BIT(28), + WC_SEL = BIT(29), + BROADCAST_MASTER = BIT(30), + BROADCAST_EN = BIT(31), +}; + +/* MDP_VP_0_VIG_0_SSPP_SRC_OP_MODE */ +enum { + BWC_DEC_EN = BIT(0), + SW_PIX_EXT_OVERRIDE = BIT(31), +}; + +/* MDP_INTF_x_INTF_CONFIG */ +enum { + INTERLACE_MODE = BIT(0), + REPEAT_PIXEL = BIT(1), + INTERLACE_INIT_SEL = BIT(2), + BORDER_ENABLE = BIT(3), + EDP_PSR_OVERRIDE_EN = BIT(7), + PACK_ALIGN = BIT(10), + DSI_VIDEO_STOP_MODE = BIT(23), + ACTIVE_H_EN = BIT(29), + ACTIVE_V_EN = BIT(30), + PROG_FETCH_START_EN = BIT(31), +}; + +/* MDP_CTL_0_LAYER_0 */ +enum { + VIG_0_OUT = BIT(0), + BORDER_OUT = BIT(24), +}; + +/* MDP_CTL_0_FETCH_PIPE_ACTIVE */ +enum { + FETCH_PIPE_VIG0_ACTIVE = BIT(16), + FETCH_PIPE_VIG1_ACTIVE = BIT(17), +}; + +/* MDP_CTL_0_INTF_ACTIVE*/ +enum { + INTF_ACTIVE_0 = BIT(0), + INTF_ACTIVE_1 = BIT(1), + INTF_ACTIVE_5 = BIT(5), +}; + +/* MDP_CTL_0_INTF_FLUSH */ +enum { + INTF_FLUSH_0 = BIT(0), + INTF_FLUSH_1 = BIT(1), + INTF_FLUSH_5 = BIT(5), +}; + +static struct dsi_regs *const dsi0 = (void *)DSI0_CTL_BASE; +static struct dsi_phy_regs *const dsi0_phy = (void *)DSI0_PHY_BASE; +static struct dsi_phy_pll_qlink_regs *const phy_pll_qlink = (void *)DSI0_PHY_PLL_QLINK_COM; +static struct mdss_hw_regs *const mdss_hw = (void *)MDSS_BASE; +static struct mdp_intf_regs *const mdp_intf = (void *)MDP_5_INTF_BASE; +static struct mdp_ctl_regs *const mdp_ctl = (void *)MDP_0_CTL_BASE; +static struct mdp_layer_mixer_regs *const mdp_layer_mixer = (void *)MDP_VP_0_LAYER_MIXER_BASE; +static struct mdp_sspp_regs *const mdp_sspp = (void *)MDP_VP_0_SSPP_BASE; +static struct vbif_rt_regs *const vbif_rt = (void *)MDP_VBIF_RT_BASE; + +void mdss_intf_tg_setup(struct edid *edid); +void mdp_dsi_video_config(struct edid *edid); +void mdp_dsi_video_on(void); +void enable_mdss_clk(void); +void mdss_ctrl_config(void); + +#endif