Merge tag 'LA.UM.9.14.1.r1-10900-QCM6490.QSSI14.0' of https://git.codelinaro.org/clo/la/platform/vendor/opensource/display-drivers into android13-5.4-lahaina

"LA.UM.9.14.1.r1-10900-QCM6490.QSSI14.0"

* tag 'LA.UM.9.14.1.r1-10900-QCM6490.QSSI14.0' of https://git.codelinaro.org/clo/la/platform/vendor/opensource/display-drivers:
  disp: msm: dp: fix compilation errors
  Revert "disp: msm: dp: skip hpd config"
  disp: msm: dp: wait for resources init in case of cont splash
  disp: msm: sde: Remove pm vote at time of handoff
  disp: msm: eDP continuous splash implementation
  disp: msm: dp: add backlight for edp
  disp: msm: dp: skip hpd config
  disp: msm: edp continuous splash implementation
  disp: msm: dp: add eDP panel notifier support
  disp: msm: dp: add eDP panel notifier support
  disp: msm: dp: add pinctrl state for backlight pwm
  drm/msm/dp: add support to multiple dp instances
  disp: msm: dp: move fsa init from dp probe to dp hotplug
  disp: msm: sde: update sde interrupt map
  disp: msm: dp: update pll and catalog sequence
  disp: msm: dp: Convert clock operations to byte2 ops
  disp: msm: dp: Masking interrupt for eDP
  disp: msm: dp: add support for eDP display
  disp: msm: dp: add eDP support as a primary display
  disp: msm: dp: Support DP display as primary
  disp: msm: dp: add pixel base offset support in device tree
  disp: msm: dp: add 7nm eDP PHY support
  disp: msm: dp: add files for 7nm eDP PHY

