From 7c1c98ef05f5c6ed06cc5ddd86eb5089e1c9f1f1 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 6 Jan 2023 17:39:28 +0530 Subject: [PATCH 01/12] disp: msm: dp: add files for 7nm eDP PHY Add new files to support 7nm eDP PHY. Change-Id: I2b2f119bfab3c9d5e5848235d8b87556cbf514b9 Signed-off-by: Nilesh Laad --- msm/dp/dp_catalog_v500.c | 476 ++++++++++++++++++++ msm/dp/edp_pll_7nm.c | 922 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1398 insertions(+) create mode 100644 msm/dp/dp_catalog_v500.c create mode 100644 msm/dp/edp_pll_7nm.c diff --git a/msm/dp/dp_catalog_v500.c b/msm/dp/dp_catalog_v500.c new file mode 100644 index 000000000000..217c4b7f9f1b --- /dev/null +++ b/msm/dp/dp_catalog_v500.c @@ -0,0 +1,476 @@ +// 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 ldo_config[TX_DRIVE_MODE_MAX] = { + 0x81, /* 600mV */ + 0x00, /* off */ + 0x41, /* 650mV */ + 0x00, /* off */ + 0x00, /* off */ + 0x00, /* off */ +}; + +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 + {0x27, 0x2F, 0x36, 0xFF}, /* sw0, 0.4v */ + {0x31, 0x3E, 0x3F, 0xFF}, /* sw1, 0.6v */ + {0x3A, 0x3F, 0xFF, 0xFF}, /* sw2, 0.8v */ + {0xFF, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */ + }, + { // MiniDP-only + {0x09, 0x17, 0x1F, 0xFF}, /* sw0, 0.4v */ + {0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6v */ + {0x1C, 0x1F, 0xFF, 0xFF}, /* sw2, 0.8v */ + {0xFF, 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); + if (revision_id > 0x5000) + dp_write(DP_PHY_MODE_V500, 0xfc); + else + dp_write(DP_PHY_MODE, 0xfc); + + dp_write(DP_PHY_PD_CTL_V500, 0x7d); + 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 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 = ldo_config[index]; + + /* program default setting first */ + io_data = catalog->io->dp_ln_tx0; + dp_write(TXn_LDO_CONFIG_V500, 0x01); + dp_write(TXn_TX_DRV_LVL_V500, 0x17); + dp_write(TXn_TX_EMP_POST1_LVL_V500, 0x00); + + io_data = catalog->io->dp_ln_tx1; + dp_write(TXn_LDO_CONFIG_V500, 0x01); + dp_write(TXn_TX_DRV_LVL_V500, 0x2A); + dp_write(TXn_TX_EMP_POST1_LVL_V500, 0x20); + + /* 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/edp_pll_7nm.c b/msm/dp/edp_pll_7nm.c new file mode 100644 index 000000000000..13e15c82035a --- /dev/null +++ b/msm/dp/edp_pll_7nm.c @@ -0,0 +1,922 @@ +// 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 = 0x02; + pdb->lock_cmp_en = 0x04; + 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); + dp_pll_write(dp_phy, DP_PHY_MODE, 0xfc); + /* 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, 0x07); + 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); + /* 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); + /* 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_TX_POL_INV, 0x00); + dp_pll_write(dp_ln_tx1, TXn_HIGHZ_DRVR_EN, drvr_en1); + 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(); + + dp_pll_write(dp_ln_tx0, TXn_TRANSCEIVER_BIAS_EN, + bias_en0); + dp_pll_write(dp_ln_tx1, TXn_TRANSCEIVER_BIAS_EN, + bias_en1); + 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 = "_phy_pll_link_clk", + .ops = &edp_pll_link_clk_ops, + }, + { + .name = "_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%s", pll->name, 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; +} From 6c72a979817a773b3846ca038b40322ba164b105 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Wed, 18 Jan 2023 13:19:02 +0530 Subject: [PATCH 02/12] disp: msm: dp: add 7nm eDP PHY support Add 7nm eDP PHY support to enable eDP. Change-Id: If04d962d964e58029f012f5ff72c632dabac602a Signed-off-by: Nilesh Laad --- msm/Makefile | 4 ++- msm/dp/dp_catalog.c | 5 +++ msm/dp/dp_catalog.h | 4 +++ msm/dp/dp_ctrl.c | 28 +++++++++++++++ msm/dp/dp_ctrl.h | 2 ++ msm/dp/dp_display.c | 2 ++ msm/dp/dp_parser.c | 23 +++++++++++++ msm/dp/dp_parser.h | 22 ++++++++++++ msm/dp/dp_pll.c | 7 ++++ msm/dp/dp_pll.h | 84 +++++++++++++++++++++++++++++---------------- msm/dp/dp_reg.h | 23 +++++++++++++ 11 files changed, 173 insertions(+), 31 deletions(-) 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..8846765db390 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. */ @@ -2751,6 +2752,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; } diff --git a/msm/dp/dp_catalog.h b/msm/dp/dp_catalog.h index f73a1713ff39..fb4f6fa1ff9f 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. */ @@ -336,4 +337,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_ctrl.c b/msm/dp/dp_ctrl.c index ad08e9019700..3a4f2badba58 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; @@ -642,6 +645,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 +672,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 +1502,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_display.c b/msm/dp/dp_display.c index 3e0fdbd26fce..209c278520da 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. */ @@ -1978,6 +1979,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)) { diff --git a/msm/dp/dp_parser.c b/msm/dp/dp_parser.c index b94133b5e04c..ab0d0ccafdc5 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. */ @@ -690,6 +691,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 +699,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; } diff --git a/msm/dp/dp_parser.h b/msm/dp/dp_parser.h index 678185705ff6..e2b5bb37029d 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. */ @@ -131,15 +132,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 +168,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) diff --git a/msm/dp/dp_pll.c b/msm/dp/dp_pll.c index b0ffe2f83e0b..2c1f6a8b9e50 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; 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_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) From dfce8892e1c4b10f2596611ef3b542d8e8ed5c99 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 31 Mar 2023 17:44:47 +0530 Subject: [PATCH 03/12] disp: msm: dp: add pixel base offset support in device tree Parse pixel base offset from "qcom,pixel-base-off" node in device tree to differentiate pixel base offset in different hardwares. Change-Id: Ia8eac1a7c3dba3eaff6b88cd7f6a467f288cdf4b Signed-off-by: Xiaowen Wu Signed-off-by: Soutrik Mukhopadhyay Signed-off-by: Nilesh Laad --- msm/dp/dp_catalog.c | 1 + msm/dp/dp_catalog.h | 1 + msm/dp/dp_parser.c | 6 ++++++ msm/dp/dp_parser.h | 1 + 4 files changed, 9 insertions(+) diff --git a/msm/dp/dp_catalog.c b/msm/dp/dp_catalog.c index 8846765db390..36683b1223ba 100644 --- a/msm/dp/dp_catalog.c +++ b/msm/dp/dp_catalog.c @@ -2886,6 +2886,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 fb4f6fa1ff9f..8ee97890b92b 100644 --- a/msm/dp/dp_catalog.h +++ b/msm/dp/dp_catalog.h @@ -257,6 +257,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, diff --git a/msm/dp/dp_parser.c b/msm/dp/dp_parser.c index ab0d0ccafdc5..6938430283ed 100644 --- a/msm/dp/dp_parser.c +++ b/msm/dp/dp_parser.c @@ -167,6 +167,12 @@ 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]); + } + return 0; } diff --git a/msm/dp/dp_parser.h b/msm/dp/dp_parser.h index e2b5bb37029d..0ae02ec0a100 100644 --- a/msm/dp/dp_parser.h +++ b/msm/dp/dp_parser.h @@ -249,6 +249,7 @@ 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]; int (*parse)(struct dp_parser *parser); struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name); From 4eb2953032db40ded060ecbd1e28e5894c8f65a9 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 24 Mar 2023 16:55:28 +0530 Subject: [PATCH 04/12] disp: msm: dp: Support DP display as primary Changes to support DP display as primary. The connector should indicate the display type as primary to the drm library. Change-Id: Ib5f6d9d72e8c42f88d38e06ed504848b8fc52029 Signed-off-by: Xiaowen Wu Signed-off-by: Soutrik Mukhopadhyay Signed-off-by: Sankeerth Billakanti Signed-off-by: Nilesh Laad --- msm/dp/dp_display.c | 43 +++++++++++++++++++++++++++++++++++++++++++ msm/dp/dp_display.h | 5 +++++ msm/dp/dp_drm.c | 13 +++++++++++++ msm/dp/dp_drm.h | 19 +++++++++++++++++++ msm/dp/dp_mst_drm.c | 26 ++++++++++++++++++++++++++ msm/dp/dp_parser.c | 10 ++++++++++ msm/dp/dp_parser.h | 4 ++++ msm/sde/sde_kms.c | 1 + 8 files changed, 121 insertions(+) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 209c278520da..ee48229f19df 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -3552,6 +3552,46 @@ 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_probe(struct platform_device *pdev) { int rc = 0; @@ -3629,6 +3669,9 @@ static int dp_display_probe(struct platform_device *pdev) g_dp_display->set_colorspace = dp_display_setup_colospace; g_dp_display->get_available_dp_resources = dp_display_get_available_dp_resources; + g_dp_display->get_display_type = dp_display_get_display_type; + g_dp_display->mst_get_fixed_topology_display_type = + dp_display_mst_get_fixed_topology_display_type; rc = component_add(&pdev->dev, &dp_display_comp_ops); if (rc) { diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index eda860c74c11..de5daae9521b 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. */ @@ -132,6 +133,10 @@ 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); }; #if IS_ENABLED(CONFIG_DRM_MSM_DP) diff --git a/msm/dp/dp_drm.c b/msm/dp/dp_drm.c index 3c29801249c3..60e9cc418ca2 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. */ @@ -594,6 +595,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_mst_drm.c b/msm/dp/dp_mst_drm.c index e1ccf47b3881..1a295edeee3b 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. */ @@ -2130,11 +2131,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, diff --git a/msm/dp/dp_parser.c b/msm/dp/dp_parser.c index 6938430283ed..87adcd2e1088 100644 --- a/msm/dp/dp_parser.c +++ b/msm/dp/dp_parser.c @@ -173,6 +173,10 @@ static int dp_parser_misc(struct dp_parser *parser) &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; } @@ -744,6 +748,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 0ae02ec0a100..cdeb85f422c2 100644 --- a/msm/dp/dp_parser.h +++ b/msm/dp/dp_parser.h @@ -224,6 +224,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; @@ -250,6 +252,8 @@ struct dp_parser { 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/sde/sde_kms.c b/msm/sde/sde_kms.c index 9f7a815c9227..95ee152f691f 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, From 372aa23ceea45131298b92ca182935a1b85a7d9e Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 24 Mar 2023 17:04:41 +0530 Subject: [PATCH 05/12] disp: msm: dp: add eDP support as a primary display Add support for new eDP interface. As the protocol is similar to DP, the new eDP driver shall re-use the DP functionality wherever applicable. Change-Id: I0bcaa2bf2c401cbfb3470ecf232bf6643fb43a7d Signed-off-by: Sankeerth Billakanti Signed-off-by: Nilesh Laad --- msm/dp/dp_display.c | 27 ++++++++++++++++++++++++++- msm/dp/dp_display.h | 1 + msm/dp/dp_drm.c | 11 +++++++++-- msm/sde/sde_encoder.c | 5 ++++- msm/sde/sde_kms.c | 2 +- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index ee48229f19df..844c2174ff95 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -68,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]; @@ -203,8 +207,19 @@ struct dp_display_private { struct notifier_block usb_nb; }; +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,}, {} }; @@ -3596,6 +3611,8 @@ 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; if (!pdev || !pdev->dev.of_node) { DP_ERR("pdev not found\n"); @@ -3603,6 +3620,12 @@ 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; + dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL); if (!dp) { rc = -ENOMEM; @@ -3673,6 +3696,8 @@ static int dp_display_probe(struct platform_device *pdev) g_dp_display->mst_get_fixed_topology_display_type = dp_display_mst_get_fixed_topology_display_type; + g_dp_display->is_edp = (info->display_type == DRM_MODE_CONNECTOR_eDP) ? true : false; + rc = component_add(&pdev->dev, &dp_display_comp_ops); if (rc) { DP_ERR("component add failed, rc=%d\n", rc); diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index de5daae9521b..ae2285becc60 100644 --- a/msm/dp/dp_display.h +++ b/msm/dp/dp_display.h @@ -71,6 +71,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; diff --git a/msm/dp/dp_drm.c b/msm/dp/dp_drm.c index 60e9cc418ca2..95d2a834ebe7 100644 --- a/msm/dp/dp_drm.c +++ b/msm/dp/dp_drm.c @@ -457,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; } 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_kms.c b/msm/sde/sde_kms.c index 95ee152f691f..4eea06bff77e 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1957,7 +1957,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; From fc6b8eb5d0658740e6d97961d2e6ff4853acf77a Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 24 Mar 2023 17:08:44 +0530 Subject: [PATCH 06/12] disp: msm: dp: add support for eDP display Changes to support eDP display detection and bringup through panel power sequencing. Change-Id: Ie20906467c209872296e8b5475cee0ca98a3b232 Signed-off-by: Soutrik Mukhopadhyay Signed-off-by: Nilesh Laad --- msm/dp/dp_catalog.c | 29 ++++++++ msm/dp/dp_catalog.h | 1 + msm/dp/dp_display.c | 174 +++++++++++++++++++++++++++++++++++++++++-- msm/dp/dp_display.h | 2 + msm/dp/dp_drm.c | 12 ++- msm/dp/dp_lphw_hpd.h | 2 + msm/dp/dp_parser.c | 9 ++- msm/dp/dp_parser.h | 14 ++++ msm/dp/dp_power.c | 36 ++++++++- msm/dp/dp_power.h | 3 + 10 files changed, 269 insertions(+), 13 deletions(-) diff --git a/msm/dp/dp_catalog.c b/msm/dp/dp_catalog.c index 36683b1223ba..e14a10bf8f52 100644 --- a/msm/dp/dp_catalog.c +++ b/msm/dp/dp_catalog.c @@ -11,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) @@ -2342,6 +2344,32 @@ static u32 dp_catalog_hpd_get_interrupt(struct dp_catalog_hpd *hpd) return isr; } +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) { struct dp_catalog_private *catalog; @@ -2836,6 +2864,7 @@ 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, }; struct dp_catalog_audio audio = { .init = dp_catalog_audio_init, diff --git a/msm/dp/dp_catalog.h b/msm/dp/dp_catalog.h index 8ee97890b92b..5ddfba2d8770 100644 --- a/msm/dp/dp_catalog.h +++ b/msm/dp/dp_catalog.h @@ -108,6 +108,7 @@ struct dp_catalog_ctrl { struct dp_catalog_hpd { 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); }; #define HEADER_BYTE_2_BIT 0 diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 844c2174ff95..df1535d0dbf2 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1069,6 +1069,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; @@ -1106,7 +1132,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 */ @@ -1220,6 +1245,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; @@ -2164,6 +2191,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"); @@ -2243,6 +2271,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; @@ -2259,7 +2294,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; @@ -2381,6 +2416,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: @@ -2400,6 +2452,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"); @@ -2438,6 +2491,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); @@ -2491,6 +2552,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) && @@ -2539,7 +2608,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; @@ -2566,6 +2635,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); @@ -2634,6 +2713,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"); @@ -2671,6 +2751,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; @@ -2689,18 +2776,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", @@ -3607,6 +3701,71 @@ static int dp_display_mst_get_fixed_topology_display_type( 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; @@ -3697,6 +3856,7 @@ static int dp_display_probe(struct platform_device *pdev) dp_display_mst_get_fixed_topology_display_type; g_dp_display->is_edp = (info->display_type == DRM_MODE_CONNECTOR_eDP) ? true : false; + g_dp_display->edp_detect = dp_display_edp_detect; rc = component_add(&pdev->dev, &dp_display_comp_ops); if (rc) { diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index ae2285becc60..60eab0f4ee31 100644 --- a/msm/dp/dp_display.h +++ b/msm/dp/dp_display.h @@ -12,6 +12,7 @@ #include #include "dp_panel.h" +#include "dp_parser.h" #define DP_MST_SIM_MAX_PORTS 8 @@ -138,6 +139,7 @@ struct 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) diff --git a/msm/dp/dp_drm.c b/msm/dp/dp_drm.c index 95d2a834ebe7..6e054af5dc3d 100644 --- a/msm/dp/dp_drm.c +++ b/msm/dp/dp_drm.c @@ -476,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); @@ -489,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; 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_parser.c b/msm/dp/dp_parser.c index 87adcd2e1088..1a18d69ed070 100644 --- a/msm/dp/dp_parser.c +++ b/msm/dp/dp_parser.c @@ -258,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; diff --git a/msm/dp/dp_parser.h b/msm/dp/dp_parser.h index cdeb85f422c2..4404930d255a 100644 --- a/msm/dp/dp_parser.h +++ b/msm/dp/dp_parser.h @@ -26,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) { diff --git a/msm/dp/dp_power.c b/msm/dp/dp_power.c index ebafae298b86..dd32941f84a7 100644 --- a/msm/dp/dp_power.c +++ b/msm/dp/dp_power.c @@ -388,6 +388,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 +411,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 +434,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 +691,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 +753,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); }; /** From 5ca6a20f2a3a240087049b0e0dba0382e029d366 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 24 Mar 2023 18:18:10 +0530 Subject: [PATCH 07/12] disp: msm: dp: Masking interrupt for eDP Changes to mask unplug, replug and plug based interrupts for eDP. Change-Id: I4ffaff67b98b647e5dbed6913b9851588d9dd5cc Signed-off-by: Soutrik Mukhopadhyay Signed-off-by: Nilesh Laad --- msm/dp/dp_catalog.c | 27 +++++++++++++++++++++++---- msm/dp/dp_catalog.h | 3 +++ msm/dp/dp_display.c | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/msm/dp/dp_catalog.c b/msm/dp/dp_catalog.c index e14a10bf8f52..f24d61d2ce6e 100644 --- a/msm/dp/dp_catalog.c +++ b/msm/dp/dp_catalog.c @@ -2287,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; @@ -2303,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); @@ -2326,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; @@ -2341,7 +2357,9 @@ 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) @@ -2865,6 +2883,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) .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, diff --git a/msm/dp/dp_catalog.h b/msm/dp/dp_catalog.h index 5ddfba2d8770..8ea9e87386f8 100644 --- a/msm/dp/dp_catalog.h +++ b/msm/dp/dp_catalog.h @@ -106,9 +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 diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index df1535d0dbf2..c03a0af9d06f 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1951,6 +1951,7 @@ 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 = dp_aux_get(dev, &dp->catalog->aux, dp->parser, dp->aux_switch_node); if (IS_ERR(dp->aux)) { From e7d707464f44b57ca54bcace791f23fae24ef011 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 24 Mar 2023 18:25:16 +0530 Subject: [PATCH 08/12] disp: msm: dp: Convert clock operations to byte2 ops Convert clock operation to byte2 ops to meet DISPCC requirement. Clock unit is changed from KHZ to HZ. Added link clock parent as freq table is no longer supported in byte2 ops. Change-Id: Iefeca1ecf7fb6335c45f7664a661d1bbe2d6f1e5 Signed-off-by: Xiaowen Wu Signed-off-by: Soutrik Mukhopadhyay Signed-off-by: Nilesh Laad --- msm/dp/dp_ctrl.c | 8 ++++++++ msm/dp/dp_power.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/msm/dp/dp_ctrl.c b/msm/dp/dp_ctrl.c index 3a4f2badba58..395d5a7e1cc9 100644 --- a/msm/dp/dp_ctrl.c +++ b/msm/dp/dp_ctrl.c @@ -621,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--; diff --git a/msm/dp/dp_power.c b/msm/dp/dp_power.c index dd32941f84a7..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", From 21b70e44d7168a0f827d208559d7dee5aa1f1abe Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 27 Jan 2023 09:17:20 +0530 Subject: [PATCH 09/12] disp: msm: dp: update pll and catalog sequence Update eDP pll and catalog sequence. Change-Id: Ic7643cdd9d68e28ea7999ebc9d2c1e87aaa8c9d6 Signed-off-by: Nilesh Laad --- msm/dp/dp_catalog_v500.c | 58 +++++++++++++++--------------------- msm/dp/edp_pll_7nm.c | 63 ++++++++++++++++++++++------------------ 2 files changed, 58 insertions(+), 63 deletions(-) diff --git a/msm/dp/dp_catalog_v500.c b/msm/dp/dp_catalog_v500.c index 217c4b7f9f1b..507c5bc31f48 100644 --- a/msm/dp/dp_catalog_v500.c +++ b/msm/dp/dp_catalog_v500.c @@ -40,15 +40,6 @@ enum { TX_DRIVE_MODE_MAX, }; -static u8 const ldo_config[TX_DRIVE_MODE_MAX] = { - 0x81, /* 600mV */ - 0x00, /* off */ - 0x41, /* 650mV */ - 0x00, /* off */ - 0x00, /* off */ - 0x00, /* off */ -}; - 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 */ @@ -119,16 +110,16 @@ static u8 const vm_voltage_swing {0x17, 0xFF, 0xFF, 0xFF} /* sw1, 0.45 v */ }, { // DP-only, DP/USB - {0x27, 0x2F, 0x36, 0xFF}, /* sw0, 0.4v */ - {0x31, 0x3E, 0x3F, 0xFF}, /* sw1, 0.6v */ - {0x3A, 0x3F, 0xFF, 0xFF}, /* sw2, 0.8v */ - {0xFF, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */ + {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 - {0x09, 0x17, 0x1F, 0xFF}, /* sw0, 0.4v */ - {0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6v */ - {0x1C, 0x1F, 0xFF, 0xFF}, /* sw2, 0.8v */ - {0xFF, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */ + {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 */ }, }; @@ -162,12 +153,8 @@ static void dp_catalog_aux_setup_v500(struct dp_catalog_aux *aux, 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); - if (revision_id > 0x5000) - dp_write(DP_PHY_MODE_V500, 0xfc); - else - dp_write(DP_PHY_MODE, 0xfc); - dp_write(DP_PHY_PD_CTL_V500, 0x7d); + dp_write(DP_PHY_PD_CTL_V500, 0x65); wmb(); /* make sure PD programming happened */ /* Turn on BIAS current for PHY/PLL */ @@ -176,6 +163,20 @@ static void dp_catalog_aux_setup_v500(struct dp_catalog_aux *aux, 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++) { @@ -353,18 +354,7 @@ static void dp_catalog_ctrl_update_vx_px_v500(struct dp_catalog_ctrl *ctrl, value0 = vm_voltage_swing[index][v_level][p_level]; value1 = vm_pre_emphasis[index][v_level][p_level]; - ldo_cfg = ldo_config[index]; - - /* program default setting first */ - io_data = catalog->io->dp_ln_tx0; - dp_write(TXn_LDO_CONFIG_V500, 0x01); - dp_write(TXn_TX_DRV_LVL_V500, 0x17); - dp_write(TXn_TX_EMP_POST1_LVL_V500, 0x00); - - io_data = catalog->io->dp_ln_tx1; - dp_write(TXn_LDO_CONFIG_V500, 0x01); - dp_write(TXn_TX_DRV_LVL_V500, 0x2A); - dp_write(TXn_TX_EMP_POST1_LVL_V500, 0x20); + ldo_cfg = 0x01; /* Configure host and panel only if both values are allowed */ if (value0 != 0xFF && value1 != 0xFF) { diff --git a/msm/dp/edp_pll_7nm.c b/msm/dp/edp_pll_7nm.c index 13e15c82035a..da26cb657884 100644 --- a/msm/dp/edp_pll_7nm.c +++ b/msm/dp/edp_pll_7nm.c @@ -149,8 +149,8 @@ static int edp_vco_pll_init_db_7nm(struct dp_pll_db *pdb, pdb->div_frac_start3_mode0 = 0x07; pdb->lock_cmp1_mode0 = 0x6f; pdb->lock_cmp2_mode0 = 0x08; - pdb->phy_vco_div = 0x02; - pdb->lock_cmp_en = 0x04; + pdb->phy_vco_div = 0x01; + pdb->lock_cmp_en = 0x08; pdb->ssc_step_size1_mode0 = 0x45; pdb->ssc_step_size2_mode0 = 0x06; break; @@ -279,7 +279,6 @@ static int edp_config_vco_rate_7nm(struct dp_pll *pll, } dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x7d); - dp_pll_write(dp_phy, DP_PHY_MODE, 0xfc); /* Make sure the PLL register writes are done */ wmb(); @@ -314,7 +313,7 @@ static int edp_config_vco_rate_7nm(struct dp_pll *pll, 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, 0x07); + 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); @@ -350,6 +349,8 @@ static int edp_config_vco_rate_7nm(struct dp_pll *pll, 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(); @@ -482,6 +483,27 @@ static int edp_pll_enable_7nm(struct dp_pll *pll) } 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(); @@ -506,32 +528,15 @@ static int edp_pll_enable_7nm(struct dp_pll *pll) } dp_pll_write(dp_ln_tx0, TXn_HIGHZ_DRVR_EN, drvr_en0); - dp_pll_write(dp_ln_tx0, TXn_TX_POL_INV, 0x00); + 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_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); + 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_ln_tx0, TXn_TRANSCEIVER_BIAS_EN, - bias_en0); - dp_pll_write(dp_ln_tx1, TXn_TRANSCEIVER_BIAS_EN, - bias_en1); dp_pll_write(dp_phy, DP_PHY_CFG_1, phy_cfg_1); if (!edp_7nm_pll_get_status(pll, PHY_READY)) { @@ -771,7 +776,7 @@ static long edp_pll_vco_div_clk_round(struct clk_hw *hw, unsigned long rate, } static int edp_pll_vco_div_clk_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) + unsigned long parent_rate) { struct dp_pll *pll = NULL; struct dp_pll_vco_clk *pll_link = NULL; @@ -815,11 +820,11 @@ static const struct clk_ops edp_pll_vco_div_clk_ops = { static struct clk_init_data edp_phy_pll_clks[DP_PLL_NUM_CLKS] = { { - .name = "_phy_pll_link_clk", + .name = "edp_phy_pll_link_clk", .ops = &edp_pll_link_clk_ops, }, { - .name = "_phy_pll_vco_div_clk", + .name = "edp_phy_pll_vco_div_clk", .ops = &edp_pll_vco_div_clk_ops, }, }; @@ -830,7 +835,7 @@ static struct dp_pll_vco_clk *edp_pll_get_clks(struct dp_pll *pll) for (i = 0; i < DP_PLL_NUM_CLKS; i++) { snprintf(pll->pll_clks[i].name, DP_PLL_NAME_MAX_SIZE, - "%s%s", pll->name, edp_phy_pll_clks[i].name); + "%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; From 1f5c327e1b8d147394f2f2d54d86a0c9990da544 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Thu, 9 Feb 2023 18:24:17 +0530 Subject: [PATCH 10/12] disp: msm: sde: update sde interrupt map Make changes to update sde interrupt map. Change-Id: I2f525c1028dcfe4c20d7e168c0afbbe5583e0b2f Signed-off-by: Nilesh Laad --- msm/sde/sde_hw_interrupts.c | 41 +++++++++++++------------------------ 1 file changed, 14 insertions(+), 27 deletions(-) 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[] = { From 4e9b8a8edad15f9dd05b4feb1cba377d0179af99 Mon Sep 17 00:00:00 2001 From: Vara Reddy Date: Tue, 24 Aug 2021 14:16:36 -0700 Subject: [PATCH 11/12] disp: msm: dp: move fsa init from dp probe to dp hotplug DP driver, at probe time, checks for fsa probe completion by registering a notifier callback. The fsa driver performs some I2C operations at this time. But occasionally, it takes multiple attempts to complete these I2C transactions,adding huge delays to display driver probing.If this delay is long enough, then display usermode services start before display driver probe completes and as a result, it fails to enumerate any displays. Since the fsa switch is needed only after an external display is hot plugged,this change moves the fsa probe check to dp hotplug handler. Change-Id: I1b592ec3921a0b406ca23142d07e1a7e8b72090e Signed-off-by: Vara Reddy --- msm/dp/dp_display.c | 107 ++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index c03a0af9d06f..60fd5111eef3 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -167,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; @@ -1379,6 +1380,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; @@ -1396,7 +1445,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; @@ -1909,6 +1962,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 = { @@ -1952,6 +2006,9 @@ static int dp_init_sub_modules(struct dp_display_private *dp) } 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)) { @@ -3194,48 +3251,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) { @@ -3800,12 +3815,6 @@ 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; - goto error; - } - rc = dp_display_create_workqueue(dp); if (rc) { DP_ERR("Failed to create workqueue\n"); From cfaa8dfa202c8aece051aa34b01e5d0027a40eac Mon Sep 17 00:00:00 2001 From: Xiaowen Wu Date: Tue, 19 Nov 2019 19:14:30 -0500 Subject: [PATCH 12/12] drm/msm/dp: add support to multiple dp instances Add support to multiple dp isntances. Clean up dp-mst global state. Change-Id: Iaa88ab8c339fa0e247656024b289faa087ea0fbd Signed-off-by: Xiaowen Wu Signed-off-by: Soutrik Mukhopadhyay --- msm/dp/dp_debug.c | 76 ++++++++-------- msm/dp/dp_display.c | 206 +++++++++++++++++++++++++++++++------------- msm/dp/dp_display.h | 13 +++ msm/dp/dp_mst_drm.c | 77 ++++++----------- msm/dp/dp_pll.c | 4 + msm/sde/sde_kms.c | 12 ++- 6 files changed, 241 insertions(+), 147 deletions(-) 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 60fd5111eef3..afa73fa3e013 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -133,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 { @@ -206,6 +206,11 @@ 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 = { @@ -758,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) @@ -1994,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)) { @@ -3788,6 +3819,8 @@ static int dp_display_probe(struct platform_device *pdev) 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"); @@ -3801,6 +3834,13 @@ static int dp_display_probe(struct platform_device *pdev) 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; @@ -3815,6 +3855,10 @@ static int dp_display_probe(struct platform_device *pdev) memset(&dp->mst, 0, sizeof(dp->mst)); + rc = dp_display_get_cell_info(dp); + if (rc) + goto error; + rc = dp_display_create_workqueue(dp); if (rc) { DP_ERR("Failed to create workqueue\n"); @@ -3823,50 +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; - g_dp_display->get_display_type = dp_display_get_display_type; - g_dp_display->mst_get_fixed_topology_display_type = + 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; - g_dp_display->is_edp = (info->display_type == DRM_MODE_CONNECTOR_eDP) ? true : false; - g_dp_display->edp_detect = dp_display_edp_detect; + 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) { @@ -3883,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, @@ -3915,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) @@ -3949,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 @@ -3979,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 60eab0f4ee31..892322fa2fc2 100644 --- a/msm/dp/dp_display.h +++ b/msm/dp/dp_display.h @@ -15,6 +15,7 @@ #include "dp_parser.h" #define DP_MST_SIM_MAX_PORTS 8 +#define MAX_DP_ACTIVE_DISPLAY 8 enum dp_drv_state { PM_DEFAULT, @@ -31,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); @@ -146,6 +154,7 @@ struct dp_display { 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) { @@ -159,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_mst_drm.c b/msm/dp/dp_mst_drm.c index 1a295edeee3b..291c1207c08a 100644 --- a/msm/dp/dp_mst_drm.c +++ b/msm/dp/dp_mst_drm.c @@ -159,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) @@ -172,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, @@ -299,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; /* @@ -1259,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]; @@ -2415,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"); @@ -2427,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; } @@ -2502,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_pll.c b/msm/dp/dp_pll.c index 2c1f6a8b9e50..19d982284407 100644 --- a/msm/dp/dp_pll.c +++ b/msm/dp/dp_pll.c @@ -124,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/sde/sde_kms.c b/msm/sde/sde_kms.c index 4eea06bff77e..308200bd8579 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1926,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; @@ -1937,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); @@ -1970,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);