mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 13:49:24 -04:00
Merge cfaa8dfa20 on remote branch
Change-Id: I6c14ac4c4a77619421785ca554f5fdaad421df0d
This commit is contained in:
commit
608c7a6360
24 changed files with 2376 additions and 265 deletions
|
|
@ -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) 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
msm/dp/dp_catalog_v500.c
Normal file
466
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;
|
||||
|
|
@ -618,6 +621,14 @@ static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl,
|
|||
{
|
||||
u32 num = ctrl->parser->mp[clk_type].num_clk;
|
||||
struct dss_clk *cfg = ctrl->parser->mp[clk_type].clk_config;
|
||||
struct dp_catalog *catalog;
|
||||
|
||||
catalog = container_of(ctrl->catalog, struct dp_catalog, ctrl);
|
||||
|
||||
if (catalog->hpd.is_edp) {
|
||||
/* convert to HZ for byte2 ops */
|
||||
rate *= 1000;
|
||||
}
|
||||
|
||||
while (num && strcmp(cfg->clk_name, name)) {
|
||||
num--;
|
||||
|
|
@ -642,6 +653,22 @@ static int dp_ctrl_enable_link_clock(struct dp_ctrl_private *ctrl)
|
|||
|
||||
dp_ctrl_set_clock_rate(ctrl, "link_clk", type, rate);
|
||||
|
||||
if (ctrl->pll->pll_cfg) {
|
||||
ret = ctrl->pll->pll_cfg(ctrl->pll, rate);
|
||||
if (ret < 0) {
|
||||
DP_ERR("DP pll cfg failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctrl->pll->pll_prepare) {
|
||||
ret = ctrl->pll->pll_prepare(ctrl->pll);
|
||||
if (ret < 0) {
|
||||
DP_ERR("DP pll prepare failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ctrl->power->clk_enable(ctrl->power, type, true);
|
||||
if (ret) {
|
||||
DP_ERR("Unabled to start link clocks\n");
|
||||
|
|
@ -653,7 +680,15 @@ static int dp_ctrl_enable_link_clock(struct dp_ctrl_private *ctrl)
|
|||
|
||||
static void dp_ctrl_disable_link_clock(struct dp_ctrl_private *ctrl)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ctrl->power->clk_enable(ctrl->power, DP_LINK_PM, false);
|
||||
if (ctrl->pll->pll_unprepare) {
|
||||
rc = ctrl->pll->pll_unprepare(ctrl->pll);
|
||||
if (rc < 0)
|
||||
DP_ERR("pll unprepare failed\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void dp_ctrl_select_training_pattern(struct dp_ctrl_private *ctrl,
|
||||
|
|
@ -1475,6 +1510,7 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
|
|||
ctrl->aux = in->aux;
|
||||
ctrl->link = in->link;
|
||||
ctrl->catalog = in->catalog;
|
||||
ctrl->pll = in->pll;
|
||||
ctrl->dev = in->dev;
|
||||
ctrl->mst_mode = false;
|
||||
ctrl->fec_mode = false;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
|
|
@ -42,6 +43,7 @@ struct dp_ctrl_in {
|
|||
struct dp_parser *parser;
|
||||
struct dp_power *power;
|
||||
struct dp_catalog_ctrl *catalog;
|
||||
struct dp_pll *pll;
|
||||
};
|
||||
|
||||
struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
|
|
@ -67,6 +68,10 @@ enum dp_display_states {
|
|||
DP_STATE_TUI_ACTIVE = BIT(11),
|
||||
};
|
||||
|
||||
struct dp_display_type_info {
|
||||
int display_type;
|
||||
};
|
||||
|
||||
static char *dp_display_state_name(enum dp_display_states state)
|
||||
{
|
||||
static char buf[SZ_1K];
|
||||
|
|
@ -128,7 +133,7 @@ static char *dp_display_state_name(enum dp_display_states state)
|
|||
return buf;
|
||||
}
|
||||
|
||||
static struct dp_display *g_dp_display;
|
||||
static struct dp_display *g_dp_display[MAX_DP_ACTIVE_DISPLAY];
|
||||
#define HPD_STRING_SIZE 30
|
||||
|
||||
struct dp_hdcp_dev {
|
||||
|
|
@ -162,6 +167,7 @@ struct dp_display_private {
|
|||
|
||||
struct platform_device *pdev;
|
||||
struct device_node *aux_switch_node;
|
||||
bool aux_switch_ready;
|
||||
struct dentry *root;
|
||||
struct completion notification_comp;
|
||||
struct completion attention_comp;
|
||||
|
|
@ -200,10 +206,26 @@ struct dp_display_private {
|
|||
bool process_hpd_connect;
|
||||
|
||||
struct notifier_block usb_nb;
|
||||
|
||||
u32 cell_idx;
|
||||
u32 intf_idx[DP_STREAM_MAX];
|
||||
u32 phy_idx;
|
||||
u32 stream_cnt;
|
||||
};
|
||||
|
||||
static const struct dp_display_type_info dp_info = {
|
||||
.display_type = DRM_MODE_CONNECTOR_DisplayPort,
|
||||
};
|
||||
|
||||
static const struct dp_display_type_info edp_info = {
|
||||
.display_type = DRM_MODE_CONNECTOR_eDP,
|
||||
};
|
||||
|
||||
static const struct of_device_id dp_dt_match[] = {
|
||||
{.compatible = "qcom,dp-display"},
|
||||
{ .compatible = "qcom,dp-display",
|
||||
.data = &dp_info,},
|
||||
{ .compatible = "qcom,edp-display",
|
||||
.data = &edp_info,},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
@ -741,6 +763,32 @@ static int dp_display_post_hw_acquire(void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_get_cell_info(struct dp_display_private *dp)
|
||||
{
|
||||
struct device_node *of_node = dp->pdev->dev.of_node;
|
||||
int i, rc;
|
||||
|
||||
of_property_read_u32(of_node,
|
||||
"cell-index", &dp->cell_idx);
|
||||
|
||||
if (of_property_read_bool(of_node, "qcom,mst-enable"))
|
||||
dp->stream_cnt = DP_STREAM_MAX;
|
||||
|
||||
of_property_read_u32_index(of_node,
|
||||
"qcom,intf-index", 0, &dp->intf_idx[0]);
|
||||
|
||||
for (i = 1; i < dp->stream_cnt; i++) {
|
||||
rc = of_property_read_u32_index(of_node,
|
||||
"qcom,intf-index", i, &dp->intf_idx[i]);
|
||||
if (rc)
|
||||
dp->intf_idx[i] = dp->intf_idx[0] + i;
|
||||
}
|
||||
|
||||
of_property_read_u32(of_node,
|
||||
"qcom,phy-index", &dp->phy_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
|
|
@ -1053,6 +1101,32 @@ error_ctrl:
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_panel_ready(struct dp_display_private *dp)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (dp->dp_display.is_edp) {
|
||||
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, true);
|
||||
if (rc) {
|
||||
DP_ERR("Cannot turn edp panel power on");
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!(dp->catalog->hpd.wait_for_edp_panel_ready(&dp->catalog->hpd))) {
|
||||
DP_ERR("EDP PANEL is not ready yet, powering off panel\n");
|
||||
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false);
|
||||
if (rc) {
|
||||
DP_ERR("Cannot turn edp panel power off");
|
||||
return rc;
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
dp->panel->init(dp->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_host_ready(struct dp_display_private *dp)
|
||||
{
|
||||
int rc = 0;
|
||||
|
|
@ -1090,7 +1164,6 @@ static int dp_display_host_ready(struct dp_display_private *dp)
|
|||
dp->ctrl->abort(dp->ctrl, false);
|
||||
|
||||
dp->aux->init(dp->aux, dp->parser->aux_cfg);
|
||||
dp->panel->init(dp->panel);
|
||||
|
||||
dp_display_state_add(DP_STATE_READY);
|
||||
/* log this as it results from user action of cable connection */
|
||||
|
|
@ -1204,6 +1277,8 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
|
|||
goto end;
|
||||
}
|
||||
|
||||
rc = dp_display_panel_ready(dp);
|
||||
|
||||
dp->link->psm_config(dp->link, &dp->panel->link_info, false);
|
||||
dp->debug->psm_enabled = false;
|
||||
|
||||
|
|
@ -1336,6 +1411,54 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_fsa4480_callback(struct notifier_block *self,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_init_aux_switch(struct dp_display_private *dp)
|
||||
{
|
||||
int rc = 0;
|
||||
struct notifier_block nb;
|
||||
const u32 max_retries = 50;
|
||||
u32 retry;
|
||||
|
||||
if (dp->aux_switch_ready)
|
||||
return rc;
|
||||
|
||||
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
|
||||
|
||||
nb.notifier_call = dp_display_fsa4480_callback;
|
||||
nb.priority = 0;
|
||||
|
||||
/*
|
||||
* Iteratively wait for reg notifier which confirms that fsa driver is probed.
|
||||
* Bootup DP with cable connected usecase can hit this scenario.
|
||||
*/
|
||||
for (retry = 0; retry < max_retries; retry++) {
|
||||
rc = fsa4480_reg_notifier(&nb, dp->aux_switch_node);
|
||||
if (rc == 0) {
|
||||
DP_DEBUG("registered notifier successfully\n");
|
||||
dp->aux_switch_ready = true;
|
||||
break;
|
||||
}
|
||||
DP_DEBUG("failed to register notifier retry=%d rc=%d\n", retry, rc);
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
if (retry == max_retries) {
|
||||
DP_WARN("Failed to register fsa notifier\n");
|
||||
dp->aux_switch_ready = false;
|
||||
return rc;
|
||||
}
|
||||
|
||||
fsa4480_unreg_notifier(&nb, dp->aux_switch_node);
|
||||
|
||||
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_usbpd_configure_cb(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
|
@ -1353,7 +1476,11 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
|
|||
}
|
||||
|
||||
if (!dp->debug->sim_mode && !dp->parser->no_aux_switch
|
||||
&& !dp->parser->gpio_aux_switch) {
|
||||
&& !dp->parser->gpio_aux_switch && dp->aux_switch_node) {
|
||||
rc = dp_display_init_aux_switch(dp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = dp->aux->aux_switch(dp->aux, true, dp->hpd->orientation);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
|
@ -1866,6 +1993,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
|
|||
{
|
||||
int rc = 0;
|
||||
bool hdcp_disabled;
|
||||
const char *phandle = "qcom,dp-aux-switch";
|
||||
struct device *dev = &dp->pdev->dev;
|
||||
struct dp_hpd_cb *cb = &dp->hpd_cb;
|
||||
struct dp_ctrl_in ctrl_in = {
|
||||
|
|
@ -1897,8 +2025,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
|
|||
goto error_catalog;
|
||||
}
|
||||
|
||||
g_dp_display->is_mst_supported = dp->parser->has_mst;
|
||||
g_dp_display->dsc_cont_pps = dp->parser->dsc_continuous_pps;
|
||||
dp->dp_display.is_mst_supported = dp->parser->has_mst;
|
||||
dp->dp_display.dsc_cont_pps = dp->parser->dsc_continuous_pps;
|
||||
|
||||
dp->catalog = dp_catalog_get(dev, dp->parser);
|
||||
if (IS_ERR(dp->catalog)) {
|
||||
|
|
@ -1908,6 +2036,10 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
|
|||
goto error_catalog;
|
||||
}
|
||||
|
||||
dp->catalog->hpd.set_edp_mode(&dp->catalog->hpd, dp->dp_display.is_edp);
|
||||
dp->aux_switch_node = of_parse_phandle(dp->pdev->dev.of_node, phandle, 0);
|
||||
if (!dp->aux_switch_node)
|
||||
DP_DEBUG("cannot parse %s handle\n", phandle);
|
||||
dp->aux = dp_aux_get(dev, &dp->catalog->aux, dp->parser,
|
||||
dp->aux_switch_node);
|
||||
if (IS_ERR(dp->aux)) {
|
||||
|
|
@ -1978,6 +2110,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
|
|||
ctrl_in.power = dp->power;
|
||||
ctrl_in.catalog = &dp->catalog->ctrl;
|
||||
ctrl_in.parser = dp->parser;
|
||||
ctrl_in.pll = dp->pll;
|
||||
|
||||
dp->ctrl = dp_ctrl_get(&ctrl_in);
|
||||
if (IS_ERR(dp->ctrl)) {
|
||||
|
|
@ -2147,6 +2280,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel)
|
|||
struct dp_display_private *dp;
|
||||
struct dp_panel *dp_panel;
|
||||
int rc = 0;
|
||||
bool shallow_mode = true;
|
||||
|
||||
if (!dp_display || !panel) {
|
||||
DP_ERR("invalid input\n");
|
||||
|
|
@ -2226,6 +2360,13 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel)
|
|||
goto end;
|
||||
}
|
||||
|
||||
rc = dp_display_panel_ready(dp);
|
||||
if (rc) {
|
||||
dp_display_host_unready(dp);
|
||||
dp_display_host_deinit(dp);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (dp->debug->psm_enabled) {
|
||||
dp->link->psm_config(dp->link, &dp->panel->link_info, false);
|
||||
dp->debug->psm_enabled = false;
|
||||
|
|
@ -2242,7 +2383,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel)
|
|||
* and required things.
|
||||
*/
|
||||
rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, dp_panel->fec_en,
|
||||
dp_panel->dsc_en, true);
|
||||
dp_panel->dsc_en, shallow_mode);
|
||||
if (rc)
|
||||
goto end;
|
||||
|
||||
|
|
@ -2364,6 +2505,23 @@ static int dp_display_enable(struct dp_display *dp_display, void *panel)
|
|||
if (rc)
|
||||
goto end;
|
||||
|
||||
/*edp backlight enable and edp pwm enable*/
|
||||
if (dp_display->is_edp) {
|
||||
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_PWR, true);
|
||||
if (rc) {
|
||||
DP_ERR("Cannot turn edp backlight power on");
|
||||
goto end;
|
||||
}
|
||||
|
||||
usleep_range(99000, 100000);
|
||||
|
||||
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_PWM, true);
|
||||
if (rc) {
|
||||
DP_ERR("Cannot turn edp PWM on ");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
dp_display_update_dsc_resources(dp, panel, true);
|
||||
dp_display_state_add(DP_STATE_ENABLED);
|
||||
end:
|
||||
|
|
@ -2383,6 +2541,7 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel)
|
|||
{
|
||||
struct dp_display_private *dp;
|
||||
struct dp_panel *dp_panel;
|
||||
int rc = 0;
|
||||
|
||||
if (!dp_display || !panel) {
|
||||
DP_ERR("invalid input\n");
|
||||
|
|
@ -2421,6 +2580,14 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel)
|
|||
|
||||
dp_display_stream_post_enable(dp, dp_panel);
|
||||
|
||||
if (dp_display->is_edp) {
|
||||
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_EN, true);
|
||||
if (rc) {
|
||||
DP_ERR("Cannot turn edp backlight power on");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
cancel_delayed_work_sync(&dp->hdcp_cb_work);
|
||||
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ);
|
||||
|
||||
|
|
@ -2474,6 +2641,14 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel)
|
|||
goto end;
|
||||
}
|
||||
|
||||
if (dp_display->is_edp) {
|
||||
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_EN, false);
|
||||
if (rc) {
|
||||
DP_ERR("Cannot turn edp backlight power off");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
dp_display_state_add(DP_STATE_HDCP_ABORTED);
|
||||
cancel_delayed_work_sync(&dp->hdcp_cb_work);
|
||||
if (dp_display_is_hdcp_enabled(dp) &&
|
||||
|
|
@ -2522,7 +2697,7 @@ end:
|
|||
|
||||
static int dp_display_disable(struct dp_display *dp_display, void *panel)
|
||||
{
|
||||
int i;
|
||||
int i, rc = 0;
|
||||
struct dp_display_private *dp = NULL;
|
||||
struct dp_panel *dp_panel = NULL;
|
||||
struct dp_link_hdcp_status *status;
|
||||
|
|
@ -2549,6 +2724,16 @@ static int dp_display_disable(struct dp_display *dp_display, void *panel)
|
|||
goto end;
|
||||
}
|
||||
|
||||
if (dp_display->is_edp) {
|
||||
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_BACKLIGHT_PWR, false);
|
||||
if (rc)
|
||||
DP_ERR("Cannot turn edp backlight power off\n");
|
||||
|
||||
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_PWM, false);
|
||||
if (rc)
|
||||
DP_ERR("Cannot turn edp PWM off\n");
|
||||
}
|
||||
|
||||
dp_display_stream_disable(dp, dp_panel);
|
||||
dp_display_update_dsc_resources(dp, dp_panel, false);
|
||||
|
||||
|
|
@ -2617,6 +2802,7 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel)
|
|||
struct dp_display_private *dp;
|
||||
struct dp_panel *dp_panel = panel;
|
||||
u32 flags = 0;
|
||||
int rc = 0;
|
||||
|
||||
if (!dp_display || !panel) {
|
||||
DP_ERR("invalid input\n");
|
||||
|
|
@ -2654,6 +2840,13 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel)
|
|||
dp_display_state_add(DP_STATE_SRC_PWRDN);
|
||||
}
|
||||
|
||||
if (dp_display->is_edp) {
|
||||
rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false);
|
||||
if (rc) {
|
||||
DP_ERR("Cannot turn edp panel power off\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
dp_display_state_remove(DP_STATE_ENABLED);
|
||||
dp->aux->state = DP_STATE_CTRL_POWERED_OFF;
|
||||
|
||||
|
|
@ -2672,18 +2865,25 @@ end:
|
|||
static int dp_display_validate_link_clock(struct dp_display_private *dp,
|
||||
struct drm_display_mode *mode, struct dp_display_mode dp_mode)
|
||||
{
|
||||
u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
|
||||
u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0, lane_count = 0;
|
||||
bool dsc_en;
|
||||
int rate;
|
||||
int rate = 0;
|
||||
|
||||
dsc_en = (dp_mode.timing.comp_info.comp_ratio > 1) ? true : false;
|
||||
mode_bpp = dsc_en ?
|
||||
DSC_BPP(dp_mode.timing.comp_info.dsc_info.config)
|
||||
: dp_mode.timing.bpp;
|
||||
|
||||
if (dp->dp_display.is_edp) {
|
||||
rate = dp->panel->link_info.rate;
|
||||
lane_count = dp->panel->link_info.num_lanes;
|
||||
} else {
|
||||
rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code);
|
||||
lane_count = dp->link->link_params.lane_count;
|
||||
}
|
||||
|
||||
mode_rate_khz = mode->clock * mode_bpp;
|
||||
rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code);
|
||||
supported_rate_khz = dp->link->link_params.lane_count * rate * 8;
|
||||
supported_rate_khz = lane_count * rate * 8;
|
||||
|
||||
if (mode_rate_khz > supported_rate_khz) {
|
||||
DP_DEBUG("mode_rate: %d kHz, supported_rate: %d kHz\n",
|
||||
|
|
@ -3082,48 +3282,6 @@ static int dp_display_create_workqueue(struct dp_display_private *dp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_fsa4480_callback(struct notifier_block *self,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_init_aux_switch(struct dp_display_private *dp)
|
||||
{
|
||||
int rc = 0;
|
||||
const char *phandle = "qcom,dp-aux-switch";
|
||||
struct notifier_block nb;
|
||||
|
||||
if (!dp->pdev->dev.of_node) {
|
||||
DP_ERR("cannot find dev.of_node\n");
|
||||
rc = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
|
||||
dp->aux_switch_node = of_parse_phandle(dp->pdev->dev.of_node,
|
||||
phandle, 0);
|
||||
if (!dp->aux_switch_node) {
|
||||
DP_WARN("cannot parse %s handle\n", phandle);
|
||||
rc = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
nb.notifier_call = dp_display_fsa4480_callback;
|
||||
nb.priority = 0;
|
||||
|
||||
rc = fsa4480_reg_notifier(&nb, dp->aux_switch_node);
|
||||
if (rc) {
|
||||
DP_ERR("failed to register notifier (%d)\n", rc);
|
||||
goto end;
|
||||
}
|
||||
|
||||
fsa4480_unreg_notifier(&nb, dp->aux_switch_node);
|
||||
end:
|
||||
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_mst_install(struct dp_display *dp_display,
|
||||
struct dp_mst_drm_install_info *mst_install_info)
|
||||
{
|
||||
|
|
@ -3550,10 +3708,119 @@ static void dp_display_wakeup_phy_layer(struct dp_display *dp_display,
|
|||
hpd->wakeup_phy(hpd, wakeup);
|
||||
}
|
||||
|
||||
static int dp_display_get_display_type(struct dp_display *dp_display,
|
||||
const char **display_type)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dp_display || !display_type) {
|
||||
pr_err("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
*display_type = dp->parser->display_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_mst_get_fixed_topology_display_type(
|
||||
struct dp_display *dp_display, u32 strm_id,
|
||||
const char **display_type)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dp_display || !display_type) {
|
||||
pr_err("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strm_id >= DP_STREAM_MAX) {
|
||||
pr_err("invalid stream id:%d\n", strm_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
*display_type = dp->parser->mst_fixed_display_type[strm_id];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_edp_detect(struct dp_display *dp_display)
|
||||
{
|
||||
struct device *dev;
|
||||
struct dp_display_private *dp;
|
||||
int rc = 0;
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
dev = &dp->pdev->dev;
|
||||
|
||||
if (dp->dp_display.is_sst_connected)
|
||||
return rc;
|
||||
|
||||
mutex_lock(&dp->session_lock);
|
||||
|
||||
rc = dp_display_host_init(dp);
|
||||
if (rc) {
|
||||
DP_ERR("Host init Failed");
|
||||
goto end;
|
||||
}
|
||||
|
||||
dp->dp_display.is_sst_connected = true;
|
||||
dp->hpd->hpd_high = true;
|
||||
dp->hpd->alt_mode_cfg_done = true;
|
||||
|
||||
dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz, dp->debug->max_pclk_khz);
|
||||
|
||||
rc = dp_display_host_ready(dp);
|
||||
if (rc) {
|
||||
dp_display_state_show("[ready failed]");
|
||||
dp_display_host_deinit(dp);
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = dp_display_panel_ready(dp);
|
||||
if (rc) {
|
||||
dp_display_host_unready(dp);
|
||||
dp_display_host_deinit(dp);
|
||||
goto end;
|
||||
}
|
||||
|
||||
dp->link->psm_config(dp->link, &dp->panel->link_info, false);
|
||||
dp->debug->psm_enabled = false;
|
||||
|
||||
rc = dp->panel->read_sink_caps(dp->panel,
|
||||
dp->dp_display.base_connector, dp->hpd->multi_func);
|
||||
|
||||
if (rc == -ETIMEDOUT || rc == -ENOTCONN)
|
||||
goto end;
|
||||
|
||||
dp_display_state_remove(DP_STATE_ABORTED);
|
||||
dp_display_state_add(DP_STATE_CONFIGURED);
|
||||
dp_display_state_add(DP_STATE_CONNECTED);
|
||||
|
||||
dp->link->process_request(dp->link);
|
||||
dp->panel->handle_sink_request(dp->panel);
|
||||
|
||||
dp_display_state_add(DP_STATE_CONNECT_NOTIFIED);
|
||||
dp_display_state_remove(DP_STATE_DISCONNECT_NOTIFIED);
|
||||
|
||||
dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, false);
|
||||
end:
|
||||
mutex_unlock(&dp->session_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
const struct of_device_id *id;
|
||||
const struct dp_display_type_info *info;
|
||||
struct dp_display *dp_display;
|
||||
int index;
|
||||
|
||||
if (!pdev || !pdev->dev.of_node) {
|
||||
DP_ERR("pdev not found\n");
|
||||
|
|
@ -3561,6 +3828,19 @@ static int dp_display_probe(struct platform_device *pdev)
|
|||
goto bail;
|
||||
}
|
||||
|
||||
id = of_match_node(dp_dt_match, pdev->dev.of_node);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
info = id->data;
|
||||
|
||||
index = dp_display_get_num_of_displays();
|
||||
if (index >= MAX_DP_ACTIVE_DISPLAY) {
|
||||
pr_err("exceeds max dp count\n");
|
||||
rc = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
|
||||
if (!dp) {
|
||||
rc = -ENOMEM;
|
||||
|
|
@ -3575,11 +3855,9 @@ static int dp_display_probe(struct platform_device *pdev)
|
|||
|
||||
memset(&dp->mst, 0, sizeof(dp->mst));
|
||||
|
||||
rc = dp_display_init_aux_switch(dp);
|
||||
if (rc) {
|
||||
rc = -EPROBE_DEFER;
|
||||
rc = dp_display_get_cell_info(dp);
|
||||
if (rc)
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = dp_display_create_workqueue(dp);
|
||||
if (rc) {
|
||||
|
|
@ -3589,44 +3867,51 @@ static int dp_display_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, dp);
|
||||
|
||||
g_dp_display = &dp->dp_display;
|
||||
dp_display = &dp->dp_display;
|
||||
g_dp_display[index] = dp_display;
|
||||
|
||||
g_dp_display->enable = dp_display_enable;
|
||||
g_dp_display->post_enable = dp_display_post_enable;
|
||||
g_dp_display->pre_disable = dp_display_pre_disable;
|
||||
g_dp_display->disable = dp_display_disable;
|
||||
g_dp_display->set_mode = dp_display_set_mode;
|
||||
g_dp_display->validate_mode = dp_display_validate_mode;
|
||||
g_dp_display->get_modes = dp_display_get_modes;
|
||||
g_dp_display->prepare = dp_display_prepare;
|
||||
g_dp_display->unprepare = dp_display_unprepare;
|
||||
g_dp_display->request_irq = dp_request_irq;
|
||||
g_dp_display->get_debug = dp_get_debug;
|
||||
g_dp_display->post_open = NULL;
|
||||
g_dp_display->post_init = dp_display_post_init;
|
||||
g_dp_display->config_hdr = dp_display_config_hdr;
|
||||
g_dp_display->mst_install = dp_display_mst_install;
|
||||
g_dp_display->mst_uninstall = dp_display_mst_uninstall;
|
||||
g_dp_display->mst_connector_install = dp_display_mst_connector_install;
|
||||
g_dp_display->mst_connector_uninstall =
|
||||
dp_display->enable = dp_display_enable;
|
||||
dp_display->post_enable = dp_display_post_enable;
|
||||
dp_display->pre_disable = dp_display_pre_disable;
|
||||
dp_display->disable = dp_display_disable;
|
||||
dp_display->set_mode = dp_display_set_mode;
|
||||
dp_display->validate_mode = dp_display_validate_mode;
|
||||
dp_display->get_modes = dp_display_get_modes;
|
||||
dp_display->prepare = dp_display_prepare;
|
||||
dp_display->unprepare = dp_display_unprepare;
|
||||
dp_display->request_irq = dp_request_irq;
|
||||
dp_display->get_debug = dp_get_debug;
|
||||
dp_display->post_open = NULL;
|
||||
dp_display->post_init = dp_display_post_init;
|
||||
dp_display->config_hdr = dp_display_config_hdr;
|
||||
dp_display->mst_install = dp_display_mst_install;
|
||||
dp_display->mst_uninstall = dp_display_mst_uninstall;
|
||||
dp_display->mst_connector_install = dp_display_mst_connector_install;
|
||||
dp_display->mst_connector_uninstall =
|
||||
dp_display_mst_connector_uninstall;
|
||||
g_dp_display->mst_connector_update_edid =
|
||||
dp_display->mst_connector_update_edid =
|
||||
dp_display_mst_connector_update_edid;
|
||||
g_dp_display->mst_connector_update_link_info =
|
||||
dp_display->mst_connector_update_link_info =
|
||||
dp_display_mst_connector_update_link_info;
|
||||
g_dp_display->get_mst_caps = dp_display_get_mst_caps;
|
||||
g_dp_display->set_stream_info = dp_display_set_stream_info;
|
||||
g_dp_display->update_pps = dp_display_update_pps;
|
||||
g_dp_display->convert_to_dp_mode = dp_display_convert_to_dp_mode;
|
||||
g_dp_display->mst_get_connector_info =
|
||||
dp_display->get_mst_caps = dp_display_get_mst_caps;
|
||||
dp_display->set_stream_info = dp_display_set_stream_info;
|
||||
dp_display->update_pps = dp_display_update_pps;
|
||||
dp_display->convert_to_dp_mode = dp_display_convert_to_dp_mode;
|
||||
dp_display->mst_get_connector_info =
|
||||
dp_display_mst_get_connector_info;
|
||||
g_dp_display->mst_get_fixed_topology_port =
|
||||
dp_display->mst_get_fixed_topology_port =
|
||||
dp_display_mst_get_fixed_topology_port;
|
||||
g_dp_display->wakeup_phy_layer =
|
||||
dp_display->wakeup_phy_layer =
|
||||
dp_display_wakeup_phy_layer;
|
||||
g_dp_display->set_colorspace = dp_display_setup_colospace;
|
||||
g_dp_display->get_available_dp_resources =
|
||||
dp_display->set_colorspace = dp_display_setup_colospace;
|
||||
dp_display->get_available_dp_resources =
|
||||
dp_display_get_available_dp_resources;
|
||||
dp_display->get_display_type = dp_display_get_display_type;
|
||||
dp_display->mst_get_fixed_topology_display_type =
|
||||
dp_display_mst_get_fixed_topology_display_type;
|
||||
|
||||
dp_display->is_edp = (info->display_type == DRM_MODE_CONNECTOR_eDP) ? true : false;
|
||||
dp_display->edp_detect = dp_display_edp_detect;
|
||||
|
||||
rc = component_add(&pdev->dev, &dp_display_comp_ops);
|
||||
if (rc) {
|
||||
|
|
@ -3643,31 +3928,71 @@ bail:
|
|||
|
||||
int dp_display_get_displays(void **displays, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!displays) {
|
||||
DP_ERR("invalid data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (count != 1) {
|
||||
DP_ERR("invalid number of displays\n");
|
||||
return -EINVAL;
|
||||
for (i = 0; i < MAX_DP_ACTIVE_DISPLAY && i < count; i++) {
|
||||
if (!g_dp_display[i])
|
||||
break;
|
||||
|
||||
displays[i] = g_dp_display[i];
|
||||
}
|
||||
|
||||
displays[0] = g_dp_display;
|
||||
return count;
|
||||
return i;
|
||||
}
|
||||
|
||||
int dp_display_get_num_of_displays(void)
|
||||
{
|
||||
if (!g_dp_display)
|
||||
return 0;
|
||||
int i;
|
||||
|
||||
return 1;
|
||||
for (i = 0; i < MAX_DP_ACTIVE_DISPLAY; i++)
|
||||
if (!g_dp_display[i])
|
||||
break;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int dp_display_get_num_of_streams(void)
|
||||
{
|
||||
return DP_STREAM_MAX;
|
||||
struct dp_display_private *dp;
|
||||
int i, count = 0;
|
||||
|
||||
for (i = 0; i < MAX_DP_ACTIVE_DISPLAY; i++) {
|
||||
if (!g_dp_display[i])
|
||||
break;
|
||||
|
||||
dp = container_of(g_dp_display[i], struct dp_display_private, dp_display);
|
||||
|
||||
count += dp->stream_cnt;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int dp_display_get_info(void *dp_display, struct dp_display_info *dp_info)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
int i;
|
||||
|
||||
if (!dp_display) {
|
||||
DP_DEBUG("dp display not initialized\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
dp_info->cell_idx = dp->cell_idx;
|
||||
dp_info->intf_idx[0] = dp->intf_idx[0];
|
||||
for (i = 1; i < dp->stream_cnt; i++)
|
||||
dp_info->intf_idx[i] = dp->intf_idx[i];
|
||||
dp_info->phy_idx = dp->phy_idx;
|
||||
dp_info->stream_cnt = dp->stream_cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dp_display_set_mst_state(void *dp_display,
|
||||
|
|
@ -3675,16 +4000,11 @@ static void dp_display_set_mst_state(void *dp_display,
|
|||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!g_dp_display) {
|
||||
DP_DEBUG("dp display not initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dp = container_of(g_dp_display, struct dp_display_private, dp_display);
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
SDE_EVT32_EXTERNAL(mst_state, dp->mst.mst_active);
|
||||
|
||||
if (dp->mst.mst_active && dp->mst.cbs.set_drv_state)
|
||||
dp->mst.cbs.set_drv_state(g_dp_display, mst_state);
|
||||
dp->mst.cbs.set_drv_state(dp_display, mst_state);
|
||||
}
|
||||
|
||||
static int dp_display_remove(struct platform_device *pdev)
|
||||
|
|
@ -3709,12 +4029,16 @@ static int dp_display_remove(struct platform_device *pdev)
|
|||
|
||||
static int dp_pm_prepare(struct device *dev)
|
||||
{
|
||||
struct dp_display_private *dp = container_of(g_dp_display,
|
||||
struct dp_display_private, dp_display);
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
dp = dev_get_drvdata(dev);
|
||||
|
||||
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
|
||||
mutex_lock(&dp->session_lock);
|
||||
dp_display_set_mst_state(g_dp_display, PM_SUSPEND);
|
||||
dp_display_set_mst_state(&dp->dp_display, PM_SUSPEND);
|
||||
|
||||
/*
|
||||
* There are a few instances where the DP is hotplugged when the device
|
||||
|
|
@ -3739,12 +4063,16 @@ static int dp_pm_prepare(struct device *dev)
|
|||
|
||||
static void dp_pm_complete(struct device *dev)
|
||||
{
|
||||
struct dp_display_private *dp = container_of(g_dp_display,
|
||||
struct dp_display_private, dp_display);
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
dp = dev_get_drvdata(dev);
|
||||
|
||||
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
|
||||
mutex_lock(&dp->session_lock);
|
||||
dp_display_set_mst_state(g_dp_display, PM_DEFAULT);
|
||||
dp_display_set_mst_state(&dp->dp_display, PM_DEFAULT);
|
||||
|
||||
/*
|
||||
* There are multiple PM suspend entry and exits observed before
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
|
|
@ -11,8 +12,10 @@
|
|||
#include <drm/sde_drm.h>
|
||||
|
||||
#include "dp_panel.h"
|
||||
#include "dp_parser.h"
|
||||
|
||||
#define DP_MST_SIM_MAX_PORTS 8
|
||||
#define MAX_DP_ACTIVE_DISPLAY 8
|
||||
|
||||
enum dp_drv_state {
|
||||
PM_DEFAULT,
|
||||
|
|
@ -29,6 +32,13 @@ struct dp_mst_hpd_info {
|
|||
int mst_sim_remove_con_id;
|
||||
};
|
||||
|
||||
struct dp_display_info {
|
||||
u32 cell_idx;
|
||||
u32 intf_idx[DP_STREAM_MAX];
|
||||
u32 phy_idx;
|
||||
u32 stream_cnt;
|
||||
};
|
||||
|
||||
struct dp_mst_drm_cbs {
|
||||
void (*hpd)(void *display, bool hpd_status);
|
||||
void (*hpd_irq)(void *display, struct dp_mst_hpd_info *info);
|
||||
|
|
@ -70,6 +80,7 @@ struct dp_display {
|
|||
void *base_dp_panel;
|
||||
bool is_sst_connected;
|
||||
bool is_mst_supported;
|
||||
bool is_edp;
|
||||
bool dsc_cont_pps;
|
||||
u32 max_pclk_khz;
|
||||
void *dp_mst_prv_info;
|
||||
|
|
@ -132,12 +143,18 @@ struct dp_display {
|
|||
int (*get_available_dp_resources)(struct dp_display *dp_display,
|
||||
const struct msm_resource_caps_info *avail_res,
|
||||
struct msm_resource_caps_info *max_dp_avail_res);
|
||||
int (*get_display_type)(struct dp_display *dp_display,
|
||||
const char **display_type);
|
||||
int (*mst_get_fixed_topology_display_type)(struct dp_display *dp_display,
|
||||
u32 strm_id, const char **display_type);
|
||||
int (*edp_detect)(struct dp_display *dp_display);
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_MSM_DP)
|
||||
int dp_display_get_num_of_displays(void);
|
||||
int dp_display_get_displays(void **displays, int count);
|
||||
int dp_display_get_num_of_streams(void);
|
||||
int dp_display_get_info(void *dp_display, struct dp_display_info *dp_info);
|
||||
#else
|
||||
static inline int dp_display_get_num_of_displays(void)
|
||||
{
|
||||
|
|
@ -151,6 +168,10 @@ static inline int dp_display_get_num_of_streams(void)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int dp_display_get_info(void *dp_display, struct dp_display_info *dp_info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int dp_connector_update_pps(struct drm_connector *connector,
|
||||
char *pps_cmd, void *display)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
|
|
@ -456,8 +457,15 @@ int dp_connector_get_info(struct drm_connector *connector,
|
|||
info->num_of_h_tiles = 1;
|
||||
info->h_tile_instance[0] = 0;
|
||||
info->is_connected = display->is_sst_connected;
|
||||
info->capabilities = MSM_DISPLAY_CAP_VID_MODE | MSM_DISPLAY_CAP_EDID |
|
||||
MSM_DISPLAY_CAP_HOT_PLUG;
|
||||
info->curr_panel_mode = MSM_DISPLAY_VIDEO_MODE;
|
||||
info->capabilities = MSM_DISPLAY_CAP_VID_MODE | MSM_DISPLAY_CAP_EDID;
|
||||
|
||||
if (display && display->is_edp) {
|
||||
info->intf_type = DRM_MODE_CONNECTOR_eDP;
|
||||
info->display_type = SDE_CONNECTOR_PRIMARY;
|
||||
} else {
|
||||
info->capabilities |= MSM_DISPLAY_CAP_HOT_PLUG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -468,11 +476,13 @@ enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
|
|||
{
|
||||
enum drm_connector_status status = connector_status_unknown;
|
||||
struct msm_display_info info;
|
||||
struct dp_display *dp_disp;
|
||||
int rc;
|
||||
|
||||
if (!conn || !display)
|
||||
return status;
|
||||
|
||||
dp_disp = display;
|
||||
/* get display dp_info */
|
||||
memset(&info, 0x0, sizeof(info));
|
||||
rc = dp_connector_get_info(conn, &info, display);
|
||||
|
|
@ -481,12 +491,18 @@ enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
|
|||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG)
|
||||
if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG) {
|
||||
status = (info.is_connected ? connector_status_connected :
|
||||
connector_status_disconnected);
|
||||
else
|
||||
} else {
|
||||
status = connector_status_connected;
|
||||
|
||||
rc = dp_disp->edp_detect(dp_disp);
|
||||
if (rc) {
|
||||
DP_ERR("error in turning on panel power sequence rc:%d\n", rc);
|
||||
return connector_status_unknown;
|
||||
}
|
||||
}
|
||||
conn->display_info.width_mm = info.width_mm;
|
||||
conn->display_info.height_mm = info.height_mm;
|
||||
|
||||
|
|
@ -594,6 +610,18 @@ int dp_connector_get_modes(struct drm_connector *connector,
|
|||
return rc;
|
||||
}
|
||||
|
||||
int dp_connector_set_info_blob(struct drm_connector *connector,
|
||||
void *info, void *display, struct msm_mode_info *mode_info)
|
||||
{
|
||||
struct dp_display *dp_display = display;
|
||||
const char *display_type = NULL;
|
||||
|
||||
dp_display->get_display_type(dp_display, &display_type);
|
||||
sde_kms_info_add_keystr(info, "display type", display_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dp_drm_bridge_init(void *data, struct drm_encoder *encoder,
|
||||
u32 max_mixer_count, u32 max_dsc_count)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
|
|
@ -136,6 +137,18 @@ void dp_connector_post_open(struct drm_connector *connector, void *display);
|
|||
* @max_mixer_count: max available mixers for dp display
|
||||
* @max_dsc_count: max available dsc for dp display
|
||||
*/
|
||||
|
||||
/**
|
||||
* dp_conn_set_info_blob - callback to perform info blob initialization
|
||||
* @connector: Pointer to drm connector structure
|
||||
* @info: Pointer to sde connector info structure
|
||||
* @display: Pointer to private display handle
|
||||
* @mode_info: Pointer to mode info structure
|
||||
* Returns: Zero on success
|
||||
*/
|
||||
int dp_connector_set_info_blob(struct drm_connector *connector,
|
||||
void *info, void *display, struct msm_mode_info *mode_info);
|
||||
|
||||
int dp_drm_bridge_init(void *display, struct drm_encoder *encoder,
|
||||
u32 max_mixer_count, u32 max_dsc_count);
|
||||
|
||||
|
|
@ -233,6 +246,12 @@ static inline void dp_connector_post_open(struct drm_connector *connector,
|
|||
{
|
||||
}
|
||||
|
||||
static inline int dp_connector_set_info_blob(struct drm_connector *connector,
|
||||
void *info, void *display, struct msm_mode_info *mode_info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int dp_drm_bridge_init(void *display, struct drm_encoder *encoder,
|
||||
u32 max_mixer_count, u32 max_dsc_count)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,5 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
|
|
@ -166,6 +167,16 @@ static int dp_parser_misc(struct dp_parser *parser)
|
|||
if (rc)
|
||||
parser->max_lclk_khz = DP_MAX_LINK_CLK_KHZ;
|
||||
|
||||
for (i = 0; i < MAX_DP_MST_STREAMS; i++) {
|
||||
of_property_read_u32_index(of_node,
|
||||
"qcom,pixel-base-off", i,
|
||||
&parser->pixel_base_off[i]);
|
||||
}
|
||||
|
||||
parser->display_type = of_get_property(of_node, "qcom,display-type", NULL);
|
||||
if (!parser->display_type)
|
||||
parser->display_type = "unknown";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -247,23 +258,26 @@ static int dp_parser_gpio(struct dp_parser *parser)
|
|||
struct device *dev = &parser->pdev->dev;
|
||||
struct device_node *of_node = dev->of_node;
|
||||
struct dss_module_power *mp = &parser->mp[DP_CORE_PM];
|
||||
static const char * const dp_gpios[] = {
|
||||
static const char * const dp_gpios[DP_GPIO_MAX] = {
|
||||
"qcom,aux-en-gpio",
|
||||
"qcom,aux-sel-gpio",
|
||||
"qcom,usbplug-cc-gpio",
|
||||
"qcom,edp-vcc-en-gpio",
|
||||
"qcom,edp-backlight-pwr-gpio",
|
||||
"qcom,edp-pwm-en-gpio",
|
||||
"qcom,edp-backlight-en-gpio",
|
||||
};
|
||||
|
||||
if (of_find_property(of_node, "qcom,dp-hpd-gpio", NULL)) {
|
||||
parser->no_aux_switch = true;
|
||||
parser->lphw_hpd = of_find_property(of_node,
|
||||
"qcom,dp-low-power-hw-hpd", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (of_find_property(of_node, "qcom,dp-gpio-aux-switch", NULL))
|
||||
parser->gpio_aux_switch = true;
|
||||
mp->gpio_config = devm_kzalloc(dev,
|
||||
sizeof(struct dss_gpio) * ARRAY_SIZE(dp_gpios), GFP_KERNEL);
|
||||
sizeof(struct dss_gpio) * DP_GPIO_MAX, GFP_KERNEL);
|
||||
if (!mp->gpio_config)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -690,6 +704,7 @@ static int dp_parser_catalog(struct dp_parser *parser)
|
|||
{
|
||||
int rc;
|
||||
u32 version;
|
||||
const char *st = NULL;
|
||||
struct device *dev = &parser->pdev->dev;
|
||||
|
||||
rc = of_property_read_u32(dev->of_node, "qcom,phy-version", &version);
|
||||
|
|
@ -697,6 +712,27 @@ static int dp_parser_catalog(struct dp_parser *parser)
|
|||
if (!rc)
|
||||
parser->hw_cfg.phy_version = version;
|
||||
|
||||
/* phy-mode */
|
||||
rc = of_property_read_string(dev->of_node, "qcom,phy-mode", &st);
|
||||
|
||||
if (!rc) {
|
||||
if (!strcmp(st, "dp"))
|
||||
parser->hw_cfg.phy_mode = DP_PHY_MODE_DP;
|
||||
else if (!strcmp(st, "minidp"))
|
||||
parser->hw_cfg.phy_mode = DP_PHY_MODE_MINIDP;
|
||||
else if (!strcmp(st, "edp"))
|
||||
parser->hw_cfg.phy_mode = DP_PHY_MODE_EDP;
|
||||
else if (!strcmp(st, "edp-highswing"))
|
||||
parser->hw_cfg.phy_mode = DP_PHY_MODE_EDP_HIGH_SWING;
|
||||
else {
|
||||
parser->hw_cfg.phy_mode = DP_PHY_MODE_UNKNOWN;
|
||||
pr_warn("unknown phy-mode %s\n", st);
|
||||
}
|
||||
} else {
|
||||
/* default to DP mode, if not set */
|
||||
parser->hw_cfg.phy_mode = DP_PHY_MODE_DP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -715,6 +751,12 @@ static int dp_parser_mst(struct dp_parser *parser)
|
|||
of_property_read_u32_index(dev->of_node,
|
||||
"qcom,mst-fixed-topology-ports", i,
|
||||
&parser->mst_fixed_port[i]);
|
||||
of_property_read_string_index(
|
||||
dev->of_node,
|
||||
"qcom,mst-fixed-topology-display-types", i,
|
||||
&parser->mst_fixed_display_type[i]);
|
||||
if (!parser->mst_fixed_display_type[i])
|
||||
parser->mst_fixed_display_type[i] = "unknown";
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
|
|
@ -25,6 +26,20 @@ enum dp_pm_type {
|
|||
DP_MAX_PM
|
||||
};
|
||||
|
||||
enum dp_pin_states {
|
||||
DP_GPIO_AUX_ENABLE,
|
||||
DP_GPIO_AUX_SEL,
|
||||
DP_GPIO_USBPLUG_CC,
|
||||
DP_GPIO_CMN_MAX = DP_GPIO_USBPLUG_CC,
|
||||
DP_GPIO_EDP_VCC_EN,
|
||||
DP_GPIO_EDP_MIN = DP_GPIO_EDP_VCC_EN,
|
||||
DP_GPIO_EDP_BACKLIGHT_PWR,
|
||||
DP_GPIO_EDP_PWM,
|
||||
DP_GPIO_EDP_BACKLIGHT_EN,
|
||||
DP_GPIO_EDP_MAX,
|
||||
DP_GPIO_MAX = DP_GPIO_EDP_MAX,
|
||||
};
|
||||
|
||||
static inline const char *dp_parser_pm_name(enum dp_pm_type module)
|
||||
{
|
||||
switch (module) {
|
||||
|
|
@ -131,15 +146,35 @@ enum dp_phy_aux_config_type {
|
|||
* enum dp_phy_version - version of the dp phy
|
||||
* @DP_PHY_VERSION_UNKNOWN: Unknown controller version
|
||||
* @DP_PHY_VERSION_4_2_0: DP phy v4.2.0 controller
|
||||
* @DP_PHY_VERSION_5_0_0: DP phy v5.0.0 controller
|
||||
* @DP_PHY_VERSION_MAX: max version
|
||||
*/
|
||||
enum dp_phy_version {
|
||||
DP_PHY_VERSION_UNKNOWN,
|
||||
DP_PHY_VERSION_2_0_0 = 0x200,
|
||||
DP_PHY_VERSION_4_2_0 = 0x420,
|
||||
DP_PHY_VERSION_5_0_0 = 0x500,
|
||||
DP_PHY_VERSION_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* enum dp_phy_mode - mode of the dp phy
|
||||
* @DP_PHY_MODE_UNKNOWN: Unknown PHY mode
|
||||
* @DP_PHY_MODE_DP: DP PHY mode
|
||||
* @DP_PHY_MODE_MINIDP: MiniDP PHY mode
|
||||
* @DP_PHY_MODE_EDP: eDP PHY mode
|
||||
* @DP_PHY_MODE_EDP_HIGH_SWING: eDP PHY mode, high swing/pre-empahsis
|
||||
* @DP_PHY_MODE_MAX: max PHY mode
|
||||
*/
|
||||
enum dp_phy_mode {
|
||||
DP_PHY_MODE_UNKNOWN = 0,
|
||||
DP_PHY_MODE_DP,
|
||||
DP_PHY_MODE_MINIDP,
|
||||
DP_PHY_MODE_EDP,
|
||||
DP_PHY_MODE_EDP_HIGH_SWING,
|
||||
DP_PHY_MODE_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dp_hw_cfg - DP HW specific configuration
|
||||
*
|
||||
|
|
@ -147,6 +182,7 @@ enum dp_phy_version {
|
|||
*/
|
||||
struct dp_hw_cfg {
|
||||
enum dp_phy_version phy_version;
|
||||
enum dp_phy_mode phy_mode;
|
||||
};
|
||||
|
||||
static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
|
||||
|
|
@ -202,6 +238,8 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
|
|||
* @get_io: function to be called by client to get io data.
|
||||
* @get_io_buf: function to be called by client to get io buffers.
|
||||
* @clear_io_buf: function to be called by client to clear io buffers.
|
||||
* @mst_fixed_display_type: mst display_type reserved for fixed topology
|
||||
* @display_type: display type as defined in device tree.
|
||||
*/
|
||||
struct dp_parser {
|
||||
struct platform_device *pdev;
|
||||
|
|
@ -227,6 +265,9 @@ struct dp_parser {
|
|||
bool gpio_aux_switch;
|
||||
bool lphw_hpd;
|
||||
u32 mst_fixed_port[MAX_DP_MST_STREAMS];
|
||||
u32 pixel_base_off[MAX_DP_MST_STREAMS];
|
||||
const char *mst_fixed_display_type[MAX_DP_MST_STREAMS];
|
||||
const char *display_type;
|
||||
|
||||
int (*parse)(struct dp_parser *parser);
|
||||
struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
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)
|
||||
|
|
|
|||
927
msm/dp/edp_pll_7nm.c
Normal file
927
msm/dp/edp_pll_7nm.c
Normal file
|
|
@ -0,0 +1,927 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Display Port PLL driver block diagram for branch clocks
|
||||
*
|
||||
* +------------------------+ +------------------------+
|
||||
* | dp_phy_pll_link_clk | | dp_phy_pll_vco_div_clk |
|
||||
* +------------------------+ +------------------------+
|
||||
* | |
|
||||
* | |
|
||||
* V V
|
||||
* dp_link_clk dp_pixel_clk
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <dt-bindings/clock/mdss-7nm-pll-clk.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/regmap.h>
|
||||
#include "clk-regmap-mux.h"
|
||||
#include "dp_hpd.h"
|
||||
#include "dp_debug.h"
|
||||
#include "dp_pll.h"
|
||||
|
||||
#define DP_PHY_CFG 0x0010
|
||||
#define DP_PHY_CFG_1 0x0014
|
||||
#define DP_PHY_PD_CTL 0x001C
|
||||
#define DP_PHY_MODE 0x0020
|
||||
|
||||
#define DP_PHY_AUX_CFG2 0x002C
|
||||
|
||||
#define DP_PHY_VCO_DIV 0x0074
|
||||
#define DP_PHY_TX0_TX1_LANE_CTL 0x007C
|
||||
#define DP_PHY_TX2_TX3_LANE_CTL 0x00A0
|
||||
|
||||
#define DP_PHY_SPARE0 0x00CC
|
||||
#define DP_PHY_STATUS 0x00E0
|
||||
|
||||
/* Tx registers */
|
||||
#define TXn_CLKBUF_ENABLE 0x0000
|
||||
#define TXn_TX_EMP_POST1_LVL 0x0004
|
||||
#define TXn_TX_DRV_LVL 0x0014
|
||||
#define TXn_TX_DRV_LVL_OFFSET 0x0018
|
||||
#define TXn_RESET_TSYNC_EN 0x001C
|
||||
#define TXn_LDO_CONFIG 0x0084
|
||||
#define TXn_TX_BAND 0x0028
|
||||
#define TXn_INTERFACE_SELECT 0x0024
|
||||
#define TXn_RES_CODE_LANE_OFFSET_TX0 0x0044
|
||||
#define TXn_RES_CODE_LANE_OFFSET_TX1 0x0048
|
||||
#define TXn_TRANSCEIVER_BIAS_EN 0x0054
|
||||
#define TXn_HIGHZ_DRVR_EN 0x0058
|
||||
#define TXn_TX_POL_INV 0x005C
|
||||
#define TXn_PARRATE_REC_DETECT_IDLE_EN 0x0060
|
||||
#define TXn_LANE_MODE_1 0x0064
|
||||
#define TXn_TRAN_DRVR_EMP_EN 0x0078
|
||||
#define TXn_VMODE_CTRL1 0x007C
|
||||
|
||||
/* PLL register offset */
|
||||
#define QSERDES_COM_BG_TIMER 0x000C
|
||||
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x0044
|
||||
#define QSERDES_COM_CLK_ENABLE1 0x0048
|
||||
#define QSERDES_COM_SYS_CLK_CTRL 0x004C
|
||||
#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x0050
|
||||
#define QSERDES_COM_PLL_IVCO 0x0058
|
||||
|
||||
#define QSERDES_COM_CP_CTRL_MODE0 0x0074
|
||||
#define QSERDES_COM_PLL_RCTRL_MODE0 0x007C
|
||||
#define QSERDES_COM_PLL_CCTRL_MODE0 0x0084
|
||||
#define QSERDES_COM_SYSCLK_EN_SEL 0x0094
|
||||
#define QSERDES_COM_RESETSM_CNTRL 0x009C
|
||||
#define QSERDES_COM_LOCK_CMP_EN 0x00A4
|
||||
#define QSERDES_COM_LOCK_CMP1_MODE0 0x00AC
|
||||
#define QSERDES_COM_LOCK_CMP2_MODE0 0x00B0
|
||||
|
||||
#define QSERDES_COM_DEC_START_MODE0 0x00BC
|
||||
#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x00CC
|
||||
#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x00D0
|
||||
#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x00D4
|
||||
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x00EC
|
||||
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x00F0
|
||||
#define QSERDES_COM_VCO_TUNE_CTRL 0x0108
|
||||
#define QSERDES_COM_VCO_TUNE_MAP 0x010C
|
||||
#define QSERDES_COM_VCO_TUNE1_MODE0 0x0110
|
||||
#define QSERDES_COM_VCO_TUNE2_MODE0 0x0114
|
||||
#define QSERDES_COM_CMN_STATUS 0x0140
|
||||
|
||||
#define QSERDES_COM_CLK_SEL 0x0154
|
||||
#define QSERDES_COM_HSCLK_SEL 0x0158
|
||||
|
||||
#define QSERDES_COM_CORECLK_DIV_MODE0 0x0168
|
||||
|
||||
#define QSERDES_COM_CORE_CLK_EN 0x0174
|
||||
#define QSERDES_COM_C_READY_STATUS 0x0178
|
||||
#define QSERDES_COM_CMN_CONFIG 0x017C
|
||||
|
||||
#define QSERDES_COM_SVS_MODE_CLK_SEL 0x0184
|
||||
|
||||
#define QSERDES_COM_SSC_EN_CENTER 0x0010
|
||||
#define QSERDES_COM_SSC_ADJ_PER1 0x0014
|
||||
#define QSERDES_COM_SSC_ADJ_PER2 0x0018
|
||||
#define QSERDES_COM_SSC_PER1 0x001C
|
||||
#define QSERDES_COM_SSC_PER2 0x0020
|
||||
#define QSERDES_COM_SSC_STEP_SIZE1_MODE0 0x0024
|
||||
#define QSERDES_COM_SSC_STEP_SIZE2_MODE0 0x0028
|
||||
|
||||
#define DP_PHY_PLL_POLL_SLEEP_US 500
|
||||
#define DP_PHY_PLL_POLL_TIMEOUT_US 10000
|
||||
|
||||
#define DP_VCO_RATE_8100MHZDIV1000 8100000UL
|
||||
#define DP_VCO_RATE_9720MHZDIV1000 9720000UL
|
||||
#define DP_VCO_RATE_10800MHZDIV1000 10800000UL
|
||||
|
||||
#define DP_7NM_C_READY BIT(0)
|
||||
#define DP_7NM_FREQ_DONE BIT(0)
|
||||
#define DP_7NM_PLL_LOCKED BIT(1)
|
||||
#define DP_7NM_PHY_READY BIT(1)
|
||||
#define DP_7NM_TSYNC_DONE BIT(0)
|
||||
|
||||
static int edp_vco_pll_init_db_7nm(struct dp_pll_db *pdb,
|
||||
unsigned long rate)
|
||||
{
|
||||
struct dp_pll *pll = pdb->pll;
|
||||
u32 spare_value = 0;
|
||||
|
||||
spare_value = dp_pll_read(dp_phy, DP_PHY_SPARE0);
|
||||
pdb->lane_cnt = spare_value & 0x0F;
|
||||
pdb->orientation = (spare_value & 0xF0) >> 4;
|
||||
|
||||
DP_DEBUG("spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
|
||||
spare_value, pdb->lane_cnt, pdb->orientation);
|
||||
|
||||
pdb->div_frac_start1_mode0 = 0x00;
|
||||
pdb->integloop_gain0_mode0 = 0x3f;
|
||||
pdb->integloop_gain1_mode0 = 0x00;
|
||||
|
||||
switch (rate) {
|
||||
case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
|
||||
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_9720MHZDIV1000);
|
||||
pdb->hsclk_sel = 0x05;
|
||||
pdb->dec_start_mode0 = 0x69;
|
||||
pdb->div_frac_start2_mode0 = 0x80;
|
||||
pdb->div_frac_start3_mode0 = 0x07;
|
||||
pdb->lock_cmp1_mode0 = 0x6f;
|
||||
pdb->lock_cmp2_mode0 = 0x08;
|
||||
pdb->phy_vco_div = 0x01;
|
||||
pdb->lock_cmp_en = 0x08;
|
||||
pdb->ssc_step_size1_mode0 = 0x45;
|
||||
pdb->ssc_step_size2_mode0 = 0x06;
|
||||
break;
|
||||
case DP_VCO_HSCLK_RATE_2160MHZDIV1000:
|
||||
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
|
||||
pdb->hsclk_sel = 0x04;
|
||||
pdb->dec_start_mode0 = 0x70;
|
||||
pdb->div_frac_start2_mode0 = 0x00;
|
||||
pdb->div_frac_start3_mode0 = 0x08;
|
||||
pdb->lock_cmp1_mode0 = 0x3f;
|
||||
pdb->lock_cmp2_mode0 = 0x0b;
|
||||
pdb->phy_vco_div = 0x1;
|
||||
pdb->lock_cmp_en = 0x04;
|
||||
pdb->ssc_step_size1_mode0 = 0xb0;
|
||||
pdb->ssc_step_size2_mode0 = 0x06;
|
||||
break;
|
||||
case DP_VCO_HSCLK_RATE_2430MHZDIV1000:
|
||||
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
|
||||
pdb->hsclk_sel = 0x04;
|
||||
pdb->dec_start_mode0 = 0x7e;
|
||||
pdb->div_frac_start2_mode0 = 0x00;
|
||||
pdb->div_frac_start3_mode0 = 0x09;
|
||||
pdb->lock_cmp1_mode0 = 0xa7;
|
||||
pdb->lock_cmp2_mode0 = 0x0c;
|
||||
pdb->phy_vco_div = 0x1;
|
||||
pdb->lock_cmp_en = 0x04;
|
||||
pdb->ssc_step_size1_mode0 = 0x86;
|
||||
pdb->ssc_step_size2_mode0 = 0x07;
|
||||
break;
|
||||
case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
|
||||
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
|
||||
pdb->hsclk_sel = 0x03;
|
||||
pdb->dec_start_mode0 = 0x69;
|
||||
pdb->div_frac_start2_mode0 = 0x80;
|
||||
pdb->div_frac_start3_mode0 = 0x07;
|
||||
pdb->lock_cmp1_mode0 = 0x0f;
|
||||
pdb->lock_cmp2_mode0 = 0x0e;
|
||||
pdb->phy_vco_div = 0x01;
|
||||
pdb->lock_cmp_en = 0x08;
|
||||
pdb->ssc_step_size1_mode0 = 0x45;
|
||||
pdb->ssc_step_size2_mode0 = 0x06;
|
||||
break;
|
||||
case DP_VCO_HSCLK_RATE_3240MHZDIV1000:
|
||||
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
|
||||
pdb->hsclk_sel = 0x03;
|
||||
pdb->dec_start_mode0 = 0x7e;
|
||||
pdb->div_frac_start2_mode0 = 0x00;
|
||||
pdb->div_frac_start3_mode0 = 0x09;
|
||||
pdb->lock_cmp1_mode0 = 0xdf;
|
||||
pdb->lock_cmp2_mode0 = 0x10;
|
||||
pdb->phy_vco_div = 0x2;
|
||||
pdb->lock_cmp_en = 0x04;
|
||||
pdb->ssc_step_size1_mode0 = 0x86;
|
||||
pdb->ssc_step_size2_mode0 = 0x07;
|
||||
break;
|
||||
case DP_VCO_HSCLK_RATE_4320MHZDIV1000:
|
||||
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
|
||||
pdb->hsclk_sel = 0x01;
|
||||
pdb->dec_start_mode0 = 0x70;
|
||||
pdb->div_frac_start2_mode0 = 0x00;
|
||||
pdb->div_frac_start3_mode0 = 0x08;
|
||||
pdb->lock_cmp1_mode0 = 0x7f;
|
||||
pdb->lock_cmp2_mode0 = 0x16;
|
||||
pdb->phy_vco_div = 0x2;
|
||||
pdb->lock_cmp_en = 0x04;
|
||||
pdb->ssc_step_size1_mode0 = 0xb0;
|
||||
pdb->ssc_step_size2_mode0 = 0x06;
|
||||
break;
|
||||
case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
|
||||
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
|
||||
pdb->hsclk_sel = 0x01;
|
||||
pdb->dec_start_mode0 = 0x8c;
|
||||
pdb->div_frac_start2_mode0 = 0x00;
|
||||
pdb->div_frac_start3_mode0 = 0x0a;
|
||||
pdb->lock_cmp1_mode0 = 0x1f;
|
||||
pdb->lock_cmp2_mode0 = 0x1c;
|
||||
pdb->phy_vco_div = 0x02;
|
||||
pdb->lock_cmp_en = 0x08;
|
||||
pdb->ssc_step_size1_mode0 = 0x5c;
|
||||
pdb->ssc_step_size2_mode0 = 0x08;
|
||||
break;
|
||||
case DP_VCO_HSCLK_RATE_5940MHZDIV1000:
|
||||
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_8100MHZDIV1000);
|
||||
pdb->hsclk_sel = 0x01;
|
||||
pdb->dec_start_mode0 = 0x9a;
|
||||
pdb->div_frac_start2_mode0 = 0x00;
|
||||
pdb->div_frac_start3_mode0 = 0x0b;
|
||||
pdb->lock_cmp1_mode0 = 0xef;
|
||||
pdb->lock_cmp2_mode0 = 0x1e;
|
||||
pdb->phy_vco_div = 0x00;
|
||||
pdb->lock_cmp_en = 0x04;
|
||||
pdb->ssc_step_size1_mode0 = 0x33;
|
||||
pdb->ssc_step_size2_mode0 = 0x09;
|
||||
break;
|
||||
case DP_VCO_HSCLK_RATE_8100MHZDIV1000:
|
||||
DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_8100MHZDIV1000);
|
||||
pdb->hsclk_sel = 0x00;
|
||||
pdb->dec_start_mode0 = 0x69;
|
||||
pdb->div_frac_start2_mode0 = 0x80;
|
||||
pdb->div_frac_start3_mode0 = 0x07;
|
||||
pdb->lock_cmp1_mode0 = 0x2f;
|
||||
pdb->lock_cmp2_mode0 = 0x2a;
|
||||
pdb->phy_vco_div = 0x00;
|
||||
pdb->lock_cmp_en = 0x08;
|
||||
pdb->ssc_step_size1_mode0 = 0x45;
|
||||
pdb->ssc_step_size2_mode0 = 0x06;
|
||||
break;
|
||||
default:
|
||||
DP_ERR("unsupported rate %ld\n", rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int edp_config_vco_rate_7nm(struct dp_pll *pll,
|
||||
unsigned long rate)
|
||||
{
|
||||
int rc;
|
||||
struct dp_pll_db *pdb = &pll->pll_db;
|
||||
u32 status;
|
||||
|
||||
rc = edp_vco_pll_init_db_7nm(pdb, rate);
|
||||
if (rc < 0) {
|
||||
DP_ERR("VCO Init DB failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x7d);
|
||||
/* Make sure the PLL register writes are done */
|
||||
wmb();
|
||||
|
||||
if (readl_poll_timeout_atomic((dp_pll_get_base(dp_pll) +
|
||||
QSERDES_COM_CMN_STATUS),
|
||||
status, ((status & BIT(7)) > 0),
|
||||
5, 100)) {
|
||||
DP_ERR("refgen not ready. Status=%x\n", status);
|
||||
}
|
||||
|
||||
dp_pll_write(dp_ln_tx0, TXn_LDO_CONFIG, 0x01);
|
||||
dp_pll_write(dp_ln_tx1, TXn_LDO_CONFIG, 0x01);
|
||||
dp_pll_write(dp_ln_tx0, TXn_LANE_MODE_1, 0x00);
|
||||
dp_pll_write(dp_ln_tx1, TXn_LANE_MODE_1, 0x00);
|
||||
|
||||
if (pll->ssc_en) {
|
||||
dp_pll_write(dp_pll, QSERDES_COM_SSC_EN_CENTER, 0x01);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_SSC_ADJ_PER1, 0x00);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_SSC_PER1, 0x36);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_SSC_PER2, 0x01);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_SSC_STEP_SIZE1_MODE0,
|
||||
pdb->ssc_step_size1_mode0);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_SSC_STEP_SIZE2_MODE0,
|
||||
pdb->ssc_step_size2_mode0);
|
||||
}
|
||||
|
||||
dp_pll_write(dp_pll, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_SYSCLK_EN_SEL, 0x0b);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_SYS_CLK_CTRL, 0x02);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_CLK_ENABLE1, 0x0c);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_CLK_SEL, 0x30);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_HSCLK_SEL, pdb->hsclk_sel);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_PLL_IVCO, 0x0f);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_LOCK_CMP_EN, pdb->lock_cmp_en);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_CP_CTRL_MODE0, 0x06);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_DEC_START_MODE0, pdb->dec_start_mode0);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_DIV_FRAC_START1_MODE0, pdb->div_frac_start1_mode0);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_DIV_FRAC_START2_MODE0, pdb->div_frac_start2_mode0);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_DIV_FRAC_START3_MODE0, pdb->div_frac_start3_mode0);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_CMN_CONFIG, 0x02);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_INTEGLOOP_GAIN0_MODE0, pdb->integloop_gain0_mode0);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_INTEGLOOP_GAIN1_MODE0, pdb->integloop_gain1_mode0);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_VCO_TUNE_MAP, 0x00);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_LOCK_CMP1_MODE0, pdb->lock_cmp1_mode0);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_LOCK_CMP2_MODE0, pdb->lock_cmp2_mode0);
|
||||
dp_pll_write(dp_phy, DP_PHY_VCO_DIV, pdb->phy_vco_div);
|
||||
/* Make sure the PLL register writes are done */
|
||||
wmb();
|
||||
|
||||
dp_pll_write(dp_pll, QSERDES_COM_BG_TIMER, 0x0a);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_CORECLK_DIV_MODE0, 0x14);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
|
||||
dp_pll_write(dp_pll,
|
||||
QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_CORE_CLK_EN, 0x0f);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE1_MODE0, 0xa0);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
|
||||
/* Make sure the PHY register writes are done */
|
||||
wmb();
|
||||
|
||||
/* TX Lane configuration */
|
||||
dp_pll_write(dp_phy, DP_PHY_TX0_TX1_LANE_CTL, 0x05);
|
||||
dp_pll_write(dp_phy, DP_PHY_TX2_TX3_LANE_CTL, 0x05);
|
||||
|
||||
/* TX-0 register configuration */
|
||||
dp_pll_write(dp_ln_tx0, TXn_TRANSCEIVER_BIAS_EN, 0x03);
|
||||
dp_pll_write(dp_ln_tx0, TXn_CLKBUF_ENABLE, 0x0f);
|
||||
dp_pll_write(dp_ln_tx0, TXn_RESET_TSYNC_EN, 0x03);
|
||||
dp_pll_write(dp_ln_tx0, TXn_TRAN_DRVR_EMP_EN, 0x01);
|
||||
dp_pll_write(dp_ln_tx0, TXn_TX_BAND, 0x4);
|
||||
|
||||
/* TX-1 register configuration */
|
||||
dp_pll_write(dp_ln_tx1, TXn_TRANSCEIVER_BIAS_EN, 0x03);
|
||||
dp_pll_write(dp_ln_tx1, TXn_CLKBUF_ENABLE, 0x0f);
|
||||
dp_pll_write(dp_ln_tx1, TXn_RESET_TSYNC_EN, 0x03);
|
||||
dp_pll_write(dp_ln_tx1, TXn_TRAN_DRVR_EMP_EN, 0x01);
|
||||
dp_pll_write(dp_ln_tx1, TXn_TX_BAND, 0x4);
|
||||
/* Make sure the PHY register writes are done */
|
||||
wmb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum edp_7nm_pll_status {
|
||||
C_READY,
|
||||
FREQ_DONE,
|
||||
PLL_LOCKED,
|
||||
PHY_READY,
|
||||
TSYNC_DONE,
|
||||
};
|
||||
|
||||
static char *edp_7nm_pll_get_status_name(enum edp_7nm_pll_status status)
|
||||
{
|
||||
switch (status) {
|
||||
case C_READY:
|
||||
return "C_READY";
|
||||
case FREQ_DONE:
|
||||
return "FREQ_DONE";
|
||||
case PLL_LOCKED:
|
||||
return "PLL_LOCKED";
|
||||
case PHY_READY:
|
||||
return "PHY_READY";
|
||||
case TSYNC_DONE:
|
||||
return "TSYNC_DONE";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static bool edp_7nm_pll_get_status(struct dp_pll *pll,
|
||||
enum edp_7nm_pll_status status)
|
||||
{
|
||||
u32 reg, state, bit;
|
||||
void __iomem *base;
|
||||
bool success = true;
|
||||
|
||||
switch (status) {
|
||||
case C_READY:
|
||||
base = dp_pll_get_base(dp_pll);
|
||||
reg = QSERDES_COM_C_READY_STATUS;
|
||||
bit = DP_7NM_C_READY;
|
||||
break;
|
||||
case FREQ_DONE:
|
||||
base = dp_pll_get_base(dp_pll);
|
||||
reg = QSERDES_COM_CMN_STATUS;
|
||||
bit = DP_7NM_FREQ_DONE;
|
||||
break;
|
||||
case PLL_LOCKED:
|
||||
base = dp_pll_get_base(dp_pll);
|
||||
reg = QSERDES_COM_CMN_STATUS;
|
||||
bit = DP_7NM_PLL_LOCKED;
|
||||
break;
|
||||
case PHY_READY:
|
||||
base = dp_pll_get_base(dp_phy);
|
||||
reg = DP_PHY_STATUS;
|
||||
bit = DP_7NM_PHY_READY;
|
||||
break;
|
||||
case TSYNC_DONE:
|
||||
base = dp_pll_get_base(dp_phy);
|
||||
reg = DP_PHY_STATUS;
|
||||
bit = DP_7NM_TSYNC_DONE;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (readl_poll_timeout_atomic((base + reg), state,
|
||||
((state & bit) > 0),
|
||||
DP_PHY_PLL_POLL_SLEEP_US,
|
||||
DP_PHY_PLL_POLL_TIMEOUT_US)) {
|
||||
DP_ERR("%s failed, status=%x\n",
|
||||
edp_7nm_pll_get_status_name(status), state);
|
||||
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static int edp_pll_enable_7nm(struct dp_pll *pll)
|
||||
{
|
||||
int rc = 0;
|
||||
u32 bias_en0, drvr_en0, bias_en1, drvr_en1, phy_cfg_1;
|
||||
|
||||
pll->aux->state &= ~DP_STATE_PLL_LOCKED;
|
||||
|
||||
dp_pll_write(dp_phy, DP_PHY_CFG, 0x01);
|
||||
dp_pll_write(dp_phy, DP_PHY_CFG, 0x05);
|
||||
dp_pll_write(dp_phy, DP_PHY_CFG, 0x01);
|
||||
dp_pll_write(dp_phy, DP_PHY_CFG, 0x09);
|
||||
dp_pll_write(dp_pll, QSERDES_COM_RESETSM_CNTRL, 0x20);
|
||||
wmb(); /* Make sure the PLL register writes are done */
|
||||
|
||||
if (!edp_7nm_pll_get_status(pll, C_READY)) {
|
||||
rc = -EINVAL;
|
||||
goto lock_err;
|
||||
}
|
||||
|
||||
if (!edp_7nm_pll_get_status(pll, FREQ_DONE)) {
|
||||
rc = -EINVAL;
|
||||
goto lock_err;
|
||||
}
|
||||
|
||||
if (!edp_7nm_pll_get_status(pll, PLL_LOCKED)) {
|
||||
rc = -EINVAL;
|
||||
goto lock_err;
|
||||
}
|
||||
|
||||
dp_pll_write(dp_phy, DP_PHY_CFG, 0x19);
|
||||
dp_pll_write(dp_ln_tx0, TXn_HIGHZ_DRVR_EN, 0x1f);
|
||||
dp_pll_write(dp_ln_tx0, TXn_HIGHZ_DRVR_EN, 0x04);
|
||||
dp_pll_write(dp_ln_tx0, TXn_TX_POL_INV, 0x00);
|
||||
dp_pll_write(dp_ln_tx1, TXn_HIGHZ_DRVR_EN, 0x1f);
|
||||
dp_pll_write(dp_ln_tx1, TXn_HIGHZ_DRVR_EN, 0x04);
|
||||
dp_pll_write(dp_ln_tx1, TXn_TX_POL_INV, 0x00);
|
||||
dp_pll_write(dp_ln_tx0, TXn_TX_DRV_LVL_OFFSET, 0x10);
|
||||
dp_pll_write(dp_ln_tx1, TXn_TX_DRV_LVL_OFFSET, 0x10);
|
||||
dp_pll_write(dp_ln_tx0,
|
||||
TXn_RES_CODE_LANE_OFFSET_TX0, 0x11);
|
||||
dp_pll_write(dp_ln_tx0,
|
||||
TXn_RES_CODE_LANE_OFFSET_TX1, 0x11);
|
||||
dp_pll_write(dp_ln_tx1,
|
||||
TXn_RES_CODE_LANE_OFFSET_TX0, 0x11);
|
||||
dp_pll_write(dp_ln_tx1,
|
||||
TXn_RES_CODE_LANE_OFFSET_TX1, 0x11);
|
||||
dp_pll_write(dp_ln_tx0, TXn_TX_EMP_POST1_LVL, 0x10);
|
||||
dp_pll_write(dp_ln_tx1, TXn_TX_EMP_POST1_LVL, 0x10);
|
||||
dp_pll_write(dp_ln_tx0, TXn_TX_DRV_LVL, 0x1f);
|
||||
dp_pll_write(dp_ln_tx1, TXn_TX_DRV_LVL, 0x1f);
|
||||
|
||||
/* Make sure the PHY register writes are done */
|
||||
wmb();
|
||||
|
||||
if (pll->pll_db.lane_cnt == 1) {
|
||||
bias_en0 = 0x01;
|
||||
bias_en1 = 0x00;
|
||||
drvr_en0 = 0x06;
|
||||
drvr_en1 = 0x07;
|
||||
phy_cfg_1 = 0x01;
|
||||
} else if (pll->pll_db.lane_cnt == 2) {
|
||||
bias_en0 = 0x03;
|
||||
bias_en1 = 0x00;
|
||||
drvr_en0 = 0x04;
|
||||
drvr_en1 = 0x07;
|
||||
phy_cfg_1 = 0x03;
|
||||
} else {
|
||||
bias_en0 = 0x03;
|
||||
bias_en1 = 0x03;
|
||||
drvr_en0 = 0x04;
|
||||
drvr_en1 = 0x04;
|
||||
phy_cfg_1 = 0x0f;
|
||||
}
|
||||
|
||||
dp_pll_write(dp_ln_tx0, TXn_HIGHZ_DRVR_EN, drvr_en0);
|
||||
dp_pll_write(dp_ln_tx0, TXn_TRANSCEIVER_BIAS_EN,
|
||||
bias_en0);
|
||||
dp_pll_write(dp_ln_tx1, TXn_HIGHZ_DRVR_EN, drvr_en1);
|
||||
dp_pll_write(dp_ln_tx1, TXn_TRANSCEIVER_BIAS_EN,
|
||||
bias_en1);
|
||||
|
||||
/* Make sure the PHY register writes are done */
|
||||
wmb();
|
||||
|
||||
dp_pll_write(dp_phy, DP_PHY_CFG_1, phy_cfg_1);
|
||||
|
||||
if (!edp_7nm_pll_get_status(pll, PHY_READY)) {
|
||||
rc = -EINVAL;
|
||||
goto lock_err;
|
||||
}
|
||||
|
||||
dp_pll_write(dp_phy, DP_PHY_CFG, 0x18);
|
||||
udelay(100);
|
||||
|
||||
dp_pll_write(dp_phy, DP_PHY_CFG, 0x19);
|
||||
/* Make sure the PHY register writes are done */
|
||||
wmb();
|
||||
|
||||
if (!edp_7nm_pll_get_status(pll, TSYNC_DONE)) {
|
||||
rc = -EINVAL;
|
||||
goto lock_err;
|
||||
}
|
||||
|
||||
if (!edp_7nm_pll_get_status(pll, PHY_READY)) {
|
||||
rc = -EINVAL;
|
||||
goto lock_err;
|
||||
}
|
||||
|
||||
pll->aux->state |= DP_STATE_PLL_LOCKED;
|
||||
DP_DEBUG("PLL is locked\n");
|
||||
|
||||
lock_err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void edp_pll_disable_7nm(struct dp_pll *pll)
|
||||
{
|
||||
/* Assert DP PHY power down */
|
||||
dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x2);
|
||||
/*
|
||||
* Make sure all the register writes to disable PLL are
|
||||
* completed before doing any other operation
|
||||
*/
|
||||
wmb();
|
||||
}
|
||||
|
||||
static int edp_vco_clk_set_div(struct dp_pll *pll, unsigned int div)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
if (!pll) {
|
||||
DP_ERR("invalid input parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (is_gdsc_disabled(pll))
|
||||
return -EINVAL;
|
||||
|
||||
val = dp_pll_read(dp_phy, DP_PHY_VCO_DIV);
|
||||
val &= ~0x03;
|
||||
|
||||
switch (div) {
|
||||
case 2:
|
||||
val |= 1;
|
||||
break;
|
||||
case 4:
|
||||
val |= 2;
|
||||
break;
|
||||
case 6:
|
||||
/* When div = 6, val is 0, so do nothing here */
|
||||
;
|
||||
break;
|
||||
case 8:
|
||||
val |= 3;
|
||||
break;
|
||||
default:
|
||||
DP_DEBUG("unsupported div value %d\n", div);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp_pll_write(dp_phy, DP_PHY_VCO_DIV, val);
|
||||
/* Make sure the PHY registers writes are done */
|
||||
wmb();
|
||||
|
||||
DP_DEBUG("val=%d div=%x\n", val, div);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int edp_vco_set_rate_7nm(struct dp_pll *pll, unsigned long rate)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!pll) {
|
||||
DP_ERR("invalid input parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DP_DEBUG("DP lane CLK rate=%ld\n", rate);
|
||||
|
||||
rc = edp_config_vco_rate_7nm(pll, rate);
|
||||
if (rc < 0) {
|
||||
DP_ERR("Failed to set clk rate\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int edp_pll_configure(struct dp_pll *pll, unsigned long rate)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!pll || !rate) {
|
||||
DP_ERR("invalid input parameters rate = %lu\n", rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rate = rate * 10;
|
||||
|
||||
if (rate <= DP_VCO_HSCLK_RATE_1620MHZDIV1000)
|
||||
rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
|
||||
else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
|
||||
rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
|
||||
else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000)
|
||||
rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
|
||||
else
|
||||
rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
|
||||
|
||||
rc = edp_vco_set_rate_7nm(pll, rate);
|
||||
if (rc < 0) {
|
||||
DP_ERR("pll rate %s set failed\n", rate);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pll->vco_cached_rate = rate;
|
||||
DP_DEBUG("pll rate %lu set success\n", rate);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int edp_pll_prepare(struct dp_pll *pll)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!pll) {
|
||||
DP_ERR("invalid input parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = edp_pll_enable_7nm(pll);
|
||||
if (rc < 0)
|
||||
DP_ERR("ndx=%d failed to enable dp pll\n", pll->index);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int edp_pll_unprepare(struct dp_pll *pll)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!pll) {
|
||||
DP_ERR("invalid input parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
edp_pll_disable_7nm(pll);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static unsigned long edp_pll_link_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct dp_pll *pll = NULL;
|
||||
struct dp_pll_vco_clk *pll_link = NULL;
|
||||
unsigned long rate = 0;
|
||||
|
||||
if (!hw) {
|
||||
DP_ERR("invalid input parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pll_link = to_dp_vco_hw(hw);
|
||||
pll = pll_link->priv;
|
||||
|
||||
rate = pll->vco_cached_rate;
|
||||
rate = pll->vco_cached_rate * 100;
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static long edp_pll_link_clk_round(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct dp_pll *pll = NULL;
|
||||
struct dp_pll_vco_clk *pll_link = NULL;
|
||||
|
||||
if (!hw) {
|
||||
DP_ERR("invalid input parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pll_link = to_dp_vco_hw(hw);
|
||||
pll = pll_link->priv;
|
||||
|
||||
rate = pll->vco_cached_rate * 100;
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static unsigned long edp_pll_vco_div_clk_get_rate(struct dp_pll *pll)
|
||||
{
|
||||
if (pll->vco_cached_rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
|
||||
return (pll->vco_cached_rate / 6 * 1000);
|
||||
else if (pll->vco_cached_rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
|
||||
return (pll->vco_cached_rate / 4 * 1000);
|
||||
else
|
||||
return (pll->vco_cached_rate / 2 * 1000);
|
||||
}
|
||||
|
||||
static unsigned long edp_pll_vco_div_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct dp_pll *pll = NULL;
|
||||
struct dp_pll_vco_clk *pll_link = NULL;
|
||||
|
||||
if (!hw) {
|
||||
DP_ERR("invalid input parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pll_link = to_dp_vco_hw(hw);
|
||||
pll = pll_link->priv;
|
||||
|
||||
return edp_pll_vco_div_clk_get_rate(pll);
|
||||
}
|
||||
|
||||
static long edp_pll_vco_div_clk_round(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
return edp_pll_vco_div_clk_recalc_rate(hw, *parent_rate);
|
||||
}
|
||||
|
||||
static int edp_pll_vco_div_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct dp_pll *pll = NULL;
|
||||
struct dp_pll_vco_clk *pll_link = NULL;
|
||||
int rc = 0;
|
||||
|
||||
if (!hw) {
|
||||
DP_ERR("invalid input parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pll_link = to_dp_vco_hw(hw);
|
||||
pll = pll_link->priv;
|
||||
|
||||
if (rate != edp_pll_vco_div_clk_get_rate(pll)) {
|
||||
DP_ERR("unsupported rate %lu failed\n", rate);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rate /= 1000;
|
||||
|
||||
rc = edp_vco_clk_set_div(pll, pll->vco_cached_rate / rate);
|
||||
if (rc < 0) {
|
||||
DP_DEBUG("set rate %lu failed\n", rate);
|
||||
return rc;
|
||||
}
|
||||
|
||||
DP_DEBUG("set rate %lu success\n", rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops edp_pll_link_clk_ops = {
|
||||
.recalc_rate = edp_pll_link_clk_recalc_rate,
|
||||
.round_rate = edp_pll_link_clk_round,
|
||||
};
|
||||
|
||||
static const struct clk_ops edp_pll_vco_div_clk_ops = {
|
||||
.recalc_rate = edp_pll_vco_div_clk_recalc_rate,
|
||||
.round_rate = edp_pll_vco_div_clk_round,
|
||||
.set_rate = edp_pll_vco_div_clk_set_rate,
|
||||
};
|
||||
|
||||
static struct clk_init_data edp_phy_pll_clks[DP_PLL_NUM_CLKS] = {
|
||||
{
|
||||
.name = "edp_phy_pll_link_clk",
|
||||
.ops = &edp_pll_link_clk_ops,
|
||||
},
|
||||
{
|
||||
.name = "edp_phy_pll_vco_div_clk",
|
||||
.ops = &edp_pll_vco_div_clk_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct dp_pll_vco_clk *edp_pll_get_clks(struct dp_pll *pll)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DP_PLL_NUM_CLKS; i++) {
|
||||
snprintf(pll->pll_clks[i].name, DP_PLL_NAME_MAX_SIZE,
|
||||
"%s", edp_phy_pll_clks[i].name);
|
||||
pll->pll_clks[i].init_data.name = pll->pll_clks[i].name;
|
||||
pll->pll_clks[i].init_data.ops = edp_phy_pll_clks[i].ops;
|
||||
pll->pll_clks[i].hw.init = &pll->pll_clks[i].init_data;
|
||||
}
|
||||
|
||||
return pll->pll_clks;
|
||||
}
|
||||
|
||||
static int dp_pll_clock_register_helper(struct dp_pll *pll,
|
||||
struct dp_pll_vco_clk *clks, int num_clks)
|
||||
{
|
||||
int rc = 0, i = 0;
|
||||
struct platform_device *pdev;
|
||||
struct clk *clk;
|
||||
|
||||
if (!pll || !clks) {
|
||||
DP_ERR("input not initialized\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdev = pll->pdev;
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
clks[i].priv = pll;
|
||||
|
||||
clk = clk_register(&pdev->dev, &clks[i].hw);
|
||||
if (IS_ERR(clk)) {
|
||||
DP_ERR("%s registration failed for DP: %d\n",
|
||||
clk_hw_get_name(&clks[i].hw), pll->index);
|
||||
return -EINVAL;
|
||||
}
|
||||
pll->clk_data->clks[i] = clk;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int edp_pll_clock_register_7nm(struct dp_pll *pll)
|
||||
{
|
||||
int rc = 0;
|
||||
struct platform_device *pdev;
|
||||
struct dp_pll_vco_clk *pll_clks;
|
||||
|
||||
if (!pll) {
|
||||
DP_ERR("pll data not initialized\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
pdev = pll->pdev;
|
||||
|
||||
pll->clk_data = kzalloc(sizeof(*pll->clk_data), GFP_KERNEL);
|
||||
if (!pll->clk_data)
|
||||
return -ENOMEM;
|
||||
|
||||
pll->clk_data->clks = kcalloc(DP_PLL_NUM_CLKS, sizeof(struct clk *),
|
||||
GFP_KERNEL);
|
||||
if (!pll->clk_data->clks) {
|
||||
kfree(pll->clk_data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pll->clk_data->clk_num = DP_PLL_NUM_CLKS;
|
||||
pll->pll_db.pll = pll;
|
||||
|
||||
pll->pll_cfg = edp_pll_configure;
|
||||
pll->pll_prepare = edp_pll_prepare;
|
||||
pll->pll_unprepare = edp_pll_unprepare;
|
||||
|
||||
pll_clks = edp_pll_get_clks(pll);
|
||||
|
||||
rc = dp_pll_clock_register_helper(pll, pll_clks, DP_PLL_NUM_CLKS);
|
||||
if (rc) {
|
||||
DP_ERR("Clock register failed rc=%d\n", rc);
|
||||
goto clk_reg_fail;
|
||||
}
|
||||
|
||||
rc = of_clk_add_provider(pdev->dev.of_node,
|
||||
of_clk_src_onecell_get, pll->clk_data);
|
||||
if (rc) {
|
||||
DP_ERR("Clock add provider failed rc=%d\n", rc);
|
||||
goto clk_reg_fail;
|
||||
}
|
||||
|
||||
DP_DEBUG("success\n");
|
||||
return rc;
|
||||
|
||||
clk_reg_fail:
|
||||
dp_pll_clock_unregister_5nm(pll);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -1776,6 +1776,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
|
|||
.set_allowed_mode_switch = NULL,
|
||||
};
|
||||
static const struct sde_connector_ops dp_ops = {
|
||||
.set_info_blob = dp_connector_set_info_blob,
|
||||
.post_init = dp_connector_post_init,
|
||||
.detect = dp_connector_detect,
|
||||
.get_modes = dp_connector_get_modes,
|
||||
|
|
@ -1925,6 +1926,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
|
|||
for (i = 0; i < sde_kms->dp_display_count &&
|
||||
priv->num_encoders < max_encoders; ++i) {
|
||||
int idx;
|
||||
struct dp_display_info dp_info = {0};
|
||||
|
||||
display = sde_kms->dp_displays[i];
|
||||
encoder = NULL;
|
||||
|
|
@ -1936,6 +1938,13 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
|
|||
continue;
|
||||
}
|
||||
|
||||
rc = dp_display_get_info(display, &dp_info);
|
||||
if (rc) {
|
||||
SDE_ERROR("failed to read dp info, %d\n", rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
info.h_tile_instance[0] = dp_info.intf_idx[0];
|
||||
encoder = sde_encoder_init(dev, &info);
|
||||
if (IS_ERR_OR_NULL(encoder)) {
|
||||
SDE_ERROR("dp encoder init failed %d\n", i);
|
||||
|
|
@ -1956,7 +1965,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
|
|||
display,
|
||||
&dp_ops,
|
||||
DRM_CONNECTOR_POLL_HPD,
|
||||
DRM_MODE_CONNECTOR_DisplayPort);
|
||||
info.intf_type);
|
||||
if (connector) {
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
priv->connectors[priv->num_connectors++] = connector;
|
||||
|
|
@ -1969,9 +1978,9 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
|
|||
/* update display cap to MST_MODE for DP MST encoders */
|
||||
info.capabilities |= MSM_DISPLAY_CAP_MST_MODE;
|
||||
|
||||
for (idx = 0; idx < sde_kms->dp_stream_count &&
|
||||
for (idx = 0; idx < dp_info.stream_cnt &&
|
||||
priv->num_encoders < max_encoders; idx++) {
|
||||
info.h_tile_instance[0] = idx;
|
||||
info.h_tile_instance[0] = dp_info.intf_idx[idx];
|
||||
encoder = sde_encoder_init(dev, &info);
|
||||
if (IS_ERR_OR_NULL(encoder)) {
|
||||
SDE_ERROR("dp mst encoder init failed %d\n", i);
|
||||
|
|
|
|||
Loading…
Reference in a new issue