From 7c1c98ef05f5c6ed06cc5ddd86eb5089e1c9f1f1 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 6 Jan 2023 17:39:28 +0530 Subject: [PATCH 01/23] 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 0000000000000..217c4b7f9f1b0 --- /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 0000000000000..13e15c82035a9 --- /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/23] 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 9c7df05e71b33..1e4e0f35fb78a 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 b3b7c59f02a84..8846765db390b 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 f73a1713ff393..fb4f6fa1ff9f6 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 ad08e90197007..3a4f2badba584 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 940aa1e8c3e30..225a4bf54dafe 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 3e0fdbd26fcea..209c278520dad 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 b94133b5e04ce..ab0d0ccafdc5c 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 678185705ff64..e2b5bb37029dc 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 b0ffe2f83e0b1..2c1f6a8b9e50f 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 37e0d10fc1f06..fa56964ab5338 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 b367a8a070c0d..fa9247cc37f37 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/23] 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 8846765db390b..36683b1223baf 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 fb4f6fa1ff9f6..8ee97890b92b3 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 ab0d0ccafdc5c..6938430283ed5 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 e2b5bb37029dc..0ae02ec0a100c 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/23] 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 209c278520dad..ee48229f19dff 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 eda860c74c11e..de5daae9521b8 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 3c29801249c36..60e9cc418ca2f 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 5d7462cd20f82..647705c2706d4 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 e1ccf47b3881c..1a295edeee3ba 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 6938430283ed5..87adcd2e10880 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 0ae02ec0a100c..cdeb85f422c25 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 9f7a815c9227d..95ee152f691f5 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/23] 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 ee48229f19dff..844c2174ff95e 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 de5daae9521b8..ae2285becc606 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 60e9cc418ca2f..95d2a834ebe7c 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 3eeb518c1a63f..d2c54c99ab142 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 95ee152f691f5..4eea06bff77ed 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/23] 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 36683b1223baf..e14a10bf8f525 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 8ee97890b92b3..5ddfba2d8770a 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 844c2174ff95e..df1535d0dbf27 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 ae2285becc606..60eab0f4ee317 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 95d2a834ebe7c..6e054af5dc3d5 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 9779331bd295e..095302e2da840 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 87adcd2e10880..1a18d69ed070c 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 cdeb85f422c25..4404930d255ab 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 ebafae298b86c..dd32941f84a7e 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 7a0302356de1e..d605c2801fbcb 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/23] 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 e14a10bf8f525..f24d61d2ce6e4 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 5ddfba2d8770a..8ea9e87386f81 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 df1535d0dbf27..c03a0af9d06f1 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/23] 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 3a4f2badba584..395d5a7e1cc9d 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 dd32941f84a7e..254f32c36b53d 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/23] 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 217c4b7f9f1b0..507c5bc31f483 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 13e15c82035a9..da26cb657884f 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/23] 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 81fddec0dce7f..2b3b7e164ccaf 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/23] 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 c03a0af9d06f1..60fd5111eef3f 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/23] 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 307eb471cde8d..bd4d4ca048445 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 60fd5111eef3f..afa73fa3e0137 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 60eab0f4ee317..892322fa2fc20 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 1a295edeee3ba..291c1207c08af 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 2c1f6a8b9e50f..19d982284407b 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 4eea06bff77ed..308200bd8579c 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); From 0074d1108bba296d6aa1dc254fde6c2fbeda2797 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Wed, 19 Apr 2023 20:51:32 +0530 Subject: [PATCH 13/23] disp: msm: dp: add pinctrl state for backlight pwm Add pinctrl state for backlight pwm to apply its settings. Change-Id: I0de1edc0a1bbd43057de77e1f26307f0de33f29f Signed-off-by: Nilesh Laad --- msm/dp/dp_display.c | 38 ++++++++++++++++++++++++++++++++++++++ msm/dp/dp_parser.h | 2 ++ 2 files changed, 40 insertions(+) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index afa73fa3e0137..d67f9f3c83c48 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -2466,10 +2466,30 @@ static void dp_display_update_dsc_resources(struct dp_display_private *dp, } } +static int dp_display_set_pinctrl_state(struct dp_pinctrl *pinctrl, + struct pinctrl_state *state) +{ + int rc = 0; + + if (pinctrl && !IS_ERR_OR_NULL(state)) { + rc = pinctrl_select_state(pinctrl->pin, state); + if (rc) { + DP_ERR("failed to set pin state, rc=%d\n", rc); + } + } else { + rc = -EINVAL; + DP_DEBUG("pinctrl state does not exist\n"); + } + + return rc; +} + static int dp_display_enable(struct dp_display *dp_display, void *panel) { int rc = 0; struct dp_display_private *dp; + struct dp_parser *parser; + struct dp_pinctrl *pinctrl; if (!dp_display || !panel) { DP_ERR("invalid input\n"); @@ -2520,6 +2540,24 @@ static int dp_display_enable(struct dp_display *dp_display, void *panel) DP_ERR("Cannot turn edp PWM on "); goto end; } + + parser = dp->parser; + if (!parser) { + DP_ERR("failed to get parser"); + } else { + pinctrl = &parser->pinctrl; + pinctrl->pin = devm_pinctrl_get(&dp->parser->pdev->dev); + + if (!IS_ERR_OR_NULL(pinctrl->pin)) { + pinctrl->state_bl_pwm = pinctrl_lookup_state(pinctrl->pin, + "mdss_edp_bl_pwm"); + rc = dp_display_set_pinctrl_state(pinctrl, pinctrl->state_bl_pwm); + if (rc) + DP_DEBUG("failed to set pinctrl, rc=%d\n", rc); + } else { + DP_DEBUG("failed to get pinctrl"); + } + } } dp_display_update_dsc_resources(dp, panel, true); diff --git a/msm/dp/dp_parser.h b/msm/dp/dp_parser.h index 4404930d255ab..f23b195947fc3 100644 --- a/msm/dp/dp_parser.h +++ b/msm/dp/dp_parser.h @@ -100,6 +100,7 @@ struct dp_io { * @state_active: active state pin control * @state_hpd_active: hpd active state pin control * @state_suspend: suspend state pin control + * @state_bl_pwm: backlight pwm pin control */ struct dp_pinctrl { struct pinctrl *pin; @@ -108,6 +109,7 @@ struct dp_pinctrl { struct pinctrl_state *state_hpd_tlmm; struct pinctrl_state *state_hpd_ctrl; struct pinctrl_state *state_suspend; + struct pinctrl_state *state_bl_pwm; }; #define DP_ENUM_STR(x) #x From 30b933f50c9c234fc59d83d5253fab24a74e7cbd Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 28 Apr 2023 15:27:44 +0530 Subject: [PATCH 14/23] disp: msm: dp: add eDP panel notifier support If panel notifier support is set, get panel phandle and add panel to drm_panel. Notifier for this panel will be called on dpms update. Change-Id: I6346eb09a551a993ec9050797d1ba9383491e612 Signed-off-by: Nilesh Laad --- msm/dp/dp_display.c | 2 ++ msm/dp/dp_panel.c | 27 +++++++++++++++++++++++++++ msm/dp/dp_panel.h | 4 ++++ msm/dp/dp_parser.c | 4 ++++ msm/dp/dp_parser.h | 1 + 5 files changed, 38 insertions(+) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index afa73fa3e0137..89301247b79d3 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -2095,6 +2095,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp) panel_in.connector = dp->dp_display.base_connector; panel_in.base_panel = NULL; panel_in.parser = dp->parser; + panel_in.is_edp = dp->dp_display.is_edp; + panel_in.panel_notifier_support = dp->parser->panel_notifier_support; dp->panel = dp_panel_get(&panel_in); if (IS_ERR(dp->panel)) { diff --git a/msm/dp/dp_panel.c b/msm/dp/dp_panel.c index 5369ab30bdb46..6c24c52b00046 100644 --- a/msm/dp/dp_panel.c +++ b/msm/dp/dp_panel.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. */ @@ -3015,6 +3016,8 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) struct dp_panel_private *panel; struct dp_panel *dp_panel; struct sde_connector *sde_conn; + struct device *dev; + struct device_node *of_node = NULL; if (!in->dev || !in->catalog || !in->aux || !in->link || !in->connector) { @@ -3029,6 +3032,13 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) goto error; } + dev = devm_kzalloc(in->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) { + DP_ERR("failed to allocate dev\n"); + rc = -ENOMEM; + goto error; + } + panel->dev = in->dev; panel->aux = in->aux; panel->catalog = in->catalog; @@ -3088,6 +3098,23 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) dp_panel_edid_register(panel); + if (in->is_edp && in->panel_notifier_support) { + of_node = of_parse_phandle(in->dev->of_node, "qcom,edp-default-panel", 0); + if (!of_node) { + DP_ERR("phandle for default panel not found\n"); + } else { + drm_panel_init(&dp_panel->drm_panel); + dp_panel->drm_panel.dev = dev; + dev->of_node = of_node; + + rc = drm_panel_add(&dp_panel->drm_panel); + if (rc) + DP_ERR("Failed to add drm_panel\n"); + + dp_panel->connector->panel = &dp_panel->drm_panel; + } + } + return dp_panel; error: return ERR_PTR(rc); diff --git a/msm/dp/dp_panel.h b/msm/dp/dp_panel.h index f388f2d940634..822393143ab8e 100644 --- a/msm/dp/dp_panel.h +++ b/msm/dp/dp_panel.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. */ @@ -74,6 +75,8 @@ struct dp_panel_in { struct drm_connector *connector; struct dp_panel *base_panel; struct dp_parser *parser; + bool is_edp; + bool panel_notifier_support; }; struct dp_dsc_caps { @@ -95,6 +98,7 @@ struct dp_panel { u8 fec_dpcd; u8 fec_sts_dpcd[DP_RECEIVER_FEC_STATUS_SIZE + 1]; + struct drm_panel drm_panel; struct drm_dp_link link_info; struct sde_edid_ctrl *edid_ctrl; struct dp_panel_info pinfo; diff --git a/msm/dp/dp_parser.c b/msm/dp/dp_parser.c index 1a18d69ed070c..7f5501a136bf4 100644 --- a/msm/dp/dp_parser.c +++ b/msm/dp/dp_parser.c @@ -177,6 +177,10 @@ static int dp_parser_misc(struct dp_parser *parser) if (!parser->display_type) parser->display_type = "unknown"; + parser->panel_notifier_support = of_property_read_bool(of_node, + "qcom,panel-notifier-support"); + DP_DEBUG("panel-notifier-support = %d\n", parser->panel_notifier_support); + return 0; } diff --git a/msm/dp/dp_parser.h b/msm/dp/dp_parser.h index 4404930d255ab..5c6c0bf0410b1 100644 --- a/msm/dp/dp_parser.h +++ b/msm/dp/dp_parser.h @@ -264,6 +264,7 @@ struct dp_parser { bool has_widebus; bool gpio_aux_switch; bool lphw_hpd; + bool panel_notifier_support; 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]; From aab778c6aaf9a42cd410506f8a9c873603992d53 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 28 Apr 2023 15:27:44 +0530 Subject: [PATCH 15/23] disp: msm: dp: add eDP panel notifier support If panel notifier support is set, get panel phandle and add panel to drm_panel. Notifier for this panel will be called on dpms update. Change-Id: I6346eb09a551a993ec9050797d1ba9383491e612 Signed-off-by: Nilesh Laad --- msm/dp/dp_display.c | 2 ++ msm/dp/dp_panel.c | 27 +++++++++++++++++++++++++++ msm/dp/dp_panel.h | 4 ++++ msm/dp/dp_parser.c | 4 ++++ msm/dp/dp_parser.h | 1 + 5 files changed, 38 insertions(+) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index d67f9f3c83c48..11349989c449b 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -2095,6 +2095,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp) panel_in.connector = dp->dp_display.base_connector; panel_in.base_panel = NULL; panel_in.parser = dp->parser; + panel_in.is_edp = dp->dp_display.is_edp; + panel_in.panel_notifier_support = dp->parser->panel_notifier_support; dp->panel = dp_panel_get(&panel_in); if (IS_ERR(dp->panel)) { diff --git a/msm/dp/dp_panel.c b/msm/dp/dp_panel.c index 5369ab30bdb46..6c24c52b00046 100644 --- a/msm/dp/dp_panel.c +++ b/msm/dp/dp_panel.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. */ @@ -3015,6 +3016,8 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) struct dp_panel_private *panel; struct dp_panel *dp_panel; struct sde_connector *sde_conn; + struct device *dev; + struct device_node *of_node = NULL; if (!in->dev || !in->catalog || !in->aux || !in->link || !in->connector) { @@ -3029,6 +3032,13 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) goto error; } + dev = devm_kzalloc(in->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) { + DP_ERR("failed to allocate dev\n"); + rc = -ENOMEM; + goto error; + } + panel->dev = in->dev; panel->aux = in->aux; panel->catalog = in->catalog; @@ -3088,6 +3098,23 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) dp_panel_edid_register(panel); + if (in->is_edp && in->panel_notifier_support) { + of_node = of_parse_phandle(in->dev->of_node, "qcom,edp-default-panel", 0); + if (!of_node) { + DP_ERR("phandle for default panel not found\n"); + } else { + drm_panel_init(&dp_panel->drm_panel); + dp_panel->drm_panel.dev = dev; + dev->of_node = of_node; + + rc = drm_panel_add(&dp_panel->drm_panel); + if (rc) + DP_ERR("Failed to add drm_panel\n"); + + dp_panel->connector->panel = &dp_panel->drm_panel; + } + } + return dp_panel; error: return ERR_PTR(rc); diff --git a/msm/dp/dp_panel.h b/msm/dp/dp_panel.h index f388f2d940634..822393143ab8e 100644 --- a/msm/dp/dp_panel.h +++ b/msm/dp/dp_panel.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. */ @@ -74,6 +75,8 @@ struct dp_panel_in { struct drm_connector *connector; struct dp_panel *base_panel; struct dp_parser *parser; + bool is_edp; + bool panel_notifier_support; }; struct dp_dsc_caps { @@ -95,6 +98,7 @@ struct dp_panel { u8 fec_dpcd; u8 fec_sts_dpcd[DP_RECEIVER_FEC_STATUS_SIZE + 1]; + struct drm_panel drm_panel; struct drm_dp_link link_info; struct sde_edid_ctrl *edid_ctrl; struct dp_panel_info pinfo; diff --git a/msm/dp/dp_parser.c b/msm/dp/dp_parser.c index 1a18d69ed070c..7f5501a136bf4 100644 --- a/msm/dp/dp_parser.c +++ b/msm/dp/dp_parser.c @@ -177,6 +177,10 @@ static int dp_parser_misc(struct dp_parser *parser) if (!parser->display_type) parser->display_type = "unknown"; + parser->panel_notifier_support = of_property_read_bool(of_node, + "qcom,panel-notifier-support"); + DP_DEBUG("panel-notifier-support = %d\n", parser->panel_notifier_support); + return 0; } diff --git a/msm/dp/dp_parser.h b/msm/dp/dp_parser.h index f23b195947fc3..5444ee9e70506 100644 --- a/msm/dp/dp_parser.h +++ b/msm/dp/dp_parser.h @@ -266,6 +266,7 @@ struct dp_parser { bool has_widebus; bool gpio_aux_switch; bool lphw_hpd; + bool panel_notifier_support; 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]; From 888df809220d548ab2adfb7b43ce66160648bb80 Mon Sep 17 00:00:00 2001 From: Sai Srujana Oruganti Date: Mon, 22 May 2023 21:34:58 +0530 Subject: [PATCH 16/23] disp: msm: edp continuous splash implementation Edp continuous splash implementation. Change-Id: I23635b43ce2a41078383bbcc89aa3f60564599dd Signed-off-by: Sai Srujana Oruganti --- msm/dp/dp_aux.c | 9 +++- msm/dp/dp_aux.h | 3 +- msm/dp/dp_ctrl.c | 68 +++++++++++++++++++----------- msm/dp/dp_ctrl.h | 10 ++--- msm/dp/dp_display.c | 99 +++++++++++++++++++++++++++++++++++++++----- msm/dp/dp_display.h | 13 +++++- msm/dp/dp_drm.c | 1 + msm/dp/dp_pll.h | 2 +- msm/dp/edp_pll_7nm.c | 10 +++++ msm/sde/sde_kms.c | 3 +- 10 files changed, 173 insertions(+), 45 deletions(-) diff --git a/msm/dp/dp_aux.c b/msm/dp/dp_aux.c index ae00856bb3da4..7c4fbc2fbb50b 100644 --- a/msm/dp/dp_aux.c +++ b/msm/dp/dp_aux.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. */ @@ -631,7 +632,7 @@ static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg) aux_cfg[i].current_index = 0; } -static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg) +static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg, bool skip_op) { struct dp_aux_private *aux; @@ -645,10 +646,16 @@ static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg) if (aux->enabled) return; + /*skip aux init when cont. splash is enabled*/ + if (skip_op) + goto skip_init; + dp_aux_reset_phy_config_indices(aux_cfg); aux->catalog->setup(aux->catalog, aux_cfg); aux->catalog->reset(aux->catalog); aux->catalog->enable(aux->catalog, true); + +skip_init: atomic_set(&aux->aborted, 0); aux->retry_cnt = 0; aux->enabled = true; diff --git a/msm/dp/dp_aux.h b/msm/dp/dp_aux.h index 850941b1e53d1..aa762e91acf71 100644 --- a/msm/dp/dp_aux.h +++ b/msm/dp/dp_aux.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. */ @@ -47,7 +48,7 @@ struct dp_aux { int (*drm_aux_register)(struct dp_aux *aux); void (*drm_aux_deregister)(struct dp_aux *aux); void (*isr)(struct dp_aux *aux); - void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg); + void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg, bool skip_op); void (*deinit)(struct dp_aux *aux); void (*reconfig)(struct dp_aux *aux); void (*abort)(struct dp_aux *aux, bool abort); diff --git a/msm/dp/dp_ctrl.c b/msm/dp/dp_ctrl.c index 395d5a7e1cc9d..4f935764cc218 100644 --- a/msm/dp/dp_ctrl.c +++ b/msm/dp/dp_ctrl.c @@ -165,8 +165,13 @@ trigger_idle: * configuration, output format and sink/panel timing information. */ static void dp_ctrl_configure_source_link_params(struct dp_ctrl_private *ctrl, - bool enable) + bool enable, bool skip_op) { + if (skip_op) { + DP_DEBUG("configuring source link params skipped\n"); + return; + } + if (enable) { ctrl->catalog->lane_mapping(ctrl->catalog, ctrl->orientation, ctrl->parser->l_map); @@ -533,7 +538,7 @@ skip_training: return ret; } -static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl) +static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl, bool skip_op) { int ret = 0; u8 const encoding = 0x1, downspread = 0x00; @@ -547,6 +552,11 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl) ctrl->link->link_params.bw_code); link_info.capabilities = ctrl->panel->link_info.capabilities; + if (skip_op) { + DP_DEBUG("link training skipped\n"); + return 0; + } + ret = drm_dp_link_configure(ctrl->aux->drm_aux, &link_info); if (ret) goto end; @@ -592,7 +602,7 @@ end: return ret; } -static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl) +static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, bool skip_op) { int ret = 0; @@ -604,13 +614,15 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl) * transitioned to PUSH_IDLE. In order to start transmitting a link * training pattern, we have to first to a DP software reset. */ - ctrl->catalog->reset(ctrl->catalog); + + if (!skip_op) + ctrl->catalog->reset(ctrl->catalog); if (ctrl->fec_mode) drm_dp_dpcd_writeb(ctrl->aux->drm_aux, DP_FEC_CONFIGURATION, 0x01); - ret = dp_ctrl_link_train(ctrl); + ret = dp_ctrl_link_train(ctrl, skip_op); end: return ret; @@ -720,7 +732,7 @@ end: ctrl->training_2_pattern = pattern; } -static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow) +static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow, bool skip_op) { int rc = -EINVAL; bool downgrade = false; @@ -746,7 +758,7 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow) ctrl->link->link_params.lane_count, ctrl->orientation); - dp_ctrl_configure_source_link_params(ctrl, true); + dp_ctrl_configure_source_link_params(ctrl, true, skip_op); if (!(--link_train_max_retries % 10)) { struct dp_link_params *link = &ctrl->link->link_params; @@ -758,7 +770,7 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow) dp_ctrl_select_training_pattern(ctrl, downgrade); - rc = dp_ctrl_setup_main_link(ctrl); + rc = dp_ctrl_setup_main_link(ctrl, skip_op); if (!rc) break; @@ -782,7 +794,7 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow) if (rc != -EAGAIN) dp_ctrl_link_rate_down_shift(ctrl); - dp_ctrl_configure_source_link_params(ctrl, false); + dp_ctrl_configure_source_link_params(ctrl, false, skip_op); dp_ctrl_disable_link_clock(ctrl); /* hw recommended delays before retrying link training */ @@ -852,7 +864,7 @@ static int dp_ctrl_disable_stream_clocks(struct dp_ctrl_private *ctrl, } return ret; } -static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset) +static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset, bool skip_op) { struct dp_ctrl_private *ctrl; struct dp_catalog_ctrl *catalog; @@ -867,7 +879,7 @@ static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset) ctrl->orientation = flip; catalog = ctrl->catalog; - if (reset) { + if (reset && !skip_op) { catalog->usb_reset(ctrl->catalog, flip); catalog->phy_reset(ctrl->catalog); } @@ -906,7 +918,7 @@ static void dp_ctrl_send_video(struct dp_ctrl_private *ctrl) ctrl->catalog->state_ctrl(ctrl->catalog, ST_SEND_VIDEO); } -static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl) +static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl, bool skip_op) { int ret = 0; struct dp_ctrl_private *ctrl; @@ -931,7 +943,7 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl) goto end; ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_STARTED; - ret = dp_ctrl_setup_main_link(ctrl); + ret = dp_ctrl_setup_main_link(ctrl, skip_op); ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_STARTED; if (ret) { @@ -949,7 +961,7 @@ end: return ret; } -static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl) +static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl, bool skip_op) { int ret = 0; struct dp_ctrl_private *ctrl; @@ -978,14 +990,14 @@ static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl) ctrl->dp_ctrl.stream_off(&ctrl->dp_ctrl, ctrl->panel); ctrl->dp_ctrl.off(&ctrl->dp_ctrl); - ctrl->aux->init(ctrl->aux, ctrl->parser->aux_cfg); + ctrl->aux->init(ctrl->aux, ctrl->parser->aux_cfg, skip_op); ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl, ctrl->mst_mode, - ctrl->fec_mode, ctrl->dsc_mode, false); + ctrl->fec_mode, ctrl->dsc_mode, false, skip_op); if (ret) DP_ERR("failed to enable DP controller\n"); - ctrl->dp_ctrl.stream_on(&ctrl->dp_ctrl, ctrl->panel); + ctrl->dp_ctrl.stream_on(&ctrl->dp_ctrl, ctrl->panel, skip_op); DP_DEBUG("end\n"); } @@ -1228,7 +1240,7 @@ static void dp_ctrl_fec_dsc_setup(struct dp_ctrl_private *ctrl) DP_WARN("failed to enable sink dsc\n"); } -static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) +static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel, bool skip_op) { int rc = 0; bool link_ready = false; @@ -1250,9 +1262,12 @@ static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) return rc; } - rc = panel->hw_cfg(panel, true); - if (rc) - return rc; + /*Skip panel config when cont. splash is enabled*/ + if (!skip_op) { + rc = panel->hw_cfg(panel, true); + if (rc) + return rc; + } if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { dp_ctrl_send_phy_test_pattern(ctrl); @@ -1343,7 +1358,7 @@ static void dp_ctrl_stream_off(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) } static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode, - bool fec_mode, bool dsc_mode, bool shallow) + bool fec_mode, bool dsc_mode, bool shallow, bool skip_op) { int rc = 0; struct dp_ctrl_private *ctrl; @@ -1389,9 +1404,13 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode, ctrl->initial_lane_count = ctrl->link->link_params.lane_count; ctrl->initial_bw_code = ctrl->link->link_params.bw_code; - rc = dp_ctrl_link_setup(ctrl, shallow); + rc = dp_ctrl_link_setup(ctrl, shallow, skip_op); if (!rc) ctrl->power_on = true; + + /*enable stream clocks when cont. splash is enabled*/ + if (skip_op) + dp_ctrl_enable_stream_clocks(ctrl, ctrl->panel); end: return rc; } @@ -1399,6 +1418,7 @@ end: static void dp_ctrl_off(struct dp_ctrl *dp_ctrl) { struct dp_ctrl_private *ctrl; + bool skip_op = false; if (!dp_ctrl) return; @@ -1409,7 +1429,7 @@ static void dp_ctrl_off(struct dp_ctrl *dp_ctrl) return; ctrl->catalog->fec_config(ctrl->catalog, false); - dp_ctrl_configure_source_link_params(ctrl, false); + dp_ctrl_configure_source_link_params(ctrl, false, skip_op); ctrl->catalog->reset(ctrl->catalog); /* Make sure DP is disabled before clk disable */ diff --git a/msm/dp/dp_ctrl.h b/msm/dp/dp_ctrl.h index 225a4bf54dafe..0f506d7294d8b 100644 --- a/msm/dp/dp_ctrl.h +++ b/msm/dp/dp_ctrl.h @@ -16,17 +16,17 @@ #include "dp_debug.h" struct dp_ctrl { - int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset); + int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset, bool skip_op); void (*deinit)(struct dp_ctrl *dp_ctrl); int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode, bool fec_en, - bool dsc_en, bool shallow); + bool dsc_en, bool shallow, bool skip_op); void (*off)(struct dp_ctrl *dp_ctrl); void (*abort)(struct dp_ctrl *dp_ctrl, bool abort); void (*isr)(struct dp_ctrl *dp_ctrl); bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl); - void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl); - int (*link_maintenance)(struct dp_ctrl *dp_ctrl); - int (*stream_on)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel); + void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl, bool skip_op); + int (*link_maintenance)(struct dp_ctrl *dp_ctrl, bool skip_op); + int (*stream_on)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel, bool skip_op); void (*stream_off)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel); void (*stream_pre_off)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel); void (*set_mst_channel_info)(struct dp_ctrl *dp_ctrl, diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 11349989c449b..87309a7ebea9c 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -136,6 +136,12 @@ static char *dp_display_state_name(enum dp_display_states state) static struct dp_display *g_dp_display[MAX_DP_ACTIVE_DISPLAY]; #define HPD_STRING_SIZE 30 +static char edp_display_primary[MAX_CMDLINE_PARAM_LEN]; + +static struct dp_display_boot_param boot_displays[MAX_DP_ACTIVE_BOOT_DISPLAY] = { + {.boot_param = edp_display_primary}, +}; + struct dp_hdcp_dev { void *fd; struct sde_hdcp_ops *ops; @@ -229,6 +235,13 @@ static const struct of_device_id dp_dt_match[] = { {} }; +bool is_skip_required(struct dp_display *display) +{ + if (!display) + return false; + return display->cont_splash_enabled; +} + static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp) { return dp->link->hdcp_status.hdcp_version && dp->hdcp.ops; @@ -304,6 +317,35 @@ static void dp_audio_enable(struct dp_display_private *dp, bool enable) } } +static int dp_display_parse_boot_display_selection(void) +{ + char *pos = NULL; + char disp_buf[MAX_CMDLINE_PARAM_LEN] = {'\0'}; + int i, j; + + for (i = 0; i < MAX_DP_ACTIVE_BOOT_DISPLAY; i++) { + strlcpy(disp_buf, boot_displays[i].boot_param, + MAX_CMDLINE_PARAM_LEN); + + pos = strnstr(disp_buf, ":", strlen(disp_buf)); + + /* Use ':' as a delimiter to retrieve the display name */ + if (!pos) { + DP_DEBUG("display name[%s]is not valid\n", disp_buf); + continue; + } + + for (j = 0; (disp_buf + j) < pos; j++) + boot_displays[i].name[j] = *(disp_buf + j); + + boot_displays[i].name[j] = '\0'; + + boot_displays[i].boot_disp_en = true; + } + + return 0; +} + static void dp_display_update_hdcp_status(struct dp_display_private *dp, bool reset) { @@ -1060,6 +1102,8 @@ static int dp_display_host_init(struct dp_display_private *dp) bool flip = false; bool reset; int rc = 0; + bool skip_op = is_skip_required(&dp->dp_display); + if (dp_display_state_is(DP_STATE_INITIALIZED)) { dp_display_state_log("[already initialized]"); @@ -1079,7 +1123,7 @@ static int dp_display_host_init(struct dp_display_private *dp) } dp->hpd->host_init(dp->hpd, &dp->catalog->hpd); - rc = dp->ctrl->init(dp->ctrl, flip, reset); + rc = dp->ctrl->init(dp->ctrl, flip, reset, skip_op); if (rc) { DP_WARN("Ctrl init Failed.\n"); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_CASE2, dp->state); @@ -1122,7 +1166,8 @@ static int dp_display_panel_ready(struct dp_display_private *dp) return -ETIMEDOUT; } } - dp->panel->init(dp->panel); + if (!dp->dp_display.cont_splash_enabled) + dp->panel->init(dp->panel); return 0; } @@ -1130,6 +1175,7 @@ static int dp_display_panel_ready(struct dp_display_private *dp) static int dp_display_host_ready(struct dp_display_private *dp) { int rc = 0; + bool skip_op; if (!dp_display_state_is(DP_STATE_INITIALIZED)) { rc = dp_display_host_init(dp); @@ -1160,11 +1206,11 @@ static int dp_display_host_ready(struct dp_display_private *dp) * the connect/disconnect notifications do not currently have any * sessions IDs. */ + skip_op = dp->dp_display.cont_splash_enabled; dp->aux->abort(dp->aux, false); dp->ctrl->abort(dp->ctrl, false); - dp->aux->init(dp->aux, dp->parser->aux_cfg); - + dp->aux->init(dp->aux, dp->parser->aux_cfg, skip_op); dp_display_state_add(DP_STATE_READY); /* log this as it results from user action of cable connection */ DP_INFO("[OK]\n"); @@ -1220,6 +1266,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) int rc = -EINVAL; unsigned long wait_timeout_ms; unsigned long t; + bool skip_op = is_skip_required(&dp->dp_display); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); @@ -1302,7 +1349,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp_display_mst_init(dp); rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, - dp->panel->fec_en, dp->panel->dsc_en, false); + dp->panel->fec_en, dp->panel->dsc_en, false, skip_op); if (rc) { dp_display_state_remove(DP_STATE_CONNECTED); goto end; @@ -1702,8 +1749,9 @@ static int dp_display_stream_enable(struct dp_display_private *dp, struct dp_panel *dp_panel) { int rc = 0; + bool skip_op = is_skip_required(&dp->dp_display); - rc = dp->ctrl->stream_on(dp->ctrl, dp_panel); + rc = dp->ctrl->stream_on(dp->ctrl, dp_panel, skip_op); if (dp->debug->tpg_state) dp_panel->tpg_config(dp_panel, true); @@ -1742,6 +1790,7 @@ static void dp_display_attention_work(struct work_struct *work) struct dp_display_private *dp = container_of(work, struct dp_display_private, attention_work); int rc = 0; + bool skip_op = is_skip_required(&dp->dp_display); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); @@ -1805,18 +1854,18 @@ static void dp_display_attention_work(struct work_struct *work) if (dp->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_PHY_TEST_PATTERN); - dp->ctrl->process_phy_test_request(dp->ctrl); + dp->ctrl->process_phy_test_request(dp->ctrl, skip_op); } if (dp->link->sink_request & DP_TEST_LINK_TRAINING) { SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_TRAINING); dp->link->send_test_response(dp->link); - rc = dp->ctrl->link_maintenance(dp->ctrl); + rc = dp->ctrl->link_maintenance(dp->ctrl, skip_op); } if (dp->link->sink_request & DP_LINK_STATUS_UPDATED) { SDE_EVT32_EXTERNAL(dp->state, DP_LINK_STATUS_UPDATED); - rc = dp->ctrl->link_maintenance(dp->ctrl); + rc = dp->ctrl->link_maintenance(dp->ctrl, skip_op); } if (!rc) @@ -2066,6 +2115,9 @@ static int dp_init_sub_modules(struct dp_display_private *dp) goto error_pll; } + if (dp->dp_display.cont_splash_enabled) + dp->pll->cont_splash_enabled = 1; + dp->power = dp_power_get(dp->parser, dp->pll); if (IS_ERR(dp->power)) { rc = PTR_ERR(dp->power); @@ -2283,6 +2335,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) struct dp_panel *dp_panel; int rc = 0; bool shallow_mode = true; + bool skip_op = is_skip_required(dp_display); if (!dp_display || !panel) { DP_ERR("invalid input\n"); @@ -2385,7 +2438,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, shallow_mode); + dp_panel->dsc_en, shallow_mode, skip_op); if (rc) goto end; @@ -2573,6 +2626,11 @@ end: static void dp_display_stream_post_enable(struct dp_display_private *dp, struct dp_panel *dp_panel) { + + if (dp->dp_display.is_edp) { + dp->dp_display.cont_splash_enabled = false; + dp->pll->cont_splash_enabled = false; + } dp_panel->spd_config(dp_panel); dp_panel->setup_hdr(dp_panel, NULL, false, 0, true); } @@ -3953,6 +4011,10 @@ static int dp_display_probe(struct platform_device *pdev) dp_display->is_edp = (info->display_type == DRM_MODE_CONNECTOR_eDP) ? true : false; dp_display->edp_detect = dp_display_edp_detect; + if (dp_display->is_edp) { + if (dp_display_get_num_of_boot_displays()) + dp_display->cont_splash_enabled = 1; + } rc = component_add(&pdev->dev, &dp_display_comp_ops); if (rc) { DP_ERR("component add failed, rc=%d\n", rc); @@ -3985,6 +4047,17 @@ int dp_display_get_displays(void **displays, int count) return i; } +int dp_display_get_num_of_boot_displays(void) +{ + int i; + int count = 0; + + for (i = 0; i < MAX_DP_ACTIVE_BOOT_DISPLAY; i++) { + if (boot_displays[i].boot_disp_en == true) + count++; + } + return count; +} int dp_display_get_num_of_displays(void) { int i; @@ -4151,7 +4224,7 @@ static struct platform_driver dp_display_driver = { void __init dp_display_register(void) { - + dp_display_parse_boot_display_selection(); platform_driver_register(&dp_display_driver); } @@ -4159,3 +4232,7 @@ void __exit dp_display_unregister(void) { platform_driver_unregister(&dp_display_driver); } +module_param_string(edp_display0, edp_display_primary, MAX_CMDLINE_PARAM_LEN, + 0600); +MODULE_PARM_DESC(edp_display0, + "msm_drm.edp_display0=: where is 'primary dp display node name' and where x represents index in the topology list"); diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index 892322fa2fc20..f99e2fd723283 100644 --- a/msm/dp/dp_display.h +++ b/msm/dp/dp_display.h @@ -16,6 +16,9 @@ #define DP_MST_SIM_MAX_PORTS 8 #define MAX_DP_ACTIVE_DISPLAY 8 +#define MAX_DP_ACTIVE_BOOT_DISPLAY 1 + +#define MAX_CMDLINE_PARAM_LEN 512 enum dp_drv_state { PM_DEFAULT, @@ -39,6 +42,13 @@ struct dp_display_info { u32 stream_cnt; }; +struct dp_display_boot_param { + char name[MAX_CMDLINE_PARAM_LEN]; + char *boot_param; + bool boot_disp_en; + void *disp; +}; + struct dp_mst_drm_cbs { void (*hpd)(void *display, bool hpd_status); void (*hpd_irq)(void *display, struct dp_mst_hpd_info *info); @@ -86,6 +96,7 @@ struct dp_display { void *dp_mst_prv_info; u32 max_mixer_count; u32 max_dsc_count; + bool cont_splash_enabled; int (*enable)(struct dp_display *dp_display, void *panel); int (*post_enable)(struct dp_display *dp_display, void *panel); @@ -149,7 +160,7 @@ struct dp_display { u32 strm_id, const char **display_type); int (*edp_detect)(struct dp_display *dp_display); }; - +int dp_display_get_num_of_boot_displays(void); #if IS_ENABLED(CONFIG_DRM_MSM_DP) int dp_display_get_num_of_displays(void); int dp_display_get_displays(void **displays, int count); diff --git a/msm/dp/dp_drm.c b/msm/dp/dp_drm.c index 6e054af5dc3d5..1342726043e83 100644 --- a/msm/dp/dp_drm.c +++ b/msm/dp/dp_drm.c @@ -463,6 +463,7 @@ int dp_connector_get_info(struct drm_connector *connector, if (display && display->is_edp) { info->intf_type = DRM_MODE_CONNECTOR_eDP; info->display_type = SDE_CONNECTOR_PRIMARY; + info->is_connected = true; } else { info->capabilities |= MSM_DISPLAY_CAP_HOT_PLUG; } diff --git a/msm/dp/dp_pll.h b/msm/dp/dp_pll.h index fa56964ab5338..780a00c6dbfad 100644 --- a/msm/dp/dp_pll.h +++ b/msm/dp/dp_pll.h @@ -127,7 +127,7 @@ struct dp_pll { bool ssc_en; bool bonding_en; - + bool cont_splash_enabled; void *priv; struct dp_pll_db pll_db; struct dp_pll_vco_clk pll_clks[DP_PLL_NUM_CLKS]; diff --git a/msm/dp/edp_pll_7nm.c b/msm/dp/edp_pll_7nm.c index da26cb657884f..1f3b60a1f3253 100644 --- a/msm/dp/edp_pll_7nm.c +++ b/msm/dp/edp_pll_7nm.c @@ -277,6 +277,8 @@ static int edp_config_vco_rate_7nm(struct dp_pll *pll, DP_ERR("VCO Init DB failed\n"); return rc; } + if (pll->cont_splash_enabled) + return 0; dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x7d); /* Make sure the PLL register writes are done */ @@ -460,6 +462,11 @@ static int edp_pll_enable_7nm(struct dp_pll *pll) pll->aux->state &= ~DP_STATE_PLL_LOCKED; + if (pll->cont_splash_enabled) { + pll->aux->state |= DP_STATE_PLL_LOCKED; + return rc; + } + 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); @@ -790,6 +797,9 @@ static int edp_pll_vco_div_clk_set_rate(struct clk_hw *hw, unsigned long rate, pll_link = to_dp_vco_hw(hw); pll = pll_link->priv; + if (pll->cont_splash_enabled) + return rc; + if (rate != edp_pll_vco_div_clk_get_rate(pll)) { DP_ERR("unsupported rate %lu failed\n", rate); return rc; diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 308200bd8579c..a24662dfaf526 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -4426,7 +4426,8 @@ static int _sde_kms_get_splash_data(struct sde_kms *sde_kms, * cont_splash_region should be collection of all memory regions * Ex: */ - num_displays = dsi_display_get_num_of_displays(); + num_displays = dsi_display_get_num_of_displays() + +dp_display_get_num_of_boot_displays(); num_regions = of_property_count_u64_elems(node, "reg") / 2; data->num_splash_displays = num_displays; From 88f941caec8c0c8c8795546ef632d180664faccb Mon Sep 17 00:00:00 2001 From: Sai Srujana Oruganti Date: Fri, 9 Jun 2023 13:47:28 +0530 Subject: [PATCH 17/23] disp: msm: dp: skip hpd config Skip hpd config when continuous splash is enabled. Change-Id: Ibeff020a981bcb29df188668f87498d29343f5a3 Signed-off-by: Sai Srujana Oruganti --- msm/dp/dp_display.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 87309a7ebea9c..e306bfb23f654 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1122,7 +1122,10 @@ static int dp_display_host_init(struct dp_display_private *dp) return rc; } - dp->hpd->host_init(dp->hpd, &dp->catalog->hpd); + /* Skip hpd config when cont. splash is enbaled*/ + if (!skip_op) + dp->hpd->host_init(dp->hpd, &dp->catalog->hpd); + rc = dp->ctrl->init(dp->ctrl, flip, reset, skip_op); if (rc) { DP_WARN("Ctrl init Failed.\n"); From 228e520fb4e7a8ee321edad60c39f6d261938144 Mon Sep 17 00:00:00 2001 From: Venkata Prahlad Valluru Date: Tue, 13 Jun 2023 12:42:28 +0530 Subject: [PATCH 18/23] disp: msm: dp: add backlight for edp Register backlight and cooling devices for eDP interface and reuse connector backlight update functions. Parse and use backlight properties from devicetree. Change-Id: I5d96a8d588d8f7266aa5bffad310718942e7dfe6 Signed-off-by: Venkata Prahlad Valluru --- msm/dp/dp_display.c | 44 +++++++++++++ msm/dp/dp_display.h | 2 + msm/dp/dp_drm.c | 21 ++++++ msm/dp/dp_drm.h | 9 +++ msm/dp/dp_panel.c | 105 ++++++++++++++++++++++++++++++ msm/dp/dp_panel.h | 21 ++++++ msm/dp/dp_parser.c | 42 ++++++++++++ msm/dp/dp_parser.h | 10 ++- msm/sde/sde_connector.c | 140 +++++++++++++++++++++++++++------------- msm/sde/sde_kms.c | 1 + 10 files changed, 350 insertions(+), 45 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 11349989c449b..f99fd658ec265 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -2581,6 +2581,9 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) { struct dp_display_private *dp; struct dp_panel *dp_panel; + struct drm_connector *connector; + struct sde_connector *sde_conn; + struct backlight_device *bl_device; int rc = 0; if (!dp_display || !panel) { @@ -2626,6 +2629,15 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) DP_ERR("Cannot turn edp backlight power on"); goto end; } + connector = dp_display->base_connector; + sde_conn = to_sde_connector(connector); + + sde_conn->allow_bl_update = true; + if (sde_conn->bl_device) { + bl_device = sde_conn->bl_device; + bl_device->props.power = FB_BLANK_UNBLANK; + bl_device->props.state &= ~BL_CORE_FBBLANK; + } } cancel_delayed_work_sync(&dp->hdcp_cb_work); @@ -3283,6 +3295,37 @@ static int dp_display_config_hdr(struct dp_display *dp_display, void *panel, core_clk_rate, flush_hdr); } +static int dp_display_set_backlight(struct dp_display *dp_display, + void *panel, u32 bl_lvl) +{ + struct dp_panel *dp_panel; + struct dp_display_private *dp; + u32 bl_scale, bl_scale_sv; + u64 bl_temp; + + if (!dp_display || !panel) { + DP_ERR("invalid input\n"); + return -EINVAL; + } + + dp = container_of(dp_display, struct dp_display_private, dp_display); + dp_panel = panel; + + dp_panel->bl_config.bl_level = bl_lvl; + + /* scale backlight */ + bl_scale = dp_panel->bl_config.bl_scale; + bl_temp = bl_lvl * bl_scale / MAX_BL_SCALE_LEVEL; + + bl_scale_sv = dp_panel->bl_config.bl_scale_sv; + bl_temp = (u32)bl_temp * bl_scale_sv / MAX_SV_BL_SCALE_LEVEL; + + DP_DEBUG("bl_scale = %u, bl_scale_sv = %u, bl_lvl = %u\n", + bl_scale, bl_scale_sv, (u32)bl_temp); + + return dp_panel->set_backlight(dp_panel, (u32)bl_temp); +} + static int dp_display_setup_colospace(struct dp_display *dp_display, void *panel, u32 colorspace) @@ -3944,6 +3987,7 @@ static int dp_display_probe(struct platform_device *pdev) dp_display->wakeup_phy_layer = dp_display_wakeup_phy_layer; dp_display->set_colorspace = dp_display_setup_colospace; + dp_display->set_backlight = dp_display_set_backlight; dp_display->get_available_dp_resources = dp_display_get_available_dp_resources; dp_display->get_display_type = dp_display_get_display_type; diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index 892322fa2fc20..a49ef045d702e 100644 --- a/msm/dp/dp_display.h +++ b/msm/dp/dp_display.h @@ -110,6 +110,8 @@ struct dp_display { bool dhdr_update); int (*set_colorspace)(struct dp_display *dp_display, void *panel, u32 colorspace); + int (*set_backlight)(struct dp_display *dp_display, void *panel, + u32 bl_lvl); int (*post_init)(struct dp_display *dp_display); int (*mst_install)(struct dp_display *dp_display, struct dp_mst_drm_install_info *mst_install_info); diff --git a/msm/dp/dp_drm.c b/msm/dp/dp_drm.c index 6e054af5dc3d5..d936aecd7c8c1 100644 --- a/msm/dp/dp_drm.c +++ b/msm/dp/dp_drm.c @@ -326,6 +326,27 @@ int dp_connector_config_hdr(struct drm_connector *connector, void *display, c_state->dyn_hdr_meta.dynamic_hdr_update); } +int dp_connector_set_backlight(struct drm_connector *connector, + void *display, u32 bl_lvl) +{ + struct dp_display *dp_display = display; + struct sde_connector *sde_conn; + + if (!dp_display || !connector) { + DP_ERR("invalid dp display\n"); + return -EINVAL; + } + + sde_conn = to_sde_connector(connector); + if (!sde_conn->drv_panel) { + DP_ERR("invalid dp panel\n"); + return -EINVAL; + } + + return dp_display->set_backlight(dp_display, + sde_conn->drv_panel, bl_lvl); +} + int dp_connector_set_colorspace(struct drm_connector *connector, void *display) { diff --git a/msm/dp/dp_drm.h b/msm/dp/dp_drm.h index 647705c2706d4..a99ab9c86f9d7 100644 --- a/msm/dp/dp_drm.h +++ b/msm/dp/dp_drm.h @@ -49,6 +49,15 @@ int dp_connector_atomic_check(struct drm_connector *connector, void *display, struct drm_atomic_state *state); +/** + * dp_connector_set_backlight - callback to set backlight + * @connector: Pointer to drm connector structure + * @display: Pointer to private display handle + * Returns: Zero on success + */ +int dp_connector_set_backlight(struct drm_connector *connector, + void *display, u32 bl_lvl); + /** * dp_connector_set_colorspace - callback to set new colorspace * @connector: Pointer to drm connector structure diff --git a/msm/dp/dp_panel.c b/msm/dp/dp_panel.c index 6c24c52b00046..8fc1b13c3e32f 100644 --- a/msm/dp/dp_panel.c +++ b/msm/dp/dp_panel.c @@ -6,6 +6,7 @@ #include "dp_panel.h" #include +#include #include #include "dp_debug.h" #include @@ -2295,6 +2296,91 @@ static int dp_panel_set_stream_info(struct dp_panel *dp_panel, return 0; } +static int dp_panel_pwm_register(struct dp_panel *dp_panel) +{ + int rc = 0; + struct dp_backlight_config *bl = &dp_panel->bl_config; + struct platform_device *pdev; + struct device *dev; + struct dp_panel_private *panel; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + pdev = panel->parser->pdev; + dev = &pdev->dev; + + bl->pwm_bl = devm_of_pwm_get(dev, dev->of_node, NULL); + if (IS_ERR_OR_NULL(bl->pwm_bl)) { + rc = PTR_ERR(bl->pwm_bl); + DP_ERR("failed to request pwm, rc=%d\n", rc); + return rc; + } + + return 0; +} + +static void dp_panel_pwm_unregister(struct dp_panel *dp_panel) +{ + struct dp_backlight_config *bl = &dp_panel->bl_config; + struct platform_device *pdev; + struct dp_panel_private *panel; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + pdev = panel->parser->pdev; + + if (bl->pwm_bl) + devm_pwm_put(&pdev->dev, bl->pwm_bl); +} + +static int dp_panel_set_backlight(struct dp_panel *panel, u32 bl_lvl) +{ + int rc = 0; + u32 duty = 0; + u32 period_ns = 0; + struct dp_backlight_config *bl; + + if (!panel) { + DP_ERR("Invalid Params\n"); + return -EINVAL; + } + + bl = &panel->bl_config; + if (!bl->pwm_bl) { + DP_ERR("pwm device not found\n"); + return -EINVAL; + } + + DP_DEBUG("backlight lvl:%d\n", bl_lvl); + + period_ns = bl->pwm_period_usecs * NSEC_PER_USEC; + duty = bl_lvl * period_ns; + duty /= bl->bl_max_level; + + rc = pwm_config(bl->pwm_bl, duty, period_ns); + if (rc) { + DP_ERR("failed to change pwm config, rc=\n", rc); + goto error; + } + + if (bl_lvl == 0 && bl->pwm_enabled) { + pwm_disable(bl->pwm_bl); + bl->pwm_enabled = false; + return 0; + } + + if (bl_lvl != 0 && !bl->pwm_enabled) { + rc = pwm_enable(bl->pwm_bl); + if (rc) { + DP_ERR("failed to enable pwm, rc=\n", rc); + goto error; + } + + bl->pwm_enabled = true; + } + +error: + return rc; +} + static int dp_panel_init_panel_info(struct dp_panel *dp_panel) { int rc = 0; @@ -3058,6 +3144,15 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) dp_panel->fec_feature_enable = panel->parser->fec_feature_enable; dp_panel->dsc_continuous_pps = panel->parser->dsc_continuous_pps; + /* backlight config for edp */ + dp_panel->bl_config.bl_min_level = panel->parser->bl_min_level; + dp_panel->bl_config.bl_max_level = panel->parser->bl_max_level; + dp_panel->bl_config.brightness_max_level = panel->parser->brightness_max_level; + dp_panel->bl_config.pwm_period_usecs = panel->parser->pwm_period_usecs; + dp_panel->bl_config.bl_scale = MAX_BL_SCALE_LEVEL; + dp_panel->bl_config.bl_scale_sv = MAX_SV_BL_SCALE_LEVEL; + + if (in->base_panel) { memcpy(dp_panel->dpcd, in->base_panel->dpcd, DP_RECEIVER_CAP_SIZE + 1); @@ -3085,6 +3180,7 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) dp_panel->spd_config = dp_panel_spd_config; dp_panel->setup_hdr = dp_panel_setup_hdr; dp_panel->set_colorspace = dp_panel_set_colorspace; + dp_panel->set_backlight = dp_panel_set_backlight; dp_panel->hdr_supported = dp_panel_hdr_supported; dp_panel->set_stream_info = dp_panel_set_stream_info; dp_panel->read_sink_status = dp_panel_read_sink_sts; @@ -3115,6 +3211,12 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) } } + if (in->is_edp) { + rc = dp_panel_pwm_register(dp_panel); + if (rc) + DP_ERR("Failed to register pwm\n"); + } + return dp_panel; error: return ERR_PTR(rc); @@ -3132,6 +3234,9 @@ void dp_panel_put(struct dp_panel *dp_panel) dp_panel_edid_deregister(panel); sde_conn = to_sde_connector(dp_panel->connector); + + dp_panel_pwm_unregister(dp_panel); + if (sde_conn) sde_conn->drv_panel = NULL; diff --git a/msm/dp/dp_panel.h b/msm/dp/dp_panel.h index 822393143ab8e..3abb412975845 100644 --- a/msm/dp/dp_panel.h +++ b/msm/dp/dp_panel.h @@ -18,6 +18,9 @@ #define DP_RECEIVER_DSC_CAP_SIZE 15 #define DP_RECEIVER_FEC_STATUS_SIZE 3 #define DP_RECEIVER_EXT_CAP_SIZE 4 +#define MAX_BL_LEVEL 4096 +#define MAX_BL_SCALE_LEVEL 1024 +#define MAX_SV_BL_SCALE_LEVEL 65535 /* * A source initiated power down flag is set * when the DP is powered off while physical @@ -86,6 +89,22 @@ struct dp_dsc_caps { u8 color_depth; }; +struct dp_backlight_config { + u32 bl_min_level; + u32 bl_max_level; + u32 brightness_max_level; + u32 bl_level; + u32 bl_scale; + u32 bl_scale_sv; + + int en_gpio; + /* PWM params */ + struct pwm_device *pwm_bl; + bool pwm_enabled; + u32 pwm_period_usecs; +}; + + struct dp_audio; #define DP_PANEL_CAPS_DSC BIT(0) @@ -102,6 +121,7 @@ struct dp_panel { struct drm_dp_link link_info; struct sde_edid_ctrl *edid_ctrl; struct dp_panel_info pinfo; + struct dp_backlight_config bl_config; bool video_test; bool spd_enabled; @@ -159,6 +179,7 @@ struct dp_panel { bool dhdr_update, u64 core_clk_rate, bool flush); int (*set_colorspace)(struct dp_panel *dp_panel, u32 colorspace); + int (*set_backlight)(struct dp_panel *dp_panel, u32 bl_lvl); void (*tpg_config)(struct dp_panel *dp_panel, bool enable); int (*spd_config)(struct dp_panel *dp_panel); bool (*hdr_supported)(struct dp_panel *dp_panel); diff --git a/msm/dp/dp_parser.c b/msm/dp/dp_parser.c index 7f5501a136bf4..4503df56a81d9 100644 --- a/msm/dp/dp_parser.c +++ b/msm/dp/dp_parser.c @@ -256,6 +256,47 @@ error: return rc; } +static void dp_parser_bl_config(struct dp_parser *parser) +{ + int rc = 0; + u32 val = 0; + struct device_node *of_node = parser->pdev->dev.of_node; + + rc = of_property_read_u32(of_node, "qcom,edp-bl-min-level", &val); + if (rc) { + DP_DEBUG("bl-min-level unspecified, defaulting to zero\n"); + parser->bl_min_level = 0; + } else { + parser->bl_min_level = val; + } + + rc = of_property_read_u32(of_node, "qcom,edp-bl-max-level", &val); + if (rc) { + DP_DEBUG("bl-max-level unspecified, defaulting to 4096\n"); + parser->bl_max_level = 4096; + } else { + parser->bl_max_level = val; + } + + rc = of_property_read_u32(of_node, "qcom,edp-brightness-max-level", + &val); + if (rc) { + DP_DEBUG("brigheness-max-level unspecified, defaulting to 255\n"); + parser->brightness_max_level = 255; + } else { + parser->brightness_max_level = val; + } + + rc = of_property_read_u32(of_node, "qcom,bl-pmic-pwm-period-usecs", + &val); + if (rc) { + DP_DEBUG("bl-pmic-pwm-period-usecs unspecified, default 100\n"); + parser->pwm_period_usecs = 100; + } else { + parser->pwm_period_usecs = val; + } +} + static int dp_parser_gpio(struct dp_parser *parser) { int i = 0; @@ -854,6 +895,7 @@ static int dp_parser_parse(struct dp_parser *parser) if (rc) goto err; + dp_parser_bl_config(parser); dp_parser_dsc(parser); dp_parser_fec(parser); dp_parser_widebus(parser); diff --git a/msm/dp/dp_parser.h b/msm/dp/dp_parser.h index 5444ee9e70506..da70b5674d516 100644 --- a/msm/dp/dp_parser.h +++ b/msm/dp/dp_parser.h @@ -235,7 +235,11 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type) * @fec_feature_enable: FEC feature enable status * @dsc_continuous_pps: PPS sent every frame by HW * @has_widebus: widebus (2PPC) feature eanble status - *@mst_fixed_port: mst port_num reserved for fixed topology + * @bl_min_level: minimum brightness value for backlight + * @bl_max_level: maximum brightness value for backlight + * @brightness_max_level: maximum brightness level for backlight + * @pwm_period_usecs: pwm period in usecs for backlight pwm + * @mst_fixed_port: mst port_num reserved for fixed topology * @parse: function to be called by client to parse device tree. * @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. @@ -267,6 +271,10 @@ struct dp_parser { bool gpio_aux_switch; bool lphw_hpd; bool panel_notifier_support; + u32 bl_min_level; + u32 bl_max_level; + u32 brightness_max_level; + u32 pwm_period_usecs; 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]; diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 404e8af8c80fe..8c23f622a97b4 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ @@ -16,6 +16,7 @@ #include #include "dsi_drm.h" #include "dsi_display.h" +#include "dp_panel.h" #include "sde_crtc.h" #include "sde_rm.h" #include "sde_vm.h" @@ -23,6 +24,7 @@ #define BL_NODE_NAME_SIZE 32 #define HDR10_PLUS_VSIF_TYPE_CODE 0x81 +#define MAX_BRIGHTNESS_LEVEL 255 /* Autorefresh will occur after FRAME_CNT frames. Large values are unlikely */ #define AUTOREFRESH_MAX_FRAME_CNT 6 @@ -93,13 +95,16 @@ static inline struct sde_kms *_sde_connector_get_kms(struct drm_connector *conn) static int sde_backlight_device_update_status(struct backlight_device *bd) { int brightness; - struct dsi_display *display; + struct dsi_display *dsi_display; + struct dp_panel *dp_panel; struct sde_connector *c_conn = bl_get_data(bd); int bl_lvl; struct drm_event event; int rc = 0; struct sde_kms *sde_kms; struct sde_vm_ops *vm_ops; + u32 bl_max_level = 0; + u32 brightness_max_level = 0; sde_kms = _sde_connector_get_kms(&c_conn->base); if (!sde_kms) { @@ -114,15 +119,27 @@ static int sde_backlight_device_update_status(struct backlight_device *bd) (bd->props.state & BL_CORE_SUSPENDED)) brightness = 0; - display = (struct dsi_display *) c_conn->display; - if (brightness > display->panel->bl_config.bl_max_level) - brightness = display->panel->bl_config.bl_max_level; + if (c_conn->connector_type == DRM_MODE_CONNECTOR_DSI) { + dsi_display = (struct dsi_display *) c_conn->display; + bl_max_level = dsi_display->panel->bl_config.bl_max_level; + brightness_max_level = + dsi_display->panel->bl_config.brightness_max_level; + } else if (c_conn->connector_type == DRM_MODE_CONNECTOR_eDP) { + dp_panel = (struct dp_panel *) c_conn->drv_panel; + if (dp_panel) { + bl_max_level = dp_panel->bl_config.bl_max_level; + brightness_max_level = + dp_panel->bl_config.brightness_max_level; + } + } + + if (brightness > bl_max_level) + brightness = bl_max_level; if (brightness > c_conn->thermal_max_brightness) brightness = c_conn->thermal_max_brightness; /* map UI brightness into driver backlight level with rounding */ - bl_lvl = mult_frac(brightness, display->panel->bl_config.bl_max_level, - display->panel->bl_config.brightness_max_level); + bl_lvl = mult_frac(brightness, bl_max_level, brightness_max_level); if (!bl_lvl && brightness) bl_lvl = 1; @@ -188,31 +205,46 @@ static int sde_backlight_setup(struct sde_connector *c_conn, { struct backlight_properties props; struct dsi_display *display; - struct dsi_backlight_config *bl_config; + struct dp_panel *dp_panel; + struct dsi_backlight_config *dsi_bl_config; struct sde_kms *sde_kms; static int display_count; char bl_node_name[BL_NODE_NAME_SIZE]; + u32 brightness_max_level = 0; sde_kms = _sde_connector_get_kms(&c_conn->base); if (!sde_kms) { SDE_ERROR("invalid kms\n"); return -EINVAL; - } else if (c_conn->connector_type != DRM_MODE_CONNECTOR_DSI) { + } + + if (c_conn->connector_type == DRM_MODE_CONNECTOR_DSI) { + display = (struct dsi_display *) c_conn->display; + dsi_bl_config = &display->panel->bl_config; + brightness_max_level = dsi_bl_config->brightness_max_level; + if (dsi_bl_config->type != DSI_BACKLIGHT_DCS && + sde_in_trusted_vm(sde_kms)) + return 0; + } else if (c_conn->connector_type == DRM_MODE_CONNECTOR_eDP) { + dp_panel = (struct dp_panel *) c_conn->drv_panel; + if (dp_panel) + brightness_max_level = + dp_panel->bl_config.brightness_max_level; + else { + brightness_max_level = MAX_BRIGHTNESS_LEVEL; + } + } else { + SDE_DEBUG("invalid connector type %d\n", + c_conn->connector_type); return 0; } - display = (struct dsi_display *) c_conn->display; - bl_config = &display->panel->bl_config; - - if (bl_config->type != DSI_BACKLIGHT_DCS && - sde_in_trusted_vm(sde_kms)) - return 0; memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; props.power = FB_BLANK_UNBLANK; - props.max_brightness = bl_config->brightness_max_level; - props.brightness = bl_config->brightness_max_level; + props.max_brightness = brightness_max_level; + props.brightness = brightness_max_level; snprintf(bl_node_name, BL_NODE_NAME_SIZE, "panel%u-backlight", display_count); c_conn->bl_device = backlight_device_register(bl_node_name, dev->dev, @@ -223,7 +255,7 @@ static int sde_backlight_setup(struct sde_connector *c_conn, c_conn->bl_device = NULL; return -ENODEV; } - c_conn->thermal_max_brightness = bl_config->brightness_max_level; + c_conn->thermal_max_brightness = brightness_max_level; /** * In TVM, thermal cooling device is not enabled. Registering with dummy @@ -639,42 +671,62 @@ static int _sde_connector_update_power_locked(struct sde_connector *c_conn) static int _sde_connector_update_bl_scale(struct sde_connector *c_conn) { struct dsi_display *dsi_display; - struct dsi_backlight_config *bl_config; + struct dp_display *dp_display; + struct dsi_backlight_config *dsi_bl_config; + struct dp_backlight_config *dp_bl_config; + struct dp_panel *dp_panel; int rc = 0; + u32 bl_scale, bl_scale_sv; if (!c_conn) { SDE_ERROR("Invalid params sde_connector null\n"); return -EINVAL; } - dsi_display = c_conn->display; - if (!dsi_display || !dsi_display->panel) { - SDE_ERROR("Invalid params(s) dsi_display %pK, panel %pK\n", - dsi_display, - ((dsi_display) ? dsi_display->panel : NULL)); - return -EINVAL; - } - - bl_config = &dsi_display->panel->bl_config; - - if (!c_conn->allow_bl_update) { - c_conn->unset_bl_level = bl_config->bl_level; - return 0; - } - - if (c_conn->unset_bl_level) - bl_config->bl_level = c_conn->unset_bl_level; - - bl_config->bl_scale = c_conn->bl_scale > MAX_BL_SCALE_LEVEL ? + bl_scale = c_conn->bl_scale > MAX_BL_SCALE_LEVEL ? MAX_BL_SCALE_LEVEL : c_conn->bl_scale; - bl_config->bl_scale_sv = c_conn->bl_scale_sv > MAX_SV_BL_SCALE_LEVEL ? + bl_scale_sv = c_conn->bl_scale_sv > MAX_SV_BL_SCALE_LEVEL ? MAX_SV_BL_SCALE_LEVEL : c_conn->bl_scale_sv; - SDE_DEBUG("bl_scale = %u, bl_scale_sv = %u, bl_level = %u\n", - bl_config->bl_scale, bl_config->bl_scale_sv, - bl_config->bl_level); - rc = c_conn->ops.set_backlight(&c_conn->base, - dsi_display, bl_config->bl_level); + if (c_conn->connector_type == DRM_MODE_CONNECTOR_DSI) { + dsi_display = c_conn->display; + if (!dsi_display || !dsi_display->panel) { + SDE_ERROR("Invalid params dsi_display %pK, panel %pK\n", + dsi_display, + ((dsi_display) ? dsi_display->panel : NULL)); + return -EINVAL; + } + dsi_bl_config = &dsi_display->panel->bl_config; + if (!c_conn->allow_bl_update) { + c_conn->unset_bl_level = dsi_bl_config->bl_level; + return 0; + } + dsi_bl_config->bl_scale = bl_scale; + dsi_bl_config->bl_scale_sv = bl_scale_sv; + if (c_conn->unset_bl_level) + dsi_bl_config->bl_level = c_conn->unset_bl_level; + + rc = c_conn->ops.set_backlight(&c_conn->base, + dsi_display, dsi_bl_config->bl_level); + } else if (c_conn->connector_type == DRM_MODE_CONNECTOR_eDP) { + dp_display = c_conn->display; + dp_panel = (struct dp_panel *) c_conn->drv_panel; + if (dp_panel) { + dp_bl_config = &dp_panel->bl_config; + if (!c_conn->allow_bl_update) { + c_conn->unset_bl_level = dp_bl_config->bl_level; + return 0; + } + dp_bl_config->bl_scale = bl_scale; + dp_bl_config->bl_scale_sv = bl_scale_sv; + if (c_conn->unset_bl_level) + dp_bl_config->bl_level = c_conn->unset_bl_level; + rc = c_conn->ops.set_backlight(&c_conn->base, + dp_display, dp_bl_config->bl_level); + } else + SDE_ERROR("Invalid dp_panel null\n"); + } + c_conn->unset_bl_level = 0; return rc; diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 308200bd8579c..00f3d28619ecb 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1785,6 +1785,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .get_info = dp_connector_get_info, .get_mode_info = dp_connector_get_mode_info, .post_open = dp_connector_post_open, + .set_backlight = dp_connector_set_backlight, .check_status = NULL, .set_colorspace = dp_connector_set_colorspace, .config_hdr = dp_connector_config_hdr, From 126db6aa9e593d75ca4489f88cb9d27b047ee9f0 Mon Sep 17 00:00:00 2001 From: Sai Srujana Oruganti Date: Mon, 22 May 2023 21:34:58 +0530 Subject: [PATCH 19/23] disp: msm: eDP continuous splash implementation For continuous splash use case bootloader already takes care of eDP hardware states including clocks and regulators. This change avoid touching eDP hardware blocks during eDP initialization sequence. Change-Id: I23635b43ce2a41078383bbcc89aa3f60564599dd Signed-off-by: Sai Srujana Oruganti --- msm/dp/dp_aux.c | 9 +++- msm/dp/dp_aux.h | 3 +- msm/dp/dp_ctrl.c | 68 +++++++++++++++++++----------- msm/dp/dp_ctrl.h | 10 ++--- msm/dp/dp_display.c | 99 +++++++++++++++++++++++++++++++++++++++----- msm/dp/dp_display.h | 17 +++++++- msm/dp/dp_drm.c | 1 + msm/dp/dp_pll.h | 2 +- msm/dp/edp_pll_7nm.c | 10 +++++ msm/sde/sde_kms.c | 3 +- 10 files changed, 177 insertions(+), 45 deletions(-) diff --git a/msm/dp/dp_aux.c b/msm/dp/dp_aux.c index ae00856bb3da4..7c4fbc2fbb50b 100644 --- a/msm/dp/dp_aux.c +++ b/msm/dp/dp_aux.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. */ @@ -631,7 +632,7 @@ static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg) aux_cfg[i].current_index = 0; } -static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg) +static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg, bool skip_op) { struct dp_aux_private *aux; @@ -645,10 +646,16 @@ static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg) if (aux->enabled) return; + /*skip aux init when cont. splash is enabled*/ + if (skip_op) + goto skip_init; + dp_aux_reset_phy_config_indices(aux_cfg); aux->catalog->setup(aux->catalog, aux_cfg); aux->catalog->reset(aux->catalog); aux->catalog->enable(aux->catalog, true); + +skip_init: atomic_set(&aux->aborted, 0); aux->retry_cnt = 0; aux->enabled = true; diff --git a/msm/dp/dp_aux.h b/msm/dp/dp_aux.h index 850941b1e53d1..aa762e91acf71 100644 --- a/msm/dp/dp_aux.h +++ b/msm/dp/dp_aux.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. */ @@ -47,7 +48,7 @@ struct dp_aux { int (*drm_aux_register)(struct dp_aux *aux); void (*drm_aux_deregister)(struct dp_aux *aux); void (*isr)(struct dp_aux *aux); - void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg); + void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg, bool skip_op); void (*deinit)(struct dp_aux *aux); void (*reconfig)(struct dp_aux *aux); void (*abort)(struct dp_aux *aux, bool abort); diff --git a/msm/dp/dp_ctrl.c b/msm/dp/dp_ctrl.c index 395d5a7e1cc9d..4f935764cc218 100644 --- a/msm/dp/dp_ctrl.c +++ b/msm/dp/dp_ctrl.c @@ -165,8 +165,13 @@ trigger_idle: * configuration, output format and sink/panel timing information. */ static void dp_ctrl_configure_source_link_params(struct dp_ctrl_private *ctrl, - bool enable) + bool enable, bool skip_op) { + if (skip_op) { + DP_DEBUG("configuring source link params skipped\n"); + return; + } + if (enable) { ctrl->catalog->lane_mapping(ctrl->catalog, ctrl->orientation, ctrl->parser->l_map); @@ -533,7 +538,7 @@ skip_training: return ret; } -static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl) +static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl, bool skip_op) { int ret = 0; u8 const encoding = 0x1, downspread = 0x00; @@ -547,6 +552,11 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl) ctrl->link->link_params.bw_code); link_info.capabilities = ctrl->panel->link_info.capabilities; + if (skip_op) { + DP_DEBUG("link training skipped\n"); + return 0; + } + ret = drm_dp_link_configure(ctrl->aux->drm_aux, &link_info); if (ret) goto end; @@ -592,7 +602,7 @@ end: return ret; } -static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl) +static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, bool skip_op) { int ret = 0; @@ -604,13 +614,15 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl) * transitioned to PUSH_IDLE. In order to start transmitting a link * training pattern, we have to first to a DP software reset. */ - ctrl->catalog->reset(ctrl->catalog); + + if (!skip_op) + ctrl->catalog->reset(ctrl->catalog); if (ctrl->fec_mode) drm_dp_dpcd_writeb(ctrl->aux->drm_aux, DP_FEC_CONFIGURATION, 0x01); - ret = dp_ctrl_link_train(ctrl); + ret = dp_ctrl_link_train(ctrl, skip_op); end: return ret; @@ -720,7 +732,7 @@ end: ctrl->training_2_pattern = pattern; } -static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow) +static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow, bool skip_op) { int rc = -EINVAL; bool downgrade = false; @@ -746,7 +758,7 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow) ctrl->link->link_params.lane_count, ctrl->orientation); - dp_ctrl_configure_source_link_params(ctrl, true); + dp_ctrl_configure_source_link_params(ctrl, true, skip_op); if (!(--link_train_max_retries % 10)) { struct dp_link_params *link = &ctrl->link->link_params; @@ -758,7 +770,7 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow) dp_ctrl_select_training_pattern(ctrl, downgrade); - rc = dp_ctrl_setup_main_link(ctrl); + rc = dp_ctrl_setup_main_link(ctrl, skip_op); if (!rc) break; @@ -782,7 +794,7 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow) if (rc != -EAGAIN) dp_ctrl_link_rate_down_shift(ctrl); - dp_ctrl_configure_source_link_params(ctrl, false); + dp_ctrl_configure_source_link_params(ctrl, false, skip_op); dp_ctrl_disable_link_clock(ctrl); /* hw recommended delays before retrying link training */ @@ -852,7 +864,7 @@ static int dp_ctrl_disable_stream_clocks(struct dp_ctrl_private *ctrl, } return ret; } -static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset) +static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset, bool skip_op) { struct dp_ctrl_private *ctrl; struct dp_catalog_ctrl *catalog; @@ -867,7 +879,7 @@ static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset) ctrl->orientation = flip; catalog = ctrl->catalog; - if (reset) { + if (reset && !skip_op) { catalog->usb_reset(ctrl->catalog, flip); catalog->phy_reset(ctrl->catalog); } @@ -906,7 +918,7 @@ static void dp_ctrl_send_video(struct dp_ctrl_private *ctrl) ctrl->catalog->state_ctrl(ctrl->catalog, ST_SEND_VIDEO); } -static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl) +static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl, bool skip_op) { int ret = 0; struct dp_ctrl_private *ctrl; @@ -931,7 +943,7 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl) goto end; ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_STARTED; - ret = dp_ctrl_setup_main_link(ctrl); + ret = dp_ctrl_setup_main_link(ctrl, skip_op); ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_STARTED; if (ret) { @@ -949,7 +961,7 @@ end: return ret; } -static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl) +static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl, bool skip_op) { int ret = 0; struct dp_ctrl_private *ctrl; @@ -978,14 +990,14 @@ static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl) ctrl->dp_ctrl.stream_off(&ctrl->dp_ctrl, ctrl->panel); ctrl->dp_ctrl.off(&ctrl->dp_ctrl); - ctrl->aux->init(ctrl->aux, ctrl->parser->aux_cfg); + ctrl->aux->init(ctrl->aux, ctrl->parser->aux_cfg, skip_op); ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl, ctrl->mst_mode, - ctrl->fec_mode, ctrl->dsc_mode, false); + ctrl->fec_mode, ctrl->dsc_mode, false, skip_op); if (ret) DP_ERR("failed to enable DP controller\n"); - ctrl->dp_ctrl.stream_on(&ctrl->dp_ctrl, ctrl->panel); + ctrl->dp_ctrl.stream_on(&ctrl->dp_ctrl, ctrl->panel, skip_op); DP_DEBUG("end\n"); } @@ -1228,7 +1240,7 @@ static void dp_ctrl_fec_dsc_setup(struct dp_ctrl_private *ctrl) DP_WARN("failed to enable sink dsc\n"); } -static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) +static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel, bool skip_op) { int rc = 0; bool link_ready = false; @@ -1250,9 +1262,12 @@ static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) return rc; } - rc = panel->hw_cfg(panel, true); - if (rc) - return rc; + /*Skip panel config when cont. splash is enabled*/ + if (!skip_op) { + rc = panel->hw_cfg(panel, true); + if (rc) + return rc; + } if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { dp_ctrl_send_phy_test_pattern(ctrl); @@ -1343,7 +1358,7 @@ static void dp_ctrl_stream_off(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) } static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode, - bool fec_mode, bool dsc_mode, bool shallow) + bool fec_mode, bool dsc_mode, bool shallow, bool skip_op) { int rc = 0; struct dp_ctrl_private *ctrl; @@ -1389,9 +1404,13 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode, ctrl->initial_lane_count = ctrl->link->link_params.lane_count; ctrl->initial_bw_code = ctrl->link->link_params.bw_code; - rc = dp_ctrl_link_setup(ctrl, shallow); + rc = dp_ctrl_link_setup(ctrl, shallow, skip_op); if (!rc) ctrl->power_on = true; + + /*enable stream clocks when cont. splash is enabled*/ + if (skip_op) + dp_ctrl_enable_stream_clocks(ctrl, ctrl->panel); end: return rc; } @@ -1399,6 +1418,7 @@ end: static void dp_ctrl_off(struct dp_ctrl *dp_ctrl) { struct dp_ctrl_private *ctrl; + bool skip_op = false; if (!dp_ctrl) return; @@ -1409,7 +1429,7 @@ static void dp_ctrl_off(struct dp_ctrl *dp_ctrl) return; ctrl->catalog->fec_config(ctrl->catalog, false); - dp_ctrl_configure_source_link_params(ctrl, false); + dp_ctrl_configure_source_link_params(ctrl, false, skip_op); ctrl->catalog->reset(ctrl->catalog); /* Make sure DP is disabled before clk disable */ diff --git a/msm/dp/dp_ctrl.h b/msm/dp/dp_ctrl.h index 225a4bf54dafe..0f506d7294d8b 100644 --- a/msm/dp/dp_ctrl.h +++ b/msm/dp/dp_ctrl.h @@ -16,17 +16,17 @@ #include "dp_debug.h" struct dp_ctrl { - int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset); + int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset, bool skip_op); void (*deinit)(struct dp_ctrl *dp_ctrl); int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode, bool fec_en, - bool dsc_en, bool shallow); + bool dsc_en, bool shallow, bool skip_op); void (*off)(struct dp_ctrl *dp_ctrl); void (*abort)(struct dp_ctrl *dp_ctrl, bool abort); void (*isr)(struct dp_ctrl *dp_ctrl); bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl); - void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl); - int (*link_maintenance)(struct dp_ctrl *dp_ctrl); - int (*stream_on)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel); + void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl, bool skip_op); + int (*link_maintenance)(struct dp_ctrl *dp_ctrl, bool skip_op); + int (*stream_on)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel, bool skip_op); void (*stream_off)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel); void (*stream_pre_off)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel); void (*set_mst_channel_info)(struct dp_ctrl *dp_ctrl, diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 11349989c449b..292c6cef76a2e 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -136,6 +136,12 @@ static char *dp_display_state_name(enum dp_display_states state) static struct dp_display *g_dp_display[MAX_DP_ACTIVE_DISPLAY]; #define HPD_STRING_SIZE 30 +static char edp_display_primary[MAX_CMDLINE_PARAM_LEN]; + +static struct dp_display_boot_param boot_displays[MAX_DP_ACTIVE_BOOT_DISPLAY] = { + {.boot_param = edp_display_primary}, +}; + struct dp_hdcp_dev { void *fd; struct sde_hdcp_ops *ops; @@ -229,6 +235,13 @@ static const struct of_device_id dp_dt_match[] = { {} }; +bool is_skip_required(struct dp_display *display) +{ + if (!display) + return false; + return display->cont_splash_enabled; +} + static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp) { return dp->link->hdcp_status.hdcp_version && dp->hdcp.ops; @@ -304,6 +317,35 @@ static void dp_audio_enable(struct dp_display_private *dp, bool enable) } } +static int dp_display_parse_boot_display_selection(void) +{ + char *pos = NULL; + char disp_buf[MAX_CMDLINE_PARAM_LEN] = {'\0'}; + int i, j; + + for (i = 0; i < MAX_DP_ACTIVE_BOOT_DISPLAY; i++) { + strlcpy(disp_buf, boot_displays[i].boot_param, + MAX_CMDLINE_PARAM_LEN); + + pos = strnstr(disp_buf, ":", strlen(disp_buf)); + + /* Use ':' as a delimiter to retrieve the display name */ + if (!pos) { + DP_DEBUG("display name[%s]is not valid\n", disp_buf); + continue; + } + + for (j = 0; (disp_buf + j) < pos; j++) + boot_displays[i].name[j] = *(disp_buf + j); + + boot_displays[i].name[j] = '\0'; + + boot_displays[i].boot_disp_en = true; + } + + return 0; +} + static void dp_display_update_hdcp_status(struct dp_display_private *dp, bool reset) { @@ -1060,6 +1102,8 @@ static int dp_display_host_init(struct dp_display_private *dp) bool flip = false; bool reset; int rc = 0; + bool skip_op = is_skip_required(&dp->dp_display); + if (dp_display_state_is(DP_STATE_INITIALIZED)) { dp_display_state_log("[already initialized]"); @@ -1079,7 +1123,7 @@ static int dp_display_host_init(struct dp_display_private *dp) } dp->hpd->host_init(dp->hpd, &dp->catalog->hpd); - rc = dp->ctrl->init(dp->ctrl, flip, reset); + rc = dp->ctrl->init(dp->ctrl, flip, reset, skip_op); if (rc) { DP_WARN("Ctrl init Failed.\n"); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_CASE2, dp->state); @@ -1122,7 +1166,8 @@ static int dp_display_panel_ready(struct dp_display_private *dp) return -ETIMEDOUT; } } - dp->panel->init(dp->panel); + if (!dp->dp_display.cont_splash_enabled) + dp->panel->init(dp->panel); return 0; } @@ -1130,6 +1175,7 @@ static int dp_display_panel_ready(struct dp_display_private *dp) static int dp_display_host_ready(struct dp_display_private *dp) { int rc = 0; + bool skip_op; if (!dp_display_state_is(DP_STATE_INITIALIZED)) { rc = dp_display_host_init(dp); @@ -1160,11 +1206,11 @@ static int dp_display_host_ready(struct dp_display_private *dp) * the connect/disconnect notifications do not currently have any * sessions IDs. */ + skip_op = dp->dp_display.cont_splash_enabled; dp->aux->abort(dp->aux, false); dp->ctrl->abort(dp->ctrl, false); - dp->aux->init(dp->aux, dp->parser->aux_cfg); - + dp->aux->init(dp->aux, dp->parser->aux_cfg, skip_op); dp_display_state_add(DP_STATE_READY); /* log this as it results from user action of cable connection */ DP_INFO("[OK]\n"); @@ -1220,6 +1266,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) int rc = -EINVAL; unsigned long wait_timeout_ms; unsigned long t; + bool skip_op = is_skip_required(&dp->dp_display); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); @@ -1302,7 +1349,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp_display_mst_init(dp); rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, - dp->panel->fec_en, dp->panel->dsc_en, false); + dp->panel->fec_en, dp->panel->dsc_en, false, skip_op); if (rc) { dp_display_state_remove(DP_STATE_CONNECTED); goto end; @@ -1702,8 +1749,9 @@ static int dp_display_stream_enable(struct dp_display_private *dp, struct dp_panel *dp_panel) { int rc = 0; + bool skip_op = is_skip_required(&dp->dp_display); - rc = dp->ctrl->stream_on(dp->ctrl, dp_panel); + rc = dp->ctrl->stream_on(dp->ctrl, dp_panel, skip_op); if (dp->debug->tpg_state) dp_panel->tpg_config(dp_panel, true); @@ -1742,6 +1790,7 @@ static void dp_display_attention_work(struct work_struct *work) struct dp_display_private *dp = container_of(work, struct dp_display_private, attention_work); int rc = 0; + bool skip_op = is_skip_required(&dp->dp_display); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); @@ -1805,18 +1854,18 @@ static void dp_display_attention_work(struct work_struct *work) if (dp->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_PHY_TEST_PATTERN); - dp->ctrl->process_phy_test_request(dp->ctrl); + dp->ctrl->process_phy_test_request(dp->ctrl, skip_op); } if (dp->link->sink_request & DP_TEST_LINK_TRAINING) { SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_TRAINING); dp->link->send_test_response(dp->link); - rc = dp->ctrl->link_maintenance(dp->ctrl); + rc = dp->ctrl->link_maintenance(dp->ctrl, skip_op); } if (dp->link->sink_request & DP_LINK_STATUS_UPDATED) { SDE_EVT32_EXTERNAL(dp->state, DP_LINK_STATUS_UPDATED); - rc = dp->ctrl->link_maintenance(dp->ctrl); + rc = dp->ctrl->link_maintenance(dp->ctrl, skip_op); } if (!rc) @@ -2066,6 +2115,9 @@ static int dp_init_sub_modules(struct dp_display_private *dp) goto error_pll; } + if (dp->dp_display.cont_splash_enabled) + dp->pll->cont_splash_enabled = 1; + dp->power = dp_power_get(dp->parser, dp->pll); if (IS_ERR(dp->power)) { rc = PTR_ERR(dp->power); @@ -2283,6 +2335,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) struct dp_panel *dp_panel; int rc = 0; bool shallow_mode = true; + bool skip_op = is_skip_required(dp_display); if (!dp_display || !panel) { DP_ERR("invalid input\n"); @@ -2385,7 +2438,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, shallow_mode); + dp_panel->dsc_en, shallow_mode, skip_op); if (rc) goto end; @@ -2573,6 +2626,11 @@ end: static void dp_display_stream_post_enable(struct dp_display_private *dp, struct dp_panel *dp_panel) { + + if (dp->dp_display.cont_splash_enabled) { + dp->dp_display.cont_splash_enabled = false; + dp->pll->cont_splash_enabled = false; + } dp_panel->spd_config(dp_panel); dp_panel->setup_hdr(dp_panel, NULL, false, 0, true); } @@ -3953,6 +4011,10 @@ static int dp_display_probe(struct platform_device *pdev) dp_display->is_edp = (info->display_type == DRM_MODE_CONNECTOR_eDP) ? true : false; dp_display->edp_detect = dp_display_edp_detect; + if (dp_display->is_edp) { + if (dp_display_get_num_of_boot_displays()) + dp_display->cont_splash_enabled = 1; + } rc = component_add(&pdev->dev, &dp_display_comp_ops); if (rc) { DP_ERR("component add failed, rc=%d\n", rc); @@ -3985,6 +4047,17 @@ int dp_display_get_displays(void **displays, int count) return i; } +int dp_display_get_num_of_boot_displays(void) +{ + int i; + int count = 0; + + for (i = 0; i < MAX_DP_ACTIVE_BOOT_DISPLAY; i++) { + if (boot_displays[i].boot_disp_en == true) + count++; + } + return count; +} int dp_display_get_num_of_displays(void) { int i; @@ -4151,7 +4224,7 @@ static struct platform_driver dp_display_driver = { void __init dp_display_register(void) { - + dp_display_parse_boot_display_selection(); platform_driver_register(&dp_display_driver); } @@ -4159,3 +4232,7 @@ void __exit dp_display_unregister(void) { platform_driver_unregister(&dp_display_driver); } +module_param_string(edp_display0, edp_display_primary, MAX_CMDLINE_PARAM_LEN, + 0600); +MODULE_PARM_DESC(edp_display0, + "msm_drm.edp_display0=: where is 'primary dp display node name' "); diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index 892322fa2fc20..13181732cf321 100644 --- a/msm/dp/dp_display.h +++ b/msm/dp/dp_display.h @@ -16,6 +16,9 @@ #define DP_MST_SIM_MAX_PORTS 8 #define MAX_DP_ACTIVE_DISPLAY 8 +#define MAX_DP_ACTIVE_BOOT_DISPLAY 1 + +#define MAX_CMDLINE_PARAM_LEN 512 enum dp_drv_state { PM_DEFAULT, @@ -39,6 +42,13 @@ struct dp_display_info { u32 stream_cnt; }; +struct dp_display_boot_param { + char name[MAX_CMDLINE_PARAM_LEN]; + char *boot_param; + bool boot_disp_en; + void *disp; +}; + struct dp_mst_drm_cbs { void (*hpd)(void *display, bool hpd_status); void (*hpd_irq)(void *display, struct dp_mst_hpd_info *info); @@ -86,6 +96,7 @@ struct dp_display { void *dp_mst_prv_info; u32 max_mixer_count; u32 max_dsc_count; + bool cont_splash_enabled; int (*enable)(struct dp_display *dp_display, void *panel); int (*post_enable)(struct dp_display *dp_display, void *panel); @@ -149,9 +160,9 @@ struct dp_display { u32 strm_id, const char **display_type); int (*edp_detect)(struct dp_display *dp_display); }; - #if IS_ENABLED(CONFIG_DRM_MSM_DP) int dp_display_get_num_of_displays(void); +int dp_display_get_num_of_boot_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); @@ -160,6 +171,10 @@ static inline int dp_display_get_num_of_displays(void) { return 0; } +static inline int dp_display_get_num_of_boot_displays(void) +{ + return 0; +} static inline int dp_display_get_displays(void **displays, int count) { return 0; diff --git a/msm/dp/dp_drm.c b/msm/dp/dp_drm.c index 6e054af5dc3d5..1342726043e83 100644 --- a/msm/dp/dp_drm.c +++ b/msm/dp/dp_drm.c @@ -463,6 +463,7 @@ int dp_connector_get_info(struct drm_connector *connector, if (display && display->is_edp) { info->intf_type = DRM_MODE_CONNECTOR_eDP; info->display_type = SDE_CONNECTOR_PRIMARY; + info->is_connected = true; } else { info->capabilities |= MSM_DISPLAY_CAP_HOT_PLUG; } diff --git a/msm/dp/dp_pll.h b/msm/dp/dp_pll.h index fa56964ab5338..780a00c6dbfad 100644 --- a/msm/dp/dp_pll.h +++ b/msm/dp/dp_pll.h @@ -127,7 +127,7 @@ struct dp_pll { bool ssc_en; bool bonding_en; - + bool cont_splash_enabled; void *priv; struct dp_pll_db pll_db; struct dp_pll_vco_clk pll_clks[DP_PLL_NUM_CLKS]; diff --git a/msm/dp/edp_pll_7nm.c b/msm/dp/edp_pll_7nm.c index da26cb657884f..1f3b60a1f3253 100644 --- a/msm/dp/edp_pll_7nm.c +++ b/msm/dp/edp_pll_7nm.c @@ -277,6 +277,8 @@ static int edp_config_vco_rate_7nm(struct dp_pll *pll, DP_ERR("VCO Init DB failed\n"); return rc; } + if (pll->cont_splash_enabled) + return 0; dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x7d); /* Make sure the PLL register writes are done */ @@ -460,6 +462,11 @@ static int edp_pll_enable_7nm(struct dp_pll *pll) pll->aux->state &= ~DP_STATE_PLL_LOCKED; + if (pll->cont_splash_enabled) { + pll->aux->state |= DP_STATE_PLL_LOCKED; + return rc; + } + 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); @@ -790,6 +797,9 @@ static int edp_pll_vco_div_clk_set_rate(struct clk_hw *hw, unsigned long rate, pll_link = to_dp_vco_hw(hw); pll = pll_link->priv; + if (pll->cont_splash_enabled) + return rc; + if (rate != edp_pll_vco_div_clk_get_rate(pll)) { DP_ERR("unsupported rate %lu failed\n", rate); return rc; diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 308200bd8579c..b416840b5f7c0 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -4426,7 +4426,8 @@ static int _sde_kms_get_splash_data(struct sde_kms *sde_kms, * cont_splash_region should be collection of all memory regions * Ex: */ - num_displays = dsi_display_get_num_of_displays(); + num_displays = dsi_display_get_num_of_displays() + + dp_display_get_num_of_boot_displays(); num_regions = of_property_count_u64_elems(node, "reg") / 2; data->num_splash_displays = num_displays; From ea245e7982147e234773528e78f64db2135fb846 Mon Sep 17 00:00:00 2001 From: Sai Srujana Oruganti Date: Sun, 18 Jun 2023 21:35:42 +0530 Subject: [PATCH 20/23] disp: msm: sde: Remove pm vote at time of handoff Additional pm vote is added when cont splash is enabled for eDP. This vote has to be removed at the time of handoff. Change-Id: Ib1c0cc58f0be1031d405ca8941d3e7984b1e9f46 Signed-off-by: Sai Srujana Oruganti --- msm/sde/sde_kms.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index b416840b5f7c0..c7c4b224ee735 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1255,6 +1255,16 @@ static void _sde_kms_release_splash_resource(struct sde_kms *sde_kms, SDE_EVT32(DRMID(crtc), crtc->state->active, sde_kms->splash_data.num_splash_displays); + /*remove all votes if eDP displays are done with splash*/ + if (dp_display_get_num_of_boot_displays()) { + for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) + sde_power_data_bus_set_quota(phandle, i, + SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA, + phandle->ib_quota[i]); + pm_runtime_put_sync(sde_kms->dev->dev); + sde_kms->splash_data.num_splash_displays--; + } + for (i = 0; i < MAX_DSI_DISPLAYS; i++) { splash_display = &sde_kms->splash_data.splash_display[i]; if (splash_display->encoder && From 452e643352bd41612f5634d8b38d7be9cbc9a875 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Fri, 23 Jun 2023 17:42:52 +0530 Subject: [PATCH 21/23] disp: msm: dp: wait for resources init in case of cont splash Driver probe has to wait for dp resource initialization, in case of continuous splash to avoid any resource resets. Change-Id: Idaa506b14132d22b9bfe558c88ab6d33171b4f3b Signed-off-by: Nilesh Laad --- msm/dp/dp_display.c | 41 ++++++++++++++++++++++++++++++++++ msm/dp/dp_display.h | 5 +++++ msm/sde/sde_kms.c | 54 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 99 insertions(+), 1 deletion(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index b33930a7e5fbd..bcb6f8f57ff44 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -177,6 +177,7 @@ struct dp_display_private { struct dentry *root; struct completion notification_comp; struct completion attention_comp; + struct completion res_init_comp; struct dp_hpd *hpd; struct dp_parser *parser; @@ -317,6 +318,42 @@ static void dp_audio_enable(struct dp_display_private *dp, bool enable) } } +/** + * dp_display_cont_splash_config() - Initialize resources for continuous splash + * @dp_display: Pointer to dp display + * Returns: Zero on success + */ +int dp_display_cont_splash_config(void *display) +{ + struct dp_display *dp_display = display; + struct dp_display_private *dp; + int rc = 0; + + /* Vote for gdsc required to read register address space */ + if (!dp_display) { + DP_ERR("invalid input display param\n"); + return -EINVAL; + } + + dp = container_of(dp_display, struct dp_display_private, dp_display); + + if (!dp_display->is_edp) { + DP_ERR("splash handoff for dp not supported\n"); + return -EOPNOTSUPP; + } + + /* wait for the all the resource initialization and voting to + * complete, before we exit driver probe + */ + if (!wait_for_completion_timeout(&dp->res_init_comp, HZ * 10)) { + DP_WARN("timeout waiting for splash_res_init\n"); + rc = -ETIMEDOUT; + } + + return rc; +} + + static int dp_display_parse_boot_display_selection(void) { char *pos = NULL; @@ -1358,6 +1395,9 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp->process_hpd_connect = false; dp_display_set_mst_mgr_state(dp, true); + + /* all dp resource init are complete */ + complete_all(&dp->res_init_comp); end: mutex_unlock(&dp->session_lock); @@ -3990,6 +4030,7 @@ static int dp_display_probe(struct platform_device *pdev) init_completion(&dp->notification_comp); init_completion(&dp->attention_comp); + init_completion(&dp->res_init_comp); dp->pdev = pdev; dp->name = "drm_dp"; diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index 9d4cea9810d28..f7088a4aa93b0 100644 --- a/msm/dp/dp_display.h +++ b/msm/dp/dp_display.h @@ -168,6 +168,7 @@ int dp_display_get_num_of_boot_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); +int dp_display_cont_splash_config(void *display); #else static inline int dp_display_get_num_of_displays(void) { @@ -194,5 +195,9 @@ static inline int dp_connector_update_pps(struct drm_connector *connector, { return 0; } +static inline int dp_display_cont_splash_config(void *display) +{ + return 0; +} #endif /* CONFIG_DRM_MSM_DP */ #endif /* _DP_DISPLAY_H_ */ diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 5bdcdd0181574..e0df3cc96fd8a 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1800,7 +1800,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .set_colorspace = dp_connector_set_colorspace, .config_hdr = dp_connector_config_hdr, .cmd_transfer = NULL, - .cont_splash_config = NULL, + .cont_splash_config = dp_display_cont_splash_config, .cont_splash_res_disable = NULL, .get_panel_vfp = NULL, .update_pps = dp_connector_update_pps, @@ -3381,6 +3381,7 @@ static int sde_kms_cont_splash_config(struct msm_kms *kms, { void *display; struct dsi_display *dsi_display; + struct dp_display *dp_display; struct msm_display_info info; struct drm_encoder *encoder = NULL; struct drm_crtc *crtc = NULL; @@ -3531,6 +3532,57 @@ static int sde_kms_cont_splash_config(struct msm_kms *kms, } } + /* dp */ + for (i = 0; i < sde_kms->dp_display_count; ++i) { + display = sde_kms->dp_displays[i]; + dp_display = (struct dp_display *)display; + + if (!dp_display->cont_splash_enabled) { + SDE_DEBUG("DP-%d splash not enabled\n", i); + continue; + } + + if (dp_display->bridge->base.encoder) { + encoder = dp_display->bridge->base.encoder; + SDE_DEBUG("encoder name = %s\n", encoder->name); + } else { + SDE_DEBUG("Invalid encoder\n"); + break; + } + + mutex_lock(&dev->mode_config.mutex); + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + /** + * SDE_KMS doesn't attach more than one encoder to + * a DSI connector. So it is safe to check only with + * the first encoder entry. Revisit this logic if we + * ever have to support continuous splash for + * external displays in MST configuration. + */ + if (connector->encoder_ids[0] == encoder->base.id) + break; + } + + drm_connector_list_iter_end(&conn_iter); + if (!connector) { + SDE_ERROR("connector not initialized\n"); + mutex_unlock(&dev->mode_config.mutex); + return -EINVAL; + } + mutex_unlock(&dev->mode_config.mutex); + + /* Enable all irqs */ + sde_irq_update(kms, true); + + sde_conn = to_sde_connector(connector); + if (sde_conn && sde_conn->ops.cont_splash_config) + sde_conn->ops.cont_splash_config(sde_conn->display); + + /* Disable irqs */ + sde_irq_update(kms, false); + } + return rc; } From 4b0b77bceb9d950b9f78a4c3e8806222547c193b Mon Sep 17 00:00:00 2001 From: Venkata Prahlad Valluru Date: Mon, 17 Jul 2023 16:47:50 +0530 Subject: [PATCH 22/23] Revert "disp: msm: dp: skip hpd config" This reverts commit 88f941caec8c0c8c8795546ef632d180664faccb. Change-Id: I34443242d0acad6b2c3f92c76b7361f9086b1327 Signed-off-by: Venkata Prahlad Valluru --- msm/dp/dp_display.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 67e807116f9d1..04818e1481f7a 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1158,14 +1158,7 @@ static int dp_display_host_init(struct dp_display_private *dp) return rc; } -<<<<<<< HEAD - /* Skip hpd config when cont. splash is enbaled*/ - if (!skip_op) - dp->hpd->host_init(dp->hpd, &dp->catalog->hpd); - -======= dp->hpd->host_init(dp->hpd, &dp->catalog->hpd); ->>>>>>> 452e643352bd41612f5634d8b38d7be9cbc9a875 rc = dp->ctrl->init(dp->ctrl, flip, reset, skip_op); if (rc) { DP_WARN("Ctrl init Failed.\n"); From cfcbecd497c3f6337525c48764a3ac450aedd427 Mon Sep 17 00:00:00 2001 From: Nilesh Laad Date: Tue, 18 Jul 2023 13:35:13 +0530 Subject: [PATCH 23/23] disp: msm: dp: fix compilation errors Changes to fix compilation errors. Change-Id: I4f54447bdf2631f5bd17ef4bef13fecc6da0e106 Signed-off-by: Nilesh Laad --- msm/dp/dp_display.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 04818e1481f7a..e3c0c85d4d688 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -2666,11 +2666,7 @@ static void dp_display_stream_post_enable(struct dp_display_private *dp, struct dp_panel *dp_panel) { -<<<<<<< HEAD - if (dp->dp_display.is_edp) { -======= if (dp->dp_display.cont_splash_enabled) { ->>>>>>> 452e643352bd41612f5634d8b38d7be9cbc9a875 dp->dp_display.cont_splash_enabled = false; dp->pll->cont_splash_enabled = false; } @@ -4323,8 +4319,4 @@ void __exit dp_display_unregister(void) module_param_string(edp_display0, edp_display_primary, MAX_CMDLINE_PARAM_LEN, 0600); MODULE_PARM_DESC(edp_display0, -<<<<<<< HEAD - "msm_drm.edp_display0=: where is 'primary dp display node name' and where x represents index in the topology list"); -======= "msm_drm.edp_display0=: where is 'primary dp display node name' "); ->>>>>>> 452e643352bd41612f5634d8b38d7be9cbc9a875