Change-Id: I614c52764b1479b90bd5a603828b5dff7e6c83db
This commit is contained in:
Michael Bestas 2023-08-29 04:01:21 +03:00
commit 0124532c0e
No known key found for this signature in database
GPG Key ID: CC95044519BE6669
29 changed files with 3091 additions and 357 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) 2012-2020, The Linux Foundation. All rights reserved.
*/
@ -631,7 +632,7 @@ static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg)
aux_cfg[i].current_index = 0;
}
static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)
static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg, bool skip_op)
{
struct dp_aux_private *aux;
@ -645,10 +646,16 @@ static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)
if (aux->enabled)
return;
/*skip aux init when cont. splash is enabled*/
if (skip_op)
goto skip_init;
dp_aux_reset_phy_config_indices(aux_cfg);
aux->catalog->setup(aux->catalog, aux_cfg);
aux->catalog->reset(aux->catalog);
aux->catalog->enable(aux->catalog, true);
skip_init:
atomic_set(&aux->aborted, 0);
aux->retry_cnt = 0;
aux->enabled = true;

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.
*/
@ -47,7 +48,7 @@ struct dp_aux {
int (*drm_aux_register)(struct dp_aux *aux);
void (*drm_aux_deregister)(struct dp_aux *aux);
void (*isr)(struct dp_aux *aux);
void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg);
void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg, bool skip_op);
void (*deinit)(struct dp_aux *aux);
void (*reconfig)(struct dp_aux *aux);
void (*abort)(struct dp_aux *aux, bool abort);

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_ */

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;
@ -162,8 +165,13 @@ trigger_idle:
* configuration, output format and sink/panel timing information.
*/
static void dp_ctrl_configure_source_link_params(struct dp_ctrl_private *ctrl,
bool enable)
bool enable, bool skip_op)
{
if (skip_op) {
DP_DEBUG("configuring source link params skipped\n");
return;
}
if (enable) {
ctrl->catalog->lane_mapping(ctrl->catalog, ctrl->orientation,
ctrl->parser->l_map);
@ -530,7 +538,7 @@ skip_training:
return ret;
}
static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl, bool skip_op)
{
int ret = 0;
u8 const encoding = 0x1, downspread = 0x00;
@ -544,6 +552,11 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
ctrl->link->link_params.bw_code);
link_info.capabilities = ctrl->panel->link_info.capabilities;
if (skip_op) {
DP_DEBUG("link training skipped\n");
return 0;
}
ret = drm_dp_link_configure(ctrl->aux->drm_aux, &link_info);
if (ret)
goto end;
@ -589,7 +602,7 @@ end:
return ret;
}
static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl)
static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, bool skip_op)
{
int ret = 0;
@ -601,13 +614,15 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl)
* transitioned to PUSH_IDLE. In order to start transmitting a link
* training pattern, we have to first to a DP software reset.
*/
ctrl->catalog->reset(ctrl->catalog);
if (!skip_op)
ctrl->catalog->reset(ctrl->catalog);
if (ctrl->fec_mode)
drm_dp_dpcd_writeb(ctrl->aux->drm_aux, DP_FEC_CONFIGURATION,
0x01);
ret = dp_ctrl_link_train(ctrl);
ret = dp_ctrl_link_train(ctrl, skip_op);
end:
return ret;
@ -618,6 +633,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 +665,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 +692,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,
@ -685,7 +732,7 @@ end:
ctrl->training_2_pattern = pattern;
}
static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow, bool skip_op)
{
int rc = -EINVAL;
bool downgrade = false;
@ -711,7 +758,7 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
ctrl->link->link_params.lane_count,
ctrl->orientation);
dp_ctrl_configure_source_link_params(ctrl, true);
dp_ctrl_configure_source_link_params(ctrl, true, skip_op);
if (!(--link_train_max_retries % 10)) {
struct dp_link_params *link = &ctrl->link->link_params;
@ -723,7 +770,7 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
dp_ctrl_select_training_pattern(ctrl, downgrade);
rc = dp_ctrl_setup_main_link(ctrl);
rc = dp_ctrl_setup_main_link(ctrl, skip_op);
if (!rc)
break;
@ -747,7 +794,7 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
if (rc != -EAGAIN)
dp_ctrl_link_rate_down_shift(ctrl);
dp_ctrl_configure_source_link_params(ctrl, false);
dp_ctrl_configure_source_link_params(ctrl, false, skip_op);
dp_ctrl_disable_link_clock(ctrl);
/* hw recommended delays before retrying link training */
@ -817,7 +864,7 @@ static int dp_ctrl_disable_stream_clocks(struct dp_ctrl_private *ctrl,
}
return ret;
}
static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset)
static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset, bool skip_op)
{
struct dp_ctrl_private *ctrl;
struct dp_catalog_ctrl *catalog;
@ -832,7 +879,7 @@ static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset)
ctrl->orientation = flip;
catalog = ctrl->catalog;
if (reset) {
if (reset && !skip_op) {
catalog->usb_reset(ctrl->catalog, flip);
catalog->phy_reset(ctrl->catalog);
}
@ -871,7 +918,7 @@ static void dp_ctrl_send_video(struct dp_ctrl_private *ctrl)
ctrl->catalog->state_ctrl(ctrl->catalog, ST_SEND_VIDEO);
}
static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl)
static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl, bool skip_op)
{
int ret = 0;
struct dp_ctrl_private *ctrl;
@ -896,7 +943,7 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl)
goto end;
ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_STARTED;
ret = dp_ctrl_setup_main_link(ctrl);
ret = dp_ctrl_setup_main_link(ctrl, skip_op);
ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_STARTED;
if (ret) {
@ -914,7 +961,7 @@ end:
return ret;
}
static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl)
static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl, bool skip_op)
{
int ret = 0;
struct dp_ctrl_private *ctrl;
@ -943,14 +990,14 @@ static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl)
ctrl->dp_ctrl.stream_off(&ctrl->dp_ctrl, ctrl->panel);
ctrl->dp_ctrl.off(&ctrl->dp_ctrl);
ctrl->aux->init(ctrl->aux, ctrl->parser->aux_cfg);
ctrl->aux->init(ctrl->aux, ctrl->parser->aux_cfg, skip_op);
ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl, ctrl->mst_mode,
ctrl->fec_mode, ctrl->dsc_mode, false);
ctrl->fec_mode, ctrl->dsc_mode, false, skip_op);
if (ret)
DP_ERR("failed to enable DP controller\n");
ctrl->dp_ctrl.stream_on(&ctrl->dp_ctrl, ctrl->panel);
ctrl->dp_ctrl.stream_on(&ctrl->dp_ctrl, ctrl->panel, skip_op);
DP_DEBUG("end\n");
}
@ -1193,7 +1240,7 @@ static void dp_ctrl_fec_dsc_setup(struct dp_ctrl_private *ctrl)
DP_WARN("failed to enable sink dsc\n");
}
static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel, bool skip_op)
{
int rc = 0;
bool link_ready = false;
@ -1215,9 +1262,12 @@ static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
return rc;
}
rc = panel->hw_cfg(panel, true);
if (rc)
return rc;
/*Skip panel config when cont. splash is enabled*/
if (!skip_op) {
rc = panel->hw_cfg(panel, true);
if (rc)
return rc;
}
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
dp_ctrl_send_phy_test_pattern(ctrl);
@ -1308,7 +1358,7 @@ static void dp_ctrl_stream_off(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
}
static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode,
bool fec_mode, bool dsc_mode, bool shallow)
bool fec_mode, bool dsc_mode, bool shallow, bool skip_op)
{
int rc = 0;
struct dp_ctrl_private *ctrl;
@ -1354,9 +1404,13 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode,
ctrl->initial_lane_count = ctrl->link->link_params.lane_count;
ctrl->initial_bw_code = ctrl->link->link_params.bw_code;
rc = dp_ctrl_link_setup(ctrl, shallow);
rc = dp_ctrl_link_setup(ctrl, shallow, skip_op);
if (!rc)
ctrl->power_on = true;
/*enable stream clocks when cont. splash is enabled*/
if (skip_op)
dp_ctrl_enable_stream_clocks(ctrl, ctrl->panel);
end:
return rc;
}
@ -1364,6 +1418,7 @@ end:
static void dp_ctrl_off(struct dp_ctrl *dp_ctrl)
{
struct dp_ctrl_private *ctrl;
bool skip_op = false;
if (!dp_ctrl)
return;
@ -1374,7 +1429,7 @@ static void dp_ctrl_off(struct dp_ctrl *dp_ctrl)
return;
ctrl->catalog->fec_config(ctrl->catalog, false);
dp_ctrl_configure_source_link_params(ctrl, false);
dp_ctrl_configure_source_link_params(ctrl, false, skip_op);
ctrl->catalog->reset(ctrl->catalog);
/* Make sure DP is disabled before clk disable */
@ -1475,6 +1530,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.
*/
@ -15,17 +16,17 @@
#include "dp_debug.h"
struct dp_ctrl {
int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset);
int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset, bool skip_op);
void (*deinit)(struct dp_ctrl *dp_ctrl);
int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode, bool fec_en,
bool dsc_en, bool shallow);
bool dsc_en, bool shallow, bool skip_op);
void (*off)(struct dp_ctrl *dp_ctrl);
void (*abort)(struct dp_ctrl *dp_ctrl, bool abort);
void (*isr)(struct dp_ctrl *dp_ctrl);
bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl);
void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl);
int (*link_maintenance)(struct dp_ctrl *dp_ctrl);
int (*stream_on)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel);
void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl, bool skip_op);
int (*link_maintenance)(struct dp_ctrl *dp_ctrl, bool skip_op);
int (*stream_on)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel, bool skip_op);
void (*stream_off)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel);
void (*stream_pre_off)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel);
void (*set_mst_channel_info)(struct dp_ctrl *dp_ctrl,
@ -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;
}

