2022-01-13 23:53:55

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v13 0/4] group dp driver related patches into one series

Group below 4 dp driver related patches into one series.

Kuogee Hsieh (4):
drm/msm/dp: do not initialize phy until plugin interrupt received
drm/msm/dp: populate connector of struct dp_panel
drm/msm/dp: add support of tps4 (training pattern 4) for HBR3
drm/msm/dp: stop link training after link training 2 failed

drivers/gpu/drm/msm/dp/dp_catalog.c | 12 ++---
drivers/gpu/drm/msm/dp/dp_catalog.h | 2 +-
drivers/gpu/drm/msm/dp/dp_ctrl.c | 100 ++++++++++++++++--------------------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 8 +--
drivers/gpu/drm/msm/dp/dp_display.c | 90 +++++++++++++++++++++-----------
5 files changed, 116 insertions(+), 96 deletions(-)

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project



2022-01-13 23:53:57

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v13 1/4] drm/msm/dp: do not initialize phy until plugin interrupt received

Current DP drivers have regulators, clocks, irq and phy are grouped
together within a function and executed not in a symmetric manner.
This increase difficulty of code maintenance and limited code scalability.
This patch divides the driver life cycle of operation into four states,
resume (including booting up), dongle plugin, dongle unplugged and suspend.
Regulators, core clocks and irq are grouped together and enabled at resume
(or booting up) so that the DP controller is armed and ready to receive HPD
plugin interrupts. HPD plugin interrupt is generated when a dongle plugs
into DUT (device under test). Once HPD plugin interrupt is received, DP
controller will initialize phy so that dpcd read/write will function and
following link training can be proceeded successfully. DP phy will be
disabled after main link is teared down at end of unplugged HPD interrupt
handle triggered by dongle unplugged out of DUT. Finally regulators, code
clocks and irq are disabled at corresponding suspension.

Changes in V2:
-- removed unnecessary dp_ctrl NULL check
-- removed unnecessary phy init_count and power_count DRM_DEBUG_DP logs
-- remove flip parameter out of dp_ctrl_irq_enable()
-- add fixes tag

Changes in V3:
-- call dp_display_host_phy_init() instead of dp_ctrl_phy_init() at
dp_display_host_init() for eDP

Changes in V4:
-- rewording commit text to match this commit changes

Changes in V5:
-- rebase on top of msm-next branch

Changes in V6:
-- delete flip variable

Changes in V7:
-- dp_ctrl_irq_enable/disabe() merged into dp_ctrl_reset_irq_ctrl()

Changes in V8:
-- add more detail comment regrading dp phy at dp_display_host_init()

Changes in V9:
-- remove set phy_initialized to false when -ECONNRESET detected

Changes in v10:
-- group into one series

Changes in v11:
-- drop drm/msm/dp: dp_link_parse_sink_count() return immediately if aux read

Changes in v12:
-- move dp_display_host_phy_exit() after dp_display_host_deinit()

Changes in v13:
-- do not execute phy_init until plugged_in interrupt for edp, same as DP.

Fixes: 8ede2ecc3e5e ("drm/msm/dp: Add DP compliance tests on Snapdragon Chipsets")
Signed-off-by: Kuogee Hsieh <[email protected]>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 80 +++++++++++++---------------------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 8 ++--
drivers/gpu/drm/msm/dp/dp_display.c | 85 +++++++++++++++++++++++--------------
3 files changed, 89 insertions(+), 84 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index c724cb0..9c80b49 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -1365,60 +1365,44 @@ static int dp_ctrl_enable_stream_clocks(struct dp_ctrl_private *ctrl)
return ret;
}

-int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset)
+void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
+{
+ struct dp_ctrl_private *ctrl;
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ dp_catalog_ctrl_reset(ctrl->catalog);
+
+ if (enable)
+ dp_catalog_ctrl_enable_irq(ctrl->catalog, enable);
+}
+
+void dp_ctrl_phy_init(struct dp_ctrl *dp_ctrl)
{
struct dp_ctrl_private *ctrl;
struct dp_io *dp_io;
struct phy *phy;

- if (!dp_ctrl) {
- DRM_ERROR("Invalid input data\n");
- return -EINVAL;
- }
-
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
dp_io = &ctrl->parser->io;
phy = dp_io->phy;

- ctrl->dp_ctrl.orientation = flip;
-
- if (reset)
- dp_catalog_ctrl_reset(ctrl->catalog);
-
- DRM_DEBUG_DP("flip=%d\n", flip);
dp_catalog_ctrl_phy_reset(ctrl->catalog);
phy_init(phy);
- dp_catalog_ctrl_enable_irq(ctrl->catalog, true);
-
- return 0;
}

