diff --git a/msm/Makefile b/msm/Makefile index 9c7df05e71b3..1e4e0f35fb78 100644 --- a/msm/Makefile +++ b/msm/Makefile @@ -11,6 +11,7 @@ msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_altmode.o \ dp/dp_catalog.o \ dp/dp_catalog_v420.o \ dp/dp_catalog_v200.o \ + dp/dp_catalog_v500.o \ dp/dp_aux.o \ dp/dp_panel.o \ dp/dp_link.o \ @@ -26,7 +27,8 @@ msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_altmode.o \ sde_hdcp_1x.o \ sde_hdcp_2x.o \ dp/dp_pll.o \ - dp/dp_pll_5nm.o + dp/dp_pll_5nm.o \ + dp/edp_pll_7nm.o msm_drm-$(CONFIG_DRM_MSM_DP_MST) += dp/dp_mst_drm.o diff --git a/msm/dp/dp_catalog.c b/msm/dp/dp_catalog.c index b3b7c59f02a8..f24d61d2ce6e 100644 --- a/msm/dp/dp_catalog.c +++ b/msm/dp/dp_catalog.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ @@ -10,6 +11,8 @@ #include "dp_catalog.h" #include "dp_reg.h" #include "dp_debug.h" +#include "dp_link.h" +#include "dp_lphw_hpd.h" #define DP_GET_MSB(x) (x >> 8) #define DP_GET_LSB(x) (x & 0xff) @@ -2284,6 +2287,16 @@ end: return 0; } +static void dp_catalog_hpd_set_edp_mode(struct dp_catalog_hpd *hpd, bool is_edp) +{ + if (!hpd) { + DP_ERR("invalid input\n"); + return; + } + + hpd->is_edp = is_edp; +} + static void dp_catalog_hpd_config_hpd(struct dp_catalog_hpd *hpd, bool en) { struct dp_catalog_private *catalog; @@ -2300,9 +2313,15 @@ static void dp_catalog_hpd_config_hpd(struct dp_catalog_hpd *hpd, bool en) if (en) { u32 reftimer = dp_read(DP_DP_HPD_REFTIMER); - /* Arm only the UNPLUG and HPD_IRQ interrupts */ + /* + * Arm only the UNPLUG and HPD_IRQ interrupts for DP + * whereas for EDP arm only the HPD_IRQ interrupt + */ dp_write(DP_DP_HPD_INT_ACK, 0xF); - dp_write(DP_DP_HPD_INT_MASK, 0xA); + if (hpd->is_edp) + dp_write(DP_DP_HPD_INT_MASK, 0x2); + else + dp_write(DP_DP_HPD_INT_MASK, 0xA); /* Enable REFTIMER to count 1ms */ reftimer |= BIT(16); @@ -2323,7 +2342,7 @@ static void dp_catalog_hpd_config_hpd(struct dp_catalog_hpd *hpd, bool en) static u32 dp_catalog_hpd_get_interrupt(struct dp_catalog_hpd *hpd) { - u32 isr = 0; + u32 isr = 0, isr_mask = 0; struct dp_catalog_private *catalog; struct dp_io_data *io_data; @@ -2338,7 +2357,35 @@ static u32 dp_catalog_hpd_get_interrupt(struct dp_catalog_hpd *hpd) isr = dp_read(DP_DP_HPD_INT_STATUS); dp_write(DP_DP_HPD_INT_ACK, (isr & 0xf)); - return isr; + isr_mask = dp_read(DP_DP_HPD_INT_MASK); + + return (isr & isr_mask); +} + +static bool dp_catalog_hpd_wait_for_edp_panel_ready(struct dp_catalog_hpd *hpd) +{ + u32 reg, state; + void __iomem *base; + bool success = true; + u32 const poll_sleep_us = 2000; + u32 const pll_timeout_us = 1000000; + struct dp_catalog_private *catalog; + + catalog = dp_catalog_get_priv(hpd); + + base = catalog->io.dp_aux->io.base; + + reg = DP_DP_HPD_INT_STATUS; + + if (readl_poll_timeout_atomic((base + reg), state, + ((state & DP_HPD_STATE_STATUS_CONNECTED) > 0), + poll_sleep_us, pll_timeout_us)) { + DP_ERR("DP_HPD_STATE_STATUS CONNECTED bit is still low, status=%x\n", state); + + success = false; + } + + return success; } static void dp_catalog_audio_init(struct dp_catalog_audio *audio) @@ -2751,6 +2798,10 @@ static int dp_catalog_init(struct device *dev, struct dp_catalog *dp_catalog, dp_catalog->sub = dp_catalog_get_v200(dev, dp_catalog, &catalog->io); break; + case DP_PHY_VERSION_5_0_0: + dp_catalog->sub = dp_catalog_get_v500(dev, dp_catalog, + &catalog->io); + break; default: goto end; } @@ -2831,6 +2882,8 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) struct dp_catalog_hpd hpd = { .config_hpd = dp_catalog_hpd_config_hpd, .get_interrupt = dp_catalog_hpd_get_interrupt, + .wait_for_edp_panel_ready = dp_catalog_hpd_wait_for_edp_panel_ready, + .set_edp_mode = dp_catalog_hpd_set_edp_mode, }; struct dp_catalog_audio audio = { .init = dp_catalog_audio_init, @@ -2881,6 +2934,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) strlcpy(catalog->exe_mode, "hw", sizeof(catalog->exe_mode)); dp_catalog = &catalog->dp_catalog; + dp_catalog->parser = parser; dp_catalog->aux = aux; dp_catalog->ctrl = ctrl; diff --git a/msm/dp/dp_catalog.h b/msm/dp/dp_catalog.h index f73a1713ff39..8ea9e87386f8 100644 --- a/msm/dp/dp_catalog.h +++ b/msm/dp/dp_catalog.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ @@ -105,8 +106,12 @@ struct dp_catalog_ctrl { }; struct dp_catalog_hpd { + bool is_edp; + void (*config_hpd)(struct dp_catalog_hpd *hpd, bool en); u32 (*get_interrupt)(struct dp_catalog_hpd *hpd); + bool (*wait_for_edp_panel_ready)(struct dp_catalog_hpd *hpd); + void (*set_edp_mode)(struct dp_catalog_hpd *hpd, bool is_edp); }; #define HEADER_BYTE_2_BIT 0 @@ -256,6 +261,7 @@ struct dp_catalog { struct dp_catalog_hpd hpd; struct dp_catalog_sub *sub; + struct dp_parser *parser; void (*set_exe_mode)(struct dp_catalog *dp_catalog, char *mode); int (*get_reg_dump)(struct dp_catalog *dp_catalog, @@ -336,4 +342,7 @@ struct dp_catalog_sub *dp_catalog_get_v420(struct device *dev, struct dp_catalog_sub *dp_catalog_get_v200(struct device *dev, struct dp_catalog *catalog, struct dp_catalog_io *io); +struct dp_catalog_sub *dp_catalog_get_v500(struct device *dev, + struct dp_catalog *catalog, struct dp_catalog_io *io); + #endif /* _DP_CATALOG_H_ */ diff --git a/msm/dp/dp_catalog_v500.c b/msm/dp/dp_catalog_v500.c new file mode 100644 index 000000000000..507c5bc31f48 --- /dev/null +++ b/msm/dp/dp_catalog_v500.c @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ + +#include "dp_catalog.h" +#include "dp_reg.h" +#include "dp_debug.h" + +#define MMSS_DP_M_OFF (0x8) +#define MMSS_DP_N_OFF (0xC) + +#define dp_catalog_get_priv_v500(x) ({ \ + struct dp_catalog *catalog; \ + catalog = container_of(x, struct dp_catalog, x); \ + container_of(catalog->sub, \ + struct dp_catalog_private_v500, sub); \ +}) + +#define dp_read(x) ({ \ + catalog->sub.read(catalog->dpc, io_data, x); \ +}) + +#define dp_write(x, y) ({ \ + catalog->sub.write(catalog->dpc, io_data, x, y); \ +}) + +#define MAX_VOLTAGE_LEVELS 4 +#define MAX_PRE_EMP_LEVELS 4 + +enum { + TX_DRIVE_MODE_LOW_SWING_LOW_HBR = 0, + TX_DRIVE_MODE_HIGH_SWING_LOW_HBR, + TX_DRIVE_MODE_LOW_SWING_HIGH_HBR, + TX_DRIVE_MODE_HIGH_SWING_HIGH_HBR, + TX_DRIVE_MODE_DP, + TX_DRIVE_MODE_MINIDP, + TX_DRIVE_MODE_MAX, +}; + +static u8 const vm_pre_emphasis + [TX_DRIVE_MODE_MAX][MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = { + /* pe0, 0 db; pe1, 2.0 db; pe2, 3.6 db; pe3, 6.0 db */ + { // Low swing/pre-emphasis, low HBR + {0x05, 0x11, 0x17, 0x1D}, /* sw0, 0.2v */ + {0x05, 0x11, 0x18, 0xFF}, /* sw1, 0.25 v */ + {0x06, 0x11, 0xFF, 0xFF}, /* sw2, 0.3 v */ + {0x00, 0xFF, 0xFF, 0xFF} /* sw3, 0.35 v */ + }, + { // High swing/pre-emphasis, low HBR + {0x02, 0x0F, 0x17, 0x1D}, /* sw0, 0.3v */ + {0x02, 0x0F, 0x17, 0xFF}, /* sw1, 0.35v */ + {0x02, 0x0E, 0xFF, 0xFF}, /* sw2, 0.4v */ + {0x00, 0xFF, 0xFF, 0xFF} /* sw3, 0.45v */ + }, + { // Low swing/pre-emphasis, high HBR + {0x0C, 0x15, 0x19, 0x1E}, /* sw0, 0.2v */ + {0x09, 0x14, 0x19, 0xFF}, /* sw1, 0.25v */ + {0x0F, 0x14, 0xFF, 0xFF}, /* sw2, 0.3v */ + {0x0D, 0xFF, 0xFF, 0xFF} /* sw3, 0.35v */ + }, + { // High swing/pre-emphasis, high HBR + {0x08, 0x11, 0x16, 0x1B}, /* sw0, 0.3v */ + {0x00, 0x0C, 0x13, 0xFF}, /* sw1, 0.35v */ + {0x05, 0x10, 0xFF, 0xFF}, /* sw2, 0.4v */ + {0x00, 0xFF, 0xFF, 0xFF} /* sw3, 0.45v */ + }, + { // DP-only, DP/USB + {0x20, 0x2E, 0x35, 0xFF}, /* sw0, 0.4v */ + {0x20, 0x2E, 0x35, 0xFF}, /* sw1, 0.6v */ + {0x20, 0x2E, 0xFF, 0xFF}, /* sw2, 0.8v */ + {0xFF, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */ + }, + { // MiniDP-only + {0x00, 0x0E, 0x17, 0xFF}, /* sw0, 0.4v */ + {0x00, 0x0D, 0x16, 0xFF}, /* sw1, 0.6v */ + {0x00, 0x0D, 0xFF, 0xFF}, /* sw2, 0.8v */ + {0xFF, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */ + }, +}; + +/* voltage swing, 0.2v and 1.0v are not support */ +static u8 const vm_voltage_swing + [TX_DRIVE_MODE_MAX][MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = { + /* pe0, 0 db; pe1, 2.0 db; pe2, 3.6 db; pe3, 6.0 db */ + { // Low swing/pre-emphasis, low HBR + {0x07, 0x0F, 0x16, 0x1F}, /* sw0, 0.2v */ + {0x0D, 0x16, 0x1E, 0xFF}, /* sw1, 0.25 v */ + {0x11, 0x1B, 0xFF, 0xFF}, /* sw1, 0.3 v */ + {0x16, 0xFF, 0xFF, 0xFF} /* sw1, 0.35 v */ + }, + { // High swing/pre-emphasis, low HBR + {0x05, 0x0C, 0x14, 0x1D}, /* sw0, 0.3v */ + {0x08, 0x13, 0x1B, 0xFF}, /* sw1, 0.35 v */ + {0x0C, 0x17, 0xFF, 0xFF}, /* sw1, 0.4 v */ + {0x14, 0xFF, 0xFF, 0xFF} /* sw1, 0.45 v */ + }, + { // Low swing/pre-emphasis, high HBR + {0x06, 0x11, 0x16, 0x1B}, /* sw0, 0.2v */ + {0x0B, 0x19, 0x1F, 0xFF}, /* sw1, 0.25 v */ + {0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.3 v */ + {0x1F, 0xFF, 0xFF, 0xFF} /* sw1, 0.35 v */ + }, + { // High swing/pre-emphasis, high HBR + {0x0A, 0x11, 0x17, 0x1F}, /* sw0, 0.3v */ + {0x0C, 0x14, 0x1D, 0xFF}, /* sw1, 0.35 v */ + {0x15, 0x1F, 0xFF, 0xFF}, /* sw1, 0.4 v */ + {0x17, 0xFF, 0xFF, 0xFF} /* sw1, 0.45 v */ + }, + { // DP-only, DP/USB + {0x02, 0x12, 0x16, 0x1A}, /* sw0, 0.4v */ + {0x09, 0x19, 0x1F, 0xFF}, /* sw1, 0.6v */ + {0x10, 0x1F, 0xFF, 0xFF}, /* sw2, 0.8v */ + {0x1F, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */ + }, + { // MiniDP-only + {0x07, 0x0F, 0x16, 0x1F}, /* sw0, 0.4v */ + {0x11, 0x1E, 0x1F, 0xFF}, /* sw1, 0.6v */ + {0x16, 0x1F, 0xFF, 0xFF}, /* sw2, 0.8v */ + {0x1F, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */ + }, +}; + +struct dp_catalog_private_v500 { + struct device *dev; + struct dp_catalog_sub sub; + struct dp_catalog_io *io; + struct dp_catalog *dpc; + + struct dp_catalog_ctrl dp_ctrl; + struct dp_catalog_audio dp_audio; +}; + +static void dp_catalog_aux_setup_v500(struct dp_catalog_aux *aux, + struct dp_aux_cfg *cfg) +{ + struct dp_catalog_private_v500 *catalog; + struct dp_io_data *io_data; + u32 revision_id = 0; + int i = 0; + + if (!aux || !cfg) { + DP_ERR("invalid input\n"); + return; + } + + catalog = dp_catalog_get_priv_v500(aux); + + io_data = catalog->io->dp_phy; + /* PHY will not work if DP_PHY_MODE is not set */ + revision_id = (dp_read(DP_PHY_REVISION_ID3) & 0xFF) << 8; + revision_id |= (dp_read(DP_PHY_REVISION_ID2) & 0xFF); + DP_DEBUG("DP phy revision_id: 0x%X\n", revision_id); + + dp_write(DP_PHY_PD_CTL_V500, 0x65); + wmb(); /* make sure PD programming happened */ + + /* Turn on BIAS current for PHY/PLL */ + io_data = catalog->io->dp_pll; + dp_write(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, + 0x17); + wmb(); /* make sure BIAS programming happened */ + + io_data = catalog->io->dp_phy; + dp_write(DP_PHY_PD_CTL_V500, 0x2); + wmb(); /* make sure PD programming happened */ + udelay(1000); + + dp_write(DP_PHY_PD_CTL_V500, 0x7d); + wmb(); /* make sure PD programming happened */ + udelay(1000); + + if (revision_id >= 0x5000) + dp_write(DP_PHY_MODE_V500, 0xfc); + else + dp_write(DP_PHY_MODE, 0xfc); + + io_data = catalog->io->dp_phy; + /* DP AUX CFG register programming */ + for (i = 0; i < PHY_AUX_CFG_MAX; i++) { + DP_DEBUG("%s: offset=0x%08x, value=0x%08x\n", + dp_phy_aux_config_type_to_string(i), + cfg[i].offset, cfg[i].lut[cfg[i].current_index]); + dp_write(cfg[i].offset, + cfg[i].lut[cfg[i].current_index]); + } + wmb(); /* make sure DP AUX CFG programming happened */ + + dp_write(DP_PHY_AUX_INTERRUPT_MASK_V500, + 0x1F); +} + +static void dp_catalog_aux_clear_hw_interrupts_v500(struct dp_catalog_aux *aux) +{ + struct dp_catalog_private_v500 *catalog; + struct dp_io_data *io_data; + u32 data = 0; + + if (!aux) { + DP_ERR("invalid input\n"); + return; + } + + catalog = dp_catalog_get_priv_v500(aux); + io_data = catalog->io->dp_phy; + + data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS_V500); + + dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V500, 0x1f); + wmb(); /* make sure 0x1f is written before next write */ + dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V500, 0x9f); + wmb(); /* make sure 0x9f is written before next write */ + dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V500, 0); + wmb(); /* make sure register is cleared */ +} + +static void dp_catalog_panel_config_msa_v500(struct dp_catalog_panel *panel, + u32 rate, u32 stream_rate_khz) +{ + u32 pixel_m, pixel_n; + u32 mvid, nvid, reg_off = 0, mvid_off = 0, nvid_off = 0; + u32 const nvid_fixed = 0x8000; + u32 const link_rate_hbr2 = 540000; + u32 const link_rate_hbr3 = 810000; + struct dp_catalog_private_v500 *catalog; + struct dp_io_data *io_data; + + if (!panel || !rate) { + DP_ERR("invalid input\n"); + return; + } + + if (panel->stream_id >= DP_STREAM_MAX) { + DP_ERR("invalid stream id:%d\n", panel->stream_id); + return; + } + + catalog = dp_catalog_get_priv_v500(panel); + io_data = catalog->io->dp_mmss_cc; + + if (panel->stream_id == DP_STREAM_1) + reg_off = catalog->dpc->parser->pixel_base_off[1]; + else + reg_off = catalog->dpc->parser->pixel_base_off[0]; + + pixel_m = dp_read(reg_off + MMSS_DP_M_OFF); + pixel_n = dp_read(reg_off + MMSS_DP_N_OFF); + DP_DEBUG("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n); + + 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; + } + + DP_DEBUG("rate = %d\n", rate); + + if (panel->widebus_en) + mvid <<= 1; + + if (link_rate_hbr2 == rate) + nvid *= 2; + + if (link_rate_hbr3 == rate) + nvid *= 3; + + io_data = catalog->io->dp_link; + + if (panel->stream_id == DP_STREAM_1) { + mvid_off = DP1_SOFTWARE_MVID - DP_SOFTWARE_MVID; + nvid_off = DP1_SOFTWARE_NVID - DP_SOFTWARE_NVID; + } + + DP_DEBUG("mvid=0x%x, nvid=0x%x\n", mvid, nvid); + dp_write(DP_SOFTWARE_MVID + mvid_off, mvid); + dp_write(DP_SOFTWARE_NVID + nvid_off, nvid); +} + +static void dp_catalog_ctrl_phy_lane_cfg_v500(struct dp_catalog_ctrl *ctrl, + bool flipped, u8 ln_cnt) +{ + u32 info = 0x0; + struct dp_catalog_private_v500 *catalog; + u8 orientation = BIT(!!flipped); + struct dp_io_data *io_data; + + if (!ctrl) { + DP_ERR("invalid input\n"); + return; + } + + catalog = dp_catalog_get_priv_v500(ctrl); + io_data = catalog->io->dp_phy; + + info |= (ln_cnt & 0x0F); + info |= ((orientation & 0x0F) << 4); + DP_DEBUG("Shared Info = 0x%x\n", info); + + dp_write(DP_PHY_SPARE0_V500, info); +} + +static void dp_catalog_ctrl_update_vx_px_v500(struct dp_catalog_ctrl *ctrl, + u8 v_level, u8 p_level, bool high) +{ + struct dp_catalog *dp_catalog; + struct dp_catalog_private_v500 *catalog; + struct dp_io_data *io_data; + u8 value0, value1, ldo_cfg; + int index; + + if (!ctrl || !((v_level < MAX_VOLTAGE_LEVELS) + && (p_level < MAX_PRE_EMP_LEVELS))) { + DP_ERR("invalid input\n"); + return; + } + + dp_catalog = container_of(ctrl, struct dp_catalog, ctrl); + + catalog = dp_catalog_get_priv_v500(ctrl); + + DP_DEBUG("hw: v=%d p=%d\n", v_level, p_level); + + io_data = catalog->io->dp_phy; + + switch (dp_catalog->parser->hw_cfg.phy_mode) { + case DP_PHY_MODE_DP: + case DP_PHY_MODE_UNKNOWN: + index = TX_DRIVE_MODE_DP; + break; + case DP_PHY_MODE_MINIDP: + index = TX_DRIVE_MODE_MINIDP; + break; + case DP_PHY_MODE_EDP: + default: + if (!high) + index = TX_DRIVE_MODE_LOW_SWING_LOW_HBR; + else + index = TX_DRIVE_MODE_LOW_SWING_HIGH_HBR; + break; + case DP_PHY_MODE_EDP_HIGH_SWING: + if (!high) + index = TX_DRIVE_MODE_HIGH_SWING_LOW_HBR; + else + index = TX_DRIVE_MODE_HIGH_SWING_HIGH_HBR; + break; + } + + value0 = vm_voltage_swing[index][v_level][p_level]; + value1 = vm_pre_emphasis[index][v_level][p_level]; + ldo_cfg = 0x01; + + /* Configure host and panel only if both values are allowed */ + if (value0 != 0xFF && value1 != 0xFF) { + io_data = catalog->io->dp_ln_tx0; + dp_write(TXn_LDO_CONFIG_V500, + ldo_cfg); + dp_write(TXn_TX_DRV_LVL_V500, + value0); + dp_write(TXn_TX_EMP_POST1_LVL_V500, + value1); + + io_data = catalog->io->dp_ln_tx1; + dp_write(TXn_LDO_CONFIG_V500, + ldo_cfg); + dp_write(TXn_TX_DRV_LVL_V500, + value0); + dp_write(TXn_TX_EMP_POST1_LVL_V500, + value1); + + DP_DEBUG("hw: vx_value=0x%x px_value=0x%x ldo_value=0x%x\n", + value0, value1, ldo_cfg); + } else { + DP_ERR("invalid vx (0x%x=0x%x), px (0x%x=0x%x)\n", + v_level, value0, p_level, value1); + } +} + +static void dp_catalog_ctrl_lane_pnswap_v500(struct dp_catalog_ctrl *ctrl, + u8 ln_pnswap) +{ + struct dp_catalog_private_v500 *catalog; + struct dp_io_data *io_data; + u32 cfg0, cfg1; + + catalog = dp_catalog_get_priv_v500(ctrl); + + cfg0 = 0x00; + cfg1 = 0x00; + + cfg0 |= ((ln_pnswap >> 0) & 0x1) << 0; + cfg0 |= ((ln_pnswap >> 1) & 0x1) << 1; + cfg1 |= ((ln_pnswap >> 2) & 0x1) << 0; + cfg1 |= ((ln_pnswap >> 3) & 0x1) << 1; + + io_data = catalog->io->dp_ln_tx0; + dp_write(TXn_TX_POL_INV_V500, cfg0); + + io_data = catalog->io->dp_ln_tx1; + dp_write(TXn_TX_POL_INV_V500, cfg1); +} + +static void dp_catalog_ctrl_usb_reset_v500(struct dp_catalog_ctrl *ctrl, + bool flip) +{ + // USB isn't available +} + +static int dp_catalog_ctrl_late_phy_init_v500(struct dp_catalog_ctrl *ctrl, + u8 lane_cnt, bool flipped) +{ + // No late init + return 0; +} + +static void dp_catalog_put_v500(struct dp_catalog *catalog) +{ + struct dp_catalog_private_v500 *catalog_priv; + + if (!catalog) + return; + + catalog_priv = container_of(catalog->sub, + struct dp_catalog_private_v500, sub); +} + +struct dp_catalog_sub *dp_catalog_get_v500(struct device *dev, + struct dp_catalog *catalog, struct dp_catalog_io *io) +{ + struct dp_catalog_private_v500 *catalog_priv; + + if (!dev || !catalog) { + DP_ERR("invalid input\n"); + return ERR_PTR(-EINVAL); + } + + catalog_priv = devm_kzalloc(dev, sizeof(*catalog_priv), GFP_KERNEL); + if (!catalog_priv) + return ERR_PTR(-ENOMEM); + + catalog_priv->dev = dev; + catalog_priv->io = io; + catalog_priv->dpc = catalog; + catalog_priv->dp_ctrl = catalog->ctrl; + catalog_priv->dp_audio = catalog->audio; + catalog_priv->sub.put = dp_catalog_put_v500; + + catalog->aux.setup = dp_catalog_aux_setup_v500; + catalog->aux.clear_hw_interrupts = + dp_catalog_aux_clear_hw_interrupts_v500; + catalog->panel.config_msa = dp_catalog_panel_config_msa_v500; + + catalog->ctrl.phy_lane_cfg = dp_catalog_ctrl_phy_lane_cfg_v500; + catalog->ctrl.update_vx_px = dp_catalog_ctrl_update_vx_px_v500; + catalog->ctrl.lane_pnswap = dp_catalog_ctrl_lane_pnswap_v500; + catalog->ctrl.usb_reset = dp_catalog_ctrl_usb_reset_v500; + catalog->ctrl.late_phy_init = dp_catalog_ctrl_late_phy_init_v500; + + return &catalog_priv->sub; +} diff --git a/msm/dp/dp_ctrl.c b/msm/dp/dp_ctrl.c index ad08e9019700..395d5a7e1cc9 100644 --- a/msm/dp/dp_ctrl.c +++ b/msm/dp/dp_ctrl.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ @@ -11,6 +12,7 @@ #include "dp_ctrl.h" #include "dp_debug.h" #include "sde_dbg.h" +#include "dp_pll.h" #define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__) @@ -62,6 +64,7 @@ struct dp_ctrl_private { struct dp_power *power; struct dp_parser *parser; struct dp_catalog_ctrl *catalog; + struct dp_pll *pll; struct completion idle_comp; struct completion video_comp; @@ -618,6 +621,14 @@ static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl, { u32 num = ctrl->parser->mp[clk_type].num_clk; struct dss_clk *cfg = ctrl->parser->mp[clk_type].clk_config; + struct dp_catalog *catalog; + + catalog = container_of(ctrl->catalog, struct dp_catalog, ctrl); + + if (catalog->hpd.is_edp) { + /* convert to HZ for byte2 ops */ + rate *= 1000; + } while (num && strcmp(cfg->clk_name, name)) { num--; @@ -642,6 +653,22 @@ static int dp_ctrl_enable_link_clock(struct dp_ctrl_private *ctrl) dp_ctrl_set_clock_rate(ctrl, "link_clk", type, rate); + if (ctrl->pll->pll_cfg) { + ret = ctrl->pll->pll_cfg(ctrl->pll, rate); + if (ret < 0) { + DP_ERR("DP pll cfg failed\n"); + return ret; + } + } + + if (ctrl->pll->pll_prepare) { + ret = ctrl->pll->pll_prepare(ctrl->pll); + if (ret < 0) { + DP_ERR("DP pll prepare failed\n"); + return ret; + } + } + ret = ctrl->power->clk_enable(ctrl->power, type, true); if (ret) { DP_ERR("Unabled to start link clocks\n"); @@ -653,7 +680,15 @@ static int dp_ctrl_enable_link_clock(struct dp_ctrl_private *ctrl) static void dp_ctrl_disable_link_clock(struct dp_ctrl_private *ctrl) { + int rc; + ctrl->power->clk_enable(ctrl->power, DP_LINK_PM, false); + if (ctrl->pll->pll_unprepare) { + rc = ctrl->pll->pll_unprepare(ctrl->pll); + if (rc < 0) + DP_ERR("pll unprepare failed\n"); + } + } static void dp_ctrl_select_training_pattern(struct dp_ctrl_private *ctrl, @@ -1475,6 +1510,7 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in) ctrl->aux = in->aux; ctrl->link = in->link; ctrl->catalog = in->catalog; + ctrl->pll = in->pll; ctrl->dev = in->dev; ctrl->mst_mode = false; ctrl->fec_mode = false; diff --git a/msm/dp/dp_ctrl.h b/msm/dp/dp_ctrl.h index 940aa1e8c3e3..225a4bf54daf 100644 --- a/msm/dp/dp_ctrl.h +++ b/msm/dp/dp_ctrl.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ @@ -42,6 +43,7 @@ struct dp_ctrl_in { struct dp_parser *parser; struct dp_power *power; struct dp_catalog_ctrl *catalog; + struct dp_pll *pll; }; struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in); diff --git a/msm/dp/dp_debug.c b/msm/dp/dp_debug.c index 307eb471cde8..bd4d4ca04844 100644 --- a/msm/dp/dp_debug.c +++ b/msm/dp/dp_debug.c @@ -32,6 +32,8 @@ struct dp_debug_private { char exe_mode[SZ_32]; char reg_dump[SZ_32]; + const char *name; + struct dp_hpd *hpd; struct dp_link *link; struct dp_panel *panel; @@ -1899,7 +1901,7 @@ static int dp_debug_init_mst(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs create mst_con_id failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -1908,7 +1910,7 @@ static int dp_debug_init_mst(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs create mst_conn_info failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -1917,7 +1919,7 @@ static int dp_debug_init_mst(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DRM_ERROR("[%s] debugfs create mst_con_add failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -1926,7 +1928,7 @@ static int dp_debug_init_mst(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DRM_ERROR("[%s] debugfs create mst_con_remove failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -1935,7 +1937,7 @@ static int dp_debug_init_mst(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs mst_mode failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -1944,7 +1946,7 @@ static int dp_debug_init_mst(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs mst_sideband_mode failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -1962,7 +1964,7 @@ static int dp_debug_init_link(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs max_bw_code failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -1971,7 +1973,7 @@ static int dp_debug_init_link(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs max_pclk_khz failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -1998,7 +2000,7 @@ static int dp_debug_init_link(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs link_bw_code failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2016,7 +2018,7 @@ static int dp_debug_init_hdcp(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs hdcp_wait_sink_sync failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2025,7 +2027,7 @@ static int dp_debug_init_hdcp(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs force_encryption failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2043,7 +2045,7 @@ static int dp_debug_init_sink_caps(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs create edid_modes failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2052,7 +2054,7 @@ static int dp_debug_init_sink_caps(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs create edid_modes_mst failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2061,7 +2063,7 @@ static int dp_debug_init_sink_caps(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs edid failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2070,7 +2072,7 @@ static int dp_debug_init_sink_caps(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs dpcd failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2088,7 +2090,7 @@ static int dp_debug_init_status(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs create file failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2097,7 +2099,7 @@ static int dp_debug_init_status(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs connected failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2105,7 +2107,7 @@ static int dp_debug_init_status(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs hdr failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2113,7 +2115,7 @@ static int dp_debug_init_status(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs hdr_mst failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2121,7 +2123,7 @@ static int dp_debug_init_status(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs hdcp failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2137,7 +2139,7 @@ static int dp_debug_init_sim(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs hpd failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2145,7 +2147,7 @@ static int dp_debug_init_sim(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs sim failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2154,7 +2156,7 @@ static int dp_debug_init_sim(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs attention failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2163,7 +2165,7 @@ static int dp_debug_init_sim(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs skip_uevent failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2172,7 +2174,7 @@ static int dp_debug_init_sim(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs force_multi_func failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2190,7 +2192,7 @@ static int dp_debug_init_dsc_fec(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs dsc_feature failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2199,7 +2201,7 @@ static int dp_debug_init_dsc_fec(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs fec_feature_enable failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2216,7 +2218,7 @@ static int dp_debug_init_tpg(struct dp_debug_private *debug, struct dentry *dir) if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs tpg failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2234,7 +2236,7 @@ static int dp_debug_init_reg_dump(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs register failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2243,7 +2245,7 @@ static int dp_debug_init_reg_dump(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs dump failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2261,7 +2263,7 @@ static int dp_debug_init_feature_toggle(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs ssc_enable failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2270,7 +2272,7 @@ static int dp_debug_init_feature_toggle(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs widebus_mode failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } @@ -2288,7 +2290,7 @@ static int dp_debug_init_configs(struct dp_debug_private *debug, if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); DP_ERR("[%s] debugfs connect_notification_delay_ms failed, rc=%d\n", - DEBUG_NAME, rc); + debug->name, rc); return rc; } debug->dp_debug.connect_notification_delay_ms = @@ -2321,14 +2323,18 @@ static int dp_debug_init(struct dp_debug *dp_debug) return 0; } - dir = debugfs_create_dir(DEBUG_NAME, NULL); + debug->name = of_get_property(debug->dev->of_node, "label", NULL); + if (!debug->name) + debug->name = DEBUG_NAME; + + dir = debugfs_create_dir(debug->name, NULL); if (IS_ERR_OR_NULL(dir)) { if (!dir) rc = -EINVAL; else rc = PTR_ERR(dir); DP_ERR("[%s] debugfs create dir failed, rc = %d\n", - DEBUG_NAME, rc); + debug->name, rc); goto error; } diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 3e0fdbd26fce..afa73fa3e013 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ @@ -67,6 +68,10 @@ enum dp_display_states { DP_STATE_TUI_ACTIVE = BIT(11), }; +struct dp_display_type_info { + int display_type; +}; + static char *dp_display_state_name(enum dp_display_states state) { static char buf[SZ_1K]; @@ -128,7 +133,7 @@ static char *dp_display_state_name(enum dp_display_states state) return buf; } -static struct dp_display *g_dp_display; +static struct dp_display *g_dp_display[MAX_DP_ACTIVE_DISPLAY]; #define HPD_STRING_SIZE 30 struct dp_hdcp_dev { @@ -162,6 +167,7 @@ struct dp_display_private { struct platform_device *pdev; struct device_node *aux_switch_node; + bool aux_switch_ready; struct dentry *root; struct completion notification_comp; struct completion attention_comp; @@ -200,10 +206,26 @@ struct dp_display_private { bool process_hpd_connect; struct notifier_block usb_nb; + + u32 cell_idx; + u32 intf_idx[DP_STREAM_MAX]; + u32 phy_idx; + u32 stream_cnt; +}; + +static const struct dp_display_type_info dp_info = { + .display_type = DRM_MODE_CONNECTOR_DisplayPort, +}; + +static const struct dp_display_type_info edp_info = { + .display_type = DRM_MODE_CONNECTOR_eDP, }; static const struct of_device_id dp_dt_match[] = { - {.compatible = "qcom,dp-display"}, + { .compatible = "qcom,dp-display", + .data = &dp_info,}, + { .compatible = "qcom,edp-display", + .data = &edp_info,}, {} }; @@ -741,6 +763,32 @@ static int dp_display_post_hw_acquire(void *data) return 0; } +static int dp_display_get_cell_info(struct dp_display_private *dp) +{ + struct device_node *of_node = dp->pdev->dev.of_node; + int i, rc; + + of_property_read_u32(of_node, + "cell-index", &dp->cell_idx); + + if (of_property_read_bool(of_node, "qcom,mst-enable")) + dp->stream_cnt = DP_STREAM_MAX; + + of_property_read_u32_index(of_node, + "qcom,intf-index", 0, &dp->intf_idx[0]); + + for (i = 1; i < dp->stream_cnt; i++) { + rc = of_property_read_u32_index(of_node, + "qcom,intf-index", i, &dp->intf_idx[i]); + if (rc) + dp->intf_idx[i] = dp->intf_idx[0] + i; + } + + of_property_read_u32(of_node, + "qcom,phy-index", &dp->phy_idx); + + return 0; +} static int dp_display_bind(struct device *dev, struct device *master, void *data) @@ -1053,6 +1101,32 @@ error_ctrl: return rc; } +static int dp_display_panel_ready(struct dp_display_private *dp) +{ + int rc = 0; + + if (dp->dp_display.is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, true); + if (rc) { + DP_ERR("Cannot turn edp panel power on"); + return rc; + } + + if (!(dp->catalog->hpd.wait_for_edp_panel_ready(&dp->catalog->hpd))) { + DP_ERR("EDP PANEL is not ready yet, powering off panel\n"); + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false); + if (rc) { + DP_ERR("Cannot turn edp panel power off"); + return rc; + } + return -ETIMEDOUT; + } + } + dp->panel->init(dp->panel); + + return 0; +} + static int dp_display_host_ready(struct dp_display_private *dp) { int rc = 0; @@ -1090,7 +1164,6 @@ static int dp_display_host_ready(struct dp_display_private *dp) dp->ctrl->abort(dp->ctrl, false); dp->aux->init(dp->aux, dp->parser->aux_cfg); - dp->panel->init(dp->panel); dp_display_state_add(DP_STATE_READY); /* log this as it results from user action of cable connection */ @@ -1204,6 +1277,8 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) goto end; } + rc = dp_display_panel_ready(dp); + dp->link->psm_config(dp->link, &dp->panel->link_info, false); dp->debug->psm_enabled = false; @@ -1336,6 +1411,54 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) return rc; } +static int dp_display_fsa4480_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + return 0; +} + +static int dp_display_init_aux_switch(struct dp_display_private *dp) +{ + int rc = 0; + struct notifier_block nb; + const u32 max_retries = 50; + u32 retry; + + if (dp->aux_switch_ready) + return rc; + + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY); + + nb.notifier_call = dp_display_fsa4480_callback; + nb.priority = 0; + + /* + * Iteratively wait for reg notifier which confirms that fsa driver is probed. + * Bootup DP with cable connected usecase can hit this scenario. + */ + for (retry = 0; retry < max_retries; retry++) { + rc = fsa4480_reg_notifier(&nb, dp->aux_switch_node); + if (rc == 0) { + DP_DEBUG("registered notifier successfully\n"); + dp->aux_switch_ready = true; + break; + } + DP_DEBUG("failed to register notifier retry=%d rc=%d\n", retry, rc); + msleep(100); + } + + if (retry == max_retries) { + DP_WARN("Failed to register fsa notifier\n"); + dp->aux_switch_ready = false; + return rc; + } + + fsa4480_unreg_notifier(&nb, dp->aux_switch_node); + + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, rc); + return rc; +} + static int dp_display_usbpd_configure_cb(struct device *dev) { int rc = 0; @@ -1353,7 +1476,11 @@ static int dp_display_usbpd_configure_cb(struct device *dev) } if (!dp->debug->sim_mode && !dp->parser->no_aux_switch - && !dp->parser->gpio_aux_switch) { + && !dp->parser->gpio_aux_switch && dp->aux_switch_node) { + rc = dp_display_init_aux_switch(dp); + if (rc) + return rc; + rc = dp->aux->aux_switch(dp->aux, true, dp->hpd->orientation); if (rc) return rc; @@ -1866,6 +1993,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp) { int rc = 0; bool hdcp_disabled; + const char *phandle = "qcom,dp-aux-switch"; struct device *dev = &dp->pdev->dev; struct dp_hpd_cb *cb = &dp->hpd_cb; struct dp_ctrl_in ctrl_in = { @@ -1897,8 +2025,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp) goto error_catalog; } - g_dp_display->is_mst_supported = dp->parser->has_mst; - g_dp_display->dsc_cont_pps = dp->parser->dsc_continuous_pps; + dp->dp_display.is_mst_supported = dp->parser->has_mst; + dp->dp_display.dsc_cont_pps = dp->parser->dsc_continuous_pps; dp->catalog = dp_catalog_get(dev, dp->parser); if (IS_ERR(dp->catalog)) { @@ -1908,6 +2036,10 @@ static int dp_init_sub_modules(struct dp_display_private *dp) goto error_catalog; } + dp->catalog->hpd.set_edp_mode(&dp->catalog->hpd, dp->dp_display.is_edp); + dp->aux_switch_node = of_parse_phandle(dp->pdev->dev.of_node, phandle, 0); + if (!dp->aux_switch_node) + DP_DEBUG("cannot parse %s handle\n", phandle); dp->aux = dp_aux_get(dev, &dp->catalog->aux, dp->parser, dp->aux_switch_node); if (IS_ERR(dp->aux)) { @@ -1978,6 +2110,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp) ctrl_in.power = dp->power; ctrl_in.catalog = &dp->catalog->ctrl; ctrl_in.parser = dp->parser; + ctrl_in.pll = dp->pll; dp->ctrl = dp_ctrl_get(&ctrl_in); if (IS_ERR(dp->ctrl)) { @@ -2147,6 +2280,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) struct dp_display_private *dp; struct dp_panel *dp_panel; int rc = 0; + bool shallow_mode = true; if (!dp_display || !panel) { DP_ERR("invalid input\n"); @@ -2226,6 +2360,13 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) goto end; } + rc = dp_display_panel_ready(dp); + if (rc) { + dp_display_host_unready(dp); + dp_display_host_deinit(dp); + goto end; + } + if (dp->debug->psm_enabled) { dp->link->psm_config(dp->link, &dp->panel->link_info, false); dp->debug->psm_enabled = false; @@ -2242,7 +2383,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) * and required things. */ rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, dp_panel->fec_en, - dp_panel->dsc_en, true); + dp_panel->dsc_en, shallow_mode); if (rc) goto end; @@ -2364,6 +2505,23 @@ static int dp_display_enable(struct dp_display *dp_display, void *panel) if (rc) goto end; + /*edp backlight enable and edp pwm enable*/ + if (dp_display->is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_PWR, true); + if (rc) { + DP_ERR("Cannot turn edp backlight power on"); + goto end; + } + + usleep_range(99000, 100000); + + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_PWM, true); + if (rc) { + DP_ERR("Cannot turn edp PWM on "); + goto end; + } + } + dp_display_update_dsc_resources(dp, panel, true); dp_display_state_add(DP_STATE_ENABLED); end: @@ -2383,6 +2541,7 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) { struct dp_display_private *dp; struct dp_panel *dp_panel; + int rc = 0; if (!dp_display || !panel) { DP_ERR("invalid input\n"); @@ -2421,6 +2580,14 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) dp_display_stream_post_enable(dp, dp_panel); + if (dp_display->is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_EN, true); + if (rc) { + DP_ERR("Cannot turn edp backlight power on"); + goto end; + } + } + cancel_delayed_work_sync(&dp->hdcp_cb_work); queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ); @@ -2474,6 +2641,14 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) goto end; } + if (dp_display->is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_EN, false); + if (rc) { + DP_ERR("Cannot turn edp backlight power off"); + goto end; + } + } + dp_display_state_add(DP_STATE_HDCP_ABORTED); cancel_delayed_work_sync(&dp->hdcp_cb_work); if (dp_display_is_hdcp_enabled(dp) && @@ -2522,7 +2697,7 @@ end: static int dp_display_disable(struct dp_display *dp_display, void *panel) { - int i; + int i, rc = 0; struct dp_display_private *dp = NULL; struct dp_panel *dp_panel = NULL; struct dp_link_hdcp_status *status; @@ -2549,6 +2724,16 @@ static int dp_display_disable(struct dp_display *dp_display, void *panel) goto end; } + if (dp_display->is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_PWR, false); + if (rc) + DP_ERR("Cannot turn edp backlight power off\n"); + + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_PWM, false); + if (rc) + DP_ERR("Cannot turn edp PWM off\n"); + } + dp_display_stream_disable(dp, dp_panel); dp_display_update_dsc_resources(dp, dp_panel, false); @@ -2617,6 +2802,7 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel) struct dp_display_private *dp; struct dp_panel *dp_panel = panel; u32 flags = 0; + int rc = 0; if (!dp_display || !panel) { DP_ERR("invalid input\n"); @@ -2654,6 +2840,13 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel) dp_display_state_add(DP_STATE_SRC_PWRDN); } + if (dp_display->is_edp) { + rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false); + if (rc) { + DP_ERR("Cannot turn edp panel power off\n"); + goto end; + } + } dp_display_state_remove(DP_STATE_ENABLED); dp->aux->state = DP_STATE_CTRL_POWERED_OFF; @@ -2672,18 +2865,25 @@ end: static int dp_display_validate_link_clock(struct dp_display_private *dp, struct drm_display_mode *mode, struct dp_display_mode dp_mode) { - u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0; + u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0, lane_count = 0; bool dsc_en; - int rate; + int rate = 0; dsc_en = (dp_mode.timing.comp_info.comp_ratio > 1) ? true : false; mode_bpp = dsc_en ? DSC_BPP(dp_mode.timing.comp_info.dsc_info.config) : dp_mode.timing.bpp; + if (dp->dp_display.is_edp) { + rate = dp->panel->link_info.rate; + lane_count = dp->panel->link_info.num_lanes; + } else { + rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code); + lane_count = dp->link->link_params.lane_count; + } + mode_rate_khz = mode->clock * mode_bpp; - rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code); - supported_rate_khz = dp->link->link_params.lane_count * rate * 8; + supported_rate_khz = lane_count * rate * 8; if (mode_rate_khz > supported_rate_khz) { DP_DEBUG("mode_rate: %d kHz, supported_rate: %d kHz\n", @@ -3082,48 +3282,6 @@ static int dp_display_create_workqueue(struct dp_display_private *dp) return 0; } -static int dp_display_fsa4480_callback(struct notifier_block *self, - unsigned long event, void *data) -{ - return 0; -} - -static int dp_display_init_aux_switch(struct dp_display_private *dp) -{ - int rc = 0; - const char *phandle = "qcom,dp-aux-switch"; - struct notifier_block nb; - - if (!dp->pdev->dev.of_node) { - DP_ERR("cannot find dev.of_node\n"); - rc = -ENODEV; - goto end; - } - - SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY); - dp->aux_switch_node = of_parse_phandle(dp->pdev->dev.of_node, - phandle, 0); - if (!dp->aux_switch_node) { - DP_WARN("cannot parse %s handle\n", phandle); - rc = -ENODEV; - goto end; - } - - nb.notifier_call = dp_display_fsa4480_callback; - nb.priority = 0; - - rc = fsa4480_reg_notifier(&nb, dp->aux_switch_node); - if (rc) { - DP_ERR("failed to register notifier (%d)\n", rc); - goto end; - } - - fsa4480_unreg_notifier(&nb, dp->aux_switch_node); -end: - SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, rc); - return rc; -} - static int dp_display_mst_install(struct dp_display *dp_display, struct dp_mst_drm_install_info *mst_install_info) { @@ -3550,10 +3708,119 @@ static void dp_display_wakeup_phy_layer(struct dp_display *dp_display, hpd->wakeup_phy(hpd, wakeup); } +static int dp_display_get_display_type(struct dp_display *dp_display, + const char **display_type) +{ + struct dp_display_private *dp; + + if (!dp_display || !display_type) { + pr_err("invalid input\n"); + return -EINVAL; + } + + dp = container_of(dp_display, struct dp_display_private, dp_display); + + *display_type = dp->parser->display_type; + + return 0; +} + +static int dp_display_mst_get_fixed_topology_display_type( + struct dp_display *dp_display, u32 strm_id, + const char **display_type) +{ + struct dp_display_private *dp; + + if (!dp_display || !display_type) { + pr_err("invalid input\n"); + return -EINVAL; + } + + if (strm_id >= DP_STREAM_MAX) { + pr_err("invalid stream id:%d\n", strm_id); + return -EINVAL; + } + + dp = container_of(dp_display, struct dp_display_private, dp_display); + + *display_type = dp->parser->mst_fixed_display_type[strm_id]; + + return 0; +} + +static int dp_display_edp_detect(struct dp_display *dp_display) +{ + struct device *dev; + struct dp_display_private *dp; + int rc = 0; + + dp = container_of(dp_display, struct dp_display_private, dp_display); + dev = &dp->pdev->dev; + + if (dp->dp_display.is_sst_connected) + return rc; + + mutex_lock(&dp->session_lock); + + rc = dp_display_host_init(dp); + if (rc) { + DP_ERR("Host init Failed"); + goto end; + } + + dp->dp_display.is_sst_connected = true; + dp->hpd->hpd_high = true; + dp->hpd->alt_mode_cfg_done = true; + + dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz, dp->debug->max_pclk_khz); + + rc = dp_display_host_ready(dp); + if (rc) { + dp_display_state_show("[ready failed]"); + dp_display_host_deinit(dp); + goto end; + } + + rc = dp_display_panel_ready(dp); + if (rc) { + dp_display_host_unready(dp); + dp_display_host_deinit(dp); + goto end; + } + + dp->link->psm_config(dp->link, &dp->panel->link_info, false); + dp->debug->psm_enabled = false; + + rc = dp->panel->read_sink_caps(dp->panel, + dp->dp_display.base_connector, dp->hpd->multi_func); + + if (rc == -ETIMEDOUT || rc == -ENOTCONN) + goto end; + + dp_display_state_remove(DP_STATE_ABORTED); + dp_display_state_add(DP_STATE_CONFIGURED); + dp_display_state_add(DP_STATE_CONNECTED); + + dp->link->process_request(dp->link); + dp->panel->handle_sink_request(dp->panel); + + dp_display_state_add(DP_STATE_CONNECT_NOTIFIED); + dp_display_state_remove(DP_STATE_DISCONNECT_NOTIFIED); + + dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false); +end: + mutex_unlock(&dp->session_lock); + return rc; +} + static int dp_display_probe(struct platform_device *pdev) { int rc = 0; struct dp_display_private *dp; + const struct of_device_id *id; + const struct dp_display_type_info *info; + struct dp_display *dp_display; + int index; if (!pdev || !pdev->dev.of_node) { DP_ERR("pdev not found\n"); @@ -3561,6 +3828,19 @@ static int dp_display_probe(struct platform_device *pdev) goto bail; } + id = of_match_node(dp_dt_match, pdev->dev.of_node); + if (!id) + return -ENODEV; + + info = id->data; + + index = dp_display_get_num_of_displays(); + if (index >= MAX_DP_ACTIVE_DISPLAY) { + pr_err("exceeds max dp count\n"); + rc = -EINVAL; + goto bail; + } + dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL); if (!dp) { rc = -ENOMEM; @@ -3575,11 +3855,9 @@ static int dp_display_probe(struct platform_device *pdev) memset(&dp->mst, 0, sizeof(dp->mst)); - rc = dp_display_init_aux_switch(dp); - if (rc) { - rc = -EPROBE_DEFER; + rc = dp_display_get_cell_info(dp); + if (rc) goto error; - } rc = dp_display_create_workqueue(dp); if (rc) { @@ -3589,44 +3867,51 @@ static int dp_display_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dp); - g_dp_display = &dp->dp_display; + dp_display = &dp->dp_display; + g_dp_display[index] = dp_display; - g_dp_display->enable = dp_display_enable; - g_dp_display->post_enable = dp_display_post_enable; - g_dp_display->pre_disable = dp_display_pre_disable; - g_dp_display->disable = dp_display_disable; - g_dp_display->set_mode = dp_display_set_mode; - g_dp_display->validate_mode = dp_display_validate_mode; - g_dp_display->get_modes = dp_display_get_modes; - g_dp_display->prepare = dp_display_prepare; - g_dp_display->unprepare = dp_display_unprepare; - g_dp_display->request_irq = dp_request_irq; - g_dp_display->get_debug = dp_get_debug; - g_dp_display->post_open = NULL; - g_dp_display->post_init = dp_display_post_init; - g_dp_display->config_hdr = dp_display_config_hdr; - g_dp_display->mst_install = dp_display_mst_install; - g_dp_display->mst_uninstall = dp_display_mst_uninstall; - g_dp_display->mst_connector_install = dp_display_mst_connector_install; - g_dp_display->mst_connector_uninstall = + dp_display->enable = dp_display_enable; + dp_display->post_enable = dp_display_post_enable; + dp_display->pre_disable = dp_display_pre_disable; + dp_display->disable = dp_display_disable; + dp_display->set_mode = dp_display_set_mode; + dp_display->validate_mode = dp_display_validate_mode; + dp_display->get_modes = dp_display_get_modes; + dp_display->prepare = dp_display_prepare; + dp_display->unprepare = dp_display_unprepare; + dp_display->request_irq = dp_request_irq; + dp_display->get_debug = dp_get_debug; + dp_display->post_open = NULL; + dp_display->post_init = dp_display_post_init; + dp_display->config_hdr = dp_display_config_hdr; + dp_display->mst_install = dp_display_mst_install; + dp_display->mst_uninstall = dp_display_mst_uninstall; + dp_display->mst_connector_install = dp_display_mst_connector_install; + dp_display->mst_connector_uninstall = dp_display_mst_connector_uninstall; - g_dp_display->mst_connector_update_edid = + dp_display->mst_connector_update_edid = dp_display_mst_connector_update_edid; - g_dp_display->mst_connector_update_link_info = + dp_display->mst_connector_update_link_info = dp_display_mst_connector_update_link_info; - g_dp_display->get_mst_caps = dp_display_get_mst_caps; - g_dp_display->set_stream_info = dp_display_set_stream_info; - g_dp_display->update_pps = dp_display_update_pps; - g_dp_display->convert_to_dp_mode = dp_display_convert_to_dp_mode; - g_dp_display->mst_get_connector_info = + dp_display->get_mst_caps = dp_display_get_mst_caps; + dp_display->set_stream_info = dp_display_set_stream_info; + dp_display->update_pps = dp_display_update_pps; + dp_display->convert_to_dp_mode = dp_display_convert_to_dp_mode; + dp_display->mst_get_connector_info = dp_display_mst_get_connector_info; - g_dp_display->mst_get_fixed_topology_port = + dp_display->mst_get_fixed_topology_port = dp_display_mst_get_fixed_topology_port; - g_dp_display->wakeup_phy_layer = + dp_display->wakeup_phy_layer = dp_display_wakeup_phy_layer; - g_dp_display->set_colorspace = dp_display_setup_colospace; - g_dp_display->get_available_dp_resources = + dp_display->set_colorspace = dp_display_setup_colospace; + dp_display->get_available_dp_resources = dp_display_get_available_dp_resources; + dp_display->get_display_type = dp_display_get_display_type; + dp_display->mst_get_fixed_topology_display_type = + dp_display_mst_get_fixed_topology_display_type; + + dp_display->is_edp = (info->display_type == DRM_MODE_CONNECTOR_eDP) ? true : false; + dp_display->edp_detect = dp_display_edp_detect; rc = component_add(&pdev->dev, &dp_display_comp_ops); if (rc) { @@ -3643,31 +3928,71 @@ bail: int dp_display_get_displays(void **displays, int count) { + int i; + if (!displays) { DP_ERR("invalid data\n"); return -EINVAL; } - if (count != 1) { - DP_ERR("invalid number of displays\n"); - return -EINVAL; + for (i = 0; i < MAX_DP_ACTIVE_DISPLAY && i < count; i++) { + if (!g_dp_display[i]) + break; + + displays[i] = g_dp_display[i]; } - displays[0] = g_dp_display; - return count; + return i; } int dp_display_get_num_of_displays(void) { - if (!g_dp_display) - return 0; + int i; - return 1; + for (i = 0; i < MAX_DP_ACTIVE_DISPLAY; i++) + if (!g_dp_display[i]) + break; + + return i; } int dp_display_get_num_of_streams(void) { - return DP_STREAM_MAX; + struct dp_display_private *dp; + int i, count = 0; + + for (i = 0; i < MAX_DP_ACTIVE_DISPLAY; i++) { + if (!g_dp_display[i]) + break; + + dp = container_of(g_dp_display[i], struct dp_display_private, dp_display); + + count += dp->stream_cnt; + } + + return count; +} + +int dp_display_get_info(void *dp_display, struct dp_display_info *dp_info) +{ + struct dp_display_private *dp; + int i; + + if (!dp_display) { + DP_DEBUG("dp display not initialized\n"); + return -EINVAL; + } + + dp = container_of(dp_display, struct dp_display_private, dp_display); + + dp_info->cell_idx = dp->cell_idx; + dp_info->intf_idx[0] = dp->intf_idx[0]; + for (i = 1; i < dp->stream_cnt; i++) + dp_info->intf_idx[i] = dp->intf_idx[i]; + dp_info->phy_idx = dp->phy_idx; + dp_info->stream_cnt = dp->stream_cnt; + + return 0; } static void dp_display_set_mst_state(void *dp_display, @@ -3675,16 +4000,11 @@ static void dp_display_set_mst_state(void *dp_display, { struct dp_display_private *dp; - if (!g_dp_display) { - DP_DEBUG("dp display not initialized\n"); - return; - } - - dp = container_of(g_dp_display, struct dp_display_private, dp_display); + dp = container_of(dp_display, struct dp_display_private, dp_display); SDE_EVT32_EXTERNAL(mst_state, dp->mst.mst_active); if (dp->mst.mst_active && dp->mst.cbs.set_drv_state) - dp->mst.cbs.set_drv_state(g_dp_display, mst_state); + dp->mst.cbs.set_drv_state(dp_display, mst_state); } static int dp_display_remove(struct platform_device *pdev) @@ -3709,12 +4029,16 @@ static int dp_display_remove(struct platform_device *pdev) static int dp_pm_prepare(struct device *dev) { - struct dp_display_private *dp = container_of(g_dp_display, - struct dp_display_private, dp_display); + struct dp_display_private *dp; + + if (!dev) + return -EINVAL; + + dp = dev_get_drvdata(dev); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY); mutex_lock(&dp->session_lock); - dp_display_set_mst_state(g_dp_display, PM_SUSPEND); + dp_display_set_mst_state(&dp->dp_display, PM_SUSPEND); /* * There are a few instances where the DP is hotplugged when the device @@ -3739,12 +4063,16 @@ static int dp_pm_prepare(struct device *dev) static void dp_pm_complete(struct device *dev) { - struct dp_display_private *dp = container_of(g_dp_display, - struct dp_display_private, dp_display); + struct dp_display_private *dp; + + if (!dev) + return; + + dp = dev_get_drvdata(dev); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY); mutex_lock(&dp->session_lock); - dp_display_set_mst_state(g_dp_display, PM_DEFAULT); + dp_display_set_mst_state(&dp->dp_display, PM_DEFAULT); /* * There are multiple PM suspend entry and exits observed before diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index eda860c74c11..892322fa2fc2 100644 --- a/msm/dp/dp_display.h +++ b/msm/dp/dp_display.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ @@ -11,8 +12,10 @@ #include #include "dp_panel.h" +#include "dp_parser.h" #define DP_MST_SIM_MAX_PORTS 8 +#define MAX_DP_ACTIVE_DISPLAY 8 enum dp_drv_state { PM_DEFAULT, @@ -29,6 +32,13 @@ struct dp_mst_hpd_info { int mst_sim_remove_con_id; }; +struct dp_display_info { + u32 cell_idx; + u32 intf_idx[DP_STREAM_MAX]; + u32 phy_idx; + u32 stream_cnt; +}; + struct dp_mst_drm_cbs { void (*hpd)(void *display, bool hpd_status); void (*hpd_irq)(void *display, struct dp_mst_hpd_info *info); @@ -70,6 +80,7 @@ struct dp_display { void *base_dp_panel; bool is_sst_connected; bool is_mst_supported; + bool is_edp; bool dsc_cont_pps; u32 max_pclk_khz; void *dp_mst_prv_info; @@ -132,12 +143,18 @@ struct dp_display { int (*get_available_dp_resources)(struct dp_display *dp_display, const struct msm_resource_caps_info *avail_res, struct msm_resource_caps_info *max_dp_avail_res); + int (*get_display_type)(struct dp_display *dp_display, + const char **display_type); + int (*mst_get_fixed_topology_display_type)(struct dp_display *dp_display, + u32 strm_id, const char **display_type); + int (*edp_detect)(struct dp_display *dp_display); }; #if IS_ENABLED(CONFIG_DRM_MSM_DP) int dp_display_get_num_of_displays(void); int dp_display_get_displays(void **displays, int count); int dp_display_get_num_of_streams(void); +int dp_display_get_info(void *dp_display, struct dp_display_info *dp_info); #else static inline int dp_display_get_num_of_displays(void) { @@ -151,6 +168,10 @@ static inline int dp_display_get_num_of_streams(void) { return 0; } +static inline int dp_display_get_info(void *dp_display, struct dp_display_info *dp_info) +{ + return 0; +} static inline int dp_connector_update_pps(struct drm_connector *connector, char *pps_cmd, void *display) { diff --git a/msm/dp/dp_drm.c b/msm/dp/dp_drm.c index 3c29801249c3..6e054af5dc3d 100644 --- a/msm/dp/dp_drm.c +++ b/msm/dp/dp_drm.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ @@ -456,8 +457,15 @@ int dp_connector_get_info(struct drm_connector *connector, info->num_of_h_tiles = 1; info->h_tile_instance[0] = 0; info->is_connected = display->is_sst_connected; - info->capabilities = MSM_DISPLAY_CAP_VID_MODE | MSM_DISPLAY_CAP_EDID | - MSM_DISPLAY_CAP_HOT_PLUG; + info->curr_panel_mode = MSM_DISPLAY_VIDEO_MODE; + info->capabilities = MSM_DISPLAY_CAP_VID_MODE | MSM_DISPLAY_CAP_EDID; + + if (display && display->is_edp) { + info->intf_type = DRM_MODE_CONNECTOR_eDP; + info->display_type = SDE_CONNECTOR_PRIMARY; + } else { + info->capabilities |= MSM_DISPLAY_CAP_HOT_PLUG; + } return 0; } @@ -468,11 +476,13 @@ enum drm_connector_status dp_connector_detect(struct drm_connector *conn, { enum drm_connector_status status = connector_status_unknown; struct msm_display_info info; + struct dp_display *dp_disp; int rc; if (!conn || !display) return status; + dp_disp = display; /* get display dp_info */ memset(&info, 0x0, sizeof(info)); rc = dp_connector_get_info(conn, &info, display); @@ -481,12 +491,18 @@ enum drm_connector_status dp_connector_detect(struct drm_connector *conn, return connector_status_disconnected; } - if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG) + if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG) { status = (info.is_connected ? connector_status_connected : connector_status_disconnected); - else + } else { status = connector_status_connected; + rc = dp_disp->edp_detect(dp_disp); + if (rc) { + DP_ERR("error in turning on panel power sequence rc:%d\n", rc); + return connector_status_unknown; + } + } conn->display_info.width_mm = info.width_mm; conn->display_info.height_mm = info.height_mm; @@ -594,6 +610,18 @@ int dp_connector_get_modes(struct drm_connector *connector, return rc; } +int dp_connector_set_info_blob(struct drm_connector *connector, + void *info, void *display, struct msm_mode_info *mode_info) +{ + struct dp_display *dp_display = display; + const char *display_type = NULL; + + dp_display->get_display_type(dp_display, &display_type); + sde_kms_info_add_keystr(info, "display type", display_type); + + return 0; +} + int dp_drm_bridge_init(void *data, struct drm_encoder *encoder, u32 max_mixer_count, u32 max_dsc_count) { diff --git a/msm/dp/dp_drm.h b/msm/dp/dp_drm.h index 5d7462cd20f8..647705c2706d 100644 --- a/msm/dp/dp_drm.h +++ b/msm/dp/dp_drm.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ @@ -136,6 +137,18 @@ void dp_connector_post_open(struct drm_connector *connector, void *display); * @max_mixer_count: max available mixers for dp display * @max_dsc_count: max available dsc for dp display */ + +/** + * dp_conn_set_info_blob - callback to perform info blob initialization + * @connector: Pointer to drm connector structure + * @info: Pointer to sde connector info structure + * @display: Pointer to private display handle + * @mode_info: Pointer to mode info structure + * Returns: Zero on success + */ +int dp_connector_set_info_blob(struct drm_connector *connector, + void *info, void *display, struct msm_mode_info *mode_info); + int dp_drm_bridge_init(void *display, struct drm_encoder *encoder, u32 max_mixer_count, u32 max_dsc_count); @@ -233,6 +246,12 @@ static inline void dp_connector_post_open(struct drm_connector *connector, { } +static inline int dp_connector_set_info_blob(struct drm_connector *connector, + void *info, void *display, struct msm_mode_info *mode_info) +{ + return 0; +} + static inline int dp_drm_bridge_init(void *display, struct drm_encoder *encoder, u32 max_mixer_count, u32 max_dsc_count) { diff --git a/msm/dp/dp_lphw_hpd.h b/msm/dp/dp_lphw_hpd.h index 9779331bd295..095302e2da84 100644 --- a/msm/dp/dp_lphw_hpd.h +++ b/msm/dp/dp_lphw_hpd.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ @@ -12,6 +13,7 @@ #define DP_IRQ_HPD_INT_STATUS BIT(1) #define DP_HPD_REPLUG_INT_STATUS BIT(2) #define DP_HPD_UNPLUG_INT_STATUS BIT(3) +#define DP_HPD_STATE_STATUS_CONNECTED BIT(30) /** * dp_lphw_hpd_get() - configure and get the DisplayPlot HPD module data diff --git a/msm/dp/dp_mst_drm.c b/msm/dp/dp_mst_drm.c index e1ccf47b3881..291c1207c08a 100644 --- a/msm/dp/dp_mst_drm.c +++ b/msm/dp/dp_mst_drm.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ @@ -158,11 +159,6 @@ struct dp_mst_private { bool mst_session_state; }; -struct dp_mst_encoder_info_cache { - u8 cnt; - struct drm_encoder *mst_enc[MAX_DP_MST_DRM_BRIDGES]; -}; - #define to_dp_mst_bridge(x) container_of((x), struct dp_mst_bridge, base) #define to_dp_mst_bridge_priv(x) \ container_of((x), struct dp_mst_bridge, obj) @@ -171,9 +167,6 @@ struct dp_mst_encoder_info_cache { #define to_dp_mst_bridge_state(x) \ to_dp_mst_bridge_priv_state((x)->obj.state) -struct dp_mst_private dp_mst; -struct dp_mst_encoder_info_cache dp_mst_enc_cache; - static void dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr) { struct dp_mst_private *mst = container_of(mgr, struct dp_mst_private, @@ -298,7 +291,7 @@ static void dp_mst_sim_add_port(struct dp_mst_private *mst, port->parent = mstb; port->port_num = port_msg->port_number; port->mgr = mstb->mgr; - port->aux.name = dp_mst.caps.drm_aux->name; + port->aux.name = mst->caps.drm_aux->name; port->aux.dev = mst->dp_display->drm_dev->dev; /* @@ -1258,19 +1251,6 @@ int dp_mst_drm_bridge_init(void *data, struct drm_encoder *encoder) struct dp_mst_private *mst = display->dp_mst_prv_info; int i; - if (!mst || !mst->mst_initialized) { - if (dp_mst_enc_cache.cnt >= MAX_DP_MST_DRM_BRIDGES) { - DP_MST_INFO("exceeding max bridge cnt %d\n", - dp_mst_enc_cache.cnt); - return 0; - } - - dp_mst_enc_cache.mst_enc[dp_mst_enc_cache.cnt] = encoder; - dp_mst_enc_cache.cnt++; - DP_MST_INFO("mst not initialized. cache encoder information\n"); - return 0; - } - for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { if (!mst->mst_bridge[i].in_use) { bridge = &mst->mst_bridge[i]; @@ -2130,11 +2110,36 @@ static void dp_mst_destroy_fixed_connector(struct drm_dp_mst_topology_mgr *mgr, dp_mst_destroy_connector(mgr, connector); } +static int dp_mst_fixed_connector_set_info_blob( + struct drm_connector *connector, + void *info, void *display, struct msm_mode_info *mode_info) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct dp_display *dp_display = display; + struct dp_mst_private *mst = dp_display->dp_mst_prv_info; + const char *display_type = NULL; + int i; + + for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { + if (mst->mst_bridge[i].base.encoder != c_conn->encoder) + continue; + + dp_display->mst_get_fixed_topology_display_type(dp_display, + mst->mst_bridge[i].id, &display_type); + sde_kms_info_add_keystr(info, "display type", display_type); + + break; + } + + return 0; +} + static struct drm_connector * dp_mst_drm_fixed_connector_init(struct dp_display *dp_display, struct drm_encoder *encoder) { static const struct sde_connector_ops dp_mst_connector_ops = { + .set_info_blob = dp_mst_fixed_connector_set_info_blob, .post_init = dp_mst_connector_post_init, .detect = dp_mst_fixed_connector_detect, .get_modes = dp_mst_connector_get_modes, @@ -2389,10 +2394,9 @@ int dp_mst_init(struct dp_display *dp_display) { struct drm_device *dev; int conn_base_id = 0; - int ret, i; + int ret; struct dp_mst_drm_install_info install_info; - - memset(&dp_mst, 0, sizeof(dp_mst)); + struct dp_mst_private *dp_mst; if (!dp_display) { DP_ERR("invalid params\n"); @@ -2401,60 +2405,57 @@ int dp_mst_init(struct dp_display *dp_display) dev = dp_display->drm_dev; + dp_mst = devm_kzalloc(dev->dev, sizeof(*dp_mst), GFP_KERNEL); + if (!dp_mst) + return -ENOMEM; + /* register with DP driver */ - install_info.dp_mst_prv_info = &dp_mst; + install_info.dp_mst_prv_info = dp_mst; install_info.cbs = &dp_mst_display_cbs; dp_display->mst_install(dp_display, &install_info); - dp_display->get_mst_caps(dp_display, &dp_mst.caps); + dp_display->get_mst_caps(dp_display, &dp_mst->caps); - if (!dp_mst.caps.has_mst) { + if (!dp_mst->caps.has_mst) { DP_MST_DEBUG("mst not supported\n"); return 0; } - dp_mst.mst_fw_cbs = &drm_dp_mst_fw_helper_ops; + dp_mst->mst_fw_cbs = &drm_dp_mst_fw_helper_ops; - memset(&dp_mst.mst_mgr, 0, sizeof(dp_mst.mst_mgr)); - dp_mst.mst_mgr.cbs = &dp_mst_drm_cbs; + memset(&dp_mst->mst_mgr, 0, sizeof(dp_mst->mst_mgr)); + dp_mst->mst_mgr.cbs = &dp_mst_drm_cbs; conn_base_id = dp_display->base_connector->base.id; - dp_mst.dp_display = dp_display; + dp_mst->dp_display = dp_display; - mutex_init(&dp_mst.mst_lock); - mutex_init(&dp_mst.edid_lock); + mutex_init(&dp_mst->mst_lock); + mutex_init(&dp_mst->edid_lock); - ret = drm_dp_mst_topology_mgr_init(&dp_mst.mst_mgr, dev, - dp_mst.caps.drm_aux, - dp_mst.caps.max_dpcd_transaction_bytes, - dp_mst.caps.max_streams_supported, + ret = drm_dp_mst_topology_mgr_init(&dp_mst->mst_mgr, dev, + dp_mst->caps.drm_aux, + dp_mst->caps.max_dpcd_transaction_bytes, + dp_mst->caps.max_streams_supported, conn_base_id); if (ret) { DP_ERR("dp drm mst topology manager init failed\n"); goto error; } - dp_mst_sim_init(&dp_mst); + dp_mst_sim_init(dp_mst); - dp_mst.mst_initialized = true; - - /* create drm_bridges for cached mst encoders and clear cache */ - for (i = 0; i < dp_mst_enc_cache.cnt; i++) { - ret = dp_mst_drm_bridge_init(dp_display, - dp_mst_enc_cache.mst_enc[i]); - } - memset(&dp_mst_enc_cache, 0, sizeof(dp_mst_enc_cache)); + dp_mst->mst_initialized = true; /* choose fixed callback function if fixed topology is found */ if (!dp_display->mst_get_fixed_topology_port(dp_display, 0, NULL)) - dp_mst.mst_mgr.cbs = &dp_mst_fixed_drm_cbs; + dp_mst->mst_mgr.cbs = &dp_mst_fixed_drm_cbs; DP_MST_INFO("dp drm mst topology manager init completed\n"); return ret; error: - mutex_destroy(&dp_mst.mst_lock); - mutex_destroy(&dp_mst.edid_lock); + mutex_destroy(&dp_mst->mst_lock); + mutex_destroy(&dp_mst->edid_lock); return ret; } @@ -2476,7 +2477,7 @@ void dp_mst_deinit(struct dp_display *dp_display) drm_dp_mst_topology_mgr_destroy(&mst->mst_mgr); - dp_mst.mst_initialized = false; + mst->mst_initialized = false; mutex_destroy(&mst->mst_lock); mutex_destroy(&mst->edid_lock); diff --git a/msm/dp/dp_parser.c b/msm/dp/dp_parser.c index b94133b5e04c..1a18d69ed070 100644 --- a/msm/dp/dp_parser.c +++ b/msm/dp/dp_parser.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ @@ -166,6 +167,16 @@ static int dp_parser_misc(struct dp_parser *parser) if (rc) parser->max_lclk_khz = DP_MAX_LINK_CLK_KHZ; + for (i = 0; i < MAX_DP_MST_STREAMS; i++) { + of_property_read_u32_index(of_node, + "qcom,pixel-base-off", i, + &parser->pixel_base_off[i]); + } + + parser->display_type = of_get_property(of_node, "qcom,display-type", NULL); + if (!parser->display_type) + parser->display_type = "unknown"; + return 0; } @@ -247,23 +258,26 @@ static int dp_parser_gpio(struct dp_parser *parser) struct device *dev = &parser->pdev->dev; struct device_node *of_node = dev->of_node; struct dss_module_power *mp = &parser->mp[DP_CORE_PM]; - static const char * const dp_gpios[] = { + static const char * const dp_gpios[DP_GPIO_MAX] = { "qcom,aux-en-gpio", "qcom,aux-sel-gpio", "qcom,usbplug-cc-gpio", + "qcom,edp-vcc-en-gpio", + "qcom,edp-backlight-pwr-gpio", + "qcom,edp-pwm-en-gpio", + "qcom,edp-backlight-en-gpio", }; if (of_find_property(of_node, "qcom,dp-hpd-gpio", NULL)) { parser->no_aux_switch = true; parser->lphw_hpd = of_find_property(of_node, "qcom,dp-low-power-hw-hpd", NULL); - return 0; } if (of_find_property(of_node, "qcom,dp-gpio-aux-switch", NULL)) parser->gpio_aux_switch = true; mp->gpio_config = devm_kzalloc(dev, - sizeof(struct dss_gpio) * ARRAY_SIZE(dp_gpios), GFP_KERNEL); + sizeof(struct dss_gpio) * DP_GPIO_MAX, GFP_KERNEL); if (!mp->gpio_config) return -ENOMEM; @@ -690,6 +704,7 @@ static int dp_parser_catalog(struct dp_parser *parser) { int rc; u32 version; + const char *st = NULL; struct device *dev = &parser->pdev->dev; rc = of_property_read_u32(dev->of_node, "qcom,phy-version", &version); @@ -697,6 +712,27 @@ static int dp_parser_catalog(struct dp_parser *parser) if (!rc) parser->hw_cfg.phy_version = version; + /* phy-mode */ + rc = of_property_read_string(dev->of_node, "qcom,phy-mode", &st); + + if (!rc) { + if (!strcmp(st, "dp")) + parser->hw_cfg.phy_mode = DP_PHY_MODE_DP; + else if (!strcmp(st, "minidp")) + parser->hw_cfg.phy_mode = DP_PHY_MODE_MINIDP; + else if (!strcmp(st, "edp")) + parser->hw_cfg.phy_mode = DP_PHY_MODE_EDP; + else if (!strcmp(st, "edp-highswing")) + parser->hw_cfg.phy_mode = DP_PHY_MODE_EDP_HIGH_SWING; + else { + parser->hw_cfg.phy_mode = DP_PHY_MODE_UNKNOWN; + pr_warn("unknown phy-mode %s\n", st); + } + } else { + /* default to DP mode, if not set */ + parser->hw_cfg.phy_mode = DP_PHY_MODE_DP; + } + return 0; } @@ -715,6 +751,12 @@ static int dp_parser_mst(struct dp_parser *parser) of_property_read_u32_index(dev->of_node, "qcom,mst-fixed-topology-ports", i, &parser->mst_fixed_port[i]); + of_property_read_string_index( + dev->of_node, + "qcom,mst-fixed-topology-display-types", i, + &parser->mst_fixed_display_type[i]); + if (!parser->mst_fixed_display_type[i]) + parser->mst_fixed_display_type[i] = "unknown"; } return 0; diff --git a/msm/dp/dp_parser.h b/msm/dp/dp_parser.h index 678185705ff6..4404930d255a 100644 --- a/msm/dp/dp_parser.h +++ b/msm/dp/dp_parser.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ @@ -25,6 +26,20 @@ enum dp_pm_type { DP_MAX_PM }; +enum dp_pin_states { + DP_GPIO_AUX_ENABLE, + DP_GPIO_AUX_SEL, + DP_GPIO_USBPLUG_CC, + DP_GPIO_CMN_MAX = DP_GPIO_USBPLUG_CC, + DP_GPIO_EDP_VCC_EN, + DP_GPIO_EDP_MIN = DP_GPIO_EDP_VCC_EN, + DP_GPIO_EDP_BACKLIGHT_PWR, + DP_GPIO_EDP_PWM, + DP_GPIO_EDP_BACKLIGHT_EN, + DP_GPIO_EDP_MAX, + DP_GPIO_MAX = DP_GPIO_EDP_MAX, +}; + static inline const char *dp_parser_pm_name(enum dp_pm_type module) { switch (module) { @@ -131,15 +146,35 @@ enum dp_phy_aux_config_type { * enum dp_phy_version - version of the dp phy * @DP_PHY_VERSION_UNKNOWN: Unknown controller version * @DP_PHY_VERSION_4_2_0: DP phy v4.2.0 controller + * @DP_PHY_VERSION_5_0_0: DP phy v5.0.0 controller * @DP_PHY_VERSION_MAX: max version */ enum dp_phy_version { DP_PHY_VERSION_UNKNOWN, DP_PHY_VERSION_2_0_0 = 0x200, DP_PHY_VERSION_4_2_0 = 0x420, + DP_PHY_VERSION_5_0_0 = 0x500, DP_PHY_VERSION_MAX }; +/** + * enum dp_phy_mode - mode of the dp phy + * @DP_PHY_MODE_UNKNOWN: Unknown PHY mode + * @DP_PHY_MODE_DP: DP PHY mode + * @DP_PHY_MODE_MINIDP: MiniDP PHY mode + * @DP_PHY_MODE_EDP: eDP PHY mode + * @DP_PHY_MODE_EDP_HIGH_SWING: eDP PHY mode, high swing/pre-empahsis + * @DP_PHY_MODE_MAX: max PHY mode + */ +enum dp_phy_mode { + DP_PHY_MODE_UNKNOWN = 0, + DP_PHY_MODE_DP, + DP_PHY_MODE_MINIDP, + DP_PHY_MODE_EDP, + DP_PHY_MODE_EDP_HIGH_SWING, + DP_PHY_MODE_MAX +}; + /** * struct dp_hw_cfg - DP HW specific configuration * @@ -147,6 +182,7 @@ enum dp_phy_version { */ struct dp_hw_cfg { enum dp_phy_version phy_version; + enum dp_phy_mode phy_mode; }; static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type) @@ -202,6 +238,8 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type) * @get_io: function to be called by client to get io data. * @get_io_buf: function to be called by client to get io buffers. * @clear_io_buf: function to be called by client to clear io buffers. + * @mst_fixed_display_type: mst display_type reserved for fixed topology + * @display_type: display type as defined in device tree. */ struct dp_parser { struct platform_device *pdev; @@ -227,6 +265,9 @@ struct dp_parser { bool gpio_aux_switch; bool lphw_hpd; u32 mst_fixed_port[MAX_DP_MST_STREAMS]; + u32 pixel_base_off[MAX_DP_MST_STREAMS]; + const char *mst_fixed_display_type[MAX_DP_MST_STREAMS]; + const char *display_type; int (*parse)(struct dp_parser *parser); struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name); diff --git a/msm/dp/dp_pll.c b/msm/dp/dp_pll.c index b0ffe2f83e0b..19d982284407 100644 --- a/msm/dp/dp_pll.c +++ b/msm/dp/dp_pll.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ @@ -55,6 +56,9 @@ static int dp_pll_clock_register(struct dp_pll *pll) case DP_PLL_7NM: rc = dp_pll_clock_register_5nm(pll); break; + case EDP_PLL_7NM: + rc = edp_pll_clock_register_7nm(pll); + break; default: rc = -ENOTSUPP; break; @@ -69,6 +73,7 @@ static void dp_pll_clock_unregister(struct dp_pll *pll) case DP_PLL_5NM_V1: case DP_PLL_5NM_V2: case DP_PLL_7NM: + case EDP_PLL_7NM: dp_pll_clock_unregister_5nm(pll); break; default: @@ -106,6 +111,8 @@ struct dp_pll *dp_pll_get(struct dp_pll_in *in) pll->revision = DP_PLL_5NM_V2; } else if (!strcmp(label, "7nm")) { pll->revision = DP_PLL_7NM; + } else if (!strcmp(label, "edp-7nm")) { + pll->revision = EDP_PLL_7NM; } else { DP_ERR("Unsupported pll revision\n"); rc = -ENOTSUPP; @@ -117,6 +124,10 @@ struct dp_pll *dp_pll_get(struct dp_pll_in *in) goto error; } + pll->name = of_get_property(pdev->dev.of_node, "label", NULL); + if (!pll->name) + pll->name = "dp0"; + pll->ssc_en = of_property_read_bool(pdev->dev.of_node, "qcom,ssc-feature-enable"); pll->bonding_en = of_property_read_bool(pdev->dev.of_node, diff --git a/msm/dp/dp_pll.h b/msm/dp/dp_pll.h index 37e0d10fc1f0..fa56964ab533 100644 --- a/msm/dp/dp_pll.h +++ b/msm/dp/dp_pll.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ @@ -13,10 +14,18 @@ #include "sde_dbg.h" #define DP_VCO_HSCLK_RATE_1620MHZDIV1000 1620000UL +#define DP_VCO_HSCLK_RATE_2160MHZDIV1000 2160000UL +#define DP_VCO_HSCLK_RATE_2430MHZDIV1000 2430000UL #define DP_VCO_HSCLK_RATE_2700MHZDIV1000 2700000UL +#define DP_VCO_HSCLK_RATE_3240MHZDIV1000 3240000UL +#define DP_VCO_HSCLK_RATE_4320MHZDIV1000 4320000UL #define DP_VCO_HSCLK_RATE_5400MHZDIV1000 5400000UL +#define DP_VCO_HSCLK_RATE_5940MHZDIV1000 5940000UL #define DP_VCO_HSCLK_RATE_8100MHZDIV1000 8100000UL +#define DP_PLL_NUM_CLKS 2 +#define DP_PLL_NAME_MAX_SIZE 32 + #define dp_pll_get_base(x) pll->io.x->io.base #define dp_pll_read(x, offset) ({ \ @@ -35,6 +44,7 @@ enum dp_pll_revision { DP_PLL_5NM_V1, DP_PLL_5NM_V2, DP_PLL_7NM, + EDP_PLL_7NM, }; static inline const char *dp_pll_get_revision(enum dp_pll_revision rev) @@ -44,6 +54,7 @@ static inline const char *dp_pll_get_revision(enum dp_pll_revision rev) case DP_PLL_5NM_V1: return "DP_PLL_5NM_V1"; case DP_PLL_5NM_V2: return "DP_PLL_5NM_V2"; case DP_PLL_7NM: return "DP_PLL_7NM"; + case EDP_PLL_7NM: return "EDP_PLL_7NM"; default: return "???"; } } @@ -62,37 +73,11 @@ struct dp_pll_vco_clk { u64 min_rate; /* min vco rate */ u64 max_rate; /* max vco rate */ void *priv; + struct clk_init_data init_data; + char name[DP_PLL_NAME_MAX_SIZE]; }; -struct dp_pll { - /* - * target pll revision information - */ - u32 revision; - - /* - * Certain plls needs to update the same vco rate after resume in - * suspend/resume scenario. Cached the vco rate for such plls. - */ - unsigned long vco_cached_rate; - - /* - * PLL index if multiple index are available. Eg. in case of - * DSI we have 2 plls. - */ - uint32_t index; - - bool ssc_en; - bool bonding_en; - - void *priv; - struct platform_device *pdev; - struct dp_parser *parser; - struct dp_power *power; - struct dp_aux *aux; - struct dp_pll_io io; - struct clk_onecell_data *clk_data; -}; +struct dp_pll; struct dp_pll_db { struct dp_pll *pll; @@ -114,11 +99,49 @@ struct dp_pll_db { u32 lock_cmp_en; u32 ssc_step_size1_mode0; u32 ssc_step_size2_mode0; - + u32 ssc_per1; + u32 cmp_code1_mode0; + u32 cmp_code2_mode0; /* PHY vco divider */ u32 phy_vco_div; }; +struct dp_pll { + /* + * target pll revision information + */ + u32 revision; + + /* + * Certain plls needs to update the same vco rate after resume in + * suspend/resume scenario. Cached the vco rate for such plls. + */ + unsigned long vco_cached_rate; + + /* + * PLL index if multiple index are available. Eg. in case of + * DSI we have 2 plls. + */ + uint32_t index; + const char *name; + + bool ssc_en; + bool bonding_en; + + void *priv; + struct dp_pll_db pll_db; + struct dp_pll_vco_clk pll_clks[DP_PLL_NUM_CLKS]; + struct platform_device *pdev; + struct dp_parser *parser; + struct dp_power *power; + struct dp_aux *aux; + struct dp_pll_io io; + struct clk_onecell_data *clk_data; + + int (*pll_cfg)(struct dp_pll *pll, unsigned long rate); + int (*pll_prepare)(struct dp_pll *pll); + int (*pll_unprepare)(struct dp_pll *pll); +}; static inline struct dp_pll_vco_clk *to_dp_vco_hw(struct clk_hw *hw) { @@ -132,6 +155,7 @@ static inline bool is_gdsc_disabled(struct dp_pll *pll) int dp_pll_clock_register_5nm(struct dp_pll *pll); void dp_pll_clock_unregister_5nm(struct dp_pll *pll); +int edp_pll_clock_register_7nm(struct dp_pll *pll); struct dp_pll_in { struct platform_device *pdev; diff --git a/msm/dp/dp_power.c b/msm/dp/dp_power.c index ebafae298b86..254f32c36b53 100644 --- a/msm/dp/dp_power.c +++ b/msm/dp/dp_power.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. */ @@ -20,6 +21,8 @@ struct dp_power_private { struct clk *pixel_clk_rcg; struct clk *pixel_parent; struct clk *pixel1_clk_rcg; + struct clk *link_clk_rcg; + struct clk *link_parent; struct dp_power dp_power; @@ -229,6 +232,22 @@ static int dp_power_clk_init(struct dp_power_private *power, bool enable) goto err_pixel1_clk_rcg; } } + + power->link_clk_rcg = clk_get(dev, "link_clk_src"); + if (IS_ERR(power->link_clk_rcg)) { + DP_ERR("Unable to get DP link clk RCG: %ld\n", + PTR_ERR(power->link_clk_rcg)); + rc = 0; + power->link_clk_rcg = NULL; + } + + power->link_parent = clk_get(dev, "link_parent"); + if (IS_ERR(power->link_parent)) { + DP_ERR("Unable to get DP link parent: %ld\n", + PTR_ERR(power->link_parent)); + rc = 0; + power->link_parent = NULL; + } } else { if (power->pixel1_clk_rcg) clk_put(power->pixel1_clk_rcg); @@ -239,6 +258,12 @@ static int dp_power_clk_init(struct dp_power_private *power, bool enable) if (power->pixel_clk_rcg) clk_put(power->pixel_clk_rcg); + if (power->link_parent) + clk_put(power->link_parent); + + if (power->link_clk_rcg) + clk_put(power->link_clk_rcg); + dp_power_clk_put(power); } @@ -349,6 +374,14 @@ static int dp_power_clk_enable(struct dp_power *dp_power, } } + if (pm_type == DP_LINK_PM && enable && power->link_parent) { + rc = clk_set_parent(power->link_clk_rcg, power->link_parent); + if (rc) { + DP_ERR("failed to set link parent\n"); + goto error; + } + } + rc = dp_power_clk_set_rate(power, pm_type, enable); if (rc) { DP_ERR("failed to '%s' clks for: %s. err=%d\n", @@ -388,6 +421,7 @@ static int dp_power_request_gpios(struct dp_power_private *power) struct dss_module_power *mp; static const char * const gpio_names[] = { "aux_enable", "aux_sel", "usbplug_cc", + "edp_vcc_enable", "edp_backlight_pwr", "edp_pwm_en", "edp_backlight_en", }; if (!power) { @@ -410,6 +444,7 @@ static int dp_power_request_gpios(struct dp_power_private *power) } } } + return 0; error: for (i = 0; i < ARRAY_SIZE(gpio_names); i++) { @@ -432,7 +467,7 @@ static void dp_power_set_gpio(struct dp_power_private *power, bool flip) struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM]; struct dss_gpio *config = mp->gpio_config; - for (i = 0; i < mp->num_gpio; i++) { + for (i = 0; i <= DP_GPIO_CMN_MAX; i++) { if (dp_power_find_gpio(config->gpio_name, "aux-sel")) config->value = flip; @@ -689,6 +724,37 @@ exit: return rc; } +static int dp_power_edp_panel_set_gpio(struct dp_power *dp_power, + enum dp_pin_states pin_state, bool enable) +{ + int rc = 0; + struct dp_power_private *power; + struct dss_module_power *mp; + struct dss_gpio *config; + + if (!dp_power) { + DP_ERR("invalid power data\n"); + return -EINVAL; + } + + power = container_of(dp_power, struct dp_power_private, dp_power); + + mp = &power->parser->mp[DP_CORE_PM]; + config = mp->gpio_config; + + if (config == NULL) + return -EINVAL; + + if ((pin_state >= DP_GPIO_EDP_MIN) && (pin_state < DP_GPIO_EDP_MAX)) { + gpio_direction_output(config[pin_state].gpio, enable); + } else { + pr_err(" Invalid GPIO call\n"); + return -EINVAL; + } + + return rc; +} + struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll) { int rc = 0; @@ -720,6 +786,7 @@ struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll) dp_power->clk_get_rate = dp_power_clk_get_rate; dp_power->power_client_init = dp_power_client_init; dp_power->power_client_deinit = dp_power_client_deinit; + dp_power->edp_panel_set_gpio = dp_power_edp_panel_set_gpio; return dp_power; error: diff --git a/msm/dp/dp_power.h b/msm/dp/dp_power.h index 7a0302356de1..d605c2801fbc 100644 --- a/msm/dp/dp_power.h +++ b/msm/dp/dp_power.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ @@ -34,6 +35,8 @@ struct dp_power { struct sde_power_handle *phandle, struct drm_device *drm_dev); void (*power_client_deinit)(struct dp_power *power); + int (*edp_panel_set_gpio)(struct dp_power *power, enum dp_pin_states pin_state, + bool enable); }; /** diff --git a/msm/dp/dp_reg.h b/msm/dp/dp_reg.h index b367a8a070c0..fa9247cc37f3 100644 --- a/msm/dp/dp_reg.h +++ b/msm/dp/dp_reg.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ @@ -389,6 +390,28 @@ #define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x044) +#define DP_PHY_PD_CTL_V500 (0x0000001C) +#define DP_PHY_MODE_V500 (0x00000020) +#define DP_PHY_AUX_CFG0_V500 (0x00000024) +#define DP_PHY_AUX_CFG1_V500 (0x00000028) +#define DP_PHY_AUX_CFG2_V500 (0x0000002C) +#define DP_PHY_AUX_CFG3_V500 (0x00000030) +#define DP_PHY_AUX_CFG4_V500 (0x00000034) +#define DP_PHY_AUX_CFG5_V500 (0x00000038) +#define DP_PHY_AUX_CFG6_V500 (0x0000003C) +#define DP_PHY_AUX_CFG7_V500 (0x00000040) +#define DP_PHY_AUX_CFG8_V500 (0x00000044) +#define DP_PHY_AUX_CFG9_V500 (0x00000048) +#define DP_PHY_AUX_INTERRUPT_MASK_V500 (0x00000058) +#define DP_PHY_AUX_INTERRUPT_CLEAR_V500 (0x0000005C) +#define DP_PHY_AUX_INTERRUPT_STATUS_V500 (0x000000DC) +#define DP_PHY_SPARE0_V500 (0x00CC) +#define DP_PHY_VCO_DIV_V500 (0x0074) +#define TXn_TX_EMP_POST1_LVL_V500 (0x0004) +#define TXn_TX_DRV_LVL_V500 (0x0014) +#define TXn_TX_POL_INV_V500 (0x005C) +#define TXn_LDO_CONFIG_V500 (0x0084) + /* DP MMSS_CC registers */ #define MMSS_DP_PIXEL_M (0x01B4) #define MMSS_DP_PIXEL_N (0x01B8) diff --git a/msm/dp/edp_pll_7nm.c b/msm/dp/edp_pll_7nm.c new file mode 100644 index 000000000000..da26cb657884 --- /dev/null +++ b/msm/dp/edp_pll_7nm.c @@ -0,0 +1,927 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +/* + * Display Port PLL driver block diagram for branch clocks + * + * +------------------------+ +------------------------+ + * | dp_phy_pll_link_clk | | dp_phy_pll_vco_div_clk | + * +------------------------+ +------------------------+ + * | | + * | | + * V V + * dp_link_clk dp_pixel_clk + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "clk-regmap-mux.h" +#include "dp_hpd.h" +#include "dp_debug.h" +#include "dp_pll.h" + +#define DP_PHY_CFG 0x0010 +#define DP_PHY_CFG_1 0x0014 +#define DP_PHY_PD_CTL 0x001C +#define DP_PHY_MODE 0x0020 + +#define DP_PHY_AUX_CFG2 0x002C + +#define DP_PHY_VCO_DIV 0x0074 +#define DP_PHY_TX0_TX1_LANE_CTL 0x007C +#define DP_PHY_TX2_TX3_LANE_CTL 0x00A0 + +#define DP_PHY_SPARE0 0x00CC +#define DP_PHY_STATUS 0x00E0 + +/* Tx registers */ +#define TXn_CLKBUF_ENABLE 0x0000 +#define TXn_TX_EMP_POST1_LVL 0x0004 +#define TXn_TX_DRV_LVL 0x0014 +#define TXn_TX_DRV_LVL_OFFSET 0x0018 +#define TXn_RESET_TSYNC_EN 0x001C +#define TXn_LDO_CONFIG 0x0084 +#define TXn_TX_BAND 0x0028 +#define TXn_INTERFACE_SELECT 0x0024 +#define TXn_RES_CODE_LANE_OFFSET_TX0 0x0044 +#define TXn_RES_CODE_LANE_OFFSET_TX1 0x0048 +#define TXn_TRANSCEIVER_BIAS_EN 0x0054 +#define TXn_HIGHZ_DRVR_EN 0x0058 +#define TXn_TX_POL_INV 0x005C +#define TXn_PARRATE_REC_DETECT_IDLE_EN 0x0060 +#define TXn_LANE_MODE_1 0x0064 +#define TXn_TRAN_DRVR_EMP_EN 0x0078 +#define TXn_VMODE_CTRL1 0x007C + +/* PLL register offset */ +#define QSERDES_COM_BG_TIMER 0x000C +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x0044 +#define QSERDES_COM_CLK_ENABLE1 0x0048 +#define QSERDES_COM_SYS_CLK_CTRL 0x004C +#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x0050 +#define QSERDES_COM_PLL_IVCO 0x0058 + +#define QSERDES_COM_CP_CTRL_MODE0 0x0074 +#define QSERDES_COM_PLL_RCTRL_MODE0 0x007C +#define QSERDES_COM_PLL_CCTRL_MODE0 0x0084 +#define QSERDES_COM_SYSCLK_EN_SEL 0x0094 +#define QSERDES_COM_RESETSM_CNTRL 0x009C +#define QSERDES_COM_LOCK_CMP_EN 0x00A4 +#define QSERDES_COM_LOCK_CMP1_MODE0 0x00AC +#define QSERDES_COM_LOCK_CMP2_MODE0 0x00B0 + +#define QSERDES_COM_DEC_START_MODE0 0x00BC +#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x00CC +#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x00D0 +#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x00D4 +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x00EC +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x00F0 +#define QSERDES_COM_VCO_TUNE_CTRL 0x0108 +#define QSERDES_COM_VCO_TUNE_MAP 0x010C +#define QSERDES_COM_VCO_TUNE1_MODE0 0x0110 +#define QSERDES_COM_VCO_TUNE2_MODE0 0x0114 +#define QSERDES_COM_CMN_STATUS 0x0140 + +#define QSERDES_COM_CLK_SEL 0x0154 +#define QSERDES_COM_HSCLK_SEL 0x0158 + +#define QSERDES_COM_CORECLK_DIV_MODE0 0x0168 + +#define QSERDES_COM_CORE_CLK_EN 0x0174 +#define QSERDES_COM_C_READY_STATUS 0x0178 +#define QSERDES_COM_CMN_CONFIG 0x017C + +#define QSERDES_COM_SVS_MODE_CLK_SEL 0x0184 + +#define QSERDES_COM_SSC_EN_CENTER 0x0010 +#define QSERDES_COM_SSC_ADJ_PER1 0x0014 +#define QSERDES_COM_SSC_ADJ_PER2 0x0018 +#define QSERDES_COM_SSC_PER1 0x001C +#define QSERDES_COM_SSC_PER2 0x0020 +#define QSERDES_COM_SSC_STEP_SIZE1_MODE0 0x0024 +#define QSERDES_COM_SSC_STEP_SIZE2_MODE0 0x0028 + +#define DP_PHY_PLL_POLL_SLEEP_US 500 +#define DP_PHY_PLL_POLL_TIMEOUT_US 10000 + +#define DP_VCO_RATE_8100MHZDIV1000 8100000UL +#define DP_VCO_RATE_9720MHZDIV1000 9720000UL +#define DP_VCO_RATE_10800MHZDIV1000 10800000UL + +#define DP_7NM_C_READY BIT(0) +#define DP_7NM_FREQ_DONE BIT(0) +#define DP_7NM_PLL_LOCKED BIT(1) +#define DP_7NM_PHY_READY BIT(1) +#define DP_7NM_TSYNC_DONE BIT(0) + +static int edp_vco_pll_init_db_7nm(struct dp_pll_db *pdb, + unsigned long rate) +{ + struct dp_pll *pll = pdb->pll; + u32 spare_value = 0; + + spare_value = dp_pll_read(dp_phy, DP_PHY_SPARE0); + pdb->lane_cnt = spare_value & 0x0F; + pdb->orientation = (spare_value & 0xF0) >> 4; + + DP_DEBUG("spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n", + spare_value, pdb->lane_cnt, pdb->orientation); + + pdb->div_frac_start1_mode0 = 0x00; + pdb->integloop_gain0_mode0 = 0x3f; + pdb->integloop_gain1_mode0 = 0x00; + + switch (rate) { + case DP_VCO_HSCLK_RATE_1620MHZDIV1000: + DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_9720MHZDIV1000); + pdb->hsclk_sel = 0x05; + pdb->dec_start_mode0 = 0x69; + pdb->div_frac_start2_mode0 = 0x80; + pdb->div_frac_start3_mode0 = 0x07; + pdb->lock_cmp1_mode0 = 0x6f; + pdb->lock_cmp2_mode0 = 0x08; + pdb->phy_vco_div = 0x01; + pdb->lock_cmp_en = 0x08; + pdb->ssc_step_size1_mode0 = 0x45; + pdb->ssc_step_size2_mode0 = 0x06; + break; + case DP_VCO_HSCLK_RATE_2160MHZDIV1000: + DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000); + pdb->hsclk_sel = 0x04; + pdb->dec_start_mode0 = 0x70; + pdb->div_frac_start2_mode0 = 0x00; + pdb->div_frac_start3_mode0 = 0x08; + pdb->lock_cmp1_mode0 = 0x3f; + pdb->lock_cmp2_mode0 = 0x0b; + pdb->phy_vco_div = 0x1; + pdb->lock_cmp_en = 0x04; + pdb->ssc_step_size1_mode0 = 0xb0; + pdb->ssc_step_size2_mode0 = 0x06; + break; + case DP_VCO_HSCLK_RATE_2430MHZDIV1000: + DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000); + pdb->hsclk_sel = 0x04; + pdb->dec_start_mode0 = 0x7e; + pdb->div_frac_start2_mode0 = 0x00; + pdb->div_frac_start3_mode0 = 0x09; + pdb->lock_cmp1_mode0 = 0xa7; + pdb->lock_cmp2_mode0 = 0x0c; + pdb->phy_vco_div = 0x1; + pdb->lock_cmp_en = 0x04; + pdb->ssc_step_size1_mode0 = 0x86; + pdb->ssc_step_size2_mode0 = 0x07; + break; + case DP_VCO_HSCLK_RATE_2700MHZDIV1000: + DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000); + pdb->hsclk_sel = 0x03; + pdb->dec_start_mode0 = 0x69; + pdb->div_frac_start2_mode0 = 0x80; + pdb->div_frac_start3_mode0 = 0x07; + pdb->lock_cmp1_mode0 = 0x0f; + pdb->lock_cmp2_mode0 = 0x0e; + pdb->phy_vco_div = 0x01; + pdb->lock_cmp_en = 0x08; + pdb->ssc_step_size1_mode0 = 0x45; + pdb->ssc_step_size2_mode0 = 0x06; + break; + case DP_VCO_HSCLK_RATE_3240MHZDIV1000: + DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000); + pdb->hsclk_sel = 0x03; + pdb->dec_start_mode0 = 0x7e; + pdb->div_frac_start2_mode0 = 0x00; + pdb->div_frac_start3_mode0 = 0x09; + pdb->lock_cmp1_mode0 = 0xdf; + pdb->lock_cmp2_mode0 = 0x10; + pdb->phy_vco_div = 0x2; + pdb->lock_cmp_en = 0x04; + pdb->ssc_step_size1_mode0 = 0x86; + pdb->ssc_step_size2_mode0 = 0x07; + break; + case DP_VCO_HSCLK_RATE_4320MHZDIV1000: + DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000); + pdb->hsclk_sel = 0x01; + pdb->dec_start_mode0 = 0x70; + pdb->div_frac_start2_mode0 = 0x00; + pdb->div_frac_start3_mode0 = 0x08; + pdb->lock_cmp1_mode0 = 0x7f; + pdb->lock_cmp2_mode0 = 0x16; + pdb->phy_vco_div = 0x2; + pdb->lock_cmp_en = 0x04; + pdb->ssc_step_size1_mode0 = 0xb0; + pdb->ssc_step_size2_mode0 = 0x06; + break; + case DP_VCO_HSCLK_RATE_5400MHZDIV1000: + DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000); + pdb->hsclk_sel = 0x01; + pdb->dec_start_mode0 = 0x8c; + pdb->div_frac_start2_mode0 = 0x00; + pdb->div_frac_start3_mode0 = 0x0a; + pdb->lock_cmp1_mode0 = 0x1f; + pdb->lock_cmp2_mode0 = 0x1c; + pdb->phy_vco_div = 0x02; + pdb->lock_cmp_en = 0x08; + pdb->ssc_step_size1_mode0 = 0x5c; + pdb->ssc_step_size2_mode0 = 0x08; + break; + case DP_VCO_HSCLK_RATE_5940MHZDIV1000: + DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_8100MHZDIV1000); + pdb->hsclk_sel = 0x01; + pdb->dec_start_mode0 = 0x9a; + pdb->div_frac_start2_mode0 = 0x00; + pdb->div_frac_start3_mode0 = 0x0b; + pdb->lock_cmp1_mode0 = 0xef; + pdb->lock_cmp2_mode0 = 0x1e; + pdb->phy_vco_div = 0x00; + pdb->lock_cmp_en = 0x04; + pdb->ssc_step_size1_mode0 = 0x33; + pdb->ssc_step_size2_mode0 = 0x09; + break; + case DP_VCO_HSCLK_RATE_8100MHZDIV1000: + DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_8100MHZDIV1000); + pdb->hsclk_sel = 0x00; + pdb->dec_start_mode0 = 0x69; + pdb->div_frac_start2_mode0 = 0x80; + pdb->div_frac_start3_mode0 = 0x07; + pdb->lock_cmp1_mode0 = 0x2f; + pdb->lock_cmp2_mode0 = 0x2a; + pdb->phy_vco_div = 0x00; + pdb->lock_cmp_en = 0x08; + pdb->ssc_step_size1_mode0 = 0x45; + pdb->ssc_step_size2_mode0 = 0x06; + break; + default: + DP_ERR("unsupported rate %ld\n", rate); + return -EINVAL; + } + return 0; +} + +static int edp_config_vco_rate_7nm(struct dp_pll *pll, + unsigned long rate) +{ + int rc; + struct dp_pll_db *pdb = &pll->pll_db; + u32 status; + + rc = edp_vco_pll_init_db_7nm(pdb, rate); + if (rc < 0) { + DP_ERR("VCO Init DB failed\n"); + return rc; + } + + dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x7d); + /* Make sure the PLL register writes are done */ + wmb(); + + if (readl_poll_timeout_atomic((dp_pll_get_base(dp_pll) + + QSERDES_COM_CMN_STATUS), + status, ((status & BIT(7)) > 0), + 5, 100)) { + DP_ERR("refgen not ready. Status=%x\n", status); + } + + dp_pll_write(dp_ln_tx0, TXn_LDO_CONFIG, 0x01); + dp_pll_write(dp_ln_tx1, TXn_LDO_CONFIG, 0x01); + dp_pll_write(dp_ln_tx0, TXn_LANE_MODE_1, 0x00); + dp_pll_write(dp_ln_tx1, TXn_LANE_MODE_1, 0x00); + + if (pll->ssc_en) { + dp_pll_write(dp_pll, QSERDES_COM_SSC_EN_CENTER, 0x01); + dp_pll_write(dp_pll, QSERDES_COM_SSC_ADJ_PER1, 0x00); + dp_pll_write(dp_pll, QSERDES_COM_SSC_PER1, 0x36); + dp_pll_write(dp_pll, QSERDES_COM_SSC_PER2, 0x01); + dp_pll_write(dp_pll, QSERDES_COM_SSC_STEP_SIZE1_MODE0, + pdb->ssc_step_size1_mode0); + dp_pll_write(dp_pll, QSERDES_COM_SSC_STEP_SIZE2_MODE0, + pdb->ssc_step_size2_mode0); + } + + dp_pll_write(dp_pll, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01); + dp_pll_write(dp_pll, QSERDES_COM_SYSCLK_EN_SEL, 0x0b); + dp_pll_write(dp_pll, QSERDES_COM_SYS_CLK_CTRL, 0x02); + dp_pll_write(dp_pll, QSERDES_COM_CLK_ENABLE1, 0x0c); + dp_pll_write(dp_pll, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06); + dp_pll_write(dp_pll, QSERDES_COM_CLK_SEL, 0x30); + dp_pll_write(dp_pll, + QSERDES_COM_HSCLK_SEL, pdb->hsclk_sel); + dp_pll_write(dp_pll, QSERDES_COM_PLL_IVCO, 0x0f); + dp_pll_write(dp_pll, + QSERDES_COM_LOCK_CMP_EN, pdb->lock_cmp_en); + dp_pll_write(dp_pll, QSERDES_COM_PLL_CCTRL_MODE0, 0x36); + dp_pll_write(dp_pll, QSERDES_COM_PLL_RCTRL_MODE0, 0x16); + dp_pll_write(dp_pll, QSERDES_COM_CP_CTRL_MODE0, 0x06); + dp_pll_write(dp_pll, + QSERDES_COM_DEC_START_MODE0, pdb->dec_start_mode0); + dp_pll_write(dp_pll, + QSERDES_COM_DIV_FRAC_START1_MODE0, pdb->div_frac_start1_mode0); + dp_pll_write(dp_pll, + QSERDES_COM_DIV_FRAC_START2_MODE0, pdb->div_frac_start2_mode0); + dp_pll_write(dp_pll, + QSERDES_COM_DIV_FRAC_START3_MODE0, pdb->div_frac_start3_mode0); + dp_pll_write(dp_pll, + QSERDES_COM_CMN_CONFIG, 0x02); + dp_pll_write(dp_pll, + QSERDES_COM_INTEGLOOP_GAIN0_MODE0, pdb->integloop_gain0_mode0); + dp_pll_write(dp_pll, + QSERDES_COM_INTEGLOOP_GAIN1_MODE0, pdb->integloop_gain1_mode0); + dp_pll_write(dp_pll, + QSERDES_COM_VCO_TUNE_MAP, 0x00); + dp_pll_write(dp_pll, + QSERDES_COM_LOCK_CMP1_MODE0, pdb->lock_cmp1_mode0); + dp_pll_write(dp_pll, + QSERDES_COM_LOCK_CMP2_MODE0, pdb->lock_cmp2_mode0); + dp_pll_write(dp_phy, DP_PHY_VCO_DIV, pdb->phy_vco_div); + /* Make sure the PLL register writes are done */ + wmb(); + + dp_pll_write(dp_pll, QSERDES_COM_BG_TIMER, 0x0a); + dp_pll_write(dp_pll, QSERDES_COM_CORECLK_DIV_MODE0, 0x14); + dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE_CTRL, 0x00); + dp_pll_write(dp_pll, + QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17); + dp_pll_write(dp_pll, QSERDES_COM_CORE_CLK_EN, 0x0f); + dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE1_MODE0, 0xa0); + dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE2_MODE0, 0x03); + /* Make sure the PHY register writes are done */ + wmb(); + + /* TX Lane configuration */ + dp_pll_write(dp_phy, DP_PHY_TX0_TX1_LANE_CTL, 0x05); + dp_pll_write(dp_phy, DP_PHY_TX2_TX3_LANE_CTL, 0x05); + + /* TX-0 register configuration */ + dp_pll_write(dp_ln_tx0, TXn_TRANSCEIVER_BIAS_EN, 0x03); + dp_pll_write(dp_ln_tx0, TXn_CLKBUF_ENABLE, 0x0f); + dp_pll_write(dp_ln_tx0, TXn_RESET_TSYNC_EN, 0x03); + dp_pll_write(dp_ln_tx0, TXn_TRAN_DRVR_EMP_EN, 0x01); + dp_pll_write(dp_ln_tx0, TXn_TX_BAND, 0x4); + + /* TX-1 register configuration */ + dp_pll_write(dp_ln_tx1, TXn_TRANSCEIVER_BIAS_EN, 0x03); + dp_pll_write(dp_ln_tx1, TXn_CLKBUF_ENABLE, 0x0f); + dp_pll_write(dp_ln_tx1, TXn_RESET_TSYNC_EN, 0x03); + dp_pll_write(dp_ln_tx1, TXn_TRAN_DRVR_EMP_EN, 0x01); + dp_pll_write(dp_ln_tx1, TXn_TX_BAND, 0x4); + /* Make sure the PHY register writes are done */ + wmb(); + + return 0; +} + +enum edp_7nm_pll_status { + C_READY, + FREQ_DONE, + PLL_LOCKED, + PHY_READY, + TSYNC_DONE, +}; + +static char *edp_7nm_pll_get_status_name(enum edp_7nm_pll_status status) +{ + switch (status) { + case C_READY: + return "C_READY"; + case FREQ_DONE: + return "FREQ_DONE"; + case PLL_LOCKED: + return "PLL_LOCKED"; + case PHY_READY: + return "PHY_READY"; + case TSYNC_DONE: + return "TSYNC_DONE"; + default: + return "unknown"; + } +} + +static bool edp_7nm_pll_get_status(struct dp_pll *pll, + enum edp_7nm_pll_status status) +{ + u32 reg, state, bit; + void __iomem *base; + bool success = true; + + switch (status) { + case C_READY: + base = dp_pll_get_base(dp_pll); + reg = QSERDES_COM_C_READY_STATUS; + bit = DP_7NM_C_READY; + break; + case FREQ_DONE: + base = dp_pll_get_base(dp_pll); + reg = QSERDES_COM_CMN_STATUS; + bit = DP_7NM_FREQ_DONE; + break; + case PLL_LOCKED: + base = dp_pll_get_base(dp_pll); + reg = QSERDES_COM_CMN_STATUS; + bit = DP_7NM_PLL_LOCKED; + break; + case PHY_READY: + base = dp_pll_get_base(dp_phy); + reg = DP_PHY_STATUS; + bit = DP_7NM_PHY_READY; + break; + case TSYNC_DONE: + base = dp_pll_get_base(dp_phy); + reg = DP_PHY_STATUS; + bit = DP_7NM_TSYNC_DONE; + break; + default: + return false; + } + + if (readl_poll_timeout_atomic((base + reg), state, + ((state & bit) > 0), + DP_PHY_PLL_POLL_SLEEP_US, + DP_PHY_PLL_POLL_TIMEOUT_US)) { + DP_ERR("%s failed, status=%x\n", + edp_7nm_pll_get_status_name(status), state); + + success = false; + } + + return success; +} + +static int edp_pll_enable_7nm(struct dp_pll *pll) +{ + int rc = 0; + u32 bias_en0, drvr_en0, bias_en1, drvr_en1, phy_cfg_1; + + pll->aux->state &= ~DP_STATE_PLL_LOCKED; + + dp_pll_write(dp_phy, DP_PHY_CFG, 0x01); + dp_pll_write(dp_phy, DP_PHY_CFG, 0x05); + dp_pll_write(dp_phy, DP_PHY_CFG, 0x01); + dp_pll_write(dp_phy, DP_PHY_CFG, 0x09); + dp_pll_write(dp_pll, QSERDES_COM_RESETSM_CNTRL, 0x20); + wmb(); /* Make sure the PLL register writes are done */ + + if (!edp_7nm_pll_get_status(pll, C_READY)) { + rc = -EINVAL; + goto lock_err; + } + + if (!edp_7nm_pll_get_status(pll, FREQ_DONE)) { + rc = -EINVAL; + goto lock_err; + } + + if (!edp_7nm_pll_get_status(pll, PLL_LOCKED)) { + rc = -EINVAL; + goto lock_err; + } + + dp_pll_write(dp_phy, DP_PHY_CFG, 0x19); + dp_pll_write(dp_ln_tx0, TXn_HIGHZ_DRVR_EN, 0x1f); + dp_pll_write(dp_ln_tx0, TXn_HIGHZ_DRVR_EN, 0x04); + dp_pll_write(dp_ln_tx0, TXn_TX_POL_INV, 0x00); + dp_pll_write(dp_ln_tx1, TXn_HIGHZ_DRVR_EN, 0x1f); + dp_pll_write(dp_ln_tx1, TXn_HIGHZ_DRVR_EN, 0x04); + dp_pll_write(dp_ln_tx1, TXn_TX_POL_INV, 0x00); + dp_pll_write(dp_ln_tx0, TXn_TX_DRV_LVL_OFFSET, 0x10); + dp_pll_write(dp_ln_tx1, TXn_TX_DRV_LVL_OFFSET, 0x10); + dp_pll_write(dp_ln_tx0, + TXn_RES_CODE_LANE_OFFSET_TX0, 0x11); + dp_pll_write(dp_ln_tx0, + TXn_RES_CODE_LANE_OFFSET_TX1, 0x11); + dp_pll_write(dp_ln_tx1, + TXn_RES_CODE_LANE_OFFSET_TX0, 0x11); + dp_pll_write(dp_ln_tx1, + TXn_RES_CODE_LANE_OFFSET_TX1, 0x11); + dp_pll_write(dp_ln_tx0, TXn_TX_EMP_POST1_LVL, 0x10); + dp_pll_write(dp_ln_tx1, TXn_TX_EMP_POST1_LVL, 0x10); + dp_pll_write(dp_ln_tx0, TXn_TX_DRV_LVL, 0x1f); + dp_pll_write(dp_ln_tx1, TXn_TX_DRV_LVL, 0x1f); + + /* Make sure the PHY register writes are done */ + wmb(); + + if (pll->pll_db.lane_cnt == 1) { + bias_en0 = 0x01; + bias_en1 = 0x00; + drvr_en0 = 0x06; + drvr_en1 = 0x07; + phy_cfg_1 = 0x01; + } else if (pll->pll_db.lane_cnt == 2) { + bias_en0 = 0x03; + bias_en1 = 0x00; + drvr_en0 = 0x04; + drvr_en1 = 0x07; + phy_cfg_1 = 0x03; + } else { + bias_en0 = 0x03; + bias_en1 = 0x03; + drvr_en0 = 0x04; + drvr_en1 = 0x04; + phy_cfg_1 = 0x0f; + } + + dp_pll_write(dp_ln_tx0, TXn_HIGHZ_DRVR_EN, drvr_en0); + dp_pll_write(dp_ln_tx0, TXn_TRANSCEIVER_BIAS_EN, + bias_en0); + dp_pll_write(dp_ln_tx1, TXn_HIGHZ_DRVR_EN, drvr_en1); + dp_pll_write(dp_ln_tx1, TXn_TRANSCEIVER_BIAS_EN, + bias_en1); + + /* Make sure the PHY register writes are done */ + wmb(); + + dp_pll_write(dp_phy, DP_PHY_CFG_1, phy_cfg_1); + + if (!edp_7nm_pll_get_status(pll, PHY_READY)) { + rc = -EINVAL; + goto lock_err; + } + + dp_pll_write(dp_phy, DP_PHY_CFG, 0x18); + udelay(100); + + dp_pll_write(dp_phy, DP_PHY_CFG, 0x19); + /* Make sure the PHY register writes are done */ + wmb(); + + if (!edp_7nm_pll_get_status(pll, TSYNC_DONE)) { + rc = -EINVAL; + goto lock_err; + } + + if (!edp_7nm_pll_get_status(pll, PHY_READY)) { + rc = -EINVAL; + goto lock_err; + } + + pll->aux->state |= DP_STATE_PLL_LOCKED; + DP_DEBUG("PLL is locked\n"); + +lock_err: + return rc; +} + +static void edp_pll_disable_7nm(struct dp_pll *pll) +{ + /* Assert DP PHY power down */ + dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x2); + /* + * Make sure all the register writes to disable PLL are + * completed before doing any other operation + */ + wmb(); +} + +static int edp_vco_clk_set_div(struct dp_pll *pll, unsigned int div) +{ + u32 val = 0; + + if (!pll) { + DP_ERR("invalid input parameters\n"); + return -EINVAL; + } + + if (is_gdsc_disabled(pll)) + return -EINVAL; + + val = dp_pll_read(dp_phy, DP_PHY_VCO_DIV); + val &= ~0x03; + + switch (div) { + case 2: + val |= 1; + break; + case 4: + val |= 2; + break; + case 6: + /* When div = 6, val is 0, so do nothing here */ + ; + break; + case 8: + val |= 3; + break; + default: + DP_DEBUG("unsupported div value %d\n", div); + return -EINVAL; + } + + dp_pll_write(dp_phy, DP_PHY_VCO_DIV, val); + /* Make sure the PHY registers writes are done */ + wmb(); + + DP_DEBUG("val=%d div=%x\n", val, div); + return 0; +} + +static int edp_vco_set_rate_7nm(struct dp_pll *pll, unsigned long rate) +{ + int rc = 0; + + if (!pll) { + DP_ERR("invalid input parameters\n"); + return -EINVAL; + } + + DP_DEBUG("DP lane CLK rate=%ld\n", rate); + + rc = edp_config_vco_rate_7nm(pll, rate); + if (rc < 0) { + DP_ERR("Failed to set clk rate\n"); + return rc; + } + + return rc; +} + +static int edp_pll_configure(struct dp_pll *pll, unsigned long rate) +{ + int rc = 0; + + if (!pll || !rate) { + DP_ERR("invalid input parameters rate = %lu\n", rate); + return -EINVAL; + } + + rate = rate * 10; + + if (rate <= DP_VCO_HSCLK_RATE_1620MHZDIV1000) + rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000; + else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000) + rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000; + else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000) + rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000; + else + rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000; + + rc = edp_vco_set_rate_7nm(pll, rate); + if (rc < 0) { + DP_ERR("pll rate %s set failed\n", rate); + return rc; + } + + pll->vco_cached_rate = rate; + DP_DEBUG("pll rate %lu set success\n", rate); + return rc; +} + +static int edp_pll_prepare(struct dp_pll *pll) +{ + int rc = 0; + + if (!pll) { + DP_ERR("invalid input parameters\n"); + return -EINVAL; + } + + rc = edp_pll_enable_7nm(pll); + if (rc < 0) + DP_ERR("ndx=%d failed to enable dp pll\n", pll->index); + + return rc; +} + +static int edp_pll_unprepare(struct dp_pll *pll) +{ + int rc = 0; + + if (!pll) { + DP_ERR("invalid input parameter\n"); + return -EINVAL; + } + + edp_pll_disable_7nm(pll); + + return rc; +} + +static unsigned long edp_pll_link_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct dp_pll *pll = NULL; + struct dp_pll_vco_clk *pll_link = NULL; + unsigned long rate = 0; + + if (!hw) { + DP_ERR("invalid input parameters\n"); + return -EINVAL; + } + + pll_link = to_dp_vco_hw(hw); + pll = pll_link->priv; + + rate = pll->vco_cached_rate; + rate = pll->vco_cached_rate * 100; + + return rate; +} + +static long edp_pll_link_clk_round(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct dp_pll *pll = NULL; + struct dp_pll_vco_clk *pll_link = NULL; + + if (!hw) { + DP_ERR("invalid input parameters\n"); + return -EINVAL; + } + + pll_link = to_dp_vco_hw(hw); + pll = pll_link->priv; + + rate = pll->vco_cached_rate * 100; + + return rate; +} + +static unsigned long edp_pll_vco_div_clk_get_rate(struct dp_pll *pll) +{ + if (pll->vco_cached_rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000) + return (pll->vco_cached_rate / 6 * 1000); + else if (pll->vco_cached_rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000) + return (pll->vco_cached_rate / 4 * 1000); + else + return (pll->vco_cached_rate / 2 * 1000); +} + +static unsigned long edp_pll_vco_div_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct dp_pll *pll = NULL; + struct dp_pll_vco_clk *pll_link = NULL; + + if (!hw) { + DP_ERR("invalid input parameters\n"); + return -EINVAL; + } + + pll_link = to_dp_vco_hw(hw); + pll = pll_link->priv; + + return edp_pll_vco_div_clk_get_rate(pll); +} + +static long edp_pll_vco_div_clk_round(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return edp_pll_vco_div_clk_recalc_rate(hw, *parent_rate); +} + +static int edp_pll_vco_div_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct dp_pll *pll = NULL; + struct dp_pll_vco_clk *pll_link = NULL; + int rc = 0; + + if (!hw) { + DP_ERR("invalid input parameters\n"); + return -EINVAL; + } + + pll_link = to_dp_vco_hw(hw); + pll = pll_link->priv; + + if (rate != edp_pll_vco_div_clk_get_rate(pll)) { + DP_ERR("unsupported rate %lu failed\n", rate); + return rc; + } + + rate /= 1000; + + rc = edp_vco_clk_set_div(pll, pll->vco_cached_rate / rate); + if (rc < 0) { + DP_DEBUG("set rate %lu failed\n", rate); + return rc; + } + + DP_DEBUG("set rate %lu success\n", rate); + return 0; +} + +static const struct clk_ops edp_pll_link_clk_ops = { + .recalc_rate = edp_pll_link_clk_recalc_rate, + .round_rate = edp_pll_link_clk_round, +}; + +static const struct clk_ops edp_pll_vco_div_clk_ops = { + .recalc_rate = edp_pll_vco_div_clk_recalc_rate, + .round_rate = edp_pll_vco_div_clk_round, + .set_rate = edp_pll_vco_div_clk_set_rate, +}; + +static struct clk_init_data edp_phy_pll_clks[DP_PLL_NUM_CLKS] = { + { + .name = "edp_phy_pll_link_clk", + .ops = &edp_pll_link_clk_ops, + }, + { + .name = "edp_phy_pll_vco_div_clk", + .ops = &edp_pll_vco_div_clk_ops, + }, +}; + +static struct dp_pll_vco_clk *edp_pll_get_clks(struct dp_pll *pll) +{ + int i; + + for (i = 0; i < DP_PLL_NUM_CLKS; i++) { + snprintf(pll->pll_clks[i].name, DP_PLL_NAME_MAX_SIZE, + "%s", edp_phy_pll_clks[i].name); + pll->pll_clks[i].init_data.name = pll->pll_clks[i].name; + pll->pll_clks[i].init_data.ops = edp_phy_pll_clks[i].ops; + pll->pll_clks[i].hw.init = &pll->pll_clks[i].init_data; + } + + return pll->pll_clks; +} + +static int dp_pll_clock_register_helper(struct dp_pll *pll, + struct dp_pll_vco_clk *clks, int num_clks) +{ + int rc = 0, i = 0; + struct platform_device *pdev; + struct clk *clk; + + if (!pll || !clks) { + DP_ERR("input not initialized\n"); + return -EINVAL; + } + + pdev = pll->pdev; + + for (i = 0; i < num_clks; i++) { + clks[i].priv = pll; + + clk = clk_register(&pdev->dev, &clks[i].hw); + if (IS_ERR(clk)) { + DP_ERR("%s registration failed for DP: %d\n", + clk_hw_get_name(&clks[i].hw), pll->index); + return -EINVAL; + } + pll->clk_data->clks[i] = clk; + } + + return rc; +} + +int edp_pll_clock_register_7nm(struct dp_pll *pll) +{ + int rc = 0; + struct platform_device *pdev; + struct dp_pll_vco_clk *pll_clks; + + if (!pll) { + DP_ERR("pll data not initialized\n"); + return -EINVAL; + } + pdev = pll->pdev; + + pll->clk_data = kzalloc(sizeof(*pll->clk_data), GFP_KERNEL); + if (!pll->clk_data) + return -ENOMEM; + + pll->clk_data->clks = kcalloc(DP_PLL_NUM_CLKS, sizeof(struct clk *), + GFP_KERNEL); + if (!pll->clk_data->clks) { + kfree(pll->clk_data); + return -ENOMEM; + } + + pll->clk_data->clk_num = DP_PLL_NUM_CLKS; + pll->pll_db.pll = pll; + + pll->pll_cfg = edp_pll_configure; + pll->pll_prepare = edp_pll_prepare; + pll->pll_unprepare = edp_pll_unprepare; + + pll_clks = edp_pll_get_clks(pll); + + rc = dp_pll_clock_register_helper(pll, pll_clks, DP_PLL_NUM_CLKS); + if (rc) { + DP_ERR("Clock register failed rc=%d\n", rc); + goto clk_reg_fail; + } + + rc = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, pll->clk_data); + if (rc) { + DP_ERR("Clock add provider failed rc=%d\n", rc); + goto clk_reg_fail; + } + + DP_DEBUG("success\n"); + return rc; + +clk_reg_fail: + dp_pll_clock_unregister_5nm(pll); + return rc; +} diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 3eeb518c1a63..d2c54c99ab14 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -4858,6 +4858,9 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, if (disp_info->intf_type == DRM_MODE_CONNECTOR_DSI) { *drm_enc_mode = DRM_MODE_ENCODER_DSI; intf_type = INTF_DSI; + } else if (disp_info->intf_type == DRM_MODE_CONNECTOR_eDP) { + *drm_enc_mode = DRM_MODE_ENCODER_TMDS; + intf_type = INTF_DP; } else if (disp_info->intf_type == DRM_MODE_CONNECTOR_HDMIA) { *drm_enc_mode = DRM_MODE_ENCODER_TMDS; intf_type = INTF_HDMI; diff --git a/msm/sde/sde_hw_interrupts.c b/msm/sde/sde_hw_interrupts.c index 81fddec0dce7..2b3b7e164cca 100644 --- a/msm/sde/sde_hw_interrupts.c +++ b/msm/sde/sde_hw_interrupts.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ @@ -73,10 +74,16 @@ #define SDE_INTR_INTF_1_UNDERRUN BIT(26) #define SDE_INTR_INTF_2_UNDERRUN BIT(28) #define SDE_INTR_INTF_3_UNDERRUN BIT(30) +#define SDE_INTR_INTF_4_UNDERRUN BIT(20) +#define SDE_INTR_INTF_5_UNDERRUN BIT(22) +#define SDE_INTR_INTF_6_UNDERRUN BIT(16) #define SDE_INTR_INTF_0_VSYNC BIT(25) #define SDE_INTR_INTF_1_VSYNC BIT(27) #define SDE_INTR_INTF_2_VSYNC BIT(29) #define SDE_INTR_INTF_3_VSYNC BIT(31) +#define SDE_INTR_INTF_4_VSYNC BIT(21) +#define SDE_INTR_INTF_5_VSYNC BIT(23) +#define SDE_INTR_INTF_6_VSYNC BIT(17) /** * Pingpong Secondary interrupt status bit definitions @@ -251,33 +258,6 @@ static struct sde_irq_type sde_irq_intr_map[] = { { SDE_IRQ_TYPE_PING_PONG_COMP, PINGPONG_3, SDE_INTR_PING_PONG_3_DONE, -1}, - { SDE_IRQ_TYPE_PING_PONG_RD_PTR, PINGPONG_0, - SDE_INTR_PING_PONG_0_RD_PTR, -1}, - { SDE_IRQ_TYPE_PING_PONG_RD_PTR, PINGPONG_1, - SDE_INTR_PING_PONG_1_RD_PTR, -1}, - { SDE_IRQ_TYPE_PING_PONG_RD_PTR, PINGPONG_2, - SDE_INTR_PING_PONG_2_RD_PTR, -1}, - { SDE_IRQ_TYPE_PING_PONG_RD_PTR, PINGPONG_3, - SDE_INTR_PING_PONG_3_RD_PTR, -1}, - - { SDE_IRQ_TYPE_PING_PONG_WR_PTR, PINGPONG_0, - SDE_INTR_PING_PONG_0_WR_PTR, -1}, - { SDE_IRQ_TYPE_PING_PONG_WR_PTR, PINGPONG_1, - SDE_INTR_PING_PONG_1_WR_PTR, -1}, - { SDE_IRQ_TYPE_PING_PONG_WR_PTR, PINGPONG_2, - SDE_INTR_PING_PONG_2_WR_PTR, -1}, - { SDE_IRQ_TYPE_PING_PONG_WR_PTR, PINGPONG_3, - SDE_INTR_PING_PONG_3_WR_PTR, -1}, - - { SDE_IRQ_TYPE_PING_PONG_AUTO_REF, PINGPONG_0, - SDE_INTR_PING_PONG_0_AUTOREFRESH_DONE, -1}, - { SDE_IRQ_TYPE_PING_PONG_AUTO_REF, PINGPONG_1, - SDE_INTR_PING_PONG_1_AUTOREFRESH_DONE, -1}, - { SDE_IRQ_TYPE_PING_PONG_AUTO_REF, PINGPONG_2, - SDE_INTR_PING_PONG_2_AUTOREFRESH_DONE, -1}, - { SDE_IRQ_TYPE_PING_PONG_AUTO_REF, PINGPONG_3, - SDE_INTR_PING_PONG_3_AUTOREFRESH_DONE, -1}, - { SDE_IRQ_TYPE_INTF_UNDER_RUN, INTF_0, SDE_INTR_INTF_0_UNDERRUN, -1}, { SDE_IRQ_TYPE_INTF_VSYNC, INTF_0, SDE_INTR_INTF_0_VSYNC, -1}, { SDE_IRQ_TYPE_INTF_UNDER_RUN, INTF_1, SDE_INTR_INTF_1_UNDERRUN, -1}, @@ -287,6 +267,13 @@ static struct sde_irq_type sde_irq_intr_map[] = { { SDE_IRQ_TYPE_INTF_VSYNC, INTF_2, SDE_INTR_INTF_2_VSYNC, -1}, { SDE_IRQ_TYPE_INTF_UNDER_RUN, INTF_3, SDE_INTR_INTF_3_UNDERRUN, -1}, { SDE_IRQ_TYPE_INTF_VSYNC, INTF_3, SDE_INTR_INTF_3_VSYNC, -1}, + + { SDE_IRQ_TYPE_INTF_UNDER_RUN, INTF_4, SDE_INTR_INTF_4_UNDERRUN, -1}, + { SDE_IRQ_TYPE_INTF_VSYNC, INTF_4, SDE_INTR_INTF_4_VSYNC, -1}, + { SDE_IRQ_TYPE_INTF_UNDER_RUN, INTF_5, SDE_INTR_INTF_5_UNDERRUN, -1}, + { SDE_IRQ_TYPE_INTF_VSYNC, INTF_5, SDE_INTR_INTF_5_VSYNC, -1}, + { SDE_IRQ_TYPE_INTF_UNDER_RUN, INTF_6, SDE_INTR_INTF_6_UNDERRUN, -1}, + { SDE_IRQ_TYPE_INTF_VSYNC, INTF_6, SDE_INTR_INTF_6_VSYNC, -1}, }; static struct sde_irq_type sde_irq_intr2_map[] = { diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 9f7a815c9227..308200bd8579 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1776,6 +1776,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .set_allowed_mode_switch = NULL, }; static const struct sde_connector_ops dp_ops = { + .set_info_blob = dp_connector_set_info_blob, .post_init = dp_connector_post_init, .detect = dp_connector_detect, .get_modes = dp_connector_get_modes, @@ -1925,6 +1926,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, for (i = 0; i < sde_kms->dp_display_count && priv->num_encoders < max_encoders; ++i) { int idx; + struct dp_display_info dp_info = {0}; display = sde_kms->dp_displays[i]; encoder = NULL; @@ -1936,6 +1938,13 @@ static int _sde_kms_setup_displays(struct drm_device *dev, continue; } + rc = dp_display_get_info(display, &dp_info); + if (rc) { + SDE_ERROR("failed to read dp info, %d\n", rc); + continue; + } + + info.h_tile_instance[0] = dp_info.intf_idx[0]; encoder = sde_encoder_init(dev, &info); if (IS_ERR_OR_NULL(encoder)) { SDE_ERROR("dp encoder init failed %d\n", i); @@ -1956,7 +1965,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, display, &dp_ops, DRM_CONNECTOR_POLL_HPD, - DRM_MODE_CONNECTOR_DisplayPort); + info.intf_type); if (connector) { priv->encoders[priv->num_encoders++] = encoder; priv->connectors[priv->num_connectors++] = connector; @@ -1969,9 +1978,9 @@ static int _sde_kms_setup_displays(struct drm_device *dev, /* update display cap to MST_MODE for DP MST encoders */ info.capabilities |= MSM_DISPLAY_CAP_MST_MODE; - for (idx = 0; idx < sde_kms->dp_stream_count && + for (idx = 0; idx < dp_info.stream_cnt && priv->num_encoders < max_encoders; idx++) { - info.h_tile_instance[0] = idx; + info.h_tile_instance[0] = dp_info.intf_idx[idx]; encoder = sde_encoder_init(dev, &info); if (IS_ERR_OR_NULL(encoder)) { SDE_ERROR("dp mst encoder init failed %d\n", i);