Merge cfaa8dfa20 on remote branch

Change-Id: I6c14ac4c4a77619421785ca554f5fdaad421df0d
This commit is contained in:
Linux Build Service Account 2023-04-19 01:30:04 -07:00
commit 608c7a6360
24 changed files with 2376 additions and 265 deletions

View file

@ -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

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
@ -10,6 +11,8 @@
#include "dp_catalog.h"
#include "dp_reg.h"
#include "dp_debug.h"
#include "dp_link.h"
#include "dp_lphw_hpd.h"
#define DP_GET_MSB(x) (x >> 8)
#define DP_GET_LSB(x) (x & 0xff)
@ -2284,6 +2287,16 @@ end:
return 0;
}
static void dp_catalog_hpd_set_edp_mode(struct dp_catalog_hpd *hpd, bool is_edp)
{
if (!hpd) {
DP_ERR("invalid input\n");
return;
}
hpd->is_edp = is_edp;
}
static void dp_catalog_hpd_config_hpd(struct dp_catalog_hpd *hpd, bool en)
{
struct dp_catalog_private *catalog;
@ -2300,9 +2313,15 @@ static void dp_catalog_hpd_config_hpd(struct dp_catalog_hpd *hpd, bool en)
if (en) {
u32 reftimer = dp_read(DP_DP_HPD_REFTIMER);
/* Arm only the UNPLUG and HPD_IRQ interrupts */
/*
* Arm only the UNPLUG and HPD_IRQ interrupts for DP
* whereas for EDP arm only the HPD_IRQ interrupt
*/
dp_write(DP_DP_HPD_INT_ACK, 0xF);
dp_write(DP_DP_HPD_INT_MASK, 0xA);
if (hpd->is_edp)
dp_write(DP_DP_HPD_INT_MASK, 0x2);
else
dp_write(DP_DP_HPD_INT_MASK, 0xA);
/* Enable REFTIMER to count 1ms */
reftimer |= BIT(16);
@ -2323,7 +2342,7 @@ static void dp_catalog_hpd_config_hpd(struct dp_catalog_hpd *hpd, bool en)
static u32 dp_catalog_hpd_get_interrupt(struct dp_catalog_hpd *hpd)
{
u32 isr = 0;
u32 isr = 0, isr_mask = 0;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
@ -2338,7 +2357,35 @@ static u32 dp_catalog_hpd_get_interrupt(struct dp_catalog_hpd *hpd)
isr = dp_read(DP_DP_HPD_INT_STATUS);
dp_write(DP_DP_HPD_INT_ACK, (isr & 0xf));
return isr;
isr_mask = dp_read(DP_DP_HPD_INT_MASK);
return (isr & isr_mask);
}
static bool dp_catalog_hpd_wait_for_edp_panel_ready(struct dp_catalog_hpd *hpd)
{
u32 reg, state;
void __iomem *base;
bool success = true;
u32 const poll_sleep_us = 2000;
u32 const pll_timeout_us = 1000000;
struct dp_catalog_private *catalog;
catalog = dp_catalog_get_priv(hpd);
base = catalog->io.dp_aux->io.base;
reg = DP_DP_HPD_INT_STATUS;
if (readl_poll_timeout_atomic((base + reg), state,
((state & DP_HPD_STATE_STATUS_CONNECTED) > 0),
poll_sleep_us, pll_timeout_us)) {
DP_ERR("DP_HPD_STATE_STATUS CONNECTED bit is still low, status=%x\n", state);
success = false;
}
return success;
}
static void dp_catalog_audio_init(struct dp_catalog_audio *audio)
@ -2751,6 +2798,10 @@ static int dp_catalog_init(struct device *dev, struct dp_catalog *dp_catalog,
dp_catalog->sub = dp_catalog_get_v200(dev, dp_catalog,
&catalog->io);
break;
case DP_PHY_VERSION_5_0_0:
dp_catalog->sub = dp_catalog_get_v500(dev, dp_catalog,
&catalog->io);
break;
default:
goto end;
}
@ -2831,6 +2882,8 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser)
struct dp_catalog_hpd hpd = {
.config_hpd = dp_catalog_hpd_config_hpd,
.get_interrupt = dp_catalog_hpd_get_interrupt,
.wait_for_edp_panel_ready = dp_catalog_hpd_wait_for_edp_panel_ready,
.set_edp_mode = dp_catalog_hpd_set_edp_mode,
};
struct dp_catalog_audio audio = {
.init = dp_catalog_audio_init,
@ -2881,6 +2934,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser)
strlcpy(catalog->exe_mode, "hw", sizeof(catalog->exe_mode));
dp_catalog = &catalog->dp_catalog;
dp_catalog->parser = parser;
dp_catalog->aux = aux;
dp_catalog->ctrl = ctrl;

View file

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
@ -105,8 +106,12 @@ struct dp_catalog_ctrl {
};
struct dp_catalog_hpd {
bool is_edp;
void (*config_hpd)(struct dp_catalog_hpd *hpd, bool en);
u32 (*get_interrupt)(struct dp_catalog_hpd *hpd);
bool (*wait_for_edp_panel_ready)(struct dp_catalog_hpd *hpd);
void (*set_edp_mode)(struct dp_catalog_hpd *hpd, bool is_edp);
};
#define HEADER_BYTE_2_BIT 0
@ -256,6 +261,7 @@ struct dp_catalog {
struct dp_catalog_hpd hpd;
struct dp_catalog_sub *sub;
struct dp_parser *parser;
void (*set_exe_mode)(struct dp_catalog *dp_catalog, char *mode);
int (*get_reg_dump)(struct dp_catalog *dp_catalog,
@ -336,4 +342,7 @@ struct dp_catalog_sub *dp_catalog_get_v420(struct device *dev,
struct dp_catalog_sub *dp_catalog_get_v200(struct device *dev,
struct dp_catalog *catalog, struct dp_catalog_io *io);
struct dp_catalog_sub *dp_catalog_get_v500(struct device *dev,
struct dp_catalog *catalog, struct dp_catalog_io *io);
#endif /* _DP_CATALOG_H_ */

466
msm/dp/dp_catalog_v500.c Normal file
View file

@ -0,0 +1,466 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
#include "dp_catalog.h"
#include "dp_reg.h"
#include "dp_debug.h"
#define MMSS_DP_M_OFF (0x8)
#define MMSS_DP_N_OFF (0xC)
#define dp_catalog_get_priv_v500(x) ({ \
struct dp_catalog *catalog; \
catalog = container_of(x, struct dp_catalog, x); \
container_of(catalog->sub, \
struct dp_catalog_private_v500, sub); \
})
#define dp_read(x) ({ \
catalog->sub.read(catalog->dpc, io_data, x); \
})
#define dp_write(x, y) ({ \
catalog->sub.write(catalog->dpc, io_data, x, y); \
})
#define MAX_VOLTAGE_LEVELS 4
#define MAX_PRE_EMP_LEVELS 4
enum {
TX_DRIVE_MODE_LOW_SWING_LOW_HBR = 0,
TX_DRIVE_MODE_HIGH_SWING_LOW_HBR,
TX_DRIVE_MODE_LOW_SWING_HIGH_HBR,
TX_DRIVE_MODE_HIGH_SWING_HIGH_HBR,
TX_DRIVE_MODE_DP,
TX_DRIVE_MODE_MINIDP,
TX_DRIVE_MODE_MAX,
};
static u8 const vm_pre_emphasis
[TX_DRIVE_MODE_MAX][MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
/* pe0, 0 db; pe1, 2.0 db; pe2, 3.6 db; pe3, 6.0 db */
{ // Low swing/pre-emphasis, low HBR
{0x05, 0x11, 0x17, 0x1D}, /* sw0, 0.2v */
{0x05, 0x11, 0x18, 0xFF}, /* sw1, 0.25 v */
{0x06, 0x11, 0xFF, 0xFF}, /* sw2, 0.3 v */
{0x00, 0xFF, 0xFF, 0xFF} /* sw3, 0.35 v */
},
{ // High swing/pre-emphasis, low HBR
{0x02, 0x0F, 0x17, 0x1D}, /* sw0, 0.3v */
{0x02, 0x0F, 0x17, 0xFF}, /* sw1, 0.35v */
{0x02, 0x0E, 0xFF, 0xFF}, /* sw2, 0.4v */
{0x00, 0xFF, 0xFF, 0xFF} /* sw3, 0.45v */
},
{ // Low swing/pre-emphasis, high HBR
{0x0C, 0x15, 0x19, 0x1E}, /* sw0, 0.2v */
{0x09, 0x14, 0x19, 0xFF}, /* sw1, 0.25v */
{0x0F, 0x14, 0xFF, 0xFF}, /* sw2, 0.3v */
{0x0D, 0xFF, 0xFF, 0xFF} /* sw3, 0.35v */
},
{ // High swing/pre-emphasis, high HBR
{0x08, 0x11, 0x16, 0x1B}, /* sw0, 0.3v */
{0x00, 0x0C, 0x13, 0xFF}, /* sw1, 0.35v */
{0x05, 0x10, 0xFF, 0xFF}, /* sw2, 0.4v */
{0x00, 0xFF, 0xFF, 0xFF} /* sw3, 0.45v */
},
{ // DP-only, DP/USB
{0x20, 0x2E, 0x35, 0xFF}, /* sw0, 0.4v */
{0x20, 0x2E, 0x35, 0xFF}, /* sw1, 0.6v */
{0x20, 0x2E, 0xFF, 0xFF}, /* sw2, 0.8v */
{0xFF, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */
},
{ // MiniDP-only
{0x00, 0x0E, 0x17, 0xFF}, /* sw0, 0.4v */
{0x00, 0x0D, 0x16, 0xFF}, /* sw1, 0.6v */
{0x00, 0x0D, 0xFF, 0xFF}, /* sw2, 0.8v */
{0xFF, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */
},
};
/* voltage swing, 0.2v and 1.0v are not support */
static u8 const vm_voltage_swing
[TX_DRIVE_MODE_MAX][MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
/* pe0, 0 db; pe1, 2.0 db; pe2, 3.6 db; pe3, 6.0 db */
{ // Low swing/pre-emphasis, low HBR
{0x07, 0x0F, 0x16, 0x1F}, /* sw0, 0.2v */
{0x0D, 0x16, 0x1E, 0xFF}, /* sw1, 0.25 v */
{0x11, 0x1B, 0xFF, 0xFF}, /* sw1, 0.3 v */
{0x16, 0xFF, 0xFF, 0xFF} /* sw1, 0.35 v */
},
{ // High swing/pre-emphasis, low HBR
{0x05, 0x0C, 0x14, 0x1D}, /* sw0, 0.3v */
{0x08, 0x13, 0x1B, 0xFF}, /* sw1, 0.35 v */
{0x0C, 0x17, 0xFF, 0xFF}, /* sw1, 0.4 v */
{0x14, 0xFF, 0xFF, 0xFF} /* sw1, 0.45 v */
},
{ // Low swing/pre-emphasis, high HBR
{0x06, 0x11, 0x16, 0x1B}, /* sw0, 0.2v */
{0x0B, 0x19, 0x1F, 0xFF}, /* sw1, 0.25 v */
{0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.3 v */
{0x1F, 0xFF, 0xFF, 0xFF} /* sw1, 0.35 v */
},
{ // High swing/pre-emphasis, high HBR
{0x0A, 0x11, 0x17, 0x1F}, /* sw0, 0.3v */
{0x0C, 0x14, 0x1D, 0xFF}, /* sw1, 0.35 v */
{0x15, 0x1F, 0xFF, 0xFF}, /* sw1, 0.4 v */
{0x17, 0xFF, 0xFF, 0xFF} /* sw1, 0.45 v */
},
{ // DP-only, DP/USB
{0x02, 0x12, 0x16, 0x1A}, /* sw0, 0.4v */
{0x09, 0x19, 0x1F, 0xFF}, /* sw1, 0.6v */
{0x10, 0x1F, 0xFF, 0xFF}, /* sw2, 0.8v */
{0x1F, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */
},
{ // MiniDP-only
{0x07, 0x0F, 0x16, 0x1F}, /* sw0, 0.4v */
{0x11, 0x1E, 0x1F, 0xFF}, /* sw1, 0.6v */
{0x16, 0x1F, 0xFF, 0xFF}, /* sw2, 0.8v */
{0x1F, 0xFF, 0xFF, 0xFF} /* sw3, 1.2 v, optional */
},
};
struct dp_catalog_private_v500 {
struct device *dev;
struct dp_catalog_sub sub;
struct dp_catalog_io *io;
struct dp_catalog *dpc;
struct dp_catalog_ctrl dp_ctrl;
struct dp_catalog_audio dp_audio;
};
static void dp_catalog_aux_setup_v500(struct dp_catalog_aux *aux,
struct dp_aux_cfg *cfg)
{
struct dp_catalog_private_v500 *catalog;
struct dp_io_data *io_data;
u32 revision_id = 0;
int i = 0;
if (!aux || !cfg) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv_v500(aux);
io_data = catalog->io->dp_phy;
/* PHY will not work if DP_PHY_MODE is not set */
revision_id = (dp_read(DP_PHY_REVISION_ID3) & 0xFF) << 8;
revision_id |= (dp_read(DP_PHY_REVISION_ID2) & 0xFF);
DP_DEBUG("DP phy revision_id: 0x%X\n", revision_id);
dp_write(DP_PHY_PD_CTL_V500, 0x65);
wmb(); /* make sure PD programming happened */
/* Turn on BIAS current for PHY/PLL */
io_data = catalog->io->dp_pll;
dp_write(QSERDES_COM_BIAS_EN_CLKBUFLR_EN,
0x17);
wmb(); /* make sure BIAS programming happened */
io_data = catalog->io->dp_phy;
dp_write(DP_PHY_PD_CTL_V500, 0x2);
wmb(); /* make sure PD programming happened */
udelay(1000);
dp_write(DP_PHY_PD_CTL_V500, 0x7d);
wmb(); /* make sure PD programming happened */
udelay(1000);
if (revision_id >= 0x5000)
dp_write(DP_PHY_MODE_V500, 0xfc);
else
dp_write(DP_PHY_MODE, 0xfc);
io_data = catalog->io->dp_phy;
/* DP AUX CFG register programming */
for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
DP_DEBUG("%s: offset=0x%08x, value=0x%08x\n",
dp_phy_aux_config_type_to_string(i),
cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
dp_write(cfg[i].offset,
cfg[i].lut[cfg[i].current_index]);
}
wmb(); /* make sure DP AUX CFG programming happened */
dp_write(DP_PHY_AUX_INTERRUPT_MASK_V500,
0x1F);
}
static void dp_catalog_aux_clear_hw_interrupts_v500(struct dp_catalog_aux *aux)
{
struct dp_catalog_private_v500 *catalog;
struct dp_io_data *io_data;
u32 data = 0;
if (!aux) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv_v500(aux);
io_data = catalog->io->dp_phy;
data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS_V500);
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V500, 0x1f);
wmb(); /* make sure 0x1f is written before next write */
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V500, 0x9f);
wmb(); /* make sure 0x9f is written before next write */
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V500, 0);
wmb(); /* make sure register is cleared */
}
static void dp_catalog_panel_config_msa_v500(struct dp_catalog_panel *panel,
u32 rate, u32 stream_rate_khz)
{
u32 pixel_m, pixel_n;
u32 mvid, nvid, reg_off = 0, mvid_off = 0, nvid_off = 0;
u32 const nvid_fixed = 0x8000;
u32 const link_rate_hbr2 = 540000;
u32 const link_rate_hbr3 = 810000;
struct dp_catalog_private_v500 *catalog;
struct dp_io_data *io_data;
if (!panel || !rate) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv_v500(panel);
io_data = catalog->io->dp_mmss_cc;
if (panel->stream_id == DP_STREAM_1)
reg_off = catalog->dpc->parser->pixel_base_off[1];
else
reg_off = catalog->dpc->parser->pixel_base_off[0];
pixel_m = dp_read(reg_off + MMSS_DP_M_OFF);
pixel_n = dp_read(reg_off + MMSS_DP_N_OFF);
DP_DEBUG("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);
mvid = (pixel_m & 0xFFFF) * 5;
nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
if (nvid < nvid_fixed) {
u32 temp;
temp = (nvid_fixed / nvid) * nvid;
mvid = (nvid_fixed / nvid) * mvid;
nvid = temp;
}
DP_DEBUG("rate = %d\n", rate);
if (panel->widebus_en)
mvid <<= 1;
if (link_rate_hbr2 == rate)
nvid *= 2;
if (link_rate_hbr3 == rate)
nvid *= 3;
io_data = catalog->io->dp_link;
if (panel->stream_id == DP_STREAM_1) {
mvid_off = DP1_SOFTWARE_MVID - DP_SOFTWARE_MVID;
nvid_off = DP1_SOFTWARE_NVID - DP_SOFTWARE_NVID;
}
DP_DEBUG("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
dp_write(DP_SOFTWARE_MVID + mvid_off, mvid);
dp_write(DP_SOFTWARE_NVID + nvid_off, nvid);
}
static void dp_catalog_ctrl_phy_lane_cfg_v500(struct dp_catalog_ctrl *ctrl,
bool flipped, u8 ln_cnt)
{
u32 info = 0x0;
struct dp_catalog_private_v500 *catalog;
u8 orientation = BIT(!!flipped);
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv_v500(ctrl);
io_data = catalog->io->dp_phy;
info |= (ln_cnt & 0x0F);
info |= ((orientation & 0x0F) << 4);
DP_DEBUG("Shared Info = 0x%x\n", info);
dp_write(DP_PHY_SPARE0_V500, info);
}
static void dp_catalog_ctrl_update_vx_px_v500(struct dp_catalog_ctrl *ctrl,
u8 v_level, u8 p_level, bool high)
{
struct dp_catalog *dp_catalog;
struct dp_catalog_private_v500 *catalog;
struct dp_io_data *io_data;
u8 value0, value1, ldo_cfg;
int index;
if (!ctrl || !((v_level < MAX_VOLTAGE_LEVELS)
&& (p_level < MAX_PRE_EMP_LEVELS))) {
DP_ERR("invalid input\n");
return;
}
dp_catalog = container_of(ctrl, struct dp_catalog, ctrl);
catalog = dp_catalog_get_priv_v500(ctrl);
DP_DEBUG("hw: v=%d p=%d\n", v_level, p_level);
io_data = catalog->io->dp_phy;
switch (dp_catalog->parser->hw_cfg.phy_mode) {
case DP_PHY_MODE_DP:
case DP_PHY_MODE_UNKNOWN:
index = TX_DRIVE_MODE_DP;
break;
case DP_PHY_MODE_MINIDP:
index = TX_DRIVE_MODE_MINIDP;
break;
case DP_PHY_MODE_EDP:
default:
if (!high)
index = TX_DRIVE_MODE_LOW_SWING_LOW_HBR;
else
index = TX_DRIVE_MODE_LOW_SWING_HIGH_HBR;
break;
case DP_PHY_MODE_EDP_HIGH_SWING:
if (!high)
index = TX_DRIVE_MODE_HIGH_SWING_LOW_HBR;
else
index = TX_DRIVE_MODE_HIGH_SWING_HIGH_HBR;
break;
}
value0 = vm_voltage_swing[index][v_level][p_level];
value1 = vm_pre_emphasis[index][v_level][p_level];
ldo_cfg = 0x01;
/* Configure host and panel only if both values are allowed */
if (value0 != 0xFF && value1 != 0xFF) {
io_data = catalog->io->dp_ln_tx0;
dp_write(TXn_LDO_CONFIG_V500,
ldo_cfg);
dp_write(TXn_TX_DRV_LVL_V500,
value0);
dp_write(TXn_TX_EMP_POST1_LVL_V500,
value1);
io_data = catalog->io->dp_ln_tx1;
dp_write(TXn_LDO_CONFIG_V500,
ldo_cfg);
dp_write(TXn_TX_DRV_LVL_V500,
value0);
dp_write(TXn_TX_EMP_POST1_LVL_V500,
value1);
DP_DEBUG("hw: vx_value=0x%x px_value=0x%x ldo_value=0x%x\n",
value0, value1, ldo_cfg);
} else {
DP_ERR("invalid vx (0x%x=0x%x), px (0x%x=0x%x)\n",
v_level, value0, p_level, value1);
}
}
static void dp_catalog_ctrl_lane_pnswap_v500(struct dp_catalog_ctrl *ctrl,
u8 ln_pnswap)
{
struct dp_catalog_private_v500 *catalog;
struct dp_io_data *io_data;
u32 cfg0, cfg1;
catalog = dp_catalog_get_priv_v500(ctrl);
cfg0 = 0x00;
cfg1 = 0x00;
cfg0 |= ((ln_pnswap >> 0) & 0x1) << 0;
cfg0 |= ((ln_pnswap >> 1) & 0x1) << 1;
cfg1 |= ((ln_pnswap >> 2) & 0x1) << 0;
cfg1 |= ((ln_pnswap >> 3) & 0x1) << 1;
io_data = catalog->io->dp_ln_tx0;
dp_write(TXn_TX_POL_INV_V500, cfg0);
io_data = catalog->io->dp_ln_tx1;
dp_write(TXn_TX_POL_INV_V500, cfg1);
}
static void dp_catalog_ctrl_usb_reset_v500(struct dp_catalog_ctrl *ctrl,
bool flip)
{
// USB isn't available
}
static int dp_catalog_ctrl_late_phy_init_v500(struct dp_catalog_ctrl *ctrl,
u8 lane_cnt, bool flipped)
{
// No late init
return 0;
}
static void dp_catalog_put_v500(struct dp_catalog *catalog)
{
struct dp_catalog_private_v500 *catalog_priv;
if (!catalog)
return;
catalog_priv = container_of(catalog->sub,
struct dp_catalog_private_v500, sub);
}
struct dp_catalog_sub *dp_catalog_get_v500(struct device *dev,
struct dp_catalog *catalog, struct dp_catalog_io *io)
{
struct dp_catalog_private_v500 *catalog_priv;
if (!dev || !catalog) {
DP_ERR("invalid input\n");
return ERR_PTR(-EINVAL);
}
catalog_priv = devm_kzalloc(dev, sizeof(*catalog_priv), GFP_KERNEL);
if (!catalog_priv)
return ERR_PTR(-ENOMEM);
catalog_priv->dev = dev;
catalog_priv->io = io;
catalog_priv->dpc = catalog;
catalog_priv->dp_ctrl = catalog->ctrl;
catalog_priv->dp_audio = catalog->audio;
catalog_priv->sub.put = dp_catalog_put_v500;
catalog->aux.setup = dp_catalog_aux_setup_v500;
catalog->aux.clear_hw_interrupts =
dp_catalog_aux_clear_hw_interrupts_v500;
catalog->panel.config_msa = dp_catalog_panel_config_msa_v500;
catalog->ctrl.phy_lane_cfg = dp_catalog_ctrl_phy_lane_cfg_v500;
catalog->ctrl.update_vx_px = dp_catalog_ctrl_update_vx_px_v500;
catalog->ctrl.lane_pnswap = dp_catalog_ctrl_lane_pnswap_v500;
catalog->ctrl.usb_reset = dp_catalog_ctrl_usb_reset_v500;
catalog->ctrl.late_phy_init = dp_catalog_ctrl_late_phy_init_v500;
return &catalog_priv->sub;
}

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
@ -11,6 +12,7 @@
#include "dp_ctrl.h"
#include "dp_debug.h"
#include "sde_dbg.h"
#include "dp_pll.h"
#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
@ -62,6 +64,7 @@ struct dp_ctrl_private {
struct dp_power *power;
struct dp_parser *parser;
struct dp_catalog_ctrl *catalog;
struct dp_pll *pll;
struct completion idle_comp;
struct completion video_comp;
@ -618,6 +621,14 @@ static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl,
{
u32 num = ctrl->parser->mp[clk_type].num_clk;
struct dss_clk *cfg = ctrl->parser->mp[clk_type].clk_config;
struct dp_catalog *catalog;
catalog = container_of(ctrl->catalog, struct dp_catalog, ctrl);
if (catalog->hpd.is_edp) {
/* convert to HZ for byte2 ops */
rate *= 1000;
}
while (num && strcmp(cfg->clk_name, name)) {
num--;
@ -642,6 +653,22 @@ static int dp_ctrl_enable_link_clock(struct dp_ctrl_private *ctrl)
dp_ctrl_set_clock_rate(ctrl, "link_clk", type, rate);
if (ctrl->pll->pll_cfg) {
ret = ctrl->pll->pll_cfg(ctrl->pll, rate);
if (ret < 0) {
DP_ERR("DP pll cfg failed\n");
return ret;
}
}
if (ctrl->pll->pll_prepare) {
ret = ctrl->pll->pll_prepare(ctrl->pll);
if (ret < 0) {
DP_ERR("DP pll prepare failed\n");
return ret;
}
}
ret = ctrl->power->clk_enable(ctrl->power, type, true);
if (ret) {
DP_ERR("Unabled to start link clocks\n");
@ -653,7 +680,15 @@ static int dp_ctrl_enable_link_clock(struct dp_ctrl_private *ctrl)
static void dp_ctrl_disable_link_clock(struct dp_ctrl_private *ctrl)
{
int rc;
ctrl->power->clk_enable(ctrl->power, DP_LINK_PM, false);
if (ctrl->pll->pll_unprepare) {
rc = ctrl->pll->pll_unprepare(ctrl->pll);
if (rc < 0)
DP_ERR("pll unprepare failed\n");
}
}
static void dp_ctrl_select_training_pattern(struct dp_ctrl_private *ctrl,
@ -1475,6 +1510,7 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
ctrl->aux = in->aux;
ctrl->link = in->link;
ctrl->catalog = in->catalog;
ctrl->pll = in->pll;
ctrl->dev = in->dev;
ctrl->mst_mode = false;
ctrl->fec_mode = false;

View file

@ -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);

View file

@ -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;
}

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
@ -67,6 +68,10 @@ enum dp_display_states {
DP_STATE_TUI_ACTIVE = BIT(11),
};
struct dp_display_type_info {
int display_type;
};
static char *dp_display_state_name(enum dp_display_states state)
{
static char buf[SZ_1K];
@ -128,7 +133,7 @@ static char *dp_display_state_name(enum dp_display_states state)
return buf;
}
static struct dp_display *g_dp_display;
static struct dp_display *g_dp_display[MAX_DP_ACTIVE_DISPLAY];
#define HPD_STRING_SIZE 30
struct dp_hdcp_dev {
@ -162,6 +167,7 @@ struct dp_display_private {
struct platform_device *pdev;
struct device_node *aux_switch_node;
bool aux_switch_ready;
struct dentry *root;
struct completion notification_comp;
struct completion attention_comp;
@ -200,10 +206,26 @@ struct dp_display_private {
bool process_hpd_connect;
struct notifier_block usb_nb;
u32 cell_idx;
u32 intf_idx[DP_STREAM_MAX];
u32 phy_idx;
u32 stream_cnt;
};
static const struct dp_display_type_info dp_info = {
.display_type = DRM_MODE_CONNECTOR_DisplayPort,
};
static const struct dp_display_type_info edp_info = {
.display_type = DRM_MODE_CONNECTOR_eDP,
};
static const struct of_device_id dp_dt_match[] = {
{.compatible = "qcom,dp-display"},
{ .compatible = "qcom,dp-display",
.data = &dp_info,},
{ .compatible = "qcom,edp-display",
.data = &edp_info,},
{}
};
@ -741,6 +763,32 @@ static int dp_display_post_hw_acquire(void *data)
return 0;
}
static int dp_display_get_cell_info(struct dp_display_private *dp)
{
struct device_node *of_node = dp->pdev->dev.of_node;
int i, rc;
of_property_read_u32(of_node,
"cell-index", &dp->cell_idx);
if (of_property_read_bool(of_node, "qcom,mst-enable"))
dp->stream_cnt = DP_STREAM_MAX;
of_property_read_u32_index(of_node,
"qcom,intf-index", 0, &dp->intf_idx[0]);
for (i = 1; i < dp->stream_cnt; i++) {
rc = of_property_read_u32_index(of_node,
"qcom,intf-index", i, &dp->intf_idx[i]);
if (rc)
dp->intf_idx[i] = dp->intf_idx[0] + i;
}
of_property_read_u32(of_node,
"qcom,phy-index", &dp->phy_idx);
return 0;
}
static int dp_display_bind(struct device *dev, struct device *master,
void *data)
@ -1053,6 +1101,32 @@ error_ctrl:
return rc;
}
static int dp_display_panel_ready(struct dp_display_private *dp)
{
int rc = 0;
if (dp->dp_display.is_edp) {
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, true);
if (rc) {
DP_ERR("Cannot turn edp panel power on");
return rc;
}
if (!(dp->catalog->hpd.wait_for_edp_panel_ready(&dp->catalog->hpd))) {
DP_ERR("EDP PANEL is not ready yet, powering off panel\n");
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false);
if (rc) {
DP_ERR("Cannot turn edp panel power off");
return rc;
}
return -ETIMEDOUT;
}
}
dp->panel->init(dp->panel);
return 0;
}
static int dp_display_host_ready(struct dp_display_private *dp)
{
int rc = 0;
@ -1090,7 +1164,6 @@ static int dp_display_host_ready(struct dp_display_private *dp)
dp->ctrl->abort(dp->ctrl, false);
dp->aux->init(dp->aux, dp->parser->aux_cfg);
dp->panel->init(dp->panel);
dp_display_state_add(DP_STATE_READY);
/* log this as it results from user action of cable connection */
@ -1204,6 +1277,8 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
goto end;
}
rc = dp_display_panel_ready(dp);
dp->link->psm_config(dp->link, &dp->panel->link_info, false);
dp->debug->psm_enabled = false;
@ -1336,6 +1411,54 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp)
return rc;
}
static int dp_display_fsa4480_callback(struct notifier_block *self,
unsigned long event, void *data)
{
return 0;
}
static int dp_display_init_aux_switch(struct dp_display_private *dp)
{
int rc = 0;
struct notifier_block nb;
const u32 max_retries = 50;
u32 retry;
if (dp->aux_switch_ready)
return rc;
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
nb.notifier_call = dp_display_fsa4480_callback;
nb.priority = 0;
/*
* Iteratively wait for reg notifier which confirms that fsa driver is probed.
* Bootup DP with cable connected usecase can hit this scenario.
*/
for (retry = 0; retry < max_retries; retry++) {
rc = fsa4480_reg_notifier(&nb, dp->aux_switch_node);
if (rc == 0) {
DP_DEBUG("registered notifier successfully\n");
dp->aux_switch_ready = true;
break;
}
DP_DEBUG("failed to register notifier retry=%d rc=%d\n", retry, rc);
msleep(100);
}
if (retry == max_retries) {
DP_WARN("Failed to register fsa notifier\n");
dp->aux_switch_ready = false;
return rc;
}
fsa4480_unreg_notifier(&nb, dp->aux_switch_node);
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, rc);
return rc;
}
static int dp_display_usbpd_configure_cb(struct device *dev)
{
int rc = 0;
@ -1353,7 +1476,11 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
}
if (!dp->debug->sim_mode && !dp->parser->no_aux_switch
&& !dp->parser->gpio_aux_switch) {
&& !dp->parser->gpio_aux_switch && dp->aux_switch_node) {
rc = dp_display_init_aux_switch(dp);
if (rc)
return rc;
rc = dp->aux->aux_switch(dp->aux, true, dp->hpd->orientation);
if (rc)
return rc;
@ -1866,6 +1993,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
{
int rc = 0;
bool hdcp_disabled;
const char *phandle = "qcom,dp-aux-switch";
struct device *dev = &dp->pdev->dev;
struct dp_hpd_cb *cb = &dp->hpd_cb;
struct dp_ctrl_in ctrl_in = {
@ -1897,8 +2025,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
goto error_catalog;
}
g_dp_display->is_mst_supported = dp->parser->has_mst;
g_dp_display->dsc_cont_pps = dp->parser->dsc_continuous_pps;
dp->dp_display.is_mst_supported = dp->parser->has_mst;
dp->dp_display.dsc_cont_pps = dp->parser->dsc_continuous_pps;
dp->catalog = dp_catalog_get(dev, dp->parser);
if (IS_ERR(dp->catalog)) {
@ -1908,6 +2036,10 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
goto error_catalog;
}
dp->catalog->hpd.set_edp_mode(&dp->catalog->hpd, dp->dp_display.is_edp);
dp->aux_switch_node = of_parse_phandle(dp->pdev->dev.of_node, phandle, 0);
if (!dp->aux_switch_node)
DP_DEBUG("cannot parse %s handle\n", phandle);
dp->aux = dp_aux_get(dev, &dp->catalog->aux, dp->parser,
dp->aux_switch_node);
if (IS_ERR(dp->aux)) {
@ -1978,6 +2110,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
ctrl_in.power = dp->power;
ctrl_in.catalog = &dp->catalog->ctrl;
ctrl_in.parser = dp->parser;
ctrl_in.pll = dp->pll;
dp->ctrl = dp_ctrl_get(&ctrl_in);
if (IS_ERR(dp->ctrl)) {
@ -2147,6 +2280,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel)
struct dp_display_private *dp;
struct dp_panel *dp_panel;
int rc = 0;
bool shallow_mode = true;
if (!dp_display || !panel) {
DP_ERR("invalid input\n");
@ -2226,6 +2360,13 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel)
goto end;
}
rc = dp_display_panel_ready(dp);
if (rc) {
dp_display_host_unready(dp);
dp_display_host_deinit(dp);
goto end;
}
if (dp->debug->psm_enabled) {
dp->link->psm_config(dp->link, &dp->panel->link_info, false);
dp->debug->psm_enabled = false;
@ -2242,7 +2383,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel)
* and required things.
*/
rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, dp_panel->fec_en,
dp_panel->dsc_en, true);
dp_panel->dsc_en, shallow_mode);
if (rc)
goto end;
@ -2364,6 +2505,23 @@ static int dp_display_enable(struct dp_display *dp_display, void *panel)
if (rc)
goto end;
/*edp backlight enable and edp pwm enable*/
if (dp_display->is_edp) {
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_PWR, true);
if (rc) {
DP_ERR("Cannot turn edp backlight power on");
goto end;
}
usleep_range(99000, 100000);
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_PWM, true);
if (rc) {
DP_ERR("Cannot turn edp PWM on ");
goto end;
}
}
dp_display_update_dsc_resources(dp, panel, true);
dp_display_state_add(DP_STATE_ENABLED);
end:
@ -2383,6 +2541,7 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel)
{
struct dp_display_private *dp;
struct dp_panel *dp_panel;
int rc = 0;
if (!dp_display || !panel) {
DP_ERR("invalid input\n");
@ -2421,6 +2580,14 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel)
dp_display_stream_post_enable(dp, dp_panel);
if (dp_display->is_edp) {
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_EN, true);
if (rc) {
DP_ERR("Cannot turn edp backlight power on");
goto end;
}
}
cancel_delayed_work_sync(&dp->hdcp_cb_work);
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ);
@ -2474,6 +2641,14 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel)
goto end;
}
if (dp_display->is_edp) {
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_EN, false);
if (rc) {
DP_ERR("Cannot turn edp backlight power off");
goto end;
}
}
dp_display_state_add(DP_STATE_HDCP_ABORTED);
cancel_delayed_work_sync(&dp->hdcp_cb_work);
if (dp_display_is_hdcp_enabled(dp) &&
@ -2522,7 +2697,7 @@ end:
static int dp_display_disable(struct dp_display *dp_display, void *panel)
{
int i;
int i, rc = 0;
struct dp_display_private *dp = NULL;
struct dp_panel *dp_panel = NULL;
struct dp_link_hdcp_status *status;
@ -2549,6 +2724,16 @@ static int dp_display_disable(struct dp_display *dp_display, void *panel)
goto end;
}
if (dp_display->is_edp) {
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_PWR, false);
if (rc)
DP_ERR("Cannot turn edp backlight power off\n");
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_PWM, false);
if (rc)
DP_ERR("Cannot turn edp PWM off\n");
}
dp_display_stream_disable(dp, dp_panel);
dp_display_update_dsc_resources(dp, dp_panel, false);
@ -2617,6 +2802,7 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel)
struct dp_display_private *dp;
struct dp_panel *dp_panel = panel;
u32 flags = 0;
int rc = 0;
if (!dp_display || !panel) {
DP_ERR("invalid input\n");
@ -2654,6 +2840,13 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel)
dp_display_state_add(DP_STATE_SRC_PWRDN);
}
if (dp_display->is_edp) {
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false);
if (rc) {
DP_ERR("Cannot turn edp panel power off\n");
goto end;
}
}
dp_display_state_remove(DP_STATE_ENABLED);
dp->aux->state = DP_STATE_CTRL_POWERED_OFF;
@ -2672,18 +2865,25 @@ end:
static int dp_display_validate_link_clock(struct dp_display_private *dp,
struct drm_display_mode *mode, struct dp_display_mode dp_mode)
{
u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0, lane_count = 0;
bool dsc_en;
int rate;
int rate = 0;
dsc_en = (dp_mode.timing.comp_info.comp_ratio > 1) ? true : false;
mode_bpp = dsc_en ?
DSC_BPP(dp_mode.timing.comp_info.dsc_info.config)
: dp_mode.timing.bpp;
if (dp->dp_display.is_edp) {
rate = dp->panel->link_info.rate;
lane_count = dp->panel->link_info.num_lanes;
} else {
rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code);
lane_count = dp->link->link_params.lane_count;
}
mode_rate_khz = mode->clock * mode_bpp;
rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code);
supported_rate_khz = dp->link->link_params.lane_count * rate * 8;
supported_rate_khz = lane_count * rate * 8;
if (mode_rate_khz > supported_rate_khz) {
DP_DEBUG("mode_rate: %d kHz, supported_rate: %d kHz\n",
@ -3082,48 +3282,6 @@ static int dp_display_create_workqueue(struct dp_display_private *dp)
return 0;
}
static int dp_display_fsa4480_callback(struct notifier_block *self,
unsigned long event, void *data)
{
return 0;
}
static int dp_display_init_aux_switch(struct dp_display_private *dp)
{
int rc = 0;
const char *phandle = "qcom,dp-aux-switch";
struct notifier_block nb;
if (!dp->pdev->dev.of_node) {
DP_ERR("cannot find dev.of_node\n");
rc = -ENODEV;
goto end;
}
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
dp->aux_switch_node = of_parse_phandle(dp->pdev->dev.of_node,
phandle, 0);
if (!dp->aux_switch_node) {
DP_WARN("cannot parse %s handle\n", phandle);
rc = -ENODEV;
goto end;
}
nb.notifier_call = dp_display_fsa4480_callback;
nb.priority = 0;
rc = fsa4480_reg_notifier(&nb, dp->aux_switch_node);
if (rc) {
DP_ERR("failed to register notifier (%d)\n", rc);
goto end;
}
fsa4480_unreg_notifier(&nb, dp->aux_switch_node);
end:
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, rc);
return rc;
}
static int dp_display_mst_install(struct dp_display *dp_display,
struct dp_mst_drm_install_info *mst_install_info)
{
@ -3550,10 +3708,119 @@ static void dp_display_wakeup_phy_layer(struct dp_display *dp_display,
hpd->wakeup_phy(hpd, wakeup);
}
static int dp_display_get_display_type(struct dp_display *dp_display,
const char **display_type)
{
struct dp_display_private *dp;
if (!dp_display || !display_type) {
pr_err("invalid input\n");
return -EINVAL;
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
*display_type = dp->parser->display_type;
return 0;
}
static int dp_display_mst_get_fixed_topology_display_type(
struct dp_display *dp_display, u32 strm_id,
const char **display_type)
{
struct dp_display_private *dp;
if (!dp_display || !display_type) {
pr_err("invalid input\n");
return -EINVAL;
}
if (strm_id >= DP_STREAM_MAX) {
pr_err("invalid stream id:%d\n", strm_id);
return -EINVAL;
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
*display_type = dp->parser->mst_fixed_display_type[strm_id];
return 0;
}
static int dp_display_edp_detect(struct dp_display *dp_display)
{
struct device *dev;
struct dp_display_private *dp;
int rc = 0;
dp = container_of(dp_display, struct dp_display_private, dp_display);
dev = &dp->pdev->dev;
if (dp->dp_display.is_sst_connected)
return rc;
mutex_lock(&dp->session_lock);
rc = dp_display_host_init(dp);
if (rc) {
DP_ERR("Host init Failed");
goto end;
}
dp->dp_display.is_sst_connected = true;
dp->hpd->hpd_high = true;
dp->hpd->alt_mode_cfg_done = true;
dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz, dp->debug->max_pclk_khz);
rc = dp_display_host_ready(dp);
if (rc) {
dp_display_state_show("[ready failed]");
dp_display_host_deinit(dp);
goto end;
}
rc = dp_display_panel_ready(dp);
if (rc) {
dp_display_host_unready(dp);
dp_display_host_deinit(dp);
goto end;
}
dp->link->psm_config(dp->link, &dp->panel->link_info, false);
dp->debug->psm_enabled = false;
rc = dp->panel->read_sink_caps(dp->panel,
dp->dp_display.base_connector, dp->hpd->multi_func);
if (rc == -ETIMEDOUT || rc == -ENOTCONN)
goto end;
dp_display_state_remove(DP_STATE_ABORTED);
dp_display_state_add(DP_STATE_CONFIGURED);
dp_display_state_add(DP_STATE_CONNECTED);
dp->link->process_request(dp->link);
dp->panel->handle_sink_request(dp->panel);
dp_display_state_add(DP_STATE_CONNECT_NOTIFIED);
dp_display_state_remove(DP_STATE_DISCONNECT_NOTIFIED);
dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false);
end:
mutex_unlock(&dp->session_lock);
return rc;
}
static int dp_display_probe(struct platform_device *pdev)
{
int rc = 0;
struct dp_display_private *dp;
const struct of_device_id *id;
const struct dp_display_type_info *info;
struct dp_display *dp_display;
int index;
if (!pdev || !pdev->dev.of_node) {
DP_ERR("pdev not found\n");
@ -3561,6 +3828,19 @@ static int dp_display_probe(struct platform_device *pdev)
goto bail;
}
id = of_match_node(dp_dt_match, pdev->dev.of_node);
if (!id)
return -ENODEV;
info = id->data;
index = dp_display_get_num_of_displays();
if (index >= MAX_DP_ACTIVE_DISPLAY) {
pr_err("exceeds max dp count\n");
rc = -EINVAL;
goto bail;
}
dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
if (!dp) {
rc = -ENOMEM;
@ -3575,11 +3855,9 @@ static int dp_display_probe(struct platform_device *pdev)
memset(&dp->mst, 0, sizeof(dp->mst));
rc = dp_display_init_aux_switch(dp);
if (rc) {
rc = -EPROBE_DEFER;
rc = dp_display_get_cell_info(dp);
if (rc)
goto error;
}
rc = dp_display_create_workqueue(dp);
if (rc) {
@ -3589,44 +3867,51 @@ static int dp_display_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dp);
g_dp_display = &dp->dp_display;
dp_display = &dp->dp_display;
g_dp_display[index] = dp_display;
g_dp_display->enable = dp_display_enable;
g_dp_display->post_enable = dp_display_post_enable;
g_dp_display->pre_disable = dp_display_pre_disable;
g_dp_display->disable = dp_display_disable;
g_dp_display->set_mode = dp_display_set_mode;
g_dp_display->validate_mode = dp_display_validate_mode;
g_dp_display->get_modes = dp_display_get_modes;
g_dp_display->prepare = dp_display_prepare;
g_dp_display->unprepare = dp_display_unprepare;
g_dp_display->request_irq = dp_request_irq;
g_dp_display->get_debug = dp_get_debug;
g_dp_display->post_open = NULL;
g_dp_display->post_init = dp_display_post_init;
g_dp_display->config_hdr = dp_display_config_hdr;
g_dp_display->mst_install = dp_display_mst_install;
g_dp_display->mst_uninstall = dp_display_mst_uninstall;
g_dp_display->mst_connector_install = dp_display_mst_connector_install;
g_dp_display->mst_connector_uninstall =
dp_display->enable = dp_display_enable;
dp_display->post_enable = dp_display_post_enable;
dp_display->pre_disable = dp_display_pre_disable;
dp_display->disable = dp_display_disable;
dp_display->set_mode = dp_display_set_mode;
dp_display->validate_mode = dp_display_validate_mode;
dp_display->get_modes = dp_display_get_modes;
dp_display->prepare = dp_display_prepare;
dp_display->unprepare = dp_display_unprepare;
dp_display->request_irq = dp_request_irq;
dp_display->get_debug = dp_get_debug;
dp_display->post_open = NULL;
dp_display->post_init = dp_display_post_init;
dp_display->config_hdr = dp_display_config_hdr;
dp_display->mst_install = dp_display_mst_install;
dp_display->mst_uninstall = dp_display_mst_uninstall;
dp_display->mst_connector_install = dp_display_mst_connector_install;
dp_display->mst_connector_uninstall =
dp_display_mst_connector_uninstall;
g_dp_display->mst_connector_update_edid =
dp_display->mst_connector_update_edid =
dp_display_mst_connector_update_edid;
g_dp_display->mst_connector_update_link_info =
dp_display->mst_connector_update_link_info =
dp_display_mst_connector_update_link_info;
g_dp_display->get_mst_caps = dp_display_get_mst_caps;
g_dp_display->set_stream_info = dp_display_set_stream_info;
g_dp_display->update_pps = dp_display_update_pps;
g_dp_display->convert_to_dp_mode = dp_display_convert_to_dp_mode;
g_dp_display->mst_get_connector_info =
dp_display->get_mst_caps = dp_display_get_mst_caps;
dp_display->set_stream_info = dp_display_set_stream_info;
dp_display->update_pps = dp_display_update_pps;
dp_display->convert_to_dp_mode = dp_display_convert_to_dp_mode;
dp_display->mst_get_connector_info =
dp_display_mst_get_connector_info;
g_dp_display->mst_get_fixed_topology_port =
dp_display->mst_get_fixed_topology_port =
dp_display_mst_get_fixed_topology_port;
g_dp_display->wakeup_phy_layer =
dp_display->wakeup_phy_layer =
dp_display_wakeup_phy_layer;
g_dp_display->set_colorspace = dp_display_setup_colospace;
g_dp_display->get_available_dp_resources =
dp_display->set_colorspace = dp_display_setup_colospace;
dp_display->get_available_dp_resources =
dp_display_get_available_dp_resources;
dp_display->get_display_type = dp_display_get_display_type;
dp_display->mst_get_fixed_topology_display_type =
dp_display_mst_get_fixed_topology_display_type;
dp_display->is_edp = (info->display_type == DRM_MODE_CONNECTOR_eDP) ? true : false;
dp_display->edp_detect = dp_display_edp_detect;
rc = component_add(&pdev->dev, &dp_display_comp_ops);
if (rc) {
@ -3643,31 +3928,71 @@ bail:
int dp_display_get_displays(void **displays, int count)
{
int i;
if (!displays) {
DP_ERR("invalid data\n");
return -EINVAL;
}
if (count != 1) {
DP_ERR("invalid number of displays\n");
return -EINVAL;
for (i = 0; i < MAX_DP_ACTIVE_DISPLAY && i < count; i++) {
if (!g_dp_display[i])
break;
displays[i] = g_dp_display[i];
}
displays[0] = g_dp_display;
return count;
return i;
}
int dp_display_get_num_of_displays(void)
{
if (!g_dp_display)
return 0;
int i;
return 1;
for (i = 0; i < MAX_DP_ACTIVE_DISPLAY; i++)
if (!g_dp_display[i])
break;
return i;
}
int dp_display_get_num_of_streams(void)
{
return DP_STREAM_MAX;
struct dp_display_private *dp;
int i, count = 0;
for (i = 0; i < MAX_DP_ACTIVE_DISPLAY; i++) {
if (!g_dp_display[i])
break;
dp = container_of(g_dp_display[i], struct dp_display_private, dp_display);
count += dp->stream_cnt;
}
return count;
}
int dp_display_get_info(void *dp_display, struct dp_display_info *dp_info)
{
struct dp_display_private *dp;
int i;
if (!dp_display) {
DP_DEBUG("dp display not initialized\n");
return -EINVAL;
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
dp_info->cell_idx = dp->cell_idx;
dp_info->intf_idx[0] = dp->intf_idx[0];
for (i = 1; i < dp->stream_cnt; i++)
dp_info->intf_idx[i] = dp->intf_idx[i];
dp_info->phy_idx = dp->phy_idx;
dp_info->stream_cnt = dp->stream_cnt;
return 0;
}
static void dp_display_set_mst_state(void *dp_display,
@ -3675,16 +4000,11 @@ static void dp_display_set_mst_state(void *dp_display,
{
struct dp_display_private *dp;
if (!g_dp_display) {
DP_DEBUG("dp display not initialized\n");
return;
}
dp = container_of(g_dp_display, struct dp_display_private, dp_display);
dp = container_of(dp_display, struct dp_display_private, dp_display);
SDE_EVT32_EXTERNAL(mst_state, dp->mst.mst_active);
if (dp->mst.mst_active && dp->mst.cbs.set_drv_state)
dp->mst.cbs.set_drv_state(g_dp_display, mst_state);
dp->mst.cbs.set_drv_state(dp_display, mst_state);
}
static int dp_display_remove(struct platform_device *pdev)
@ -3709,12 +4029,16 @@ static int dp_display_remove(struct platform_device *pdev)
static int dp_pm_prepare(struct device *dev)
{
struct dp_display_private *dp = container_of(g_dp_display,
struct dp_display_private, dp_display);
struct dp_display_private *dp;
if (!dev)
return -EINVAL;
dp = dev_get_drvdata(dev);
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
mutex_lock(&dp->session_lock);
dp_display_set_mst_state(g_dp_display, PM_SUSPEND);
dp_display_set_mst_state(&dp->dp_display, PM_SUSPEND);
/*
* There are a few instances where the DP is hotplugged when the device
@ -3739,12 +4063,16 @@ static int dp_pm_prepare(struct device *dev)
static void dp_pm_complete(struct device *dev)
{
struct dp_display_private *dp = container_of(g_dp_display,
struct dp_display_private, dp_display);
struct dp_display_private *dp;
if (!dev)
return;
dp = dev_get_drvdata(dev);
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
mutex_lock(&dp->session_lock);
dp_display_set_mst_state(g_dp_display, PM_DEFAULT);
dp_display_set_mst_state(&dp->dp_display, PM_DEFAULT);
/*
* There are multiple PM suspend entry and exits observed before

View file

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
@ -11,8 +12,10 @@
#include <drm/sde_drm.h>
#include "dp_panel.h"
#include "dp_parser.h"
#define DP_MST_SIM_MAX_PORTS 8
#define MAX_DP_ACTIVE_DISPLAY 8
enum dp_drv_state {
PM_DEFAULT,
@ -29,6 +32,13 @@ struct dp_mst_hpd_info {
int mst_sim_remove_con_id;
};
struct dp_display_info {
u32 cell_idx;
u32 intf_idx[DP_STREAM_MAX];
u32 phy_idx;
u32 stream_cnt;
};
struct dp_mst_drm_cbs {
void (*hpd)(void *display, bool hpd_status);
void (*hpd_irq)(void *display, struct dp_mst_hpd_info *info);
@ -70,6 +80,7 @@ struct dp_display {
void *base_dp_panel;
bool is_sst_connected;
bool is_mst_supported;
bool is_edp;
bool dsc_cont_pps;
u32 max_pclk_khz;
void *dp_mst_prv_info;
@ -132,12 +143,18 @@ struct dp_display {
int (*get_available_dp_resources)(struct dp_display *dp_display,
const struct msm_resource_caps_info *avail_res,
struct msm_resource_caps_info *max_dp_avail_res);
int (*get_display_type)(struct dp_display *dp_display,
const char **display_type);
int (*mst_get_fixed_topology_display_type)(struct dp_display *dp_display,
u32 strm_id, const char **display_type);
int (*edp_detect)(struct dp_display *dp_display);
};
#if IS_ENABLED(CONFIG_DRM_MSM_DP)
int dp_display_get_num_of_displays(void);
int dp_display_get_displays(void **displays, int count);
int dp_display_get_num_of_streams(void);
int dp_display_get_info(void *dp_display, struct dp_display_info *dp_info);
#else
static inline int dp_display_get_num_of_displays(void)
{
@ -151,6 +168,10 @@ static inline int dp_display_get_num_of_streams(void)
{
return 0;
}
static inline int dp_display_get_info(void *dp_display, struct dp_display_info *dp_info)
{
return 0;
}
static inline int dp_connector_update_pps(struct drm_connector *connector,
char *pps_cmd, void *display)
{

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
@ -456,8 +457,15 @@ int dp_connector_get_info(struct drm_connector *connector,
info->num_of_h_tiles = 1;
info->h_tile_instance[0] = 0;
info->is_connected = display->is_sst_connected;
info->capabilities = MSM_DISPLAY_CAP_VID_MODE | MSM_DISPLAY_CAP_EDID |
MSM_DISPLAY_CAP_HOT_PLUG;
info->curr_panel_mode = MSM_DISPLAY_VIDEO_MODE;
info->capabilities = MSM_DISPLAY_CAP_VID_MODE | MSM_DISPLAY_CAP_EDID;
if (display && display->is_edp) {
info->intf_type = DRM_MODE_CONNECTOR_eDP;
info->display_type = SDE_CONNECTOR_PRIMARY;
} else {
info->capabilities |= MSM_DISPLAY_CAP_HOT_PLUG;
}
return 0;
}
@ -468,11 +476,13 @@ enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
{
enum drm_connector_status status = connector_status_unknown;
struct msm_display_info info;
struct dp_display *dp_disp;
int rc;
if (!conn || !display)
return status;
dp_disp = display;
/* get display dp_info */
memset(&info, 0x0, sizeof(info));
rc = dp_connector_get_info(conn, &info, display);
@ -481,12 +491,18 @@ enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
return connector_status_disconnected;
}
if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG)
if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG) {
status = (info.is_connected ? connector_status_connected :
connector_status_disconnected);
else
} else {
status = connector_status_connected;
rc = dp_disp->edp_detect(dp_disp);
if (rc) {
DP_ERR("error in turning on panel power sequence rc:%d\n", rc);
return connector_status_unknown;
}
}
conn->display_info.width_mm = info.width_mm;
conn->display_info.height_mm = info.height_mm;
@ -594,6 +610,18 @@ int dp_connector_get_modes(struct drm_connector *connector,
return rc;
}
int dp_connector_set_info_blob(struct drm_connector *connector,
void *info, void *display, struct msm_mode_info *mode_info)
{
struct dp_display *dp_display = display;
const char *display_type = NULL;
dp_display->get_display_type(dp_display, &display_type);
sde_kms_info_add_keystr(info, "display type", display_type);
return 0;
}
int dp_drm_bridge_init(void *data, struct drm_encoder *encoder,
u32 max_mixer_count, u32 max_dsc_count)
{

View file

@ -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)
{

View file

@ -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

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*/
@ -158,11 +159,6 @@ struct dp_mst_private {
bool mst_session_state;
};
struct dp_mst_encoder_info_cache {
u8 cnt;
struct drm_encoder *mst_enc[MAX_DP_MST_DRM_BRIDGES];
};
#define to_dp_mst_bridge(x) container_of((x), struct dp_mst_bridge, base)
#define to_dp_mst_bridge_priv(x) \
container_of((x), struct dp_mst_bridge, obj)
@ -171,9 +167,6 @@ struct dp_mst_encoder_info_cache {
#define to_dp_mst_bridge_state(x) \
to_dp_mst_bridge_priv_state((x)->obj.state)
struct dp_mst_private dp_mst;
struct dp_mst_encoder_info_cache dp_mst_enc_cache;
static void dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
{
struct dp_mst_private *mst = container_of(mgr, struct dp_mst_private,
@ -298,7 +291,7 @@ static void dp_mst_sim_add_port(struct dp_mst_private *mst,
port->parent = mstb;
port->port_num = port_msg->port_number;
port->mgr = mstb->mgr;
port->aux.name = dp_mst.caps.drm_aux->name;
port->aux.name = mst->caps.drm_aux->name;
port->aux.dev = mst->dp_display->drm_dev->dev;
/*
@ -1258,19 +1251,6 @@ int dp_mst_drm_bridge_init(void *data, struct drm_encoder *encoder)
struct dp_mst_private *mst = display->dp_mst_prv_info;
int i;
if (!mst || !mst->mst_initialized) {
if (dp_mst_enc_cache.cnt >= MAX_DP_MST_DRM_BRIDGES) {
DP_MST_INFO("exceeding max bridge cnt %d\n",
dp_mst_enc_cache.cnt);
return 0;
}
dp_mst_enc_cache.mst_enc[dp_mst_enc_cache.cnt] = encoder;
dp_mst_enc_cache.cnt++;
DP_MST_INFO("mst not initialized. cache encoder information\n");
return 0;
}
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
if (!mst->mst_bridge[i].in_use) {
bridge = &mst->mst_bridge[i];
@ -2130,11 +2110,36 @@ static void dp_mst_destroy_fixed_connector(struct drm_dp_mst_topology_mgr *mgr,
dp_mst_destroy_connector(mgr, connector);
}
static int dp_mst_fixed_connector_set_info_blob(
struct drm_connector *connector,
void *info, void *display, struct msm_mode_info *mode_info)
{
struct sde_connector *c_conn = to_sde_connector(connector);
struct dp_display *dp_display = display;
struct dp_mst_private *mst = dp_display->dp_mst_prv_info;
const char *display_type = NULL;
int i;
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
if (mst->mst_bridge[i].base.encoder != c_conn->encoder)
continue;
dp_display->mst_get_fixed_topology_display_type(dp_display,
mst->mst_bridge[i].id, &display_type);
sde_kms_info_add_keystr(info, "display type", display_type);
break;
}
return 0;
}
static struct drm_connector *
dp_mst_drm_fixed_connector_init(struct dp_display *dp_display,
struct drm_encoder *encoder)
{
static const struct sde_connector_ops dp_mst_connector_ops = {
.set_info_blob = dp_mst_fixed_connector_set_info_blob,
.post_init = dp_mst_connector_post_init,
.detect = dp_mst_fixed_connector_detect,
.get_modes = dp_mst_connector_get_modes,
@ -2389,10 +2394,9 @@ int dp_mst_init(struct dp_display *dp_display)
{
struct drm_device *dev;
int conn_base_id = 0;
int ret, i;
int ret;
struct dp_mst_drm_install_info install_info;
memset(&dp_mst, 0, sizeof(dp_mst));
struct dp_mst_private *dp_mst;
if (!dp_display) {
DP_ERR("invalid params\n");
@ -2401,60 +2405,57 @@ int dp_mst_init(struct dp_display *dp_display)
dev = dp_display->drm_dev;
dp_mst = devm_kzalloc(dev->dev, sizeof(*dp_mst), GFP_KERNEL);
if (!dp_mst)
return -ENOMEM;
/* register with DP driver */
install_info.dp_mst_prv_info = &dp_mst;
install_info.dp_mst_prv_info = dp_mst;
install_info.cbs = &dp_mst_display_cbs;
dp_display->mst_install(dp_display, &install_info);
dp_display->get_mst_caps(dp_display, &dp_mst.caps);
dp_display->get_mst_caps(dp_display, &dp_mst->caps);
if (!dp_mst.caps.has_mst) {
if (!dp_mst->caps.has_mst) {
DP_MST_DEBUG("mst not supported\n");
return 0;
}
dp_mst.mst_fw_cbs = &drm_dp_mst_fw_helper_ops;
dp_mst->mst_fw_cbs = &drm_dp_mst_fw_helper_ops;
memset(&dp_mst.mst_mgr, 0, sizeof(dp_mst.mst_mgr));
dp_mst.mst_mgr.cbs = &dp_mst_drm_cbs;
memset(&dp_mst->mst_mgr, 0, sizeof(dp_mst->mst_mgr));
dp_mst->mst_mgr.cbs = &dp_mst_drm_cbs;
conn_base_id = dp_display->base_connector->base.id;
dp_mst.dp_display = dp_display;
dp_mst->dp_display = dp_display;
mutex_init(&dp_mst.mst_lock);
mutex_init(&dp_mst.edid_lock);
mutex_init(&dp_mst->mst_lock);
mutex_init(&dp_mst->edid_lock);
ret = drm_dp_mst_topology_mgr_init(&dp_mst.mst_mgr, dev,
dp_mst.caps.drm_aux,
dp_mst.caps.max_dpcd_transaction_bytes,
dp_mst.caps.max_streams_supported,
ret = drm_dp_mst_topology_mgr_init(&dp_mst->mst_mgr, dev,
dp_mst->caps.drm_aux,
dp_mst->caps.max_dpcd_transaction_bytes,
dp_mst->caps.max_streams_supported,
conn_base_id);
if (ret) {
DP_ERR("dp drm mst topology manager init failed\n");
goto error;
}
dp_mst_sim_init(&dp_mst);
dp_mst_sim_init(dp_mst);
dp_mst.mst_initialized = true;
/* create drm_bridges for cached mst encoders and clear cache */
for (i = 0; i < dp_mst_enc_cache.cnt; i++) {
ret = dp_mst_drm_bridge_init(dp_display,
dp_mst_enc_cache.mst_enc[i]);
}
memset(&dp_mst_enc_cache, 0, sizeof(dp_mst_enc_cache));
dp_mst->mst_initialized = true;
/* choose fixed callback function if fixed topology is found */
if (!dp_display->mst_get_fixed_topology_port(dp_display, 0, NULL))
dp_mst.mst_mgr.cbs = &dp_mst_fixed_drm_cbs;
dp_mst->mst_mgr.cbs = &dp_mst_fixed_drm_cbs;
DP_MST_INFO("dp drm mst topology manager init completed\n");
return ret;
error:
mutex_destroy(&dp_mst.mst_lock);
mutex_destroy(&dp_mst.edid_lock);
mutex_destroy(&dp_mst->mst_lock);
mutex_destroy(&dp_mst->edid_lock);
return ret;
}
@ -2476,7 +2477,7 @@ void dp_mst_deinit(struct dp_display *dp_display)
drm_dp_mst_topology_mgr_destroy(&mst->mst_mgr);
dp_mst.mst_initialized = false;
mst->mst_initialized = false;
mutex_destroy(&mst->mst_lock);
mutex_destroy(&mst->edid_lock);

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
@ -166,6 +167,16 @@ static int dp_parser_misc(struct dp_parser *parser)
if (rc)
parser->max_lclk_khz = DP_MAX_LINK_CLK_KHZ;
for (i = 0; i < MAX_DP_MST_STREAMS; i++) {
of_property_read_u32_index(of_node,
"qcom,pixel-base-off", i,
&parser->pixel_base_off[i]);
}
parser->display_type = of_get_property(of_node, "qcom,display-type", NULL);
if (!parser->display_type)
parser->display_type = "unknown";
return 0;
}
@ -247,23 +258,26 @@ static int dp_parser_gpio(struct dp_parser *parser)
struct device *dev = &parser->pdev->dev;
struct device_node *of_node = dev->of_node;
struct dss_module_power *mp = &parser->mp[DP_CORE_PM];
static const char * const dp_gpios[] = {
static const char * const dp_gpios[DP_GPIO_MAX] = {
"qcom,aux-en-gpio",
"qcom,aux-sel-gpio",
"qcom,usbplug-cc-gpio",
"qcom,edp-vcc-en-gpio",
"qcom,edp-backlight-pwr-gpio",
"qcom,edp-pwm-en-gpio",
"qcom,edp-backlight-en-gpio",
};
if (of_find_property(of_node, "qcom,dp-hpd-gpio", NULL)) {
parser->no_aux_switch = true;
parser->lphw_hpd = of_find_property(of_node,
"qcom,dp-low-power-hw-hpd", NULL);
return 0;
}
if (of_find_property(of_node, "qcom,dp-gpio-aux-switch", NULL))
parser->gpio_aux_switch = true;
mp->gpio_config = devm_kzalloc(dev,
sizeof(struct dss_gpio) * ARRAY_SIZE(dp_gpios), GFP_KERNEL);
sizeof(struct dss_gpio) * DP_GPIO_MAX, GFP_KERNEL);
if (!mp->gpio_config)
return -ENOMEM;
@ -690,6 +704,7 @@ static int dp_parser_catalog(struct dp_parser *parser)
{
int rc;
u32 version;
const char *st = NULL;
struct device *dev = &parser->pdev->dev;
rc = of_property_read_u32(dev->of_node, "qcom,phy-version", &version);
@ -697,6 +712,27 @@ static int dp_parser_catalog(struct dp_parser *parser)
if (!rc)
parser->hw_cfg.phy_version = version;
/* phy-mode */
rc = of_property_read_string(dev->of_node, "qcom,phy-mode", &st);
if (!rc) {
if (!strcmp(st, "dp"))
parser->hw_cfg.phy_mode = DP_PHY_MODE_DP;
else if (!strcmp(st, "minidp"))
parser->hw_cfg.phy_mode = DP_PHY_MODE_MINIDP;
else if (!strcmp(st, "edp"))
parser->hw_cfg.phy_mode = DP_PHY_MODE_EDP;
else if (!strcmp(st, "edp-highswing"))
parser->hw_cfg.phy_mode = DP_PHY_MODE_EDP_HIGH_SWING;
else {
parser->hw_cfg.phy_mode = DP_PHY_MODE_UNKNOWN;
pr_warn("unknown phy-mode %s\n", st);
}
} else {
/* default to DP mode, if not set */
parser->hw_cfg.phy_mode = DP_PHY_MODE_DP;
}
return 0;
}
@ -715,6 +751,12 @@ static int dp_parser_mst(struct dp_parser *parser)
of_property_read_u32_index(dev->of_node,
"qcom,mst-fixed-topology-ports", i,
&parser->mst_fixed_port[i]);
of_property_read_string_index(
dev->of_node,
"qcom,mst-fixed-topology-display-types", i,
&parser->mst_fixed_display_type[i]);
if (!parser->mst_fixed_display_type[i])
parser->mst_fixed_display_type[i] = "unknown";
}
return 0;

View file

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
@ -25,6 +26,20 @@ enum dp_pm_type {
DP_MAX_PM
};
enum dp_pin_states {
DP_GPIO_AUX_ENABLE,
DP_GPIO_AUX_SEL,
DP_GPIO_USBPLUG_CC,
DP_GPIO_CMN_MAX = DP_GPIO_USBPLUG_CC,
DP_GPIO_EDP_VCC_EN,
DP_GPIO_EDP_MIN = DP_GPIO_EDP_VCC_EN,
DP_GPIO_EDP_BACKLIGHT_PWR,
DP_GPIO_EDP_PWM,
DP_GPIO_EDP_BACKLIGHT_EN,
DP_GPIO_EDP_MAX,
DP_GPIO_MAX = DP_GPIO_EDP_MAX,
};
static inline const char *dp_parser_pm_name(enum dp_pm_type module)
{
switch (module) {
@ -131,15 +146,35 @@ enum dp_phy_aux_config_type {
* enum dp_phy_version - version of the dp phy
* @DP_PHY_VERSION_UNKNOWN: Unknown controller version
* @DP_PHY_VERSION_4_2_0: DP phy v4.2.0 controller
* @DP_PHY_VERSION_5_0_0: DP phy v5.0.0 controller
* @DP_PHY_VERSION_MAX: max version
*/
enum dp_phy_version {
DP_PHY_VERSION_UNKNOWN,
DP_PHY_VERSION_2_0_0 = 0x200,
DP_PHY_VERSION_4_2_0 = 0x420,
DP_PHY_VERSION_5_0_0 = 0x500,
DP_PHY_VERSION_MAX
};
/**
* enum dp_phy_mode - mode of the dp phy
* @DP_PHY_MODE_UNKNOWN: Unknown PHY mode
* @DP_PHY_MODE_DP: DP PHY mode
* @DP_PHY_MODE_MINIDP: MiniDP PHY mode
* @DP_PHY_MODE_EDP: eDP PHY mode
* @DP_PHY_MODE_EDP_HIGH_SWING: eDP PHY mode, high swing/pre-empahsis
* @DP_PHY_MODE_MAX: max PHY mode
*/
enum dp_phy_mode {
DP_PHY_MODE_UNKNOWN = 0,
DP_PHY_MODE_DP,
DP_PHY_MODE_MINIDP,
DP_PHY_MODE_EDP,
DP_PHY_MODE_EDP_HIGH_SWING,
DP_PHY_MODE_MAX
};
/**
* struct dp_hw_cfg - DP HW specific configuration
*
@ -147,6 +182,7 @@ enum dp_phy_version {
*/
struct dp_hw_cfg {
enum dp_phy_version phy_version;
enum dp_phy_mode phy_mode;
};
static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
@ -202,6 +238,8 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
* @get_io: function to be called by client to get io data.
* @get_io_buf: function to be called by client to get io buffers.
* @clear_io_buf: function to be called by client to clear io buffers.
* @mst_fixed_display_type: mst display_type reserved for fixed topology
* @display_type: display type as defined in device tree.
*/
struct dp_parser {
struct platform_device *pdev;
@ -227,6 +265,9 @@ struct dp_parser {
bool gpio_aux_switch;
bool lphw_hpd;
u32 mst_fixed_port[MAX_DP_MST_STREAMS];
u32 pixel_base_off[MAX_DP_MST_STREAMS];
const char *mst_fixed_display_type[MAX_DP_MST_STREAMS];
const char *display_type;
int (*parse)(struct dp_parser *parser);
struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name);

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
*/
@ -55,6 +56,9 @@ static int dp_pll_clock_register(struct dp_pll *pll)
case DP_PLL_7NM:
rc = dp_pll_clock_register_5nm(pll);
break;
case EDP_PLL_7NM:
rc = edp_pll_clock_register_7nm(pll);
break;
default:
rc = -ENOTSUPP;
break;
@ -69,6 +73,7 @@ static void dp_pll_clock_unregister(struct dp_pll *pll)
case DP_PLL_5NM_V1:
case DP_PLL_5NM_V2:
case DP_PLL_7NM:
case EDP_PLL_7NM:
dp_pll_clock_unregister_5nm(pll);
break;
default:
@ -106,6 +111,8 @@ struct dp_pll *dp_pll_get(struct dp_pll_in *in)
pll->revision = DP_PLL_5NM_V2;
} else if (!strcmp(label, "7nm")) {
pll->revision = DP_PLL_7NM;
} else if (!strcmp(label, "edp-7nm")) {
pll->revision = EDP_PLL_7NM;
} else {
DP_ERR("Unsupported pll revision\n");
rc = -ENOTSUPP;
@ -117,6 +124,10 @@ struct dp_pll *dp_pll_get(struct dp_pll_in *in)
goto error;
}
pll->name = of_get_property(pdev->dev.of_node, "label", NULL);
if (!pll->name)
pll->name = "dp0";
pll->ssc_en = of_property_read_bool(pdev->dev.of_node,
"qcom,ssc-feature-enable");
pll->bonding_en = of_property_read_bool(pdev->dev.of_node,

View file

@ -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;

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
*/
@ -20,6 +21,8 @@ struct dp_power_private {
struct clk *pixel_clk_rcg;
struct clk *pixel_parent;
struct clk *pixel1_clk_rcg;
struct clk *link_clk_rcg;
struct clk *link_parent;
struct dp_power dp_power;
@ -229,6 +232,22 @@ static int dp_power_clk_init(struct dp_power_private *power, bool enable)
goto err_pixel1_clk_rcg;
}
}
power->link_clk_rcg = clk_get(dev, "link_clk_src");
if (IS_ERR(power->link_clk_rcg)) {
DP_ERR("Unable to get DP link clk RCG: %ld\n",
PTR_ERR(power->link_clk_rcg));
rc = 0;
power->link_clk_rcg = NULL;
}
power->link_parent = clk_get(dev, "link_parent");
if (IS_ERR(power->link_parent)) {
DP_ERR("Unable to get DP link parent: %ld\n",
PTR_ERR(power->link_parent));
rc = 0;
power->link_parent = NULL;
}
} else {
if (power->pixel1_clk_rcg)
clk_put(power->pixel1_clk_rcg);
@ -239,6 +258,12 @@ static int dp_power_clk_init(struct dp_power_private *power, bool enable)
if (power->pixel_clk_rcg)
clk_put(power->pixel_clk_rcg);
if (power->link_parent)
clk_put(power->link_parent);
if (power->link_clk_rcg)
clk_put(power->link_clk_rcg);
dp_power_clk_put(power);
}
@ -349,6 +374,14 @@ static int dp_power_clk_enable(struct dp_power *dp_power,
}
}
if (pm_type == DP_LINK_PM && enable && power->link_parent) {
rc = clk_set_parent(power->link_clk_rcg, power->link_parent);
if (rc) {
DP_ERR("failed to set link parent\n");
goto error;
}
}
rc = dp_power_clk_set_rate(power, pm_type, enable);
if (rc) {
DP_ERR("failed to '%s' clks for: %s. err=%d\n",
@ -388,6 +421,7 @@ static int dp_power_request_gpios(struct dp_power_private *power)
struct dss_module_power *mp;
static const char * const gpio_names[] = {
"aux_enable", "aux_sel", "usbplug_cc",
"edp_vcc_enable", "edp_backlight_pwr", "edp_pwm_en", "edp_backlight_en",
};
if (!power) {
@ -410,6 +444,7 @@ static int dp_power_request_gpios(struct dp_power_private *power)
}
}
}
return 0;
error:
for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
@ -432,7 +467,7 @@ static void dp_power_set_gpio(struct dp_power_private *power, bool flip)
struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM];
struct dss_gpio *config = mp->gpio_config;
for (i = 0; i < mp->num_gpio; i++) {
for (i = 0; i <= DP_GPIO_CMN_MAX; i++) {
if (dp_power_find_gpio(config->gpio_name, "aux-sel"))
config->value = flip;
@ -689,6 +724,37 @@ exit:
return rc;
}
static int dp_power_edp_panel_set_gpio(struct dp_power *dp_power,
enum dp_pin_states pin_state, bool enable)
{
int rc = 0;
struct dp_power_private *power;
struct dss_module_power *mp;
struct dss_gpio *config;
if (!dp_power) {
DP_ERR("invalid power data\n");
return -EINVAL;
}
power = container_of(dp_power, struct dp_power_private, dp_power);
mp = &power->parser->mp[DP_CORE_PM];
config = mp->gpio_config;
if (config == NULL)
return -EINVAL;
if ((pin_state >= DP_GPIO_EDP_MIN) && (pin_state < DP_GPIO_EDP_MAX)) {
gpio_direction_output(config[pin_state].gpio, enable);
} else {
pr_err(" Invalid GPIO call\n");
return -EINVAL;
}
return rc;
}
struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll)
{
int rc = 0;
@ -720,6 +786,7 @@ struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll)
dp_power->clk_get_rate = dp_power_clk_get_rate;
dp_power->power_client_init = dp_power_client_init;
dp_power->power_client_deinit = dp_power_client_deinit;
dp_power->edp_panel_set_gpio = dp_power_edp_panel_set_gpio;
return dp_power;
error:

View file

@ -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);
};
/**

View file

@ -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)

927
msm/dp/edp_pll_7nm.c Normal file
View file

@ -0,0 +1,927 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
/*
* Display Port PLL driver block diagram for branch clocks
*
* +------------------------+ +------------------------+
* | dp_phy_pll_link_clk | | dp_phy_pll_vco_div_clk |
* +------------------------+ +------------------------+
* | |
* | |
* V V
* dp_link_clk dp_pixel_clk
*
*
*/
#include <dt-bindings/clock/mdss-7nm-pll-clk.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/regmap.h>
#include "clk-regmap-mux.h"
#include "dp_hpd.h"
#include "dp_debug.h"
#include "dp_pll.h"
#define DP_PHY_CFG 0x0010
#define DP_PHY_CFG_1 0x0014
#define DP_PHY_PD_CTL 0x001C
#define DP_PHY_MODE 0x0020
#define DP_PHY_AUX_CFG2 0x002C
#define DP_PHY_VCO_DIV 0x0074
#define DP_PHY_TX0_TX1_LANE_CTL 0x007C
#define DP_PHY_TX2_TX3_LANE_CTL 0x00A0
#define DP_PHY_SPARE0 0x00CC
#define DP_PHY_STATUS 0x00E0
/* Tx registers */
#define TXn_CLKBUF_ENABLE 0x0000
#define TXn_TX_EMP_POST1_LVL 0x0004
#define TXn_TX_DRV_LVL 0x0014
#define TXn_TX_DRV_LVL_OFFSET 0x0018
#define TXn_RESET_TSYNC_EN 0x001C
#define TXn_LDO_CONFIG 0x0084
#define TXn_TX_BAND 0x0028
#define TXn_INTERFACE_SELECT 0x0024
#define TXn_RES_CODE_LANE_OFFSET_TX0 0x0044
#define TXn_RES_CODE_LANE_OFFSET_TX1 0x0048
#define TXn_TRANSCEIVER_BIAS_EN 0x0054
#define TXn_HIGHZ_DRVR_EN 0x0058
#define TXn_TX_POL_INV 0x005C
#define TXn_PARRATE_REC_DETECT_IDLE_EN 0x0060
#define TXn_LANE_MODE_1 0x0064
#define TXn_TRAN_DRVR_EMP_EN 0x0078
#define TXn_VMODE_CTRL1 0x007C
/* PLL register offset */
#define QSERDES_COM_BG_TIMER 0x000C
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x0044
#define QSERDES_COM_CLK_ENABLE1 0x0048
#define QSERDES_COM_SYS_CLK_CTRL 0x004C
#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x0050
#define QSERDES_COM_PLL_IVCO 0x0058
#define QSERDES_COM_CP_CTRL_MODE0 0x0074
#define QSERDES_COM_PLL_RCTRL_MODE0 0x007C
#define QSERDES_COM_PLL_CCTRL_MODE0 0x0084
#define QSERDES_COM_SYSCLK_EN_SEL 0x0094
#define QSERDES_COM_RESETSM_CNTRL 0x009C
#define QSERDES_COM_LOCK_CMP_EN 0x00A4
#define QSERDES_COM_LOCK_CMP1_MODE0 0x00AC
#define QSERDES_COM_LOCK_CMP2_MODE0 0x00B0
#define QSERDES_COM_DEC_START_MODE0 0x00BC
#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x00CC
#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x00D0
#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x00D4
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x00EC
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x00F0
#define QSERDES_COM_VCO_TUNE_CTRL 0x0108
#define QSERDES_COM_VCO_TUNE_MAP 0x010C
#define QSERDES_COM_VCO_TUNE1_MODE0 0x0110
#define QSERDES_COM_VCO_TUNE2_MODE0 0x0114
#define QSERDES_COM_CMN_STATUS 0x0140
#define QSERDES_COM_CLK_SEL 0x0154
#define QSERDES_COM_HSCLK_SEL 0x0158
#define QSERDES_COM_CORECLK_DIV_MODE0 0x0168
#define QSERDES_COM_CORE_CLK_EN 0x0174
#define QSERDES_COM_C_READY_STATUS 0x0178
#define QSERDES_COM_CMN_CONFIG 0x017C
#define QSERDES_COM_SVS_MODE_CLK_SEL 0x0184
#define QSERDES_COM_SSC_EN_CENTER 0x0010
#define QSERDES_COM_SSC_ADJ_PER1 0x0014
#define QSERDES_COM_SSC_ADJ_PER2 0x0018
#define QSERDES_COM_SSC_PER1 0x001C
#define QSERDES_COM_SSC_PER2 0x0020
#define QSERDES_COM_SSC_STEP_SIZE1_MODE0 0x0024
#define QSERDES_COM_SSC_STEP_SIZE2_MODE0 0x0028
#define DP_PHY_PLL_POLL_SLEEP_US 500
#define DP_PHY_PLL_POLL_TIMEOUT_US 10000
#define DP_VCO_RATE_8100MHZDIV1000 8100000UL
#define DP_VCO_RATE_9720MHZDIV1000 9720000UL
#define DP_VCO_RATE_10800MHZDIV1000 10800000UL
#define DP_7NM_C_READY BIT(0)
#define DP_7NM_FREQ_DONE BIT(0)
#define DP_7NM_PLL_LOCKED BIT(1)
#define DP_7NM_PHY_READY BIT(1)
#define DP_7NM_TSYNC_DONE BIT(0)
static int edp_vco_pll_init_db_7nm(struct dp_pll_db *pdb,
unsigned long rate)
{
struct dp_pll *pll = pdb->pll;
u32 spare_value = 0;
spare_value = dp_pll_read(dp_phy, DP_PHY_SPARE0);
pdb->lane_cnt = spare_value & 0x0F;
pdb->orientation = (spare_value & 0xF0) >> 4;
DP_DEBUG("spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
spare_value, pdb->lane_cnt, pdb->orientation);
pdb->div_frac_start1_mode0 = 0x00;
pdb->integloop_gain0_mode0 = 0x3f;
pdb->integloop_gain1_mode0 = 0x00;
switch (rate) {
case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_9720MHZDIV1000);
pdb->hsclk_sel = 0x05;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->lock_cmp1_mode0 = 0x6f;
pdb->lock_cmp2_mode0 = 0x08;
pdb->phy_vco_div = 0x01;
pdb->lock_cmp_en = 0x08;
pdb->ssc_step_size1_mode0 = 0x45;
pdb->ssc_step_size2_mode0 = 0x06;
break;
case DP_VCO_HSCLK_RATE_2160MHZDIV1000:
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
pdb->hsclk_sel = 0x04;
pdb->dec_start_mode0 = 0x70;
pdb->div_frac_start2_mode0 = 0x00;
pdb->div_frac_start3_mode0 = 0x08;
pdb->lock_cmp1_mode0 = 0x3f;
pdb->lock_cmp2_mode0 = 0x0b;
pdb->phy_vco_div = 0x1;
pdb->lock_cmp_en = 0x04;
pdb->ssc_step_size1_mode0 = 0xb0;
pdb->ssc_step_size2_mode0 = 0x06;
break;
case DP_VCO_HSCLK_RATE_2430MHZDIV1000:
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
pdb->hsclk_sel = 0x04;
pdb->dec_start_mode0 = 0x7e;
pdb->div_frac_start2_mode0 = 0x00;
pdb->div_frac_start3_mode0 = 0x09;
pdb->lock_cmp1_mode0 = 0xa7;
pdb->lock_cmp2_mode0 = 0x0c;
pdb->phy_vco_div = 0x1;
pdb->lock_cmp_en = 0x04;
pdb->ssc_step_size1_mode0 = 0x86;
pdb->ssc_step_size2_mode0 = 0x07;
break;
case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
pdb->hsclk_sel = 0x03;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->lock_cmp1_mode0 = 0x0f;
pdb->lock_cmp2_mode0 = 0x0e;
pdb->phy_vco_div = 0x01;
pdb->lock_cmp_en = 0x08;
pdb->ssc_step_size1_mode0 = 0x45;
pdb->ssc_step_size2_mode0 = 0x06;
break;
case DP_VCO_HSCLK_RATE_3240MHZDIV1000:
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
pdb->hsclk_sel = 0x03;
pdb->dec_start_mode0 = 0x7e;
pdb->div_frac_start2_mode0 = 0x00;
pdb->div_frac_start3_mode0 = 0x09;
pdb->lock_cmp1_mode0 = 0xdf;
pdb->lock_cmp2_mode0 = 0x10;
pdb->phy_vco_div = 0x2;
pdb->lock_cmp_en = 0x04;
pdb->ssc_step_size1_mode0 = 0x86;
pdb->ssc_step_size2_mode0 = 0x07;
break;
case DP_VCO_HSCLK_RATE_4320MHZDIV1000:
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
pdb->hsclk_sel = 0x01;
pdb->dec_start_mode0 = 0x70;
pdb->div_frac_start2_mode0 = 0x00;
pdb->div_frac_start3_mode0 = 0x08;
pdb->lock_cmp1_mode0 = 0x7f;
pdb->lock_cmp2_mode0 = 0x16;
pdb->phy_vco_div = 0x2;
pdb->lock_cmp_en = 0x04;
pdb->ssc_step_size1_mode0 = 0xb0;
pdb->ssc_step_size2_mode0 = 0x06;
break;
case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
pdb->hsclk_sel = 0x01;
pdb->dec_start_mode0 = 0x8c;
pdb->div_frac_start2_mode0 = 0x00;
pdb->div_frac_start3_mode0 = 0x0a;
pdb->lock_cmp1_mode0 = 0x1f;
pdb->lock_cmp2_mode0 = 0x1c;
pdb->phy_vco_div = 0x02;
pdb->lock_cmp_en = 0x08;
pdb->ssc_step_size1_mode0 = 0x5c;
pdb->ssc_step_size2_mode0 = 0x08;
break;
case DP_VCO_HSCLK_RATE_5940MHZDIV1000:
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_8100MHZDIV1000);
pdb->hsclk_sel = 0x01;
pdb->dec_start_mode0 = 0x9a;
pdb->div_frac_start2_mode0 = 0x00;
pdb->div_frac_start3_mode0 = 0x0b;
pdb->lock_cmp1_mode0 = 0xef;
pdb->lock_cmp2_mode0 = 0x1e;
pdb->phy_vco_div = 0x00;
pdb->lock_cmp_en = 0x04;
pdb->ssc_step_size1_mode0 = 0x33;
pdb->ssc_step_size2_mode0 = 0x09;
break;
case DP_VCO_HSCLK_RATE_8100MHZDIV1000:
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_8100MHZDIV1000);
pdb->hsclk_sel = 0x00;
pdb->dec_start_mode0 = 0x69;
pdb->div_frac_start2_mode0 = 0x80;
pdb->div_frac_start3_mode0 = 0x07;
pdb->lock_cmp1_mode0 = 0x2f;
pdb->lock_cmp2_mode0 = 0x2a;
pdb->phy_vco_div = 0x00;
pdb->lock_cmp_en = 0x08;
pdb->ssc_step_size1_mode0 = 0x45;
pdb->ssc_step_size2_mode0 = 0x06;
break;
default:
DP_ERR("unsupported rate %ld\n", rate);
return -EINVAL;
}
return 0;
}
static int edp_config_vco_rate_7nm(struct dp_pll *pll,
unsigned long rate)
{
int rc;
struct dp_pll_db *pdb = &pll->pll_db;
u32 status;
rc = edp_vco_pll_init_db_7nm(pdb, rate);
if (rc < 0) {
DP_ERR("VCO Init DB failed\n");
return rc;
}
dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x7d);
/* Make sure the PLL register writes are done */
wmb();
if (readl_poll_timeout_atomic((dp_pll_get_base(dp_pll) +
QSERDES_COM_CMN_STATUS),
status, ((status & BIT(7)) > 0),
5, 100)) {
DP_ERR("refgen not ready. Status=%x\n", status);
}
dp_pll_write(dp_ln_tx0, TXn_LDO_CONFIG, 0x01);
dp_pll_write(dp_ln_tx1, TXn_LDO_CONFIG, 0x01);
dp_pll_write(dp_ln_tx0, TXn_LANE_MODE_1, 0x00);
dp_pll_write(dp_ln_tx1, TXn_LANE_MODE_1, 0x00);
if (pll->ssc_en) {
dp_pll_write(dp_pll, QSERDES_COM_SSC_EN_CENTER, 0x01);
dp_pll_write(dp_pll, QSERDES_COM_SSC_ADJ_PER1, 0x00);
dp_pll_write(dp_pll, QSERDES_COM_SSC_PER1, 0x36);
dp_pll_write(dp_pll, QSERDES_COM_SSC_PER2, 0x01);
dp_pll_write(dp_pll, QSERDES_COM_SSC_STEP_SIZE1_MODE0,
pdb->ssc_step_size1_mode0);
dp_pll_write(dp_pll, QSERDES_COM_SSC_STEP_SIZE2_MODE0,
pdb->ssc_step_size2_mode0);
}
dp_pll_write(dp_pll, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
dp_pll_write(dp_pll, QSERDES_COM_SYSCLK_EN_SEL, 0x0b);
dp_pll_write(dp_pll, QSERDES_COM_SYS_CLK_CTRL, 0x02);
dp_pll_write(dp_pll, QSERDES_COM_CLK_ENABLE1, 0x0c);
dp_pll_write(dp_pll, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
dp_pll_write(dp_pll, QSERDES_COM_CLK_SEL, 0x30);
dp_pll_write(dp_pll,
QSERDES_COM_HSCLK_SEL, pdb->hsclk_sel);
dp_pll_write(dp_pll, QSERDES_COM_PLL_IVCO, 0x0f);
dp_pll_write(dp_pll,
QSERDES_COM_LOCK_CMP_EN, pdb->lock_cmp_en);
dp_pll_write(dp_pll, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
dp_pll_write(dp_pll, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
dp_pll_write(dp_pll, QSERDES_COM_CP_CTRL_MODE0, 0x06);
dp_pll_write(dp_pll,
QSERDES_COM_DEC_START_MODE0, pdb->dec_start_mode0);
dp_pll_write(dp_pll,
QSERDES_COM_DIV_FRAC_START1_MODE0, pdb->div_frac_start1_mode0);
dp_pll_write(dp_pll,
QSERDES_COM_DIV_FRAC_START2_MODE0, pdb->div_frac_start2_mode0);
dp_pll_write(dp_pll,
QSERDES_COM_DIV_FRAC_START3_MODE0, pdb->div_frac_start3_mode0);
dp_pll_write(dp_pll,
QSERDES_COM_CMN_CONFIG, 0x02);
dp_pll_write(dp_pll,
QSERDES_COM_INTEGLOOP_GAIN0_MODE0, pdb->integloop_gain0_mode0);
dp_pll_write(dp_pll,
QSERDES_COM_INTEGLOOP_GAIN1_MODE0, pdb->integloop_gain1_mode0);
dp_pll_write(dp_pll,
QSERDES_COM_VCO_TUNE_MAP, 0x00);
dp_pll_write(dp_pll,
QSERDES_COM_LOCK_CMP1_MODE0, pdb->lock_cmp1_mode0);
dp_pll_write(dp_pll,
QSERDES_COM_LOCK_CMP2_MODE0, pdb->lock_cmp2_mode0);
dp_pll_write(dp_phy, DP_PHY_VCO_DIV, pdb->phy_vco_div);
/* Make sure the PLL register writes are done */
wmb();
dp_pll_write(dp_pll, QSERDES_COM_BG_TIMER, 0x0a);
dp_pll_write(dp_pll, QSERDES_COM_CORECLK_DIV_MODE0, 0x14);
dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
dp_pll_write(dp_pll,
QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
dp_pll_write(dp_pll, QSERDES_COM_CORE_CLK_EN, 0x0f);
dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE1_MODE0, 0xa0);
dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
/* Make sure the PHY register writes are done */
wmb();
/* TX Lane configuration */
dp_pll_write(dp_phy, DP_PHY_TX0_TX1_LANE_CTL, 0x05);
dp_pll_write(dp_phy, DP_PHY_TX2_TX3_LANE_CTL, 0x05);
/* TX-0 register configuration */
dp_pll_write(dp_ln_tx0, TXn_TRANSCEIVER_BIAS_EN, 0x03);
dp_pll_write(dp_ln_tx0, TXn_CLKBUF_ENABLE, 0x0f);
dp_pll_write(dp_ln_tx0, TXn_RESET_TSYNC_EN, 0x03);
dp_pll_write(dp_ln_tx0, TXn_TRAN_DRVR_EMP_EN, 0x01);
dp_pll_write(dp_ln_tx0, TXn_TX_BAND, 0x4);
/* TX-1 register configuration */
dp_pll_write(dp_ln_tx1, TXn_TRANSCEIVER_BIAS_EN, 0x03);
dp_pll_write(dp_ln_tx1, TXn_CLKBUF_ENABLE, 0x0f);
dp_pll_write(dp_ln_tx1, TXn_RESET_TSYNC_EN, 0x03);
dp_pll_write(dp_ln_tx1, TXn_TRAN_DRVR_EMP_EN, 0x01);
dp_pll_write(dp_ln_tx1, TXn_TX_BAND, 0x4);
/* Make sure the PHY register writes are done */
wmb();
return 0;
}
enum edp_7nm_pll_status {
C_READY,
FREQ_DONE,
PLL_LOCKED,
PHY_READY,
TSYNC_DONE,
};
static char *edp_7nm_pll_get_status_name(enum edp_7nm_pll_status status)
{
switch (status) {
case C_READY:
return "C_READY";
case FREQ_DONE:
return "FREQ_DONE";
case PLL_LOCKED:
return "PLL_LOCKED";
case PHY_READY:
return "PHY_READY";
case TSYNC_DONE:
return "TSYNC_DONE";
default:
return "unknown";
}
}
static bool edp_7nm_pll_get_status(struct dp_pll *pll,
enum edp_7nm_pll_status status)
{
u32 reg, state, bit;
void __iomem *base;
bool success = true;
switch (status) {
case C_READY:
base = dp_pll_get_base(dp_pll);
reg = QSERDES_COM_C_READY_STATUS;
bit = DP_7NM_C_READY;
break;
case FREQ_DONE:
base = dp_pll_get_base(dp_pll);
reg = QSERDES_COM_CMN_STATUS;
bit = DP_7NM_FREQ_DONE;
break;
case PLL_LOCKED:
base = dp_pll_get_base(dp_pll);
reg = QSERDES_COM_CMN_STATUS;
bit = DP_7NM_PLL_LOCKED;
break;
case PHY_READY:
base = dp_pll_get_base(dp_phy);
reg = DP_PHY_STATUS;
bit = DP_7NM_PHY_READY;
break;
case TSYNC_DONE:
base = dp_pll_get_base(dp_phy);
reg = DP_PHY_STATUS;
bit = DP_7NM_TSYNC_DONE;
break;
default:
return false;
}
if (readl_poll_timeout_atomic((base + reg), state,
((state & bit) > 0),
DP_PHY_PLL_POLL_SLEEP_US,
DP_PHY_PLL_POLL_TIMEOUT_US)) {
DP_ERR("%s failed, status=%x\n",
edp_7nm_pll_get_status_name(status), state);
success = false;
}
return success;
}
static int edp_pll_enable_7nm(struct dp_pll *pll)
{
int rc = 0;
u32 bias_en0, drvr_en0, bias_en1, drvr_en1, phy_cfg_1;
pll->aux->state &= ~DP_STATE_PLL_LOCKED;
dp_pll_write(dp_phy, DP_PHY_CFG, 0x01);
dp_pll_write(dp_phy, DP_PHY_CFG, 0x05);
dp_pll_write(dp_phy, DP_PHY_CFG, 0x01);
dp_pll_write(dp_phy, DP_PHY_CFG, 0x09);
dp_pll_write(dp_pll, QSERDES_COM_RESETSM_CNTRL, 0x20);
wmb(); /* Make sure the PLL register writes are done */
if (!edp_7nm_pll_get_status(pll, C_READY)) {
rc = -EINVAL;
goto lock_err;
}
if (!edp_7nm_pll_get_status(pll, FREQ_DONE)) {
rc = -EINVAL;
goto lock_err;
}
if (!edp_7nm_pll_get_status(pll, PLL_LOCKED)) {
rc = -EINVAL;
goto lock_err;
}
dp_pll_write(dp_phy, DP_PHY_CFG, 0x19);
dp_pll_write(dp_ln_tx0, TXn_HIGHZ_DRVR_EN, 0x1f);
dp_pll_write(dp_ln_tx0, TXn_HIGHZ_DRVR_EN, 0x04);
dp_pll_write(dp_ln_tx0, TXn_TX_POL_INV, 0x00);
dp_pll_write(dp_ln_tx1, TXn_HIGHZ_DRVR_EN, 0x1f);
dp_pll_write(dp_ln_tx1, TXn_HIGHZ_DRVR_EN, 0x04);
dp_pll_write(dp_ln_tx1, TXn_TX_POL_INV, 0x00);
dp_pll_write(dp_ln_tx0, TXn_TX_DRV_LVL_OFFSET, 0x10);
dp_pll_write(dp_ln_tx1, TXn_TX_DRV_LVL_OFFSET, 0x10);
dp_pll_write(dp_ln_tx0,
TXn_RES_CODE_LANE_OFFSET_TX0, 0x11);
dp_pll_write(dp_ln_tx0,
TXn_RES_CODE_LANE_OFFSET_TX1, 0x11);
dp_pll_write(dp_ln_tx1,
TXn_RES_CODE_LANE_OFFSET_TX0, 0x11);
dp_pll_write(dp_ln_tx1,
TXn_RES_CODE_LANE_OFFSET_TX1, 0x11);
dp_pll_write(dp_ln_tx0, TXn_TX_EMP_POST1_LVL, 0x10);
dp_pll_write(dp_ln_tx1, TXn_TX_EMP_POST1_LVL, 0x10);
dp_pll_write(dp_ln_tx0, TXn_TX_DRV_LVL, 0x1f);
dp_pll_write(dp_ln_tx1, TXn_TX_DRV_LVL, 0x1f);
/* Make sure the PHY register writes are done */
wmb();
if (pll->pll_db.lane_cnt == 1) {
bias_en0 = 0x01;
bias_en1 = 0x00;
drvr_en0 = 0x06;
drvr_en1 = 0x07;
phy_cfg_1 = 0x01;
} else if (pll->pll_db.lane_cnt == 2) {
bias_en0 = 0x03;
bias_en1 = 0x00;
drvr_en0 = 0x04;
drvr_en1 = 0x07;
phy_cfg_1 = 0x03;
} else {
bias_en0 = 0x03;
bias_en1 = 0x03;
drvr_en0 = 0x04;
drvr_en1 = 0x04;
phy_cfg_1 = 0x0f;
}
dp_pll_write(dp_ln_tx0, TXn_HIGHZ_DRVR_EN, drvr_en0);
dp_pll_write(dp_ln_tx0, TXn_TRANSCEIVER_BIAS_EN,
bias_en0);
dp_pll_write(dp_ln_tx1, TXn_HIGHZ_DRVR_EN, drvr_en1);
dp_pll_write(dp_ln_tx1, TXn_TRANSCEIVER_BIAS_EN,
bias_en1);
/* Make sure the PHY register writes are done */
wmb();
dp_pll_write(dp_phy, DP_PHY_CFG_1, phy_cfg_1);
if (!edp_7nm_pll_get_status(pll, PHY_READY)) {
rc = -EINVAL;
goto lock_err;
}
dp_pll_write(dp_phy, DP_PHY_CFG, 0x18);
udelay(100);
dp_pll_write(dp_phy, DP_PHY_CFG, 0x19);
/* Make sure the PHY register writes are done */
wmb();
if (!edp_7nm_pll_get_status(pll, TSYNC_DONE)) {
rc = -EINVAL;
goto lock_err;
}
if (!edp_7nm_pll_get_status(pll, PHY_READY)) {
rc = -EINVAL;
goto lock_err;
}
pll->aux->state |= DP_STATE_PLL_LOCKED;
DP_DEBUG("PLL is locked\n");
lock_err:
return rc;
}
static void edp_pll_disable_7nm(struct dp_pll *pll)
{
/* Assert DP PHY power down */
dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x2);
/*
* Make sure all the register writes to disable PLL are
* completed before doing any other operation
*/
wmb();
}
static int edp_vco_clk_set_div(struct dp_pll *pll, unsigned int div)
{
u32 val = 0;
if (!pll) {
DP_ERR("invalid input parameters\n");
return -EINVAL;
}
if (is_gdsc_disabled(pll))
return -EINVAL;
val = dp_pll_read(dp_phy, DP_PHY_VCO_DIV);
val &= ~0x03;
switch (div) {
case 2:
val |= 1;
break;
case 4:
val |= 2;
break;
case 6:
/* When div = 6, val is 0, so do nothing here */
;
break;
case 8:
val |= 3;
break;
default:
DP_DEBUG("unsupported div value %d\n", div);
return -EINVAL;
}
dp_pll_write(dp_phy, DP_PHY_VCO_DIV, val);
/* Make sure the PHY registers writes are done */
wmb();
DP_DEBUG("val=%d div=%x\n", val, div);
return 0;
}
static int edp_vco_set_rate_7nm(struct dp_pll *pll, unsigned long rate)
{
int rc = 0;
if (!pll) {
DP_ERR("invalid input parameters\n");
return -EINVAL;
}
DP_DEBUG("DP lane CLK rate=%ld\n", rate);
rc = edp_config_vco_rate_7nm(pll, rate);
if (rc < 0) {
DP_ERR("Failed to set clk rate\n");
return rc;
}
return rc;
}
static int edp_pll_configure(struct dp_pll *pll, unsigned long rate)
{
int rc = 0;
if (!pll || !rate) {
DP_ERR("invalid input parameters rate = %lu\n", rate);
return -EINVAL;
}
rate = rate * 10;
if (rate <= DP_VCO_HSCLK_RATE_1620MHZDIV1000)
rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000)
rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
else
rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
rc = edp_vco_set_rate_7nm(pll, rate);
if (rc < 0) {
DP_ERR("pll rate %s set failed\n", rate);
return rc;
}
pll->vco_cached_rate = rate;
DP_DEBUG("pll rate %lu set success\n", rate);
return rc;
}
static int edp_pll_prepare(struct dp_pll *pll)
{
int rc = 0;
if (!pll) {
DP_ERR("invalid input parameters\n");
return -EINVAL;
}
rc = edp_pll_enable_7nm(pll);
if (rc < 0)
DP_ERR("ndx=%d failed to enable dp pll\n", pll->index);
return rc;
}
static int edp_pll_unprepare(struct dp_pll *pll)
{
int rc = 0;
if (!pll) {
DP_ERR("invalid input parameter\n");
return -EINVAL;
}
edp_pll_disable_7nm(pll);
return rc;
}
static unsigned long edp_pll_link_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct dp_pll *pll = NULL;
struct dp_pll_vco_clk *pll_link = NULL;
unsigned long rate = 0;
if (!hw) {
DP_ERR("invalid input parameters\n");
return -EINVAL;
}
pll_link = to_dp_vco_hw(hw);
pll = pll_link->priv;
rate = pll->vco_cached_rate;
rate = pll->vco_cached_rate * 100;
return rate;
}
static long edp_pll_link_clk_round(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct dp_pll *pll = NULL;
struct dp_pll_vco_clk *pll_link = NULL;
if (!hw) {
DP_ERR("invalid input parameters\n");
return -EINVAL;
}
pll_link = to_dp_vco_hw(hw);
pll = pll_link->priv;
rate = pll->vco_cached_rate * 100;
return rate;
}
static unsigned long edp_pll_vco_div_clk_get_rate(struct dp_pll *pll)
{
if (pll->vco_cached_rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
return (pll->vco_cached_rate / 6 * 1000);
else if (pll->vco_cached_rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
return (pll->vco_cached_rate / 4 * 1000);
else
return (pll->vco_cached_rate / 2 * 1000);
}
static unsigned long edp_pll_vco_div_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct dp_pll *pll = NULL;
struct dp_pll_vco_clk *pll_link = NULL;
if (!hw) {
DP_ERR("invalid input parameters\n");
return -EINVAL;
}
pll_link = to_dp_vco_hw(hw);
pll = pll_link->priv;
return edp_pll_vco_div_clk_get_rate(pll);
}
static long edp_pll_vco_div_clk_round(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
return edp_pll_vco_div_clk_recalc_rate(hw, *parent_rate);
}
static int edp_pll_vco_div_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct dp_pll *pll = NULL;
struct dp_pll_vco_clk *pll_link = NULL;
int rc = 0;
if (!hw) {
DP_ERR("invalid input parameters\n");
return -EINVAL;
}
pll_link = to_dp_vco_hw(hw);
pll = pll_link->priv;
if (rate != edp_pll_vco_div_clk_get_rate(pll)) {
DP_ERR("unsupported rate %lu failed\n", rate);
return rc;
}
rate /= 1000;
rc = edp_vco_clk_set_div(pll, pll->vco_cached_rate / rate);
if (rc < 0) {
DP_DEBUG("set rate %lu failed\n", rate);
return rc;
}
DP_DEBUG("set rate %lu success\n", rate);
return 0;
}
static const struct clk_ops edp_pll_link_clk_ops = {
.recalc_rate = edp_pll_link_clk_recalc_rate,
.round_rate = edp_pll_link_clk_round,
};
static const struct clk_ops edp_pll_vco_div_clk_ops = {
.recalc_rate = edp_pll_vco_div_clk_recalc_rate,
.round_rate = edp_pll_vco_div_clk_round,
.set_rate = edp_pll_vco_div_clk_set_rate,
};
static struct clk_init_data edp_phy_pll_clks[DP_PLL_NUM_CLKS] = {
{
.name = "edp_phy_pll_link_clk",
.ops = &edp_pll_link_clk_ops,
},
{
.name = "edp_phy_pll_vco_div_clk",
.ops = &edp_pll_vco_div_clk_ops,
},
};
static struct dp_pll_vco_clk *edp_pll_get_clks(struct dp_pll *pll)
{
int i;
for (i = 0; i < DP_PLL_NUM_CLKS; i++) {
snprintf(pll->pll_clks[i].name, DP_PLL_NAME_MAX_SIZE,
"%s", edp_phy_pll_clks[i].name);
pll->pll_clks[i].init_data.name = pll->pll_clks[i].name;
pll->pll_clks[i].init_data.ops = edp_phy_pll_clks[i].ops;
pll->pll_clks[i].hw.init = &pll->pll_clks[i].init_data;
}
return pll->pll_clks;
}
static int dp_pll_clock_register_helper(struct dp_pll *pll,
struct dp_pll_vco_clk *clks, int num_clks)
{
int rc = 0, i = 0;
struct platform_device *pdev;
struct clk *clk;
if (!pll || !clks) {
DP_ERR("input not initialized\n");
return -EINVAL;
}
pdev = pll->pdev;
for (i = 0; i < num_clks; i++) {
clks[i].priv = pll;
clk = clk_register(&pdev->dev, &clks[i].hw);
if (IS_ERR(clk)) {
DP_ERR("%s registration failed for DP: %d\n",
clk_hw_get_name(&clks[i].hw), pll->index);
return -EINVAL;
}
pll->clk_data->clks[i] = clk;
}
return rc;
}
int edp_pll_clock_register_7nm(struct dp_pll *pll)
{
int rc = 0;
struct platform_device *pdev;
struct dp_pll_vco_clk *pll_clks;
if (!pll) {
DP_ERR("pll data not initialized\n");
return -EINVAL;
}
pdev = pll->pdev;
pll->clk_data = kzalloc(sizeof(*pll->clk_data), GFP_KERNEL);
if (!pll->clk_data)
return -ENOMEM;
pll->clk_data->clks = kcalloc(DP_PLL_NUM_CLKS, sizeof(struct clk *),
GFP_KERNEL);
if (!pll->clk_data->clks) {
kfree(pll->clk_data);
return -ENOMEM;
}
pll->clk_data->clk_num = DP_PLL_NUM_CLKS;
pll->pll_db.pll = pll;
pll->pll_cfg = edp_pll_configure;
pll->pll_prepare = edp_pll_prepare;
pll->pll_unprepare = edp_pll_unprepare;
pll_clks = edp_pll_get_clks(pll);
rc = dp_pll_clock_register_helper(pll, pll_clks, DP_PLL_NUM_CLKS);
if (rc) {
DP_ERR("Clock register failed rc=%d\n", rc);
goto clk_reg_fail;
}
rc = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, pll->clk_data);
if (rc) {
DP_ERR("Clock add provider failed rc=%d\n", rc);
goto clk_reg_fail;
}
DP_DEBUG("success\n");
return rc;
clk_reg_fail:
dp_pll_clock_unregister_5nm(pll);
return rc;
}

View file

@ -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 <robdclark@gmail.com>
@ -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;

View file

@ -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[] = {

View file

@ -1776,6 +1776,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
.set_allowed_mode_switch = NULL,
};
static const struct sde_connector_ops dp_ops = {
.set_info_blob = dp_connector_set_info_blob,
.post_init = dp_connector_post_init,
.detect = dp_connector_detect,
.get_modes = dp_connector_get_modes,
@ -1925,6 +1926,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
for (i = 0; i < sde_kms->dp_display_count &&
priv->num_encoders < max_encoders; ++i) {
int idx;
struct dp_display_info dp_info = {0};
display = sde_kms->dp_displays[i];
encoder = NULL;
@ -1936,6 +1938,13 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
continue;
}
rc = dp_display_get_info(display, &dp_info);
if (rc) {
SDE_ERROR("failed to read dp info, %d\n", rc);
continue;
}
info.h_tile_instance[0] = dp_info.intf_idx[0];
encoder = sde_encoder_init(dev, &info);
if (IS_ERR_OR_NULL(encoder)) {
SDE_ERROR("dp encoder init failed %d\n", i);
@ -1956,7 +1965,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
display,
&dp_ops,
DRM_CONNECTOR_POLL_HPD,
DRM_MODE_CONNECTOR_DisplayPort);
info.intf_type);
if (connector) {
priv->encoders[priv->num_encoders++] = encoder;
priv->connectors[priv->num_connectors++] = connector;
@ -1969,9 +1978,9 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
/* update display cap to MST_MODE for DP MST encoders */
info.capabilities |= MSM_DISPLAY_CAP_MST_MODE;
for (idx = 0; idx < sde_kms->dp_stream_count &&
for (idx = 0; idx < dp_info.stream_cnt &&
priv->num_encoders < max_encoders; idx++) {
info.h_tile_instance[0] = idx;
info.h_tile_instance[0] = dp_info.intf_idx[idx];
encoder = sde_encoder_init(dev, &info);
if (IS_ERR_OR_NULL(encoder)) {
SDE_ERROR("dp mst encoder init failed %d\n", i);