-/**
- * dp_ctrl_host_deinit() - Uninitialize DP controller
- * @dp_ctrl: Display Port Driver data
- *
- * Perform required steps to uninitialize DP controller
- * and its resources.
- */
-void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl)
+void dp_ctrl_phy_exit(struct dp_ctrl *dp_ctrl)
{
struct dp_ctrl_private *ctrl;
struct dp_io *dp_io;
struct phy *phy;

- if (!dp_ctrl) {
- DRM_ERROR("Invalid input data\n");
- return;
- }
-
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
dp_io = &ctrl->parser->io;
phy = dp_io->phy;

- dp_catalog_ctrl_enable_irq(ctrl->catalog, false);
+ dp_catalog_ctrl_phy_reset(ctrl->catalog);
phy_exit(phy);
-
- DRM_DEBUG_DP("Host deinitialized successfully\n");
}

static bool dp_ctrl_use_fixed_nvid(struct dp_ctrl_private *ctrl)
@@ -1488,7 +1472,10 @@ static int dp_ctrl_deinitialize_mainlink(struct dp_ctrl_private *ctrl)
}

phy_power_off(phy);
+
+ /* aux channel down, reinit phy */
phy_exit(phy);
+ phy_init(phy);

return 0;
}
@@ -1893,8 +1880,14 @@ int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl)
return ret;
}

+ DRM_DEBUG_DP("Before, phy=%x init_count=%d power_on=%d\n",
+ (u32)(uintptr_t)phy, phy->init_count, phy->power_count);
+
phy_power_off(phy);

+ DRM_DEBUG_DP("After, phy=%x init_count=%d power_on=%d\n",
+ (u32)(uintptr_t)phy, phy->init_count, phy->power_count);
+
/* aux channel down, reinit phy */
phy_exit(phy);
phy_init(phy);
@@ -1903,23 +1896,6 @@ int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl)
return ret;
}

-void dp_ctrl_off_phy(struct dp_ctrl *dp_ctrl)
-{
- struct dp_ctrl_private *ctrl;
- struct dp_io *dp_io;
- struct phy *phy;
-
- ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
- dp_io = &ctrl->parser->io;
- phy = dp_io->phy;
-
- dp_catalog_ctrl_reset(ctrl->catalog);
-
- phy_exit(phy);
-
- DRM_DEBUG_DP("DP off phy done\n");
-}
-
int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
{
struct dp_ctrl_private *ctrl;
@@ -1947,10 +1923,14 @@ int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret);
}

+ DRM_DEBUG_DP("Before, phy=%x init_count=%d power_on=%d\n",
+ (u32)(uintptr_t)phy, phy->init_count, phy->power_count);
+
phy_power_off(phy);
- phy_exit(phy);

- DRM_DEBUG_DP("DP off done\n");
+ DRM_DEBUG_DP("After, phy=%x init_count=%d power_on=%d\n",
+ (u32)(uintptr_t)phy, phy->init_count, phy->power_count);
+
return ret;
}

diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 2363a2d..2433edb 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -19,12 +19,9 @@ struct dp_ctrl {
u32 pixel_rate;
};

-int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset);
-void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl);
int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl);
int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl);
int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl);
-void dp_ctrl_off_phy(struct dp_ctrl *dp_ctrl);
int dp_ctrl_off(struct dp_ctrl *dp_ctrl);
void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl);
void dp_ctrl_isr(struct dp_ctrl *dp_ctrl);
@@ -34,4 +31,9 @@ struct dp_ctrl *dp_ctrl_get(struct device *dev, struct dp_link *link,
struct dp_power *power, struct dp_catalog *catalog,
struct dp_parser *parser);

+void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable);
+void dp_ctrl_phy_init(struct dp_ctrl *dp_ctrl);
+void dp_ctrl_phy_exit(struct dp_ctrl *dp_ctrl);
+void dp_ctrl_irq_phy_exit(struct dp_ctrl *dp_ctrl);
+
#endif /* _DP_CTRL_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 7cc4d21..b3c5404 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -83,6 +83,7 @@ struct dp_display_private {

/* state variables */
bool core_initialized;
+ bool phy_initialized;
bool hpd_irq_on;
bool audio_supported;

@@ -372,21 +373,38 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
return rc;
}