File diff suppressed because it is too large Load Diff

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,13 @@
#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
#define MAX_DP_ACTIVE_BOOT_DISPLAY 1
#define MAX_CMDLINE_PARAM_LEN 512
enum dp_drv_state {
PM_DEFAULT,
@ -29,6 +35,20 @@ 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_display_boot_param {
char name[MAX_CMDLINE_PARAM_LEN];
char *boot_param;
bool boot_disp_en;
void *disp;
};
struct dp_mst_drm_cbs {
void (*hpd)(void *display, bool hpd_status);
void (*hpd_irq)(void *display, struct dp_mst_hpd_info *info);
@ -70,11 +90,13 @@ 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;
u32 max_mixer_count;
u32 max_dsc_count;
bool cont_splash_enabled;
int (*enable)(struct dp_display *dp_display, void *panel);
int (*post_enable)(struct dp_display *dp_display, void *panel);
@ -99,6 +121,8 @@ struct dp_display {
bool dhdr_update);
int (*set_colorspace)(struct dp_display *dp_display, void *panel,
u32 colorspace);
int (*set_backlight)(struct dp_display *dp_display, void *panel,
u32 bl_lvl);
int (*post_init)(struct dp_display *dp_display);
int (*mst_install)(struct dp_display *dp_display,
struct dp_mst_drm_install_info *mst_install_info);
@ -132,17 +156,29 @@ 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_num_of_boot_displays(void);
int dp_display_get_displays(void **displays, int count);
int dp_display_get_num_of_streams(void);
int dp_display_get_info(void *dp_display, struct dp_display_info *dp_info);
int dp_display_cont_splash_config(void *display);
#else
static inline int dp_display_get_num_of_displays(void)
{
return 0;
}
static inline int dp_display_get_num_of_boot_displays(void)
{
return 0;
}
static inline int dp_display_get_displays(void **displays, int count)
{
return 0;
@ -151,10 +187,18 @@ 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)
{
return 0;
}
static inline int dp_display_cont_splash_config(void *display)
{
return 0;
}
#endif /* CONFIG_DRM_MSM_DP */
#endif /* _DP_DISPLAY_H_ */

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.
*/
@ -325,6 +326,27 @@ int dp_connector_config_hdr(struct drm_connector *connector, void *display,
c_state->dyn_hdr_meta.dynamic_hdr_update);
}
int dp_connector_set_backlight(struct drm_connector *connector,
void *display, u32 bl_lvl)
{
struct dp_display *dp_display = display;
struct sde_connector *sde_conn;
if (!dp_display || !connector) {
DP_ERR("invalid dp display\n");
return -EINVAL;
}
sde_conn = to_sde_connector(connector);
if (!sde_conn->drv_panel) {
DP_ERR("invalid dp panel\n");
return -EINVAL;
}
return dp_display->set_backlight(dp_display,
sde_conn->drv_panel, bl_lvl);
}
int dp_connector_set_colorspace(struct drm_connector *connector,
void *display)
{
@ -456,8 +478,16 @@ 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;
info->is_connected = true;
} else {
info->capabilities |= MSM_DISPLAY_CAP_HOT_PLUG;
}
return 0;
}
@ -468,11 +498,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 +513,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 +632,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.
*/
@ -48,6 +49,15 @@ int dp_connector_atomic_check(struct drm_connector *connector,
void *display,
struct drm_atomic_state *state);
/**
* dp_connector_set_backlight - callback to set backlight
* @connector: Pointer to drm connector structure
* @display: Pointer to private display handle
* Returns: Zero on success
*/
int dp_connector_set_backlight(struct drm_connector *connector,
void *display, u32 bl_lvl);
/**
* dp_connector_set_colorspace - callback to set new colorspace
* @connector: Pointer to drm connector structure
@ -136,6 +146,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 +255,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,10 +1,12 @@
// 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.
*/
#include "dp_panel.h"
#include <linux/unistd.h>
#include <linux/pwm.h>
#include <drm/drm_fixed.h>
#include "dp_debug.h"
#include <drm/drm_dsc.h>
@ -2294,6 +2296,91 @@ static int dp_panel_set_stream_info(struct dp_panel *dp_panel,
return 0;
}
static int dp_panel_pwm_register(struct dp_panel *dp_panel)
{
int rc = 0;
struct dp_backlight_config *bl = &dp_panel->bl_config;
struct platform_device *pdev;
struct device *dev;
struct dp_panel_private *panel;
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
pdev = panel->parser->pdev;
dev = &pdev->dev;
bl->pwm_bl = devm_of_pwm_get(dev, dev->of_node, NULL);
if (IS_ERR_OR_NULL(bl->pwm_bl)) {
rc = PTR_ERR(bl->pwm_bl);
DP_ERR("failed to request pwm, rc=%d\n", rc);
return rc;
}
return 0;
}
static void dp_panel_pwm_unregister(struct dp_panel *dp_panel)
{
struct dp_backlight_config *bl = &dp_panel->bl_config;
struct platform_device *pdev;
struct dp_panel_private *panel;
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
pdev = panel->parser->pdev;
if (bl->pwm_bl)
devm_pwm_put(&pdev->dev, bl->pwm_bl);
}
static int dp_panel_set_backlight(struct dp_panel *panel, u32 bl_lvl)
{
int rc = 0;
u32 duty = 0;
u32 period_ns = 0;
struct dp_backlight_config *bl;
if (!panel) {
DP_ERR("Invalid Params\n");
return -EINVAL;
}
bl = &panel->bl_config;
if (!bl->pwm_bl) {
DP_ERR("pwm device not found\n");
return -EINVAL;
}
DP_DEBUG("backlight lvl:%d\n", bl_lvl);
period_ns = bl->pwm_period_usecs * NSEC_PER_USEC;
duty = bl_lvl * period_ns;
duty /= bl->bl_max_level;
rc = pwm_config(bl->pwm_bl, duty, period_ns);
if (rc) {
DP_ERR("failed to change pwm config, rc=\n", rc);
goto error;
}
if (bl_lvl == 0 && bl->pwm_enabled) {
pwm_disable(bl->pwm_bl);
bl->pwm_enabled = false;
return 0;
}
if (bl_lvl != 0 && !bl->pwm_enabled) {
rc = pwm_enable(bl->pwm_bl);
if (rc) {
DP_ERR("failed to enable pwm, rc=\n", rc);
goto error;
}
bl->pwm_enabled = true;
}
error:
return rc;
}
static int dp_panel_init_panel_info(struct dp_panel *dp_panel)
{
int rc = 0;
@ -3015,6 +3102,8 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
struct dp_panel_private *panel;
struct dp_panel *dp_panel;
struct sde_connector *sde_conn;
struct device *dev;
struct device_node *of_node = NULL;
if (!in->dev || !in->catalog || !in->aux ||
!in->link || !in->connector) {
@ -3029,6 +3118,13 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
goto error;
}
dev = devm_kzalloc(in->dev, sizeof(*dev), GFP_KERNEL);
if (!dev) {
DP_ERR("failed to allocate dev\n");
rc = -ENOMEM;
goto error;
}
panel->dev = in->dev;
panel->aux = in->aux;
panel->catalog = in->catalog;
@ -3048,6 +3144,15 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
dp_panel->fec_feature_enable = panel->parser->fec_feature_enable;
dp_panel->dsc_continuous_pps = panel->parser->dsc_continuous_pps;
/* backlight config for edp */
dp_panel->bl_config.bl_min_level = panel->parser->bl_min_level;
dp_panel->bl_config.bl_max_level = panel->parser->bl_max_level;
dp_panel->bl_config.brightness_max_level = panel->parser->brightness_max_level;
dp_panel->bl_config.pwm_period_usecs = panel->parser->pwm_period_usecs;
dp_panel->bl_config.bl_scale = MAX_BL_SCALE_LEVEL;
dp_panel->bl_config.bl_scale_sv = MAX_SV_BL_SCALE_LEVEL;
if (in->base_panel) {
memcpy(dp_panel->dpcd, in->base_panel->dpcd,
DP_RECEIVER_CAP_SIZE + 1);
@ -3075,6 +3180,7 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
dp_panel->spd_config = dp_panel_spd_config;
dp_panel->setup_hdr = dp_panel_setup_hdr;
dp_panel->set_colorspace = dp_panel_set_colorspace;
dp_panel->set_backlight = dp_panel_set_backlight;
dp_panel->hdr_supported = dp_panel_hdr_supported;
dp_panel->set_stream_info = dp_panel_set_stream_info;
dp_panel->read_sink_status = dp_panel_read_sink_sts;
@ -3088,6 +3194,29 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
dp_panel_edid_register(panel);
if (in->is_edp && in->panel_notifier_support) {
of_node = of_parse_phandle(in->dev->of_node, "qcom,edp-default-panel", 0);
if (!of_node) {
DP_ERR("phandle for default panel not found\n");
} else {
drm_panel_init(&dp_panel->drm_panel);
dp_panel->drm_panel.dev = dev;
dev->of_node = of_node;
rc = drm_panel_add(&dp_panel->drm_panel);
if (rc)
DP_ERR("Failed to add drm_panel\n");
dp_panel->connector->panel = &dp_panel->drm_panel;
}
}
if (in->is_edp) {
rc = dp_panel_pwm_register(dp_panel);
if (rc)
DP_ERR("Failed to register pwm\n");
}
return dp_panel;
error:
return ERR_PTR(rc);
@ -3105,6 +3234,9 @@ void dp_panel_put(struct dp_panel *dp_panel)
dp_panel_edid_deregister(panel);
sde_conn = to_sde_connector(dp_panel->connector);
dp_panel_pwm_unregister(dp_panel);
if (sde_conn)
sde_conn->drv_panel = NULL;

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.
*/
@ -17,6 +18,9 @@
#define DP_RECEIVER_DSC_CAP_SIZE 15
#define DP_RECEIVER_FEC_STATUS_SIZE 3
#define DP_RECEIVER_EXT_CAP_SIZE 4
#define MAX_BL_LEVEL 4096
#define MAX_BL_SCALE_LEVEL 1024
#define MAX_SV_BL_SCALE_LEVEL 65535
/*
* A source initiated power down flag is set
* when the DP is powered off while physical
@ -74,6 +78,8 @@ struct dp_panel_in {
struct drm_connector *connector;
struct dp_panel *base_panel;
struct dp_parser *parser;
bool is_edp;
bool panel_notifier_support;
};
struct dp_dsc_caps {
@ -83,6 +89,22 @@ struct dp_dsc_caps {
u8 color_depth;
};
struct dp_backlight_config {
u32 bl_min_level;
u32 bl_max_level;
u32 brightness_max_level;
u32 bl_level;
u32 bl_scale;
u32 bl_scale_sv;
int en_gpio;
/* PWM params */
struct pwm_device *pwm_bl;
bool pwm_enabled;
u32 pwm_period_usecs;
};
struct dp_audio;
#define DP_PANEL_CAPS_DSC BIT(0)
@ -95,9 +117,11 @@ struct dp_panel {
u8 fec_dpcd;
u8 fec_sts_dpcd[DP_RECEIVER_FEC_STATUS_SIZE + 1];
struct drm_panel drm_panel;
struct drm_dp_link link_info;
struct sde_edid_ctrl *edid_ctrl;
struct dp_panel_info pinfo;
struct dp_backlight_config bl_config;
bool video_test;
bool spd_enabled;
@ -155,6 +179,7 @@ struct dp_panel {
bool dhdr_update, u64 core_clk_rate, bool flush);
int (*set_colorspace)(struct dp_panel *dp_panel,
u32 colorspace);
int (*set_backlight)(struct dp_panel *dp_panel, u32 bl_lvl);
void (*tpg_config)(struct dp_panel *dp_panel, bool enable);
int (*spd_config)(struct dp_panel *dp_panel);
bool (*hdr_supported)(struct dp_panel *dp_panel);

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,20 @@ 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";
parser->panel_notifier_support = of_property_read_bool(of_node,
"qcom,panel-notifier-support");
DP_DEBUG("panel-notifier-support = %d\n", parser->panel_notifier_support);
return 0;
}
@ -241,29 +256,73 @@ error:
return rc;
}
static void dp_parser_bl_config(struct dp_parser *parser)
{
int rc = 0;
u32 val = 0;
struct device_node *of_node = parser->pdev->dev.of_node;
rc = of_property_read_u32(of_node, "qcom,edp-bl-min-level", &val);
if (rc) {
DP_DEBUG("bl-min-level unspecified, defaulting to zero\n");
parser->bl_min_level = 0;
} else {
parser->bl_min_level = val;
}
rc = of_property_read_u32(of_node, "qcom,edp-bl-max-level", &val);
if (rc) {
DP_DEBUG("bl-max-level unspecified, defaulting to 4096\n");
parser->bl_max_level = 4096;
} else {
parser->bl_max_level = val;
}
rc = of_property_read_u32(of_node, "qcom,edp-brightness-max-level",
&val);
if (rc) {
DP_DEBUG("brigheness-max-level unspecified, defaulting to 255\n");
parser->brightness_max_level = 255;
} else {
parser->brightness_max_level = val;
}
rc = of_property_read_u32(of_node, "qcom,bl-pmic-pwm-period-usecs",
&val);
if (rc) {
DP_DEBUG("bl-pmic-pwm-period-usecs unspecified, default 100\n");
parser->pwm_period_usecs = 100;
} else {
parser->pwm_period_usecs = val;
}
}
static int dp_parser_gpio(struct dp_parser *parser)
{
int i = 0;
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 +749,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 +757,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 +796,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;
@ -808,6 +895,7 @@ static int dp_parser_parse(struct dp_parser *parser)
if (rc)
goto err;
dp_parser_bl_config(parser);
dp_parser_dsc(parser);
dp_parser_fec(parser);
dp_parser_widebus(parser);

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) {
@ -85,6 +100,7 @@ struct dp_io {
* @state_active: active state pin control
* @state_hpd_active: hpd active state pin control
* @state_suspend: suspend state pin control
* @state_bl_pwm: backlight pwm pin control
*/
struct dp_pinctrl {
struct pinctrl *pin;
@ -93,6 +109,7 @@ struct dp_pinctrl {
struct pinctrl_state *state_hpd_tlmm;
struct pinctrl_state *state_hpd_ctrl;
struct pinctrl_state *state_suspend;
struct pinctrl_state *state_bl_pwm;
};
#define DP_ENUM_STR(x) #x
@ -131,15 +148,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 +184,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)
@ -197,11 +235,17 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
* @fec_feature_enable: FEC feature enable status
* @dsc_continuous_pps: PPS sent every frame by HW
* @has_widebus: widebus (2PPC) feature eanble status
*@mst_fixed_port: mst port_num reserved for fixed topology
* @bl_min_level: minimum brightness value for backlight
* @bl_max_level: maximum brightness value for backlight
* @brightness_max_level: maximum brightness level for backlight
* @pwm_period_usecs: pwm period in usecs for backlight pwm
* @mst_fixed_port: mst port_num reserved for fixed topology
* @parse: function to be called by client to parse device tree.
* @get_io: function to be called by client to get io data.
* @get_io_buf: function to be called by client to get io buffers.
* @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;
@ -226,7 +270,15 @@ struct dp_parser {
bool has_widebus;
bool gpio_aux_switch;
bool lphw_hpd;
bool panel_notifier_support;
u32 bl_min_level;
u32 bl_max_level;
u32 brightness_max_level;
u32 pwm_period_usecs;
u32 mst_fixed_port[MAX_DP_MST_STREAMS];
u32 pixel_base_off[MAX_DP_MST_STREAMS];
const char *mst_fixed_display_type[MAX_DP_MST_STREAMS];
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;
bool cont_splash_enabled;
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)

View File

@ -0,0 +1,937 @@
// 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;
}
if (pll->cont_splash_enabled)
return 0;
dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x7d);
/* Make sure the PLL register writes are done */
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;
if (pll->cont_splash_enabled) {
pll->aux->state |= DP_STATE_PLL_LOCKED;
return rc;
}
dp_pll_write(dp_phy, DP_PHY_CFG, 0x01);
dp_pll_write(dp_phy, DP_PHY_CFG, 0x05);
dp_pll_write(dp_phy, DP_PHY_CFG, 0x01);
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 (pll->cont_splash_enabled)
return rc;
if (rate != edp_pll_vco_div_clk_get_rate(pll)) {
DP_ERR("unsupported rate %lu failed\n", rate);
return rc;
}
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,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
*/
@ -16,6 +16,7 @@
#include <linux/string.h>
#include "dsi_drm.h"
#include "dsi_display.h"
#include "dp_panel.h"
#include "sde_crtc.h"
#include "sde_rm.h"
#include "sde_vm.h"
@ -23,6 +24,7 @@
#define BL_NODE_NAME_SIZE 32
#define HDR10_PLUS_VSIF_TYPE_CODE 0x81
#define MAX_BRIGHTNESS_LEVEL 255
/* Autorefresh will occur after FRAME_CNT frames. Large values are unlikely */
#define AUTOREFRESH_MAX_FRAME_CNT 6
@ -93,13 +95,16 @@ static inline struct sde_kms *_sde_connector_get_kms(struct drm_connector *conn)
static int sde_backlight_device_update_status(struct backlight_device *bd)
{
int brightness;
struct dsi_display *display;
struct dsi_display *dsi_display;
struct dp_panel *dp_panel;
struct sde_connector *c_conn = bl_get_data(bd);
int bl_lvl;
struct drm_event event;
int rc = 0;
struct sde_kms *sde_kms;
struct sde_vm_ops *vm_ops;
u32 bl_max_level = 0;
u32 brightness_max_level = 0;
sde_kms = _sde_connector_get_kms(&c_conn->base);
if (!sde_kms) {
@ -114,15 +119,27 @@ static int sde_backlight_device_update_status(struct backlight_device *bd)
(bd->props.state & BL_CORE_SUSPENDED))
brightness = 0;
display = (struct dsi_display *) c_conn->display;
if (brightness > display->panel->bl_config.bl_max_level)
brightness = display->panel->bl_config.bl_max_level;
if (c_conn->connector_type == DRM_MODE_CONNECTOR_DSI) {
dsi_display = (struct dsi_display *) c_conn->display;
bl_max_level = dsi_display->panel->bl_config.bl_max_level;
brightness_max_level =
dsi_display->panel->bl_config.brightness_max_level;
} else if (c_conn->connector_type == DRM_MODE_CONNECTOR_eDP) {
dp_panel = (struct dp_panel *) c_conn->drv_panel;
if (dp_panel) {
bl_max_level = dp_panel->bl_config.bl_max_level;
brightness_max_level =
dp_panel->bl_config.brightness_max_level;
}
}
if (brightness > bl_max_level)
brightness = bl_max_level;
if (brightness > c_conn->thermal_max_brightness)
brightness = c_conn->thermal_max_brightness;
/* map UI brightness into driver backlight level with rounding */
bl_lvl = mult_frac(brightness, display->panel->bl_config.bl_max_level,
display->panel->bl_config.brightness_max_level);
bl_lvl = mult_frac(brightness, bl_max_level, brightness_max_level);
if (!bl_lvl && brightness)
bl_lvl = 1;
@ -188,31 +205,46 @@ static int sde_backlight_setup(struct sde_connector *c_conn,
{
struct backlight_properties props;
struct dsi_display *display;
struct dsi_backlight_config *bl_config;
struct dp_panel *dp_panel;
struct dsi_backlight_config *dsi_bl_config;
struct sde_kms *sde_kms;
static int display_count;
char bl_node_name[BL_NODE_NAME_SIZE];
u32 brightness_max_level = 0;
sde_kms = _sde_connector_get_kms(&c_conn->base);
if (!sde_kms) {
SDE_ERROR("invalid kms\n");
return -EINVAL;
} else if (c_conn->connector_type != DRM_MODE_CONNECTOR_DSI) {
}
if (c_conn->connector_type == DRM_MODE_CONNECTOR_DSI) {
display = (struct dsi_display *) c_conn->display;
dsi_bl_config = &display->panel->bl_config;
brightness_max_level = dsi_bl_config->brightness_max_level;
if (dsi_bl_config->type != DSI_BACKLIGHT_DCS &&
sde_in_trusted_vm(sde_kms))
return 0;
} else if (c_conn->connector_type == DRM_MODE_CONNECTOR_eDP) {
dp_panel = (struct dp_panel *) c_conn->drv_panel;
if (dp_panel)
brightness_max_level =
dp_panel->bl_config.brightness_max_level;
else {
brightness_max_level = MAX_BRIGHTNESS_LEVEL;
}
} else {
SDE_DEBUG("invalid connector type %d\n",
c_conn->connector_type);
return 0;
}
display = (struct dsi_display *) c_conn->display;
bl_config = &display->panel->bl_config;
if (bl_config->type != DSI_BACKLIGHT_DCS &&
sde_in_trusted_vm(sde_kms))
return 0;
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_RAW;
props.power = FB_BLANK_UNBLANK;
props.max_brightness = bl_config->brightness_max_level;
props.brightness = bl_config->brightness_max_level;
props.max_brightness = brightness_max_level;
props.brightness = brightness_max_level;
snprintf(bl_node_name, BL_NODE_NAME_SIZE, "panel%u-backlight",
display_count);
c_conn->bl_device = backlight_device_register(bl_node_name, dev->dev,
@ -223,7 +255,7 @@ static int sde_backlight_setup(struct sde_connector *c_conn,
c_conn->bl_device = NULL;
return -ENODEV;
}
c_conn->thermal_max_brightness = bl_config->brightness_max_level;
c_conn->thermal_max_brightness = brightness_max_level;
/**
* In TVM, thermal cooling device is not enabled. Registering with dummy
@ -639,42 +671,62 @@ static int _sde_connector_update_power_locked(struct sde_connector *c_conn)
static int _sde_connector_update_bl_scale(struct sde_connector *c_conn)
{
struct dsi_display *dsi_display;
struct dsi_backlight_config *bl_config;
struct dp_display *dp_display;
struct dsi_backlight_config *dsi_bl_config;
struct dp_backlight_config *dp_bl_config;
struct dp_panel *dp_panel;
int rc = 0;
u32 bl_scale, bl_scale_sv;
if (!c_conn) {
SDE_ERROR("Invalid params sde_connector null\n");
return -EINVAL;
}
dsi_display = c_conn->display;
if (!dsi_display || !dsi_display->panel) {
SDE_ERROR("Invalid params(s) dsi_display %pK, panel %pK\n",
dsi_display,
((dsi_display) ? dsi_display->panel : NULL));
return -EINVAL;
}
bl_config = &dsi_display->panel->bl_config;
if (!c_conn->allow_bl_update) {
c_conn->unset_bl_level = bl_config->bl_level;
return 0;
}
if (c_conn->unset_bl_level)
bl_config->bl_level = c_conn->unset_bl_level;
bl_config->bl_scale = c_conn->bl_scale > MAX_BL_SCALE_LEVEL ?
bl_scale = c_conn->bl_scale > MAX_BL_SCALE_LEVEL ?
MAX_BL_SCALE_LEVEL : c_conn->bl_scale;
bl_config->bl_scale_sv = c_conn->bl_scale_sv > MAX_SV_BL_SCALE_LEVEL ?
bl_scale_sv = c_conn->bl_scale_sv > MAX_SV_BL_SCALE_LEVEL ?
MAX_SV_BL_SCALE_LEVEL : c_conn->bl_scale_sv;
SDE_DEBUG("bl_scale = %u, bl_scale_sv = %u, bl_level = %u\n",
bl_config->bl_scale, bl_config->bl_scale_sv,
bl_config->bl_level);
rc = c_conn->ops.set_backlight(&c_conn->base,
dsi_display, bl_config->bl_level);
if (c_conn->connector_type == DRM_MODE_CONNECTOR_DSI) {
dsi_display = c_conn->display;
if (!dsi_display || !dsi_display->panel) {
SDE_ERROR("Invalid params dsi_display %pK, panel %pK\n",
dsi_display,
((dsi_display) ? dsi_display->panel : NULL));
return -EINVAL;
}
dsi_bl_config = &dsi_display->panel->bl_config;
if (!c_conn->allow_bl_update) {
c_conn->unset_bl_level = dsi_bl_config->bl_level;
return 0;
}
dsi_bl_config->bl_scale = bl_scale;
dsi_bl_config->bl_scale_sv = bl_scale_sv;
if (c_conn->unset_bl_level)
dsi_bl_config->bl_level = c_conn->unset_bl_level;
rc = c_conn->ops.set_backlight(&c_conn->base,
dsi_display, dsi_bl_config->bl_level);
} else if (c_conn->connector_type == DRM_MODE_CONNECTOR_eDP) {
dp_display = c_conn->display;
dp_panel = (struct dp_panel *) c_conn->drv_panel;
if (dp_panel) {
dp_bl_config = &dp_panel->bl_config;
if (!c_conn->allow_bl_update) {
c_conn->unset_bl_level = dp_bl_config->bl_level;
return 0;
}
dp_bl_config->bl_scale = bl_scale;
dp_bl_config->bl_scale_sv = bl_scale_sv;
if (c_conn->unset_bl_level)
dp_bl_config->bl_level = c_conn->unset_bl_level;
rc = c_conn->ops.set_backlight(&c_conn->base,
dp_display, dp_bl_config->bl_level);
} else
SDE_ERROR("Invalid dp_panel null\n");
}
c_conn->unset_bl_level = 0;
return rc;

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

@ -1255,6 +1255,16 @@ static void _sde_kms_release_splash_resource(struct sde_kms *sde_kms,
SDE_EVT32(DRMID(crtc), crtc->state->active,
sde_kms->splash_data.num_splash_displays);
/*remove all votes if eDP displays are done with splash*/
if (dp_display_get_num_of_boot_displays()) {
for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++)
sde_power_data_bus_set_quota(phandle, i,
SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA,
phandle->ib_quota[i]);
pm_runtime_put_sync(sde_kms->dev->dev);
sde_kms->splash_data.num_splash_displays--;
}
for (i = 0; i < MAX_DSI_DISPLAYS; i++) {
splash_display = &sde_kms->splash_data.splash_display[i];
if (splash_display->encoder &&
@ -1776,6 +1786,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,
@ -1784,11 +1795,12 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
.get_info = dp_connector_get_info,
.get_mode_info = dp_connector_get_mode_info,
.post_open = dp_connector_post_open,
.set_backlight = dp_connector_set_backlight,
.check_status = NULL,
.set_colorspace = dp_connector_set_colorspace,
.config_hdr = dp_connector_config_hdr,
.cmd_transfer = NULL,
.cont_splash_config = NULL,
.cont_splash_config = dp_display_cont_splash_config,
.cont_splash_res_disable = NULL,
.get_panel_vfp = NULL,
.update_pps = dp_connector_update_pps,
@ -1925,6 +1937,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 +1949,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 +1976,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 +1989,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);
@ -3361,6 +3381,7 @@ static int sde_kms_cont_splash_config(struct msm_kms *kms,
{
void *display;
struct dsi_display *dsi_display;
struct dp_display *dp_display;
struct msm_display_info info;
struct drm_encoder *encoder = NULL;
struct drm_crtc *crtc = NULL;
@ -3511,6 +3532,57 @@ static int sde_kms_cont_splash_config(struct msm_kms *kms,
}
}
/* dp */
for (i = 0; i < sde_kms->dp_display_count; ++i) {
display = sde_kms->dp_displays[i];
dp_display = (struct dp_display *)display;
if (!dp_display->cont_splash_enabled) {
SDE_DEBUG("DP-%d splash not enabled\n", i);
continue;
}
if (dp_display->bridge->base.encoder) {
encoder = dp_display->bridge->base.encoder;
SDE_DEBUG("encoder name = %s\n", encoder->name);
} else {
SDE_DEBUG("Invalid encoder\n");
break;
}
mutex_lock(&dev->mode_config.mutex);
drm_connector_list_iter_begin(dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter) {
/**
* SDE_KMS doesn't attach more than one encoder to
* a DSI connector. So it is safe to check only with
* the first encoder entry. Revisit this logic if we
* ever have to support continuous splash for
* external displays in MST configuration.
*/
if (connector->encoder_ids[0] == encoder->base.id)
break;
}
drm_connector_list_iter_end(&conn_iter);
if (!connector) {
SDE_ERROR("connector not initialized\n");
mutex_unlock(&dev->mode_config.mutex);
return -EINVAL;
}
mutex_unlock(&dev->mode_config.mutex);
/* Enable all irqs */
sde_irq_update(kms, true);
sde_conn = to_sde_connector(connector);
if (sde_conn && sde_conn->ops.cont_splash_config)
sde_conn->ops.cont_splash_config(sde_conn->display);
/* Disable irqs */
sde_irq_update(kms, false);
}
return rc;
}
@ -4417,7 +4489,8 @@ static int _sde_kms_get_splash_data(struct sde_kms *sde_kms,
* cont_splash_region should be collection of all memory regions
* Ex: <r1.start r1.end r2.start r2.end ... rn.start, rn.end>
*/
num_displays = dsi_display_get_num_of_displays();
num_displays = dsi_display_get_num_of_displays()
+ dp_display_get_num_of_boot_displays();
num_regions = of_property_count_u64_elems(node, "reg") / 2;
data->num_splash_displays = num_displays;