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:
commit
0124532c0e
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
@ -105,8 +106,12 @@ struct dp_catalog_ctrl {
|
||||
};
|
||||
|
||||
struct dp_catalog_hpd {
|
||||
bool is_edp;
|
||||
|
||||
void (*config_hpd)(struct dp_catalog_hpd *hpd, bool en);
|
||||
u32 (*get_interrupt)(struct dp_catalog_hpd *hpd);
|
||||
bool (*wait_for_edp_panel_ready)(struct dp_catalog_hpd *hpd);
|
||||
void (*set_edp_mode)(struct dp_catalog_hpd *hpd, bool is_edp);
|
||||
};
|
||||
|
||||
#define HEADER_BYTE_2_BIT 0
|
||||
@ -256,6 +261,7 @@ struct dp_catalog {
|
||||
struct dp_catalog_hpd hpd;
|
||||
|
||||
struct dp_catalog_sub *sub;
|
||||
struct dp_parser *parser;
|
||||
|
||||
void (*set_exe_mode)(struct dp_catalog *dp_catalog, char *mode);
|
||||
int (*get_reg_dump)(struct dp_catalog *dp_catalog,
|
||||
@ -336,4 +342,7 @@ struct dp_catalog_sub *dp_catalog_get_v420(struct device *dev,
|
||||
struct dp_catalog_sub *dp_catalog_get_v200(struct device *dev,
|
||||
struct dp_catalog *catalog, struct dp_catalog_io *io);
|
||||
|
||||
struct dp_catalog_sub *dp_catalog_get_v500(struct device *dev,
|
||||
struct dp_catalog *catalog, struct dp_catalog_io *io);
|
||||
|
||||
#endif /* _DP_CATALOG_H_ */
|
||||
|
466
techpack/display/msm/dp/dp_catalog_v500.c
Normal file
466
techpack/display/msm/dp/dp_catalog_v500.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
@ -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_ */
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
937
techpack/display/msm/dp/edp_pll_7nm.c
Normal file
937
techpack/display/msm/dp/edp_pll_7nm.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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[] = {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user