-static void dp_display_host_init(struct dp_display_private *dp, int reset)
+static void dp_display_host_phy_init(struct dp_display_private *dp)
{
- bool flip = false;
+ DRM_DEBUG_DP("core_init=%d phy_init=%d\n",
+ dp->core_initialized, dp->phy_initialized);

+ if (!dp->phy_initialized) {
+ dp_ctrl_phy_init(dp->ctrl);
+ dp->phy_initialized = true;
+ }
+}
+
+static void dp_display_host_phy_exit(struct dp_display_private *dp)
+{
+ DRM_DEBUG_DP("core_init=%d phy_init=%d\n",
+ dp->core_initialized, dp->phy_initialized);
+
+ if (dp->phy_initialized) {
+ dp_ctrl_phy_exit(dp->ctrl);
+ dp->phy_initialized = false;
+ }
+}
+
+static void dp_display_host_init(struct dp_display_private *dp)
+{
DRM_DEBUG_DP("core_initialized=%d\n", dp->core_initialized);
if (dp->core_initialized) {
DRM_DEBUG_DP("DP core already initialized\n");
return;
}

- if (dp->usbpd->orientation == ORIENTATION_CC2)
- flip = true;
-
- dp_power_init(dp->power, flip);
- dp_ctrl_host_init(dp->ctrl, flip, reset);
+ dp_power_init(dp->power, false);
+ dp_ctrl_reset_irq_ctrl(dp->ctrl, true);
dp_aux_init(dp->aux);
dp->core_initialized = true;
}
@@ -398,7 +416,7 @@ static void dp_display_host_deinit(struct dp_display_private *dp)
return;
}

- dp_ctrl_host_deinit(dp->ctrl);
+ dp_ctrl_reset_irq_ctrl(dp->ctrl, false);
dp_aux_deinit(dp->aux);
dp_power_deinit(dp->power);

@@ -409,7 +427,7 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
{
struct dp_display_private *dp = dev_get_dp_display_private(dev);

- dp_display_host_init(dp, false);
+ dp_display_host_phy_init(dp);

return dp_display_process_hpd_high(dp);
}
@@ -530,11 +548,6 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
ret = dp_display_usbpd_configure_cb(&dp->pdev->dev);
if (ret) { /* link train failed */
dp->hpd_state = ST_DISCONNECTED;
-
- if (ret == -ECONNRESET) { /* cable unplugged */
- dp->core_initialized = false;
- }
-
} else {
/* start sentinel checking in case of missing uevent */
dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout);
@@ -604,8 +617,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
if (state == ST_DISCONNECTED) {
/* triggered by irq_hdp with sink_count = 0 */
if (dp->link->sink_count == 0) {
- dp_ctrl_off_phy(dp->ctrl);
- dp->core_initialized = false;
+ dp_display_host_phy_exit(dp);
}
mutex_unlock(&dp->event_mutex);
return 0;
@@ -696,12 +708,9 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
* dp core (ahb/aux clks) must be initialized before
* irq_hpd be handled
*/
- if (dp->core_initialized) {
+ if (dp->core_initialized)
ret = dp_display_usbpd_attention_cb(&dp->pdev->dev);
- if (ret == -ECONNRESET) { /* cable unplugged */
- dp->core_initialized = false;
- }
- }
+
DRM_DEBUG_DP("hpd_state=%d\n", state);

mutex_unlock(&dp->event_mutex);
@@ -892,12 +901,19 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)

dp_display->audio_enabled = false;

- /* triggered by irq_hpd with sink_count = 0 */
if (dp->link->sink_count == 0) {
+ /*
+ * irq_hpd with sink_count = 0
+ * hdmi unplugged out of dongle
+ */
dp_ctrl_off_link_stream(dp->ctrl);
} else {
+ /*
+ * unplugged interrupt
+ * dongle unplugged out of DUT
+ */
dp_ctrl_off(dp->ctrl);
- dp->core_initialized = false;
+ dp_display_host_phy_exit(dp);
}

dp_display->power_on = false;
@@ -1027,7 +1043,7 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp)
static void dp_display_config_hpd(struct dp_display_private *dp)
{

- dp_display_host_init(dp, true);
+ dp_display_host_init(dp);
dp_catalog_ctrl_hpd_config(dp->catalog);

/* Enable interrupt first time
@@ -1306,20 +1322,23 @@ static int dp_pm_resume(struct device *dev)
dp->hpd_state = ST_DISCONNECTED;

/* turn on dp ctrl/phy */
- dp_display_host_init(dp, true);
+ dp_display_host_init(dp);

dp_catalog_ctrl_hpd_config(dp->catalog);

- /*
- * set sink to normal operation mode -- D0
- * before dpcd read
- */
- dp_link_psm_config(dp->link, &dp->panel->link_info, false);

if (dp_catalog_link_is_connected(dp->catalog)) {
+ /*
+ * set sink to normal operation mode -- D0
+ * before dpcd read
+ */
+ dp_display_host_phy_init(dp);
+ dp_link_psm_config(dp->link, &dp->panel->link_info, false);
sink_count = drm_dp_read_sink_count(dp->aux);
if (sink_count < 0)
sink_count = 0;
+
+ dp_display_host_phy_exit(dp);
}

dp->link->sink_count = sink_count;
@@ -1363,7 +1382,11 @@ static int dp_pm_suspend(struct device *dev)
if (dp_power_clk_status(dp->power, DP_CTRL_PM))
dp_ctrl_off_link_stream(dp->ctrl);

+ dp_display_host_phy_exit(dp);
+
dp_display_host_deinit(dp);
+ } else {
+ dp_display_host_phy_exit(dp);
}

dp->hpd_state = ST_SUSPENDED;
@@ -1535,7 +1558,7 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
state = dp_display->hpd_state;

if (state == ST_DISPLAY_OFF)
- dp_display_host_init(dp_display, true);
+ dp_display_host_phy_init(dp_display);

dp_display_enable(dp_display, 0);

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2022-01-13 23:54:04

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v13 2/4] drm/msm/dp: populate connector of struct dp_panel

DP CTS test case 4.2.2.6 has valid edid with bad checksum on purpose
and expect DP source return correct checksum. During drm edid read,
correct edid checksum is calculated and stored at
connector::real_edid_checksum.

The problem is struct dp_panel::connector never be assigned, instead the
connector is stored in struct msm_dp::connector. When we run compliance
testing test case 4.2.2.6 dp_panel_handle_sink_request() won't have a valid
edid set in struct dp_panel::edid so we'll try to use the connectors
real_edid_checksum and hit a NULL pointer dereference error because the
connector pointer is never assigned.

Changes in V2:
-- populate panel connector at msm_dp_modeset_init() instead of at dp_panel_read_sink_caps()

Changes in V3:
-- remove unhelpful kernel crash trace commit text
-- remove renaming dp_display parameter to dp

Changes in V4:
-- add more details to commit text

Changes in v10:
-- group into one series

Changes in v11:
-- drop drm/msm/dp: dp_link_parse_sink_count() return immediately if aux read

Fixes: 7948fe12d47 ("drm/msm/dp: return correct edid checksum after corrupted edid checksum read")
Signee-off-by: Kuogee Hsieh <[email protected]>

Reviewed-by: Bjorn Andersson <[email protected]>
Reviewed-by: Stephen Boyd <[email protected]>
---
drivers/gpu/drm/msm/dp/dp_display.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index b3c5404..52dcb56 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1483,6 +1483,7 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
struct drm_encoder *encoder)
{
struct msm_drm_private *priv;
+ struct dp_display_private *dp_priv;
int ret;

if (WARN_ON(!encoder) || WARN_ON(!dp_display) || WARN_ON(!dev))
@@ -1491,6 +1492,8 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
priv = dev->dev_private;
dp_display->drm_dev = dev;

+ dp_priv = container_of(dp_display, struct dp_display_private, dp_display);
+
ret = dp_display_request_irq(dp_display);
if (ret) {
DRM_ERROR("request_irq failed, ret=%d\n", ret);
@@ -1508,6 +1511,8 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
return ret;
}

+ dp_priv->panel->connector = dp_display->connector;
+
priv->connectors[priv->num_connectors++] = dp_display->connector;

dp_display->bridge = msm_dp_bridge_init(dp_display, dev, encoder);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2022-01-13 23:54:04

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v13 3/4] drm/msm/dp: add support of tps4 (training pattern 4) for HBR3

From: Kuogee Hsieh <[email protected]>

Some DP sinkers prefer to use tps4 instead of tps3 during training #2.
This patch will use tps4 to perform link training #2 if sinker's DPCD
supports it.

Changes in V2:
-- replace dp_catalog_ctrl_set_pattern() with dp_catalog_ctrl_set_pattern_state_bit()

Changes in V3:
-- change state_ctrl_bits type to u32 and pattern type to u8

Changes in V4:
-- align } else if { and } else {

Changes in v10:
-- group into one series

Changes in v11:
-- drop drm/msm/dp: dp_link_parse_sink_count() return immediately if aux read

Signed-off-by: Kuogee Hsieh <[email protected]>

Reviewed-by: Stephen Boyd <[email protected]>
---
drivers/gpu/drm/msm/dp/dp_catalog.c | 12 ++++++------
drivers/gpu/drm/msm/dp/dp_catalog.h | 2 +-
drivers/gpu/drm/msm/dp/dp_ctrl.c | 17 ++++++++++++-----
3 files changed, 19 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 6ae9b29..64f0b26 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -456,19 +456,19 @@ void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog,
dp_write_p0(catalog, MMSS_DP_DSC_DTO, 0x0);
}

-int dp_catalog_ctrl_set_pattern(struct dp_catalog *dp_catalog,
- u32 pattern)
+int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog,
+ u32 state_bit)
{
int bit, ret;
u32 data;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);

- bit = BIT(pattern - 1);
- DRM_DEBUG_DP("hw: bit=%d train=%d\n", bit, pattern);
+ bit = BIT(state_bit - 1);
+ DRM_DEBUG_DP("hw: bit=%d train=%d\n", bit, state_bit);
dp_catalog_ctrl_state_ctrl(dp_catalog, bit);

- bit = BIT(pattern - 1) << DP_MAINLINK_READY_LINK_TRAINING_SHIFT;
+ bit = BIT(state_bit - 1) << DP_MAINLINK_READY_LINK_TRAINING_SHIFT;

/* Poll for mainlink ready status */
ret = readx_poll_timeout(readl, catalog->io->dp_controller.link.base +
@@ -476,7 +476,7 @@ int dp_catalog_ctrl_set_pattern(struct dp_catalog *dp_catalog,
data, data & bit,
POLLING_SLEEP_US, POLLING_TIMEOUT_US);
if (ret < 0) {
- DRM_ERROR("set pattern for link_train=%d failed\n", pattern);
+ DRM_ERROR("set state_bit for link_train=%d failed\n", state_bit);
return ret;
}
return 0;
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 6965afa..7dea101 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -94,7 +94,7 @@ void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog, bool enable);
void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog, u32 cc, u32 tb);
void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, u32 rate,
u32 stream_rate_khz, bool fixed_nvid);
-int dp_catalog_ctrl_set_pattern(struct dp_catalog *dp_catalog, u32 pattern);
+int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog, u32 pattern);
void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog);
bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 9c80b49..f98df93 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -1083,7 +1083,7 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl,

*training_step = DP_TRAINING_1;

- ret = dp_catalog_ctrl_set_pattern(ctrl->catalog, DP_TRAINING_PATTERN_1);
+ ret = dp_catalog_ctrl_set_pattern_state_bit(ctrl->catalog, 1);
if (ret)
return ret;
dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 |
@@ -1181,7 +1181,8 @@ static int dp_ctrl_link_train_2(struct dp_ctrl_private *ctrl,
int *training_step)
{
int tries = 0, ret = 0;
- char pattern;
+ u8 pattern;
+ u32 state_ctrl_bit;
int const maximum_retries = 5;
u8 link_status[DP_LINK_STATUS_SIZE];

@@ -1189,12 +1190,18 @@ static int dp_ctrl_link_train_2(struct dp_ctrl_private *ctrl,

*training_step = DP_TRAINING_2;

- if (drm_dp_tps3_supported(ctrl->panel->dpcd))
+ if (drm_dp_tps4_supported(ctrl->panel->dpcd)) {
+ pattern = DP_TRAINING_PATTERN_4;
+ state_ctrl_bit = 4;
+ } else if (drm_dp_tps3_supported(ctrl->panel->dpcd)) {
pattern = DP_TRAINING_PATTERN_3;
- else
+ state_ctrl_bit = 3;
+ } else {
pattern = DP_TRAINING_PATTERN_2;
+ state_ctrl_bit = 2;
+ }

- ret = dp_catalog_ctrl_set_pattern(ctrl->catalog, pattern);
+ ret = dp_catalog_ctrl_set_pattern_state_bit(ctrl->catalog, state_ctrl_bit);
if (ret)
return ret;

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2022-01-13 23:54:07

by Kuogee Hsieh

[permalink] [raw]
Subject: [PATCH v13 4/4] drm/msm/dp: stop link training after link training 2 failed

Each DP link training contains link training 1 followed by link
training 2. There is maximum of 5 retries of DP link training
before declared link training failed. It is required to stop link
training at end of link training 2 if it is failed so that next
link training 1 can start freshly. This patch fixes link compliance
test case 4.3.1.13 (Source Device Link Training EQ Fallback Test).

Changes in v10:
-- group into one series

Changes in v11:
-- drop drm/msm/dp: dp_link_parse_sink_count() return immediately if aux read

Fixes: 2e0adc765d88 ("drm/msm/dp: do not end dp link training until video is ready")
Signed-off-by: Kuogee Hsieh <[email protected]>
Reviewed-by: Stephen Boyd <[email protected]>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index f98df93..245e1b9 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -1755,6 +1755,9 @@ int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl)
/* end with failure */
break; /* lane == 1 already */
}
+
+ /* stop link training before start re training */
+ dp_ctrl_clear_training_pattern(ctrl);
}
}

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2022-01-14 02:43:00

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v13 1/4] drm/msm/dp: do not initialize phy until plugin interrupt received

Quoting Kuogee Hsieh (2022-01-13 15:53:36)
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 7cc4d21..b3c5404 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -83,6 +83,7 @@ struct dp_display_private {
>
> /* state variables */
> bool core_initialized;
> + bool phy_initialized;
> bool hpd_irq_on;
> bool audio_supported;
>
> @@ -372,21 +373,38 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
> return rc;
> }
>
> -static void dp_display_host_init(struct dp_display_private *dp, int reset)
> +static void dp_display_host_phy_init(struct dp_display_private *dp)
> {
> - bool flip = false;
> + DRM_DEBUG_DP("core_init=%d phy_init=%d\n",
> + dp->core_initialized, dp->phy_initialized);
>
> + if (!dp->phy_initialized) {
> + dp_ctrl_phy_init(dp->ctrl);
> + dp->phy_initialized = true;
> + }
> +}
> +
> +static void dp_display_host_phy_exit(struct dp_display_private *dp)
> +{
> + DRM_DEBUG_DP("core_init=%d phy_init=%d\n",
> + dp->core_initialized, dp->phy_initialized);
> +
> + if (dp->phy_initialized) {
> + dp_ctrl_phy_exit(dp->ctrl);
> + dp->phy_initialized = false;
> + }
> +}
> +
> +static void dp_display_host_init(struct dp_display_private *dp)
> +{
> DRM_DEBUG_DP("core_initialized=%d\n", dp->core_initialized);
> if (dp->core_initialized) {

When is this true? From what I see dp_display_host_init() is only called
from two places: resume where core_initialized has been set to false
during suspend or from dp_display_config_hpd() kicked by the kthread
where core_initialized is also false.

Also, I see that dp_display_host_deinit() is only called from suspend
now, so 'core_initialized' is almost always true, except for on the
resume path and before the kthread is started and in the case that the
driver probes but can't start the kthread for some reason (is that
real?).

> DRM_DEBUG_DP("DP core already initialized\n");
> return;
> }
>
> - if (dp->usbpd->orientation == ORIENTATION_CC2)
> - flip = true;
> -
> - dp_power_init(dp->power, flip);
> - dp_ctrl_host_init(dp->ctrl, flip, reset);
> + dp_power_init(dp->power, false);
> + dp_ctrl_reset_irq_ctrl(dp->ctrl, true);
> dp_aux_init(dp->aux);
> dp->core_initialized = true;
> }
> @@ -892,12 +901,19 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)
>
> dp_display->audio_enabled = false;
>
> - /* triggered by irq_hpd with sink_count = 0 */
> if (dp->link->sink_count == 0) {
> + /*
> + * irq_hpd with sink_count = 0
> + * hdmi unplugged out of dongle
> + */
> dp_ctrl_off_link_stream(dp->ctrl);
> } else {
> + /*
> + * unplugged interrupt
> + * dongle unplugged out of DUT
> + */
> dp_ctrl_off(dp->ctrl);
> - dp->core_initialized = false;
> + dp_display_host_phy_exit(dp);
> }
>
> dp_display->power_on = false;
> @@ -1027,7 +1043,7 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp)
> static void dp_display_config_hpd(struct dp_display_private *dp)
> {
>
> - dp_display_host_init(dp, true);
> + dp_display_host_init(dp);
> dp_catalog_ctrl_hpd_config(dp->catalog);
>
> /* Enable interrupt first time
> @@ -1306,20 +1322,23 @@ static int dp_pm_resume(struct device *dev)
> dp->hpd_state = ST_DISCONNECTED;
>
> /* turn on dp ctrl/phy */
> - dp_display_host_init(dp, true);
> + dp_display_host_init(dp);
>
> dp_catalog_ctrl_hpd_config(dp->catalog);
>
> - /*
> - * set sink to normal operation mode -- D0
> - * before dpcd read
> - */
> - dp_link_psm_config(dp->link, &dp->panel->link_info, false);
>
> if (dp_catalog_link_is_connected(dp->catalog)) {
> + /*
> + * set sink to normal operation mode -- D0
> + * before dpcd read
> + */
> + dp_display_host_phy_init(dp);
> + dp_link_psm_config(dp->link, &dp->panel->link_info, false);
> sink_count = drm_dp_read_sink_count(dp->aux);
> if (sink_count < 0)
> sink_count = 0;
> +
> + dp_display_host_phy_exit(dp);
> }
>
> dp->link->sink_count = sink_count;
> @@ -1363,7 +1382,11 @@ static int dp_pm_suspend(struct device *dev)
> if (dp_power_clk_status(dp->power, DP_CTRL_PM))
> dp_ctrl_off_link_stream(dp->ctrl);
>
> + dp_display_host_phy_exit(dp);
> +

Why is there a newline here?

> dp_display_host_deinit(dp);
> + } else {
> + dp_display_host_phy_exit(dp);
> }
>
> dp->hpd_state = ST_SUSPENDED;

There's a dp->core_initialized = false right here but it's not in the
diff window. It's redundant now because the hunk above is basically

if (dp->core_initialized == true) {
...
dp_display_host_phy_exit(dp);
dp_display_host_deinit(dp);
} else {
dp_display_host_phy_exit(dp);
}

dp->hpd_state = ST_SUSPENDED;

and dp_display_host_deinit() sets core_initialized to false, thus
core_initialized will be false here already. Can you remove the
duplicate assignment?

> @@ -1535,7 +1558,7 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
> state = dp_display->hpd_state;
>
> if (state == ST_DISPLAY_OFF)
> - dp_display_host_init(dp_display, true);
> + dp_display_host_phy_init(dp_display);
>
> dp_display_enable(dp_display, 0);
>

2022-01-14 22:55:18

by Kuogee Hsieh

[permalink] [raw]
Subject: Re: [PATCH v13 1/4] drm/msm/dp: do not initialize phy until plugin interrupt received


On 1/13/2022 6:42 PM, Stephen Boyd wrote:
> Quoting Kuogee Hsieh (2022-01-13 15:53:36)
>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
>> index 7cc4d21..b3c5404 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -83,6 +83,7 @@ struct dp_display_private {
>>
>> /* state variables */
>> bool core_initialized;
>> + bool phy_initialized;
>> bool hpd_irq_on;
>> bool audio_supported;
>>
>> @@ -372,21 +373,38 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
>> return rc;
>> }
>>
>> -static void dp_display_host_init(struct dp_display_private *dp, int reset)
>> +static void dp_display_host_phy_init(struct dp_display_private *dp)
>> {
>> - bool flip = false;
>> + DRM_DEBUG_DP("core_init=%d phy_init=%d\n",
>> + dp->core_initialized, dp->phy_initialized);
>>
>> + if (!dp->phy_initialized) {
>> + dp_ctrl_phy_init(dp->ctrl);
>> + dp->phy_initialized = true;
>> + }
>> +}
>> +
>> +static void dp_display_host_phy_exit(struct dp_display_private *dp)
>> +{
>> + DRM_DEBUG_DP("core_init=%d phy_init=%d\n",
>> + dp->core_initialized, dp->phy_initialized);
>> +
>> + if (dp->phy_initialized) {
>> + dp_ctrl_phy_exit(dp->ctrl);
>> + dp->phy_initialized = false;
>> + }
>> +}
>> +
>> +static void dp_display_host_init(struct dp_display_private *dp)
>> +{
>> DRM_DEBUG_DP("core_initialized=%d\n", dp->core_initialized);
>> if (dp->core_initialized) {
> When is this true? From what I see dp_display_host_init() is only called
> from two places: resume where core_initialized has been set to false
> during suspend or from dp_display_config_hpd() kicked by the kthread
> where core_initialized is also false.
>
> Also, I see that dp_display_host_deinit() is only called from suspend
> now, so 'core_initialized' is almost always true, except for on the
> resume path and before the kthread is started and in the case that the
> driver probes but can't start the kthread for some reason (is that
> real?).

Yes, that true.

The purposeĀ  of dp_display_host_init() is to configure both dp
controller and hpd controller to be ready to receive

HPD plugged-in interrupts and at plugged-in handler enable dp phy.
Therefore core_initialized is set to true at both booting up and resume
and false at suspend.

>> DRM_DEBUG_DP("DP core already initialized\n");
>> return;
>> }
>>
>> - if (dp->usbpd->orientation == ORIENTATION_CC2)
>> - flip = true;
>> -
>> - dp_power_init(dp->power, flip);
>> - dp_ctrl_host_init(dp->ctrl, flip, reset);
>> + dp_power_init(dp->power, false);
>> + dp_ctrl_reset_irq_ctrl(dp->ctrl, true);
>> dp_aux_init(dp->aux);
>> dp->core_initialized = true;
>> }
>> @@ -892,12 +901,19 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)
>>
>> dp_display->audio_enabled = false;
>>
>> - /* triggered by irq_hpd with sink_count = 0 */
>> if (dp->link->sink_count == 0) {
>> + /*
>> + * irq_hpd with sink_count = 0
>> + * hdmi unplugged out of dongle
>> + */
>> dp_ctrl_off_link_stream(dp->ctrl);
>> } else {
>> + /*
>> + * unplugged interrupt
>> + * dongle unplugged out of DUT
>> + */
>> dp_ctrl_off(dp->ctrl);
>> - dp->core_initialized = false;
>> + dp_display_host_phy_exit(dp);
>> }
>>
>> dp_display->power_on = false;
>> @@ -1027,7 +1043,7 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp)
>> static void dp_display_config_hpd(struct dp_display_private *dp)
>> {
>>
>> - dp_display_host_init(dp, true);
>> + dp_display_host_init(dp);
>> dp_catalog_ctrl_hpd_config(dp->catalog);
>>
>> /* Enable interrupt first time
>> @@ -1306,20 +1322,23 @@ static int dp_pm_resume(struct device *dev)
>> dp->hpd_state = ST_DISCONNECTED;
>>
>> /* turn on dp ctrl/phy */
>> - dp_display_host_init(dp, true);
>> + dp_display_host_init(dp);
>>
>> dp_catalog_ctrl_hpd_config(dp->catalog);
>>
>> - /*
>> - * set sink to normal operation mode -- D0
>> - * before dpcd read
>> - */
>> - dp_link_psm_config(dp->link, &dp->panel->link_info, false);
>>
>> if (dp_catalog_link_is_connected(dp->catalog)) {
>> + /*
>> + * set sink to normal operation mode -- D0
>> + * before dpcd read
>> + */
>> + dp_display_host_phy_init(dp);
>> + dp_link_psm_config(dp->link, &dp->panel->link_info, false);
>> sink_count = drm_dp_read_sink_count(dp->aux);
>> if (sink_count < 0)
>> sink_count = 0;
>> +
>> + dp_display_host_phy_exit(dp);
>> }
>>
>> dp->link->sink_count = sink_count;
>> @@ -1363,7 +1382,11 @@ static int dp_pm_suspend(struct device *dev)
>> if (dp_power_clk_status(dp->power, DP_CTRL_PM))
>> dp_ctrl_off_link_stream(dp->ctrl);
>>
>> + dp_display_host_phy_exit(dp);
>> +
> Why is there a newline here?
>
>> dp_display_host_deinit(dp);
>> + } else {
>> + dp_display_host_phy_exit(dp);
>> }
>>
>> dp->hpd_state = ST_SUSPENDED;
> There's a dp->core_initialized = false right here but it's not in the
> diff window. It's redundant now because the hunk above is basically
>
> if (dp->core_initialized == true) {
> ...
> dp_display_host_phy_exit(dp);
> dp_display_host_deinit(dp);
> } else {
> dp_display_host_phy_exit(dp);
> }
>
> dp->hpd_state = ST_SUSPENDED;
>
> and dp_display_host_deinit() sets core_initialized to false, thus
> core_initialized will be false here already. Can you remove the
> duplicate assignment?
yes,
>> @@ -1535,7 +1558,7 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
>> state = dp_display->hpd_state;
>>
>> if (state == ST_DISPLAY_OFF)
>> - dp_display_host_init(dp_display, true);
>> + dp_display_host_phy_init(dp_display);
>>
>> dp_display_enable(dp_display, 0);
>>