From 08c81e5cfef4fc51dd851277692f41028bf211d8 Mon Sep 17 00:00:00 2001 From: Fuad Hossain Date: Tue, 11 Dec 2018 11:35:23 -0500 Subject: [PATCH 01/28] drm/msm/dp: fix mst mode switch issue During a mode switch operation, the connector is detached from the mst bridge, but not reattached via the atomic_best_encoder function. Cache the old connector info in the mst bridge, and reconnect it in the bridge_mode_set function. Clear the old connector info in the bridge_pre_enable to disallow new connections from using the old configuration. Change-Id: I8524be6843c4b0a9f0ba02a75aedbc51dd9e6377 Signed-off-by: Fuad Hossain Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_mst_drm.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c index f528485effb9..cf5e6067f877 100644 --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c @@ -93,7 +93,9 @@ struct dp_mst_bridge { struct drm_display_mode drm_mode; struct dp_display_mode dp_mode; struct drm_connector *connector; + struct drm_connector *old_connector; void *dp_panel; + void *old_dp_panel; int vcpi; int pbn; @@ -682,6 +684,9 @@ static void dp_mst_bridge_pre_enable(struct drm_bridge *drm_bridge) bridge = to_dp_mst_bridge(drm_bridge); dp = bridge->display; + bridge->old_connector = NULL; + bridge->old_dp_panel = NULL; + if (!bridge->connector) { pr_err("Invalid connector\n"); return; @@ -832,6 +837,10 @@ static void dp_mst_bridge_post_disable(struct drm_bridge *drm_bridge) /* maintain the connector to encoder link during suspend/resume */ if (mst->state != PM_SUSPEND) { /* Disconnect the connector and panel info from bridge */ + mst->mst_bridge[bridge->id].old_connector = + mst->mst_bridge[bridge->id].connector; + mst->mst_bridge[bridge->id].old_dp_panel = + mst->mst_bridge[bridge->id].dp_panel; mst->mst_bridge[bridge->id].connector = NULL; mst->mst_bridge[bridge->id].dp_panel = NULL; mst->mst_bridge[bridge->id].encoder_active_sts = false; @@ -856,13 +865,21 @@ static void dp_mst_bridge_mode_set(struct drm_bridge *drm_bridge, bridge = to_dp_mst_bridge(drm_bridge); if (!bridge->connector) { - pr_err("Invalid connector\n"); - return; + if (!bridge->old_connector) { + pr_err("Invalid connector\n"); + return; + } + bridge->connector = bridge->old_connector; + bridge->old_connector = NULL; } if (!bridge->dp_panel) { - pr_err("Invalid dp_panel\n"); - return; + if (!bridge->old_dp_panel) { + pr_err("Invalid dp_panel\n"); + return; + } + bridge->dp_panel = bridge->old_dp_panel; + bridge->old_dp_panel = NULL; } dp = bridge->display; From 14f2283c6d7cf48173f0f6e9c48e6649aa5051cf Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Fri, 21 Dec 2018 14:38:26 -0800 Subject: [PATCH 02/28] drm/msm/dp: handle hdcp 2.2 polling in link work When polling for a message from sink during hdcp 2.2 authentication process, handle the sink response in link worker thread only. The link worker thread is triggered by CP_IRQ set by the sink. This avoids possible race condition of handling the sink request which may result in reading same message twice from sink. Change-Id: If3ee31ec84d3f47195bcba6917c0b76ec89ae535 Signed-off-by: Ajay Singh Parmar Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_hdcp2p2.c | 35 ++++++++++------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c index 3dd0fa1d655d..c3e5b4f18e1b 100644 --- a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c +++ b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c @@ -51,7 +51,6 @@ struct dp_hdcp2p2_ctrl { u8 rx_status; char abort_mask; - bool cp_irq_done; bool polling; }; @@ -174,6 +173,7 @@ static int dp_hdcp2p2_wakeup(struct hdcp_transport_wakeup_data *data) if (dp_hdcp2p2_copy_buf(ctrl, data)) goto exit; + ctrl->polling = false; switch (data->cmd) { case HDCP_TRANSPORT_CMD_STATUS_SUCCESS: atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATED); @@ -493,26 +493,12 @@ static void dp_hdcp2p2_recv_msg(struct dp_hdcp2p2_ctrl *ctrl) return; } - if (ctrl->rx_status) { - if (!ctrl->cp_irq_done) { - pr_debug("waiting for CP_IRQ\n"); - ctrl->polling = true; - return; - } - - if (ctrl->rx_status & ctrl->sink_rx_status) { - ctrl->cp_irq_done = false; - ctrl->sink_rx_status = 0; - ctrl->rx_status = 0; - } - } - dp_hdcp2p2_get_msg_from_sink(ctrl); } static void dp_hdcp2p2_link_check(struct dp_hdcp2p2_ctrl *ctrl) { - int rc = 0; + int rc = 0, retries = 10; struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID}; if (!ctrl) { @@ -545,6 +531,11 @@ static void dp_hdcp2p2_link_check(struct dp_hdcp2p2_ctrl *ctrl) goto exit; } + /* wait for polling to start till spec allowed timeout */ + while (!ctrl->polling && retries--) + msleep(20); + + /* check if sink has made a message available */ if (ctrl->polling && (ctrl->sink_rx_status & ctrl->rx_status)) { ctrl->sink_rx_status = 0; ctrl->rx_status = 0; @@ -552,8 +543,6 @@ static void dp_hdcp2p2_link_check(struct dp_hdcp2p2_ctrl *ctrl) dp_hdcp2p2_get_msg_from_sink(ctrl); ctrl->polling = false; - } else { - ctrl->cp_irq_done = true; } exit: if (rc) @@ -769,7 +758,10 @@ static int dp_hdcp2p2_main(void *data) dp_hdcp2p2_send_msg(ctrl); break; case HDCP_TRANSPORT_CMD_RECV_MESSAGE: - dp_hdcp2p2_recv_msg(ctrl); + if (ctrl->rx_status) + ctrl->polling = true; + else + dp_hdcp2p2_recv_msg(ctrl); break; case HDCP_TRANSPORT_CMD_STATUS_SUCCESS: dp_hdcp2p2_send_auth_status(ctrl); @@ -779,10 +771,7 @@ static int dp_hdcp2p2_main(void *data) dp_hdcp2p2_send_auth_status(ctrl); break; case HDCP_TRANSPORT_CMD_LINK_POLL: - if (ctrl->cp_irq_done) - dp_hdcp2p2_recv_msg(ctrl); - else - ctrl->polling = true; + ctrl->polling = true; break; case HDCP_TRANSPORT_CMD_LINK_CHECK: dp_hdcp2p2_link_check(ctrl); From 1467906da4db737d08bc7cd3564212a7895ee965 Mon Sep 17 00:00:00 2001 From: Govinda Rajulu Chenna Date: Sat, 29 Dec 2018 12:18:03 -0500 Subject: [PATCH 03/28] drm/msm/dp: cleanup log messages in mst layer Introduce a new macro definition for critical log messages and use this new macro for topology state related message logs. Change-Id: I68c8c981ae781c490446449cd0ef80f97ae90adf Signed-off-by: Govinda Rajulu Chenna Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_mst_drm.c | 63 +++++++++++++---------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c index cf5e6067f877..be2ba0e09951 100644 --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c @@ -21,6 +21,7 @@ #include "dp_drm.h" #define DP_MST_DEBUG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) +#define DP_MST_INFO_LOG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) #define MAX_DP_MST_STREAMS 2 #define MAX_DP_MST_DRM_ENCODERS 2 @@ -674,8 +675,6 @@ static void dp_mst_bridge_pre_enable(struct drm_bridge *drm_bridge) struct dp_display *dp; struct dp_mst_private *mst; - DP_MST_DEBUG("enter\n"); - if (!drm_bridge) { pr_err("Invalid params\n"); return; @@ -723,7 +722,14 @@ static void dp_mst_bridge_pre_enable(struct drm_bridge *drm_bridge) _dp_mst_bridge_pre_enable_part2(bridge); } - DP_MST_DEBUG("mst bridge [%d] pre enable complete\n", bridge->id); + DP_MST_INFO_LOG("mode: id(%d) mode(%s), refresh(%d)\n", + bridge->id, bridge->drm_mode.name, + bridge->drm_mode.vrefresh); + DP_MST_INFO_LOG("dsc: id(%d) dsc(%d)\n", bridge->id, + bridge->dp_mode.timing.comp_info.comp_ratio); + DP_MST_INFO_LOG("channel: id(%d) vcpi(%d) start(%d) tot(%d)\n", + bridge->id, bridge->vcpi, bridge->start_slot, + bridge->num_slots); end: mutex_unlock(&mst->mst_lock); } @@ -734,8 +740,6 @@ static void dp_mst_bridge_enable(struct drm_bridge *drm_bridge) struct dp_mst_bridge *bridge; struct dp_display *dp; - DP_MST_DEBUG("enter\n"); - if (!drm_bridge) { pr_err("Invalid params\n"); return; @@ -756,7 +760,8 @@ static void dp_mst_bridge_enable(struct drm_bridge *drm_bridge) return; } - DP_MST_DEBUG("mst bridge [%d] post enable complete\n", bridge->id); + DP_MST_INFO_LOG("mst bridge [%d] post enable complete\n", + bridge->id); } static void dp_mst_bridge_disable(struct drm_bridge *drm_bridge) @@ -766,8 +771,6 @@ static void dp_mst_bridge_disable(struct drm_bridge *drm_bridge) struct dp_display *dp; struct dp_mst_private *mst; - DP_MST_DEBUG("enter\n"); - if (!drm_bridge) { pr_err("Invalid params\n"); return; @@ -796,7 +799,7 @@ static void dp_mst_bridge_disable(struct drm_bridge *drm_bridge) _dp_mst_bridge_pre_disable_part2(bridge); - DP_MST_DEBUG("mst bridge [%d] disable complete\n", bridge->id); + DP_MST_INFO_LOG("mst bridge [%d] disable complete\n", bridge->id); mutex_unlock(&mst->mst_lock); } @@ -808,8 +811,6 @@ static void dp_mst_bridge_post_disable(struct drm_bridge *drm_bridge) struct dp_display *dp; struct dp_mst_private *mst; - DP_MST_DEBUG("enter\n"); - if (!drm_bridge) { pr_err("Invalid params\n"); return; @@ -846,7 +847,8 @@ static void dp_mst_bridge_post_disable(struct drm_bridge *drm_bridge) mst->mst_bridge[bridge->id].encoder_active_sts = false; } - DP_MST_DEBUG("mst bridge [%d] post disable complete\n", bridge->id); + DP_MST_INFO_LOG("mst bridge [%d] post disable complete\n", + bridge->id); } static void dp_mst_bridge_mode_set(struct drm_bridge *drm_bridge, @@ -1389,7 +1391,7 @@ dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr, /* unlock connector and make it accessible */ drm_modeset_unlock_all(dev); - DP_MST_DEBUG("add mst connector:%d\n", connector->base.id); + DP_MST_INFO_LOG("add mst connector id:%d\n", connector->base.id); return connector; } @@ -1400,7 +1402,8 @@ static void dp_mst_register_connector(struct drm_connector *connector) connector->status = connector->funcs->detect(connector, false); - DP_MST_DEBUG("register mst connector:%d\n", connector->base.id); + DP_MST_INFO_LOG("register mst connector id:%d\n", + connector->base.id); drm_connector_register(connector); } @@ -1409,7 +1412,7 @@ static void dp_mst_destroy_connector(struct drm_dp_mst_topology_mgr *mgr, { DP_MST_DEBUG("enter\n"); - DP_MST_DEBUG("destroy mst connector:%d\n", connector->base.id); + DP_MST_INFO_LOG("destroy mst connector id:%d\n", connector->base.id); drm_connector_unregister(connector); drm_connector_put(connector); @@ -1428,7 +1431,7 @@ static void dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr) kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp); - DP_MST_DEBUG("mst hot plug event\n"); + DP_MST_INFO_LOG("mst hot plug event\n"); } static void dp_mst_hpd_event_notify(struct dp_mst_private *mst, bool hpd_status) @@ -1449,7 +1452,7 @@ static void dp_mst_hpd_event_notify(struct dp_mst_private *mst, bool hpd_status) kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp); - DP_MST_DEBUG("%s finished\n", __func__); + DP_MST_INFO_LOG("%s finished\n", __func__); } /* DP Driver Callback OPs */ @@ -1461,8 +1464,6 @@ static void dp_mst_display_hpd(void *dp_display, bool hpd_status, struct dp_display *dp = dp_display; struct dp_mst_private *mst = dp->dp_mst_prv_info; - DP_MST_DEBUG("enter:\n"); - if (!hpd_status) rc = mst->mst_fw_cbs->topology_mgr_set_mst(&mst->mst_mgr, hpd_status); @@ -1483,9 +1484,7 @@ static void dp_mst_display_hpd(void *dp_display, bool hpd_status, dp_mst_hpd_event_notify(mst, hpd_status); - DP_MST_DEBUG("mst display hpd:%d, rc:%d\n", hpd_status, rc); - - DP_MST_DEBUG("exit:\n"); + DP_MST_INFO_LOG("mst display hpd:%d, rc:%d\n", hpd_status, rc); } static void dp_mst_display_hpd_irq(void *dp_display, @@ -1494,12 +1493,10 @@ static void dp_mst_display_hpd_irq(void *dp_display, int rc; struct dp_display *dp = dp_display; struct dp_mst_private *mst = dp->dp_mst_prv_info; - u8 esi[14], idx; + u8 esi[14]; unsigned int esi_res = DP_SINK_COUNT_ESI + 1; bool handled; - DP_MST_DEBUG("enter:\n"); - if (info->mst_hpd_sim) { dp_mst_hotplug(&mst->mst_mgr); return; @@ -1508,12 +1505,12 @@ static void dp_mst_display_hpd_irq(void *dp_display, rc = drm_dp_dpcd_read(mst->caps.drm_aux, DP_SINK_COUNT_ESI, esi, 14); if (rc != 14) { - pr_err("dpcd sync status read failed, rlen=%d\n", rc); - goto end; + pr_err("dpcd sink status read failed, rlen=%d\n", rc); + return; } - for (idx = 0; idx < 14; idx++) - DP_MST_DEBUG("mst irq: esi[%d]: 0x%x\n", idx, esi[idx]); + DP_MST_DEBUG("mst irq: esi1[0x%x] esi2[0x%x] esi3[%x]\n", + esi[1], esi[2], esi[3]); rc = drm_dp_mst_hpd_irq(&mst->mst_mgr, esi, &handled); @@ -1526,9 +1523,6 @@ static void dp_mst_display_hpd_irq(void *dp_display, } DP_MST_DEBUG("mst display hpd_irq handled:%d rc:%d\n", handled, rc); - -end: - DP_MST_DEBUG("exit:\n"); } static void dp_mst_set_state(void *dp_display, enum dp_drv_state mst_state) @@ -1542,6 +1536,7 @@ static void dp_mst_set_state(void *dp_display, enum dp_drv_state mst_state) } mst->state = mst_state; + DP_MST_INFO_LOG("mst power state:%d\n", mst_state); } /* DP MST APIs */ @@ -1623,7 +1618,7 @@ int dp_mst_init(struct dp_display *dp_display) } memset(&dp_mst_enc_cache, 0, sizeof(dp_mst_enc_cache)); - DP_MST_DEBUG("dp drm mst topology manager init completed\n"); + DP_MST_INFO_LOG("dp drm mst topology manager init completed\n"); return ret; @@ -1654,6 +1649,6 @@ void dp_mst_deinit(struct dp_display *dp_display) mutex_destroy(&mst->mst_lock); - DP_MST_DEBUG("dp drm mst topology manager deinit completed\n"); + DP_MST_INFO_LOG("dp drm mst topology manager deinit completed\n"); } From ca23004add368c5395c04658b1b1d36e2f2a2779 Mon Sep 17 00:00:00 2001 From: Govinda Rajulu Chenna Date: Tue, 1 Jan 2019 19:30:18 -0500 Subject: [PATCH 04/28] drm/msm/dp: avoid mst_hpd_irq process before mst session start Processing mst_hpd_irq before mst session start, that is mst_hpd, leads in accessing uninitialized mst data structures. This change verifies the mst session start state before processing mst_hpd_irq. Change-Id: I75f568ca05a8f0d80c7a4fe890013c374baf3c15 Signed-off-by: Govinda Rajulu Chenna Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_mst_drm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c index be2ba0e09951..f7ccd319f4a4 100644 --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c @@ -114,6 +114,7 @@ struct dp_mst_private { struct dp_mst_sim_mode simulator; struct mutex mst_lock; enum dp_drv_state state; + bool mst_session_state; }; struct dp_mst_encoder_info_cache { @@ -1464,6 +1465,10 @@ static void dp_mst_display_hpd(void *dp_display, bool hpd_status, struct dp_display *dp = dp_display; struct dp_mst_private *mst = dp->dp_mst_prv_info; + mutex_lock(&mst->mst_lock); + mst->mst_session_state = hpd_status; + mutex_unlock(&mst->mst_lock); + if (!hpd_status) rc = mst->mst_fw_cbs->topology_mgr_set_mst(&mst->mst_mgr, hpd_status); @@ -1502,6 +1507,11 @@ static void dp_mst_display_hpd_irq(void *dp_display, return; } + if (!mst->mst_session_state) { + pr_err("mst_hpd_irq received before mst session start\n"); + return; + } + rc = drm_dp_dpcd_read(mst->caps.drm_aux, DP_SINK_COUNT_ESI, esi, 14); if (rc != 14) { From 18745973593f30bcbcfec608cc22d34fd9e8b615 Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Wed, 20 Mar 2019 16:21:05 -0700 Subject: [PATCH 05/28] dt-bindings: drm: msm: add the link clk property information Add information about the DT property specifying the maximum DP link clock for a platform. Change-Id: I91c7fa6eabd047666cb121d25b25985e82d0b769 Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- Documentation/devicetree/bindings/display/msm/sde-dp.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/sde-dp.txt b/Documentation/devicetree/bindings/display/msm/sde-dp.txt index a17b73815e58..370798e91c83 100644 --- a/Documentation/devicetree/bindings/display/msm/sde-dp.txt +++ b/Documentation/devicetree/bindings/display/msm/sde-dp.txt @@ -105,6 +105,7 @@ Optional properties: - pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin controller. These pin configurations are installed in the pinctrl device node. Refer to pinctrl-bindings.txt +- qcom,max-lclk-frequency-khz: An integer specifying the max. link clock in KHz supported by Display Port. [Optional child nodes]: These nodes are for devices which are dependent on msm_ext_disp. If msm_ext_disp is disabled then From 2c3511ca7beb84306dfc1d01b3735fd2cccc0f2d Mon Sep 17 00:00:00 2001 From: Sankeerth Billakanti Date: Thu, 20 Dec 2018 16:50:03 +0530 Subject: [PATCH 06/28] drm/msm/dp: implementation to limit the dp link clock frequency Some targets do not support the DP link clock frequency greater than 5.4Gbps. These changes limit the link clock frequency support in the DP driver based on the input from DT property. Change-Id: Ic76629f9d705d2b9d9e49cd5bc1137dd3ed83fc3 Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_panel.c | 4 ++-- drivers/gpu/drm/msm/dp/dp_parser.c | 5 +++++ drivers/gpu/drm/msm/dp/dp_parser.h | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index eca79090b079..f9f85e0f9780 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -1657,8 +1657,8 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func) panel->minor = link_info->revision & 0x0f; pr_debug("version: %d.%d\n", panel->major, panel->minor); - link_info->rate = - drm_dp_bw_code_to_link_rate(dp_panel->dpcd[DP_MAX_LINK_RATE]); + link_info->rate = min_t(unsigned long, panel->parser->max_lclk_khz, + drm_dp_bw_code_to_link_rate(dp_panel->dpcd[DP_MAX_LINK_RATE])); pr_debug("link_rate=%d\n", link_info->rate); link_info->num_lanes = dp_panel->dpcd[DP_MAX_LANE_COUNT] & diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c index b0a6d245312f..34e795602b41 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.c +++ b/drivers/gpu/drm/msm/dp/dp_parser.c @@ -156,6 +156,11 @@ static int dp_parser_misc(struct dp_parser *parser) if (rc) parser->max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ; + rc = of_property_read_u32(of_node, + "qcom,max-lclk-frequency-khz", &parser->max_lclk_khz); + if (rc) + parser->max_lclk_khz = DP_MAX_LINK_CLK_KHZ; + return 0; } diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h index 7fb90c9fece9..9dba175dd94b 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.h +++ b/drivers/gpu/drm/msm/dp/dp_parser.h @@ -11,6 +11,7 @@ #define DP_LABEL "MDSS DP DISPLAY" #define AUX_CFG_LEN 10 #define DP_MAX_PIXEL_CLK_KHZ 675000 +#define DP_MAX_LINK_CLK_KHZ 810000 enum dp_pm_type { DP_CORE_PM, @@ -181,6 +182,8 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type) * @mp: gpio, regulator and clock related data * @pinctrl: pin-control related data * @disp_data: controller's display related data + * @max_pclk_khz: maximum pixel clock supported for the platform + * @max_lclk_khz: maximum link clock supported for the platform * @hw_cfg: DP HW specific settings * @has_mst: MST feature enable status * @has_mst_sideband: MST sideband feature enable status @@ -207,6 +210,7 @@ struct dp_parser { u8 l_map[4]; struct dp_aux_cfg aux_cfg[AUX_CFG_LEN]; u32 max_pclk_khz; + u32 max_lclk_khz; struct dp_hw_cfg hw_cfg; bool has_mst; bool has_mst_sideband; From e36177f2a93ffbba6bd2990d1a832b3f81113a71 Mon Sep 17 00:00:00 2001 From: Govinda Rajulu Chenna Date: Mon, 31 Dec 2018 10:06:53 -0500 Subject: [PATCH 07/28] drm/msm/dp: remove dp_display_post_open operations The current driver implementation delays the connect_work until drm client initializations are complete. This is performed to avoid sending hpd event notification to drm client when hpd is connected before drm client is initialized as it may cause drm client to miss the hpd event notification and may not able to create the display. This delayed connect_work not required anymore as drm client can handle plug-in display creation based on the connector status reported from drm_get_connector api. This change removes dp_display_post_open operations from driver as this functionality is not required anymore as explained. Change-Id: Id6a5558c49a0b941209ab9e4b0a0a45154dfc118 Signed-off-by: Govinda Rajulu Chenna Signed-off-by: Fuad Hossain Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_display.c | 90 ++++++++++------------------- 1 file changed, 29 insertions(+), 61 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index bdd2478476e9..eafd430bf84c 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -103,6 +103,8 @@ struct dp_display_private { u32 tot_dsc_blks_in_use; + bool process_hpd_connect; + struct notifier_block usb_nb; }; @@ -111,11 +113,6 @@ static const struct of_device_id dp_dt_match[] = { {} }; -static bool dp_display_framework_ready(struct dp_display_private *dp) -{ - return dp->dp_display.post_open ? false : true; -} - static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp) { return dp->link->hdcp_status.hdcp_version && dp->hdcp.ops; @@ -502,36 +499,6 @@ static void dp_display_send_hpd_event(struct dp_display_private *dp) envp); } -static void dp_display_post_open(struct dp_display *dp_display) -{ - struct drm_connector *connector; - struct dp_display_private *dp; - - if (!dp_display) { - pr_err("invalid input\n"); - return; - } - - dp = container_of(dp_display, struct dp_display_private, dp_display); - if (IS_ERR_OR_NULL(dp)) { - pr_err("invalid params\n"); - return; - } - - connector = dp->dp_display.base_connector; - - if (!connector) { - pr_err("base connector not set\n"); - return; - } - - /* if cable is already connected, send notification */ - if (dp->hpd->hpd_high) - queue_work(dp->wq, &dp->connect_work); - else - dp_display->post_open = NULL; -} - static int dp_display_send_hpd_notification(struct dp_display_private *dp) { int ret = 0; @@ -541,6 +508,8 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp) if (!dp->mst.mst_active) dp->dp_display.is_sst_connected = hpd; + else + dp->dp_display.is_sst_connected = false; reinit_completion(&dp->notification_comp); dp_display_send_hpd_event(dp); @@ -551,9 +520,6 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp) if (!dp->mst.mst_active && (dp->power_on == hpd)) goto skip_wait; - if (!dp_display_framework_ready(dp)) - goto skip_wait; - if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 5)) { pr_warn("%s timeout\n", hpd ? "connect" : "disconnect"); @@ -650,7 +616,17 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) { int rc = 0; + mutex_lock(&dp->session_lock); + + if (dp->is_connected) { + pr_debug("dp already connected, skipping hpd high\n"); + mutex_unlock(&dp->session_lock); + rc = -EISCONN; + goto end; + } + dp->is_connected = true; + mutex_unlock(&dp->session_lock); dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz, dp->debug->max_pclk_khz); @@ -671,8 +647,12 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) * ETIMEDOUT --> cable may have been removed * ENOTCONN --> no downstream device connected */ - if (rc == -ETIMEDOUT || rc == -ENOTCONN) + if (rc == -ETIMEDOUT || rc == -ENOTCONN) { + mutex_lock(&dp->session_lock); + dp->is_connected = false; + mutex_unlock(&dp->session_lock); goto end; + } dp->link->process_request(dp->link); dp->panel->handle_sink_request(dp->panel); @@ -683,11 +663,13 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, dp->panel->fec_en, false); if (rc) { + dp->is_connected = false; mutex_unlock(&dp->session_lock); goto end; } mutex_unlock(&dp->session_lock); + dp->process_hpd_connect = false; dp_display_send_hpd_notification(dp); end: return rc; @@ -715,6 +697,7 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) int rc = 0; dp->is_connected = false; + dp->process_hpd_connect = false; dp_display_process_mst_hpd_low(dp); @@ -760,6 +743,9 @@ static int dp_display_usbpd_configure_cb(struct device *dev) /* check for hpd high */ if (dp->hpd->hpd_high) queue_work(dp->wq, &dp->connect_work); + else + dp->process_hpd_connect = true; + end: return rc; } @@ -878,18 +864,10 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) goto end; } - /* - * In case cable/dongle is disconnected during adb shell stop, - * reset psm_enabled flag to false since it is no more needed - */ - if (dp->dp_display.post_open) - dp->debug->psm_enabled = false; - if (dp->debug->psm_enabled) dp->link->psm_config(dp->link, &dp->panel->link_info, true); dp_display_disconnect_sync(dp); - dp->dp_display.post_open = NULL; if (!dp->debug->sim_mode && !dp->parser->no_aux_switch && !dp->parser->gpio_aux_switch) @@ -997,16 +975,16 @@ static int dp_display_usbpd_attention_cb(struct device *dev) return -ENODEV; } - pr_debug("hpd_irq:%d, hpd_high:%d, power_on:%d\n", + pr_debug("hpd_irq:%d, hpd_high:%d, power_on:%d, is_connected:%d\n", dp->hpd->hpd_irq, dp->hpd->hpd_high, - dp->power_on); + dp->power_on, dp->is_connected); if (!dp->hpd->hpd_high) dp_display_disconnect_sync(dp); else if ((dp->hpd->hpd_irq && dp->core_initialized) || dp->debug->mst_hpd_sim) queue_work(dp->wq, &dp->attention_work); - else if (!dp->power_on) + else if (dp->process_hpd_connect || !dp->is_connected) queue_work(dp->wq, &dp->connect_work); else pr_debug("ignored\n"); @@ -1539,8 +1517,6 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) cancel_delayed_work_sync(&dp->hdcp_cb_work); queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ); end: - /* clear framework event notifier */ - dp_display->post_open = NULL; dp->aux->state |= DP_STATE_CTRL_POWERED_ON; complete_all(&dp->notification_comp); @@ -1689,14 +1665,6 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel) dp->link->psm_config(dp->link, &dp->panel->link_info, true); dp->debug->psm_enabled = true; - /* - * In case of framework reboot, the DP off sequence is executed - * without any notification from driver. Initialize post_open - * callback to notify DP connection once framework restarts. - */ - dp_display->post_open = dp_display_post_open; - dp->dp_display.is_sst_connected = false; - dp->ctrl->off(dp->ctrl); dp_display_host_deinit(dp); } @@ -2332,7 +2300,7 @@ static int dp_display_probe(struct platform_device *pdev) 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 = dp_display_post_open; + 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; From 70f1db1c45c93d4237db263e5e4607aa519cfc46 Mon Sep 17 00:00:00 2001 From: Govinda Rajulu Chenna Date: Wed, 9 Jan 2019 23:09:16 -0500 Subject: [PATCH 08/28] drm/msm/dp: add debugfs node for max_lclk_khz Add debugfs node to read/write max_lclk_khz to validate different link clock configurations dynamically with the device that supports more than one link clock frequency. Change-Id: I11b7860e601fa087e7fd31894fb9cec379bdf639 Signed-off-by: Govinda Rajulu Chenna Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_debug.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c index e5f1b3ebbc86..0dba03983469 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.c +++ b/drivers/gpu/drm/msm/dp/dp_debug.c @@ -1941,6 +1941,14 @@ static int dp_debug_init(struct dp_debug *dp_debug) DEBUG_NAME, rc); } + file = debugfs_create_u32("max_lclk_khz", 0644, dir, + &debug->parser->max_lclk_khz); + if (IS_ERR_OR_NULL(file)) { + rc = PTR_ERR(file); + pr_err("[%s] debugfs max_lclk_khz failed, rc=%d\n", + DEBUG_NAME, rc); + } + return 0; error_remove_dir: From d4a43a7032f58c82f97fb460a13eb43f0d9ba0df Mon Sep 17 00:00:00 2001 From: Govinda Rajulu Chenna Date: Tue, 8 Jan 2019 12:13:22 -0500 Subject: [PATCH 09/28] drm/msm/dp: perform mst probe after link training Perform mst topology probing after link training is complete as some of the sink devices are not able to handle mst topology probe and link training simultaneously. Change-Id: If2a03bb22f6f8e758c31e86d16ac41995552eaf7 Signed-off-by: Govinda Rajulu Chenna Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_display.c | 54 ++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index eafd430bf84c..982174709ef4 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -537,30 +537,47 @@ static void dp_display_update_mst_state(struct dp_display_private *dp, dp->panel->mst_state = state; } -static void dp_display_process_mst_hpd_high(struct dp_display_private *dp) +static void dp_display_process_mst_hpd_high(struct dp_display_private *dp, + bool mst_probe) { bool is_mst_receiver; struct dp_mst_hpd_info info; + int ret; - if (dp->parser->has_mst && dp->mst.drm_registered) { - DP_MST_DEBUG("mst_hpd_high work\n"); + if (!dp->parser->has_mst || !dp->mst.drm_registered) { + DP_MST_DEBUG("mst not enabled. has_mst:%d, registered:%d\n", + dp->parser->has_mst, dp->mst.drm_registered); + return; + } + DP_MST_DEBUG("mst_hpd_high work. mst_probe:%d\n", mst_probe); + + if (!dp->mst.mst_active) { is_mst_receiver = dp->panel->read_mst_cap(dp->panel); - if (is_mst_receiver && !dp->mst.mst_active) { - - /* clear sink mst state */ - drm_dp_dpcd_writeb(dp->aux->drm_aux, DP_MSTM_CTRL, 0); - - dp_display_update_mst_state(dp, true); - - info.mst_protocol = dp->parser->has_mst_sideband; - info.mst_port_cnt = dp->debug->mst_port_cnt; - info.edid = dp->debug->get_edid(dp->debug); - - if (dp->mst.cbs.hpd) - dp->mst.cbs.hpd(&dp->dp_display, true, &info); + if (!is_mst_receiver) { + DP_MST_DEBUG("sink doesn't support mst\n"); + return; } + + /* clear sink mst state */ + drm_dp_dpcd_writeb(dp->aux->drm_aux, DP_MSTM_CTRL, 0); + + ret = drm_dp_dpcd_writeb(dp->aux->drm_aux, DP_MSTM_CTRL, + DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC); + if (ret < 0) { + pr_err("sink mst enablement failed\n"); + return; + } + + dp_display_update_mst_state(dp, true); + } else if (dp->mst.mst_active && mst_probe) { + info.mst_protocol = dp->parser->has_mst_sideband; + info.mst_port_cnt = dp->debug->mst_port_cnt; + info.edid = dp->debug->get_edid(dp->debug); + + if (dp->mst.cbs.hpd) + dp->mst.cbs.hpd(&dp->dp_display, true, &info); } DP_MST_DEBUG("mst_hpd_high. mst_active:%d\n", dp->mst.mst_active); @@ -657,7 +674,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp->link->process_request(dp->link); dp->panel->handle_sink_request(dp->panel); - dp_display_process_mst_hpd_high(dp); + dp_display_process_mst_hpd_high(dp, false); mutex_lock(&dp->session_lock); rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, @@ -670,6 +687,9 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) mutex_unlock(&dp->session_lock); dp->process_hpd_connect = false; + + dp_display_process_mst_hpd_high(dp, true); + dp_display_send_hpd_notification(dp); end: return rc; From 32a2488454f7c3776e15fc4f590a487d145883da Mon Sep 17 00:00:00 2001 From: Govinda Rajulu Chenna Date: Wed, 9 Jan 2019 23:51:37 -0500 Subject: [PATCH 10/28] drm/msm/dp: add vcpi parameter for set_stream_info Introduce a new parameter for set_stream_info function to pass virtual channel id, vcpi, information from dp_mst layers to dp_display and dp_panel modules. dp_display and dp_panel modules need vcpi information for mst, mst-dsc and mst-hdcp use-cases. Change-Id: Ic8c21223928e3da234a4e9f59e798a3786a037da Signed-off-by: Govinda Rajulu Chenna Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_display.c | 4 ++-- drivers/gpu/drm/msm/dp/dp_display.h | 3 ++- drivers/gpu/drm/msm/dp/dp_drm.c | 2 +- drivers/gpu/drm/msm/dp/dp_mst_drm.c | 6 ++++-- drivers/gpu/drm/msm/dp/dp_panel.c | 5 +++-- drivers/gpu/drm/msm/dp/dp_panel.h | 3 ++- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 982174709ef4..d641085d9d68 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -1397,7 +1397,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) static int dp_display_set_stream_info(struct dp_display *dp_display, void *panel, u32 strm_id, u32 start_slot, - u32 num_slots, u32 pbn) + u32 num_slots, u32 pbn, int vcpi) { int rc = 0; struct dp_panel *dp_panel; @@ -1430,7 +1430,7 @@ static int dp_display_set_stream_info(struct dp_display *dp_display, if (panel) { dp_panel = panel; dp_panel->set_stream_info(dp_panel, strm_id, start_slot, - num_slots, pbn); + num_slots, pbn, vcpi); } mutex_unlock(&dp->session_lock); diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h index 410cee75959e..eb5994b1ed38 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.h +++ b/drivers/gpu/drm/msm/dp/dp_display.h @@ -106,7 +106,8 @@ struct dp_display { int (*get_mst_caps)(struct dp_display *dp_display, struct dp_mst_caps *mst_caps); int (*set_stream_info)(struct dp_display *dp_display, void *panel, - u32 strm_id, u32 start_slot, u32 num_slots, u32 pbn); + u32 strm_id, u32 start_slot, u32 num_slots, u32 pbn, + int vcpi); void (*convert_to_dp_mode)(struct dp_display *dp_display, void *panel, const struct drm_display_mode *drm_mode, struct dp_display_mode *dp_mode); diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index 9b3bb246aa18..b3b116a874a4 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -114,7 +114,7 @@ static void dp_bridge_pre_enable(struct drm_bridge *drm_bridge) } /* for SST force stream id, start slot and total slots to 0 */ - dp->set_stream_info(dp, bridge->dp_panel, 0, 0, 0, 0); + dp->set_stream_info(dp, bridge->dp_panel, 0, 0, 0, 0, 0); rc = dp->enable(dp, bridge->dp_panel); if (rc) { diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c index f7ccd319f4a4..9112815b9020 100644 --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c @@ -529,7 +529,8 @@ static void _dp_mst_update_timeslots(struct dp_mst_private *mst, mst->dp_display->set_stream_info(mst->dp_display, dp_bridge->dp_panel, - dp_bridge->id, start_slot, num_slots, pbn); + dp_bridge->id, start_slot, num_slots, pbn, + dp_bridge->vcpi); pr_info("bridge:%d vcpi:%d start_slot:%d num_slots:%d, pbn:%d\n", dp_bridge->id, dp_bridge->vcpi, @@ -554,7 +555,8 @@ static void _dp_mst_update_single_timeslot(struct dp_mst_private *mst, mst->dp_display->set_stream_info(mst->dp_display, mst_bridge->dp_panel, - mst_bridge->id, start_slot, num_slots, pbn); + mst_bridge->id, start_slot, num_slots, pbn, + mst_bridge->vcpi); } } diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index f9f85e0f9780..e2c4923a7998 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -2305,13 +2305,14 @@ static void dp_panel_edid_deregister(struct dp_panel_private *panel) static int dp_panel_set_stream_info(struct dp_panel *dp_panel, enum dp_stream_id stream_id, u32 ch_start_slot, - u32 ch_tot_slots, u32 pbn) + u32 ch_tot_slots, u32 pbn, int vcpi) { if (!dp_panel || stream_id > DP_STREAM_MAX) { pr_err("invalid input. stream_id: %d\n", stream_id); return -EINVAL; } + dp_panel->vcpi = vcpi; dp_panel->stream_id = stream_id; dp_panel->channel_start_slot = ch_start_slot; dp_panel->channel_total_slots = ch_tot_slots; @@ -2376,7 +2377,7 @@ static int dp_panel_deinit_panel_info(struct dp_panel *dp_panel, u32 flags) if (!panel->custom_edid && dp_panel->edid_ctrl->edid) sde_free_edid((void **)&dp_panel->edid_ctrl); - dp_panel_set_stream_info(dp_panel, DP_STREAM_MAX, 0, 0, 0); + dp_panel_set_stream_info(dp_panel, DP_STREAM_MAX, 0, 0, 0, 0); memset(&dp_panel->pinfo, 0, sizeof(dp_panel->pinfo)); memset(&hdr->hdr_meta, 0, sizeof(hdr->hdr_meta)); panel->panel_on = false; diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h index 90d53467f932..dc96090ba5c5 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.h +++ b/drivers/gpu/drm/msm/dp/dp_panel.h @@ -110,6 +110,7 @@ struct dp_panel { * Client sets the stream id value using set_stream_id interface. */ enum dp_stream_id stream_id; + int vcpi; u32 channel_start_slot; u32 channel_total_slots; @@ -154,7 +155,7 @@ struct dp_panel { int (*set_stream_info)(struct dp_panel *dp_panel, enum dp_stream_id stream_id, u32 ch_start_slot, - u32 ch_tot_slots, u32 pbn); + u32 ch_tot_slots, u32 pbn, int vcpi); int (*read_sink_status)(struct dp_panel *dp_panel, u8 *sts, u32 size); int (*update_edid)(struct dp_panel *dp_panel, struct edid *edid); From a2dce5393fa9a34365f3320076e4897611af339c Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Tue, 15 Jan 2019 16:53:11 -0800 Subject: [PATCH 11/28] drm/msm/dp: abort aux and ctrl when simulation is switched off While running the DisplayPort simulation, if the simulation is abruptly switched off, AUX and CTRL may continue processing like in a normal DisplayPort connected mode. This may result in an unstable DP driver state and may affect subsequent hot plugs. Abort the AUX and CTRL processing when DP simulation is switched off. This brings the DP driver into stable state when simulation is switched off. Change-Id: Ib243b0628e1c642ec2f50adfaa17ca3aebdc2687 Signed-off-by: Ajay Singh Parmar Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_debug.c | 9 +++++++-- drivers/gpu/drm/msm/dp/dp_debug.h | 2 ++ drivers/gpu/drm/msm/dp/dp_display.c | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c index 0dba03983469..05c11519a0b8 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.c +++ b/drivers/gpu/drm/msm/dp/dp_debug.c @@ -11,7 +11,6 @@ #include "dp_power.h" #include "dp_catalog.h" #include "dp_aux.h" -#include "dp_ctrl.h" #include "dp_debug.h" #include "drm_connector.h" #include "sde_connector.h" @@ -41,6 +40,7 @@ struct dp_debug_private { struct device *dev; struct dp_debug dp_debug; struct dp_parser *parser; + struct dp_ctrl *ctrl; }; static int dp_debug_get_edid_buf(struct dp_debug_private *debug) @@ -1444,6 +1444,9 @@ static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim) debug->aux->set_sim_mode(debug->aux, true, debug->edid, debug->dpcd); } else { + debug->aux->abort(debug->aux); + debug->ctrl->abort(debug->ctrl); + debug->aux->set_sim_mode(debug->aux, false, NULL, NULL); debug->dp_debug.sim_mode = false; @@ -1989,7 +1992,8 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in) struct dp_debug_private *debug; struct dp_debug *dp_debug; - if (!in->dev || !in->panel || !in->hpd || !in->link || !in->catalog) { + if (!in->dev || !in->panel || !in->hpd || !in->link || + !in->catalog || !in->ctrl) { pr_err("invalid input\n"); rc = -EINVAL; goto error; @@ -2010,6 +2014,7 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in) debug->connector = in->connector; debug->catalog = in->catalog; debug->parser = in->parser; + debug->ctrl = in->ctrl; dp_debug = &debug->dp_debug; dp_debug->vdisplay = 0; diff --git a/drivers/gpu/drm/msm/dp/dp_debug.h b/drivers/gpu/drm/msm/dp/dp_debug.h index dfbc65248896..11b890eb13b9 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.h +++ b/drivers/gpu/drm/msm/dp/dp_debug.h @@ -7,6 +7,7 @@ #define _DP_DEBUG_H_ #include "dp_panel.h" +#include "dp_ctrl.h" #include "dp_link.h" #include "dp_usbpd.h" #include "dp_aux.h" @@ -63,6 +64,7 @@ struct dp_debug_in { struct drm_connector **connector; struct dp_catalog *catalog; struct dp_parser *parser; + struct dp_ctrl *ctrl; }; /** diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index d641085d9d68..f2a384a130ac 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -1226,6 +1226,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp) debug_in.connector = &dp->dp_display.base_connector; debug_in.catalog = dp->catalog; debug_in.parser = dp->parser; + debug_in.ctrl = dp->ctrl; dp->debug = dp_debug_get(&debug_in); if (IS_ERR(dp->debug)) { From 73cc323247b23eb2c0250149b6afdd637282124e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Aug 2018 10:07:42 +0200 Subject: [PATCH 12/28] drm_dp_mst_topology: fix broken drm_dp_sideband_parse_remote_dpcd_read() When parsing the reply of a DP_REMOTE_DPCD_READ DPCD command the result is wrong due to a missing idx increment. This was never noticed since DP_REMOTE_DPCD_READ is currently not used, but if you enable it, then it is all wrong. Change-Id: I1d4e82306eece23fdb87cef00c615efe933c4616 Signed-off-by: Hans Verkuil Reviewed-by: Lyude Paul Acked-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/e72ddac2-1dc0-100a-d816-9ac98ac009dd@xs4all.nl Git-commit: a4c30a4861c54af78c4eb8b7855524c1a96d9f80 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Fuad Hossain Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/drm_dp_mst_topology.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index d708472d93c4..0e0df398222d 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -439,6 +439,7 @@ static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx if (idx > raw->curlen) goto fail_len; repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx]; + idx++; if (idx > raw->curlen) goto fail_len; From 307531740ec7f10ed9fd6d257556d61da6c185ee Mon Sep 17 00:00:00 2001 From: Sankeerth Billakanti Date: Wed, 2 Jan 2019 13:22:31 +0530 Subject: [PATCH 13/28] drm/msm/dp: changes to prevent null pointer dereference Change the buffer size to fix a possible null pointer access Change-Id: I457d66e9ebe3902a68367a5f0556cf65b617db93 Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_debug.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c index 05c11519a0b8..71f53dd4104a 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.c +++ b/drivers/gpu/drm/msm/dp/dp_debug.c @@ -747,7 +747,7 @@ static ssize_t dp_debug_write_exe_mode(struct file *file, const char __user *user_buff, size_t count, loff_t *ppos) { struct dp_debug_private *debug = file->private_data; - char *buf; + char buf[SZ_32]; size_t len = 0; if (!debug) @@ -757,7 +757,9 @@ static ssize_t dp_debug_write_exe_mode(struct file *file, return 0; len = min_t(size_t, count, SZ_32 - 1); - buf = memdup_user(user_buff, len); + if (copy_from_user(buf, user_buff, len)) + goto end; + buf[len] = '\0'; if (sscanf(buf, "%3s", debug->exe_mode) != 1) From 320451eb1050e710300168054cce77cb1ed57dd2 Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Wed, 16 Jan 2019 21:50:19 -0800 Subject: [PATCH 14/28] drm/msm/dp: protect edid and dpcd buffers with mutex The EDID and DPCD buffers are allocated and deallocated in debug module while running in simulation mode. There can be a race condition if multiple scripts are run and result in double free of the buffers. Protect the buffer allocation and deallocation functionalities with a mutex so that such race conditions can be avoided. Change-Id: Id00c0c95dc0151bf50389ec509ab8372c4ce5102 Signed-off-by: Ajay Singh Parmar Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_debug.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c index 71f53dd4104a..e5813034f71e 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.c +++ b/drivers/gpu/drm/msm/dp/dp_debug.c @@ -41,6 +41,7 @@ struct dp_debug_private { struct dp_debug dp_debug; struct dp_parser *parser; struct dp_ctrl *ctrl; + struct mutex lock; }; static int dp_debug_get_edid_buf(struct dp_debug_private *debug) @@ -90,6 +91,8 @@ static ssize_t dp_debug_write_edid(struct file *file, if (!debug) return -ENODEV; + mutex_lock(&debug->lock); + if (*ppos) goto bail; @@ -161,6 +164,7 @@ static ssize_t dp_debug_write_edid(struct file *file, */ pr_info("[%s]\n", edid ? "SET" : "CLEAR"); + mutex_unlock(&debug->lock); return rc; } @@ -180,6 +184,8 @@ static ssize_t dp_debug_write_dpcd(struct file *file, if (!debug) return -ENODEV; + mutex_lock(&debug->lock); + if (*ppos) goto bail; @@ -260,6 +266,7 @@ static ssize_t dp_debug_write_dpcd(struct file *file, } else debug->aux->dpcd_updated(debug->aux); + mutex_unlock(&debug->lock); return rc; } @@ -1439,6 +1446,7 @@ static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim) if (dp_debug_get_dpcd_buf(debug)) { devm_kfree(debug->dev, debug->edid); + debug->edid = NULL; return; } @@ -1487,6 +1495,8 @@ static ssize_t dp_debug_write_sim(struct file *file, if (*ppos) return 0; + mutex_lock(&debug->lock); + /* Leave room for termination char */ len = min_t(size_t, count, SZ_8 - 1); if (copy_from_user(buf, user_buff, len)) @@ -1499,6 +1509,7 @@ static ssize_t dp_debug_write_sim(struct file *file, dp_debug_set_sim_mode(debug, sim); end: + mutex_unlock(&debug->lock); return len; } @@ -1985,7 +1996,9 @@ static void dp_debug_abort(struct dp_debug *dp_debug) debug = container_of(dp_debug, struct dp_debug_private, dp_debug); + mutex_lock(&debug->lock); dp_debug_set_sim_mode(debug, false); + mutex_unlock(&debug->lock); } struct dp_debug *dp_debug_get(struct dp_debug_in *in) @@ -2023,6 +2036,8 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in) dp_debug->hdisplay = 0; dp_debug->vrefresh = 0; + mutex_init(&debug->lock); + rc = dp_debug_init(dp_debug); if (rc) { devm_kfree(in->dev, debug); @@ -2074,6 +2089,8 @@ void dp_debug_put(struct dp_debug *dp_debug) dp_debug_deinit(dp_debug); + mutex_destroy(&debug->lock); + if (debug->edid) devm_kfree(debug->dev, debug->edid); From 9ba5ace1ec222c9a84429051f2906519f9d3090c Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Fri, 18 Jan 2019 17:43:05 -0800 Subject: [PATCH 15/28] drm/msm/dp: protect power on/off functionalities with mutex In case of multiple threads trying to power on/off DP driver simultaneously, un-clocked register access may happen. Use session_lock in all conditions to avoid such scenario. Change-Id: I6d8f82b3c37bf54965176dae50e0f9dca5343e2e Signed-off-by: Ajay Singh Parmar Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_display.c | 36 ++++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index f2a384a130ac..9d4346d7ccf9 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -631,7 +631,7 @@ static void dp_display_host_deinit(struct dp_display_private *dp) static int dp_display_process_hpd_high(struct dp_display_private *dp) { - int rc = 0; + int rc = -EINVAL; mutex_lock(&dp->session_lock); @@ -643,7 +643,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) } dp->is_connected = true; - mutex_unlock(&dp->session_lock); dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz, dp->debug->max_pclk_khz); @@ -665,9 +664,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) * ENOTCONN --> no downstream device connected */ if (rc == -ETIMEDOUT || rc == -ENOTCONN) { - mutex_lock(&dp->session_lock); dp->is_connected = false; - mutex_unlock(&dp->session_lock); goto end; } @@ -676,22 +673,22 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp_display_process_mst_hpd_high(dp, false); - mutex_lock(&dp->session_lock); rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, dp->panel->fec_en, false); if (rc) { dp->is_connected = false; - mutex_unlock(&dp->session_lock); goto end; } - mutex_unlock(&dp->session_lock); dp->process_hpd_connect = false; dp_display_process_mst_hpd_high(dp, true); - - dp_display_send_hpd_notification(dp); end: + mutex_unlock(&dp->session_lock); + + if (!rc) + dp_display_send_hpd_notification(dp); + return rc; } @@ -758,6 +755,7 @@ static int dp_display_usbpd_configure_cb(struct device *dev) goto end; } + mutex_lock(&dp->session_lock); dp_display_host_init(dp); /* check for hpd high */ @@ -765,7 +763,7 @@ static int dp_display_usbpd_configure_cb(struct device *dev) queue_work(dp->wq, &dp->connect_work); else dp->process_hpd_connect = true; - + mutex_unlock(&dp->session_lock); end: return rc; } @@ -884,8 +882,10 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) goto end; } - if (dp->debug->psm_enabled) + mutex_lock(&dp->session_lock); + if (dp->debug->psm_enabled && dp->core_initialized) dp->link->psm_config(dp->link, &dp->panel->link_info, true); + mutex_unlock(&dp->session_lock); dp_display_disconnect_sync(dp); @@ -934,11 +934,19 @@ static void dp_display_attention_work(struct work_struct *work) struct dp_display_private *dp = container_of(work, struct dp_display_private, attention_work); - if (dp->debug->mst_hpd_sim) - goto mst_attention; + mutex_lock(&dp->session_lock); - if (dp->link->process_request(dp->link)) + if (dp->debug->mst_hpd_sim || !dp->core_initialized) { + mutex_unlock(&dp->session_lock); + goto mst_attention; + } + + if (dp->link->process_request(dp->link)) { + mutex_unlock(&dp->session_lock); goto cp_irq; + } + + mutex_unlock(&dp->session_lock); if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) { if (dp_display_is_sink_count_zero(dp)) { From 3723400e5b79fb763023ae0bed288672cfad563a Mon Sep 17 00:00:00 2001 From: Govinda Rajulu Chenna Date: Tue, 18 Dec 2018 08:54:14 -0500 Subject: [PATCH 16/28] drm/dp: complete the implementation for remote dpcd read Current DP MST topology layer has incomplete implementation for drm_dp_send_dpcd_read function. This change completes the implementation in order to make it ready for exporting the function to the dp mst drivers. Change-Id: I59ce468d0353dab54b37bee7d6917d15cad16020 Signed-off-by: Govinda Rajulu Chenna Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/drm_dp_mst_topology.c | 48 +++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 0e0df398222d..29f9cb7f8e27 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1985,21 +1985,57 @@ EXPORT_SYMBOL(drm_dp_update_payload_part2); #if 0 /* unused as of yet */ static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, - int offset, int size) + int offset, int size, u8 *bytes) { int len; + int ret; struct drm_dp_sideband_msg_tx *txmsg; + struct drm_dp_mst_branch *mstb; + + memset(bytes, 0, size); + + mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); + if (!mstb) + return -EINVAL; txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); - if (!txmsg) - return -ENOMEM; + if (!txmsg) { + ret = -ENOMEM; + goto fail_put; + } - len = build_dpcd_read(txmsg, port->port_num, 0, 8); - txmsg->dst = port->parent; + len = build_dpcd_read(txmsg, port->port_num, offset, size); + txmsg->dst = mstb; drm_dp_queue_down_tx(mgr, txmsg); + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret <= 0) { + DRM_ERROR("dpcd read failed\n"); + goto fail_free_msg; + } - return 0; + if (txmsg->reply.reply_type == 1) { + DRM_ERROR("dpcd read nack received\n"); + ret = -EINVAL; + goto fail_free_msg; + } + + if (port->port_num != txmsg->reply.u.remote_dpcd_read_ack.port_number) { + DRM_ERROR("got incorrect port in response\n"); + ret = -EINVAL; + goto fail_free_msg; + } + + if (size > txmsg->reply.u.remote_dpcd_read_ack.num_bytes) + size = txmsg->reply.u.remote_dpcd_read_ack.num_bytes; + + memcpy(bytes, txmsg->reply.u.remote_dpcd_read_ack.bytes, size); + +fail_free_msg: + kfree(txmsg); +fail_put: + drm_dp_put_mst_branch_device(mstb); + return ret; } #endif From c4ac38b58e1dc001420c6ad405e6cabb0ccef84a Mon Sep 17 00:00:00 2001 From: Fuad Hossain Date: Tue, 22 Jan 2019 16:19:24 -0500 Subject: [PATCH 17/28] drm/msm/dp: release extra mst port reference When adding a mst port in simulator mode, an extra reference is acquired which never gets released. This causes the connectors to not be destroyed during a hpd disconnect, therefore the reference should be released. Change-Id: I80e661bcefae52f9e0e57e9b234c4955b56d4d6a Signed-off-by: Fuad Hossain Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_mst_drm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c index 9112815b9020..2f79541e5c40 100644 --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c @@ -171,10 +171,13 @@ static void dp_mst_sim_add_port(struct dp_mst_private *mst, mutex_lock(&mstb->mgr->lock); list_del(&port->next); mutex_unlock(&mstb->mgr->lock); - return; + goto put_port; } (*mstb->mgr->cbs->register_connector)(port->connector); } + +put_port: + kref_put(&port->kref, NULL); } static void dp_mst_sim_link_probe_work(struct work_struct *work) From 81fbc4cd2063fa236f4d154b03067d187213b453 Mon Sep 17 00:00:00 2001 From: Fuad Hossain Date: Wed, 30 Jan 2019 15:16:40 -0500 Subject: [PATCH 18/28] drm/msm/dp: ensure dsc slice width does not exceed maximum width A dp sink can support dsc slices up to a maximum width specified by its capabilities register. Ensure that the calculated dsc slice width does not exceed this value. Change-Id: I6b9a578ef5c763259f592d59638e1b7352064d7f Signed-off-by: Fuad Hossain Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_panel.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index e2c4923a7998..d98ebcffb1c5 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -1530,12 +1530,14 @@ static int dp_panel_dsc_prepare_basic_params( struct dp_dsc_slices_per_line *rec; int slice_width; u32 ppr = dp_mode->timing.pixel_clk_khz/1000; + int max_slice_width; comp_info->dsc_info.slice_per_pkt = 0; for (i = 0; i < ARRAY_SIZE(slice_per_line_tbl); i++) { rec = &slice_per_line_tbl[i]; if ((ppr > rec->min_ppr) && (ppr <= rec->max_ppr)) { comp_info->dsc_info.slice_per_pkt = rec->num_slices; + i++; break; } } @@ -1543,9 +1545,21 @@ static int dp_panel_dsc_prepare_basic_params( if (comp_info->dsc_info.slice_per_pkt == 0) return -EINVAL; + max_slice_width = dp_panel->dsc_dpcd[12] * 320; slice_width = (dp_mode->timing.h_active / comp_info->dsc_info.slice_per_pkt); + while (slice_width >= max_slice_width) { + if (i == ARRAY_SIZE(slice_per_line_tbl)) + return -EINVAL; + + rec = &slice_per_line_tbl[i]; + comp_info->dsc_info.slice_per_pkt = rec->num_slices; + slice_width = (dp_mode->timing.h_active / + comp_info->dsc_info.slice_per_pkt); + i++; + } + comp_info->dsc_info.block_pred_enable = dp_panel->sink_dsc_caps.block_pred_en; comp_info->dsc_info.vbr_enable = 0; @@ -2932,6 +2946,8 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) if (in->base_panel) { memcpy(dp_panel->dpcd, in->base_panel->dpcd, DP_RECEIVER_CAP_SIZE + 1); + memcpy(dp_panel->dsc_dpcd, in->base_panel->dsc_dpcd, + DP_RECEIVER_DSC_CAP_SIZE + 1); memcpy(&dp_panel->link_info, &in->base_panel->link_info, sizeof(dp_panel->link_info)); dp_panel->mst_state = in->base_panel->mst_state; From 86411ada8478679693e15afd53727c73ab55d269 Mon Sep 17 00:00:00 2001 From: Christopher Braga Date: Tue, 29 Jan 2019 15:02:25 -0500 Subject: [PATCH 19/28] drm/msm/hdcp: Update HDCP QSEECOM to start auth on user command Split HDCP QSEECOM interface to separate authentication start from library initialization. Update DP and SDE driver code to conform to this new interface. Change-Id: I4780f41cba6b08e6b4a139c0fbe324ec8be803f7 Signed-off-by: Shivaprasad Hongal Signed-off-by: Christopher Braga Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_display.c | 29 +++++- drivers/gpu/drm/msm/dp/dp_hdcp2p2.c | 153 ++++++++++++++++++---------- drivers/gpu/drm/msm/sde_hdcp.h | 4 +- drivers/gpu/drm/msm/sde_hdcp_2x.c | 67 +++++++++--- drivers/gpu/drm/msm/sde_hdcp_2x.h | 12 ++- drivers/misc/hdcp_qseecom.c | 15 ++- include/linux/hdcp_qseecom.h | 5 +- 7 files changed, 202 insertions(+), 83 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 9d4346d7ccf9..566e1dd36ea0 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -233,10 +233,15 @@ static void dp_display_check_source_hdcp_caps(struct dp_display_private *dp) struct sde_hdcp_ops *ops = dev->ops; void *fd = dev->fd; - if (!fd || !ops || (dp->hdcp.source_cap & dev->ver)) + if (!fd || !ops) continue; - if (ops->feature_supported(fd)) + if (ops->set_mode && ops->set_mode(fd, dp->mst.mst_active)) + continue; + + if (!(dp->hdcp.source_cap & dev->ver) && + ops->feature_supported && + ops->feature_supported(fd)) dp->hdcp.source_cap |= dev->ver; } @@ -265,6 +270,11 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) dp_display_update_hdcp_info(dp); if (dp_display_is_hdcp_enabled(dp)) { + if (dp->hdcp.ops && dp->hdcp.ops->on && + dp->hdcp.ops->on(dp->hdcp.data)) { + dp_display_update_hdcp_status(dp, true); + return; + } status->hdcp_state = HDCP_STATE_AUTHENTICATING; } else { dp_display_update_hdcp_status(dp, true); @@ -296,6 +306,11 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) break; case HDCP_STATE_AUTH_FAIL: if (dp_display_is_ready(dp) && dp->power_on) { + if (ops && ops->on && ops->on(data)) { + dp_display_update_hdcp_status(dp, true); + return; + } + status->hdcp_state = HDCP_STATE_AUTHENTICATING; if (ops && ops->reauthenticate) { rc = ops->reauthenticate(data); if (rc) @@ -797,8 +812,10 @@ static void dp_display_clean(struct dp_display_private *dp) { int idx; struct dp_panel *dp_panel; + struct dp_link_hdcp_status *status = &dp->link->hdcp_status; - if (dp_display_is_hdcp_enabled(dp)) { + if (dp_display_is_hdcp_enabled(dp) && + status->hdcp_state != HDCP_STATE_INACTIVE) { cancel_delayed_work_sync(&dp->hdcp_cb_work); if (dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); @@ -1557,6 +1574,7 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) { struct dp_display_private *dp; struct dp_panel *dp_panel = panel; + struct dp_link_hdcp_status *status; int rc = 0; if (!dp_display || !panel) { @@ -1568,12 +1586,15 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) mutex_lock(&dp->session_lock); + status = &dp->link->hdcp_status; + if (!dp->power_on) { pr_debug("stream already powered off, return\n"); goto end; } - if (dp_display_is_hdcp_enabled(dp)) { + if (dp_display_is_hdcp_enabled(dp) && + status->hdcp_state != HDCP_STATE_INACTIVE) { cancel_delayed_work_sync(&dp->hdcp_cb_work); if (dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); diff --git a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c index c3e5b4f18e1b..2dabf5fd0589 100644 --- a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c +++ b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c @@ -65,6 +65,25 @@ struct dp_hdcp2p2_interrupts { struct dp_hdcp2p2_int_set *int_set; }; +static inline int dp_hdcp2p2_valid_handle(struct dp_hdcp2p2_ctrl *ctrl) +{ + if (!ctrl) { + pr_err("invalid input\n"); + return -EINVAL; + } + + if (!ctrl->lib_ctx) { + pr_err("HDCP library needs to be acquired\n"); + return -EINVAL; + } + + if (!ctrl->lib) { + pr_err("invalid lib ops data\n"); + return -EINVAL; + } + return 0; +} + static inline bool dp_hdcp2p2_is_valid_state(struct dp_hdcp2p2_ctrl *ctrl) { enum hdcp_transport_wakeup_cmd cmd; @@ -216,38 +235,77 @@ static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl) atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE); } +static int dp_hdcp2p2_register(void *input, bool mst_enabled) +{ + int rc; + enum sde_hdcp_2x_device_type device_type; + struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input; + + rc = dp_hdcp2p2_valid_handle(ctrl); + if (rc) + return rc; + + if (mst_enabled) + device_type = HDCP_TXMTR_DP_MST; + else + device_type = HDCP_TXMTR_DP; + + return sde_hdcp_2x_enable(ctrl->lib_ctx, device_type); +} + +static int dp_hdcp2p2_on(void *input) +{ + int rc = 0; + struct dp_hdcp2p2_ctrl *ctrl = input; + struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID}; + + rc = dp_hdcp2p2_valid_handle(ctrl); + if (rc) + return rc; + + cdata.cmd = HDCP_2X_CMD_START; + cdata.context = ctrl->lib_ctx; + rc = ctrl->lib->wakeup(&cdata); + if (rc) + pr_err("Unable to start the HDCP 2.2 library (%d)\n", rc); + + return rc; +} + static void dp_hdcp2p2_off(void *input) { + int rc; struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input; - struct hdcp_transport_wakeup_data cdata = { - HDCP_TRANSPORT_CMD_AUTHENTICATE}; + struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID}; - if (!ctrl) { - pr_err("invalid input\n"); + rc = dp_hdcp2p2_valid_handle(ctrl); + if (rc) return; - } - if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { - pr_err("hdcp is off\n"); - return; + if (atomic_read(&ctrl->auth_state) != HDCP_STATE_AUTH_FAIL) { + cdata.cmd = HDCP_2X_CMD_STOP; + cdata.context = ctrl->lib_ctx; + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); } dp_hdcp2p2_set_interrupts(ctrl, false); dp_hdcp2p2_reset(ctrl); - cdata.context = input; - dp_hdcp2p2_wakeup(&cdata); - kthread_park(ctrl->thread); + + sde_hdcp_2x_disable(ctrl->lib_ctx); } static int dp_hdcp2p2_authenticate(void *input) { + int rc; struct dp_hdcp2p2_ctrl *ctrl = input; struct hdcp_transport_wakeup_data cdata = { HDCP_TRANSPORT_CMD_AUTHENTICATE}; - int rc = 0; + rc = dp_hdcp2p2_valid_handle(ctrl); + if (rc) + return rc; dp_hdcp2p2_set_interrupts(ctrl, true); @@ -370,44 +428,34 @@ static int dp_hdcp2p2_aux_write_message(struct dp_hdcp2p2_ctrl *ctrl, static bool dp_hdcp2p2_feature_supported(void *input) { + int rc; struct dp_hdcp2p2_ctrl *ctrl = input; struct sde_hdcp_2x_ops *lib = NULL; bool supported = false; - if (!ctrl) { - pr_err("invalid input\n"); - goto end; - } + rc = dp_hdcp2p2_valid_handle(ctrl); + if (rc) + return supported; lib = ctrl->lib; - if (!lib) { - pr_err("invalid lib ops data\n"); - goto end; - } - if (lib->feature_supported) supported = lib->feature_supported( ctrl->lib_ctx); -end: + return supported; } static void dp_hdcp2p2_force_encryption(void *data, bool enable) { + int rc; struct dp_hdcp2p2_ctrl *ctrl = data; struct sde_hdcp_2x_ops *lib = NULL; - if (!ctrl) { - pr_err("invalid input\n"); + rc = dp_hdcp2p2_valid_handle(ctrl); + if (rc) return; - } lib = ctrl->lib; - if (!lib) { - pr_err("invalid lib ops data\n"); - return; - } - if (lib->force_encryption) lib->force_encryption(ctrl->lib_ctx, enable); } @@ -549,18 +597,13 @@ static void dp_hdcp2p2_link_check(struct dp_hdcp2p2_ctrl *ctrl) dp_hdcp2p2_wakeup_lib(ctrl, &cdata); } -static void dp_hdcp2p2_manage_session(struct dp_hdcp2p2_ctrl *ctrl) +static void dp_hdcp2p2_start_auth(struct dp_hdcp2p2_ctrl *ctrl) { - struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID}; - + struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_START_AUTH}; cdata.context = ctrl->lib_ctx; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING) - cdata.cmd = HDCP_2X_CMD_START; - else - cdata.cmd = HDCP_2X_CMD_STOP; - - dp_hdcp2p2_wakeup_lib(ctrl, &cdata); + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); } static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl, @@ -606,34 +649,31 @@ static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl, static int dp_hdcp2p2_cp_irq(void *input) { - int rc = 0; + int rc; struct dp_hdcp2p2_ctrl *ctrl = input; - if (!ctrl) { - pr_err("invalid input\n"); - return -EINVAL; - } + rc = dp_hdcp2p2_valid_handle(ctrl); + if (rc) + return rc; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTH_FAIL || atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { pr_err("invalid hdcp state\n"); - rc = -EINVAL; - goto error; + return -EINVAL; } ctrl->sink_rx_status = 0; rc = dp_hdcp2p2_read_rx_status(ctrl, &ctrl->sink_rx_status); if (rc) { pr_err("failed to read rx status\n"); - goto error; + return rc; } pr_debug("sink_rx_status=0x%x\n", ctrl->sink_rx_status); if (!ctrl->sink_rx_status) { pr_debug("not a hdcp 2.2 irq\n"); - rc = -EINVAL; - goto error; + return -EINVAL; } @@ -641,8 +681,6 @@ static int dp_hdcp2p2_cp_irq(void *input) wake_up(&ctrl->wait_q); return 0; -error: - return rc; } static int dp_hdcp2p2_isr(void *input) @@ -720,9 +758,13 @@ void sde_dp_hdcp2p2_deinit(void *input) return; } - cdata.cmd = HDCP_2X_CMD_STOP; - cdata.context = ctrl->lib_ctx; - dp_hdcp2p2_wakeup_lib(ctrl, &cdata); + if (atomic_read(&ctrl->auth_state) != HDCP_STATE_AUTH_FAIL) { + cdata.cmd = HDCP_2X_CMD_STOP; + cdata.context = ctrl->lib_ctx; + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); + } + + sde_hdcp_2x_deregister(ctrl->lib_ctx); kthread_stop(ctrl->thread); @@ -777,7 +819,7 @@ static int dp_hdcp2p2_main(void *data) dp_hdcp2p2_link_check(ctrl); break; case HDCP_TRANSPORT_CMD_AUTHENTICATE: - dp_hdcp2p2_manage_session(ctrl); + dp_hdcp2p2_start_auth(ctrl); break; default: break; @@ -798,6 +840,8 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data) .feature_supported = dp_hdcp2p2_feature_supported, .force_encryption = dp_hdcp2p2_force_encryption, .sink_support = dp_hdcp2p2_supported, + .set_mode = dp_hdcp2p2_register, + .on = dp_hdcp2p2_on, .off = dp_hdcp2p2_off, .cp_irq = dp_hdcp2p2_cp_irq, }; @@ -854,7 +898,6 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data) register_data.hdcp_data = &ctrl->lib_ctx; register_data.client_ops = &client_ops; register_data.ops = &hdcp2x_ops; - register_data.device_type = HDCP_TXMTR_DP; register_data.client_data = ctrl; rc = sde_hdcp_2x_register(®ister_data); diff --git a/drivers/gpu/drm/msm/sde_hdcp.h b/drivers/gpu/drm/msm/sde_hdcp.h index 03178ca01548..2b5c37cc30f2 100644 --- a/drivers/gpu/drm/msm/sde_hdcp.h +++ b/drivers/gpu/drm/msm/sde_hdcp.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2012, 2014-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2012, 2014-2019, The Linux Foundation. All rights reserved. */ #ifndef __SDE_HDCP_H__ @@ -67,6 +67,8 @@ struct sde_hdcp_ops { bool (*feature_supported)(void *input); void (*force_encryption)(void *input, bool enable); bool (*sink_support)(void *input); + int (*set_mode)(void *input, bool mst_enabled); + int (*on)(void *input); void (*off)(void *hdcp_ctrl); }; diff --git a/drivers/gpu/drm/msm/sde_hdcp_2x.c b/drivers/gpu/drm/msm/sde_hdcp_2x.c index c72ab1bd313f..e8adf32a058f 100644 --- a/drivers/gpu/drm/msm/sde_hdcp_2x.c +++ b/drivers/gpu/drm/msm/sde_hdcp_2x.c @@ -477,19 +477,26 @@ static void sde_hdcp_2x_msg_sent(struct sde_hdcp_2x_ctrl *hdcp) static void sde_hdcp_2x_init(struct sde_hdcp_2x_ctrl *hdcp) { int rc; - rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_START, &hdcp->app_data); if (rc) - goto exit; + sde_hdcp_2x_clean(hdcp); +} - pr_debug("[tz]: %s\n", sde_hdcp_2x_message_name( - hdcp->app_data.response.data[0])); +static void sde_hdcp_2x_start_auth(struct sde_hdcp_2x_ctrl *hdcp) +{ + int rc; + + rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_START_AUTH, + &hdcp->app_data); + if (rc) { + sde_hdcp_2x_clean(hdcp); + return; + } + + pr_debug("message received from TZ: %s\n", + sde_hdcp_2x_message_name(hdcp->app_data.response.data[0])); sde_hdcp_2x_send_message(hdcp); - - return; -exit: - sde_hdcp_2x_clean(hdcp); } static void sde_hdcp_2x_timeout(struct sde_hdcp_2x_ctrl *hdcp) @@ -709,6 +716,9 @@ static int sde_hdcp_2x_main(void *data) case HDCP_2X_CMD_STOP: sde_hdcp_2x_clean(hdcp); break; + case HDCP_2X_CMD_START_AUTH: + sde_hdcp_2x_start_auth(hdcp); + break; case HDCP_2X_CMD_MSG_SEND_SUCCESS: sde_hdcp_2x_msg_sent(hdcp); break; @@ -779,14 +789,11 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data) hdcp->client_data = data->client_data; hdcp->client_ops = data->client_ops; - hdcp->device_type = data->device_type; - - hdcp->hdcp2_ctx = hdcp2_init(hdcp->device_type); INIT_KFIFO(hdcp->cmd_q); init_waitqueue_head(&hdcp->wait_q); - atomic_set(&hdcp->hdcp_off, 0); + atomic_set(&hdcp->hdcp_off, 1); init_completion(&hdcp->response_completion); @@ -811,6 +818,40 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data) return rc; } +int sde_hdcp_2x_enable(void *data, enum sde_hdcp_2x_device_type device_type) +{ + int rc = 0; + struct sde_hdcp_2x_ctrl *hdcp = data; + + if (!hdcp) + return -EINVAL; + + if (hdcp->hdcp2_ctx) { + pr_debug("HDCP library context already acquired\n"); + return 0; + } + + hdcp->device_type = device_type; + hdcp->hdcp2_ctx = hdcp2_init(hdcp->device_type); + if (!hdcp->hdcp2_ctx) { + pr_err("Unable to acquire HDCP library handle\n"); + return -ENOMEM; + } + + return rc; +} + +void sde_hdcp_2x_disable(void *data) +{ + struct sde_hdcp_2x_ctrl *hdcp = data; + + if (!hdcp->hdcp2_ctx) + return; + + hdcp2_deinit(hdcp->hdcp2_ctx); + hdcp->hdcp2_ctx = NULL; +} + void sde_hdcp_2x_deregister(void *data) { struct sde_hdcp_2x_ctrl *hdcp = data; @@ -818,7 +859,7 @@ void sde_hdcp_2x_deregister(void *data) if (!hdcp) return; + sde_hdcp_2x_disable(data); kthread_stop(hdcp->thread); - hdcp2_deinit(hdcp->hdcp2_ctx); kzfree(hdcp); } diff --git a/drivers/gpu/drm/msm/sde_hdcp_2x.h b/drivers/gpu/drm/msm/sde_hdcp_2x.h index 47247e40bde2..e6c10a745622 100644 --- a/drivers/gpu/drm/msm/sde_hdcp_2x.h +++ b/drivers/gpu/drm/msm/sde_hdcp_2x.h @@ -15,8 +15,9 @@ /** * enum sde_hdcp_2x_wakeup_cmd - commands for interacting with HDCP driver * @HDCP_2X_CMD_INVALID: initialization value - * @HDCP_2X_CMD_START: start authentication - * @HDCP_2X_CMD_STOP: stop authentication + * @HDCP_2X_CMD_START: start HDCP driver + * @HDCP_2X_CMD_START_AUTH: start authentication + * @HDCP_2X_CMD_STOP: stop HDCP driver * @HDCP_2X_CMD_MSG_SEND_SUCCESS: sending message to sink succeeded * @HDCP_2X_CMD_MSG_SEND_FAILED: sending message to sink failed * @HDCP_2X_CMD_MSG_SEND_TIMEOUT: sending message to sink timed out @@ -30,6 +31,7 @@ enum sde_hdcp_2x_wakeup_cmd { HDCP_2X_CMD_INVALID, HDCP_2X_CMD_START, + HDCP_2X_CMD_START_AUTH, HDCP_2X_CMD_STOP, HDCP_2X_CMD_MSG_SEND_SUCCESS, HDCP_2X_CMD_MSG_SEND_FAILED, @@ -66,7 +68,8 @@ enum hdcp_transport_wakeup_cmd { enum sde_hdcp_2x_device_type { HDCP_TXMTR_HDMI = 0x8001, - HDCP_TXMTR_DP = 0x8002 + HDCP_TXMTR_DP = 0x8002, + HDCP_TXMTR_DP_MST = 0x8003 }; /** @@ -190,12 +193,13 @@ struct hdcp_transport_ops { struct sde_hdcp_2x_register_data { struct hdcp_transport_ops *client_ops; struct sde_hdcp_2x_ops *ops; - enum sde_hdcp_2x_device_type device_type; void *client_data; void **hdcp_data; }; /* functions for the HDCP 2.2 state machine module */ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data); +int sde_hdcp_2x_enable(void *data, enum sde_hdcp_2x_device_type device_type); +void sde_hdcp_2x_disable(void *data); void sde_hdcp_2x_deregister(void *data); #endif diff --git a/drivers/misc/hdcp_qseecom.c b/drivers/misc/hdcp_qseecom.c index 53947b53daa0..6de29bcc6cc0 100644 --- a/drivers/misc/hdcp_qseecom.c +++ b/drivers/misc/hdcp_qseecom.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "[hdcp-qseecom] %s: " fmt, __func__ @@ -1044,11 +1044,7 @@ static int hdcp2_app_start(struct hdcp2_handle *handle) } rc = handle->tx_init(handle); - if (rc) - goto error; - if (!handle->legacy_app) - rc = hdcp2_app_start_auth(handle); error: return rc; } @@ -1188,6 +1184,7 @@ int hdcp2_force_encryption(void *ctx, uint32_t enable) pr_err("failed, rc=%d\n", rc); return rc; } +EXPORT_SYMBOL(hdcp2_force_encryption); static int hdcp2_app_query_stream(struct hdcp2_handle *handle) { @@ -1236,6 +1233,9 @@ int hdcp2_app_comm(void *ctx, enum hdcp2_app_cmd cmd, case HDCP2_CMD_START: rc = hdcp2_app_start(handle); break; + case HDCP2_CMD_START_AUTH: + rc = hdcp2_app_start_auth(handle); + break; case HDCP2_CMD_PROCESS_MSG: rc = hdcp2_app_process_msg(handle); break; @@ -1268,6 +1268,7 @@ int hdcp2_app_comm(void *ctx, enum hdcp2_app_cmd cmd, error: return rc; } +EXPORT_SYMBOL(hdcp2_app_comm); static int hdcp2_open_stream_helper(struct hdcp2_handle *handle, uint8_t vc_payload_id, @@ -1322,6 +1323,7 @@ int hdcp2_open_stream(void *ctx, uint8_t vc_payload_id, uint8_t stream_number, return hdcp2_open_stream_helper(handle, vc_payload_id, stream_number, stream_id); } +EXPORT_SYMBOL(hdcp2_open_stream); static int hdcp2_close_stream_helper(struct hdcp2_handle *handle, uint32_t stream_id) @@ -1368,6 +1370,7 @@ int hdcp2_close_stream(void *ctx, uint32_t stream_id) return hdcp2_close_stream_helper(handle, stream_id); } +EXPORT_SYMBOL(hdcp2_close_stream); void *hdcp2_init(u32 device_type) { @@ -1382,11 +1385,13 @@ void *hdcp2_init(u32 device_type) error: return handle; } +EXPORT_SYMBOL(hdcp2_init); void hdcp2_deinit(void *ctx) { kzfree(ctx); } +EXPORT_SYMBOL(hdcp2_deinit); void *hdcp1_init(void) { diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h index 96b24a1c248b..34ffb9fe75f8 100644 --- a/include/linux/hdcp_qseecom.h +++ b/include/linux/hdcp_qseecom.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. */ #ifndef __HDCP_QSEECOM_H @@ -11,6 +11,7 @@ enum hdcp2_app_cmd { HDCP2_CMD_START, + HDCP2_CMD_START_AUTH, HDCP2_CMD_STOP, HDCP2_CMD_PROCESS_MSG, HDCP2_CMD_TIMEOUT, @@ -35,6 +36,8 @@ static inline const char *hdcp2_app_cmd_str(enum hdcp2_app_cmd cmd) switch (cmd) { case HDCP2_CMD_START: return HDCP_QSEECOM_ENUM_STR(HDCP2_CMD_START); + case HDCP2_CMD_START_AUTH: + return HDCP_QSEECOM_ENUM_STR(HDCP2_CMD_START_AUTH); case HDCP2_CMD_STOP: return HDCP_QSEECOM_ENUM_STR(HDCP2_CMD_STOP); case HDCP2_CMD_PROCESS_MSG: From 0d3fb743c8dfae4678d0b0659303ca55a436459c Mon Sep 17 00:00:00 2001 From: Christopher Braga Date: Thu, 24 Jan 2019 12:59:42 -0500 Subject: [PATCH 20/28] drm/msm/hdcp: Extend SDE HDCP to support stream registration MST HDCP functionality requires lower level HDCP libraries to be aware of all active DP MST streams. Update the HDCP interface to support DP MST mode, and allow drivers to register and deregister streams for use with HDCP. Change-Id: I03b89a8f34056c7f92c7483d0503f47b007b6f6b Signed-off-by: Christopher Braga Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/sde_hdcp.h | 19 ++++ drivers/gpu/drm/msm/sde_hdcp_2x.c | 175 +++++++++++++++++++++++++++++- drivers/gpu/drm/msm/sde_hdcp_2x.h | 22 +++- 3 files changed, 207 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/msm/sde_hdcp.h b/drivers/gpu/drm/msm/sde_hdcp.h index 2b5c37cc30f2..5c1dc4af206f 100644 --- a/drivers/gpu/drm/msm/sde_hdcp.h +++ b/drivers/gpu/drm/msm/sde_hdcp.h @@ -13,12 +13,15 @@ #include #include #include +#include #include #include #include #include #include "sde_kms.h" +#define MAX_STREAM_COUNT 2 + enum sde_hdcp_client_id { HDCP_CLIENT_HDMI, HDCP_CLIENT_DP, @@ -38,6 +41,18 @@ enum sde_hdcp_version { HDCP_VERSION_MAX = BIT(2), }; +struct stream_info { + u8 stream_id; + u8 virtual_channel; +}; + +struct sde_hdcp_stream { + struct list_head list; + u8 stream_id; + u8 virtual_channel; + u32 stream_handle; +}; + struct sde_hdcp_init_data { struct device *msm_hdcp_dev; struct dss_io_data *core_io; @@ -70,6 +85,10 @@ struct sde_hdcp_ops { int (*set_mode)(void *input, bool mst_enabled); int (*on)(void *input); void (*off)(void *hdcp_ctrl); + int (*register_streams)(void *input, u8 num_streams, + struct stream_info *streams); + int (*deregister_streams)(void *input, u8 num_streams, + struct stream_info *streams); }; static inline const char *sde_hdcp_state_name(enum sde_hdcp_state hdcp_state) diff --git a/drivers/gpu/drm/msm/sde_hdcp_2x.c b/drivers/gpu/drm/msm/sde_hdcp_2x.c index e8adf32a058f..f578e09cd26e 100644 --- a/drivers/gpu/drm/msm/sde_hdcp_2x.c +++ b/drivers/gpu/drm/msm/sde_hdcp_2x.c @@ -64,6 +64,10 @@ struct sde_hdcp_2x_ctrl { atomic_t hdcp_off; enum sde_hdcp_2x_device_type device_type; u8 min_enc_level; + struct list_head stream_handles; + u8 stream_count; + struct stream_info *streams; + u8 num_streams; struct task_struct *thread; struct completion response_completion; @@ -315,6 +319,8 @@ static void sde_hdcp_2x_force_encryption(void *data, bool enable) static void sde_hdcp_2x_clean(struct sde_hdcp_2x_ctrl *hdcp) { + struct list_head *element; + struct sde_hdcp_stream *stream_entry; struct hdcp_transport_wakeup_data cdata = {HDCP_TRANSPORT_CMD_INVALID}; hdcp->authenticated = false; @@ -322,10 +328,20 @@ static void sde_hdcp_2x_clean(struct sde_hdcp_2x_ctrl *hdcp) cdata.context = hdcp->client_data; cdata.cmd = HDCP_TRANSPORT_CMD_STATUS_FAILED; - if (!atomic_read(&hdcp->hdcp_off)) - sde_hdcp_2x_wakeup_client(hdcp, &cdata); + while (!list_empty(&hdcp->stream_handles)) { + element = hdcp->stream_handles.next; + list_del(element); - atomic_set(&hdcp->hdcp_off, 1); + stream_entry = list_entry(element, struct sde_hdcp_stream, + list); + hdcp2_close_stream(hdcp->hdcp2_ctx, + stream_entry->stream_handle); + kzfree(stream_entry); + hdcp->stream_count--; + } + + if (!atomic_xchg(&hdcp->hdcp_off, 1)) + sde_hdcp_2x_wakeup_client(hdcp, &cdata); hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_STOP, &hdcp->app_data); } @@ -546,7 +562,8 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp) goto exit; } - if (hdcp->device_type == HDCP_TXMTR_DP) { + if (hdcp->device_type == HDCP_TXMTR_DP || + hdcp->device_type == HDCP_TXMTR_DP_MST) { msg[0] = hdcp->last_msg; message_id_bytes = 1; } @@ -632,6 +649,147 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp) sde_hdcp_2x_clean(hdcp); } +static struct list_head *sde_hdcp_2x_stream_present( + struct sde_hdcp_2x_ctrl *hdcp, u8 stream_id, u8 virtual_channel) +{ + struct sde_hdcp_stream *stream_entry; + struct list_head *entry; + bool present = false; + + list_for_each(entry, &hdcp->stream_handles) { + stream_entry = list_entry(entry, + struct sde_hdcp_stream, list); + if (stream_entry->virtual_channel == virtual_channel && + stream_entry->stream_id == stream_id) { + present = true; + break; + } + } + + if (!present) + entry = NULL; + return entry; +} + +static void sde_hdcp_2x_open_stream(struct sde_hdcp_2x_ctrl *hdcp) +{ + int rc; + size_t iterations, i; + u8 stream_id; + u8 virtual_channel; + u32 stream_handle = 0; + bool query_streams = false; + + if (!hdcp->streams) { + pr_err("Array of streams to register is NULL\n"); + return; + } + + iterations = min(hdcp->num_streams, (u8)(MAX_STREAM_COUNT)); + + for (i = 0; i < iterations; i++) { + if (hdcp->stream_count == MAX_STREAM_COUNT) { + pr_debug("Registered the maximum amount of streams\n"); + break; + } + + stream_id = hdcp->streams[i].stream_id; + virtual_channel = hdcp->streams[i].virtual_channel; + + pr_debug("Opening stream %d, virtual channel %d\n", + stream_id, virtual_channel); + + if (sde_hdcp_2x_stream_present(hdcp, stream_id, + virtual_channel)) { + pr_debug("Stream %d, virtual channel %d already open\n", + stream_id, virtual_channel); + continue; + } + + rc = hdcp2_open_stream(hdcp->hdcp2_ctx, virtual_channel, + stream_id, &stream_handle); + if (rc) { + pr_err("Unable to open stream %d, virtual channel %d\n", + stream_id, virtual_channel); + } else { + struct sde_hdcp_stream *stream = + kzalloc(sizeof(struct sde_hdcp_stream), + GFP_KERNEL); + if (!stream) + break; + + INIT_LIST_HEAD(&stream->list); + stream->stream_handle = stream_handle; + stream->stream_id = stream_id; + stream->virtual_channel = virtual_channel; + + list_add(&stream->list, &hdcp->stream_handles); + hdcp->stream_count++; + + query_streams = true; + } + } + + if (query_streams && hdcp->authenticated) + sde_hdcp_2x_query_stream(hdcp); +} + +static void sde_hdcp_2x_close_stream(struct sde_hdcp_2x_ctrl *hdcp) +{ + int rc; + size_t iterations, i; + u8 stream_id; + u8 virtual_channel; + struct list_head *entry; + struct sde_hdcp_stream *stream_entry; + bool query_streams = false; + + if (!hdcp->streams) { + pr_err("Array of streams to register is NULL\n"); + return; + } + + iterations = min(hdcp->num_streams, (u8)(MAX_STREAM_COUNT)); + + for (i = 0; i < iterations; i++) { + if (hdcp->stream_count == 0) { + pr_debug("No streams are currently registered\n"); + return; + } + + stream_id = hdcp->streams[i].stream_id; + virtual_channel = hdcp->streams[i].virtual_channel; + + pr_debug("Closing stream %d, virtual channel %d\n", + stream_id, virtual_channel); + + entry = sde_hdcp_2x_stream_present(hdcp, stream_id, + virtual_channel); + + if (!entry) { + pr_err("Unable to find stream %d, virtual channel %d\n" + , stream_id, virtual_channel); + continue; + } + + stream_entry = list_entry(entry, struct sde_hdcp_stream, + list); + + rc = hdcp2_close_stream(hdcp->hdcp2_ctx, + stream_entry->stream_handle); + if (rc) + pr_err("Unable to close stream %d, virtual channel %d\n" + , stream_id, virtual_channel); + hdcp->stream_count--; + list_del(entry); + kzfree(stream_entry); + query_streams = true; + } + + if (query_streams && hdcp->authenticated) + sde_hdcp_2x_query_stream(hdcp); +} + /** sde_hdcp_2x_wakeup() - wakeup the module to execute a requested command * @data: data required for executing corresponding command. * @@ -655,6 +813,8 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data) hdcp->timeout_left = data->timeout; hdcp->total_message_length = data->total_message_length; hdcp->min_enc_level = data->min_enc_level; + hdcp->streams = data->streams; + hdcp->num_streams = data->num_streams; if (!completion_done(&hdcp->response_completion)) complete_all(&hdcp->response_completion); @@ -743,6 +903,12 @@ static int sde_hdcp_2x_main(void *data) } sde_hdcp_2x_query_stream(hdcp); break; + case HDCP_2X_CMD_OPEN_STREAMS: + sde_hdcp_2x_open_stream(hdcp); + break; + case HDCP_2X_CMD_CLOSE_STREAMS: + sde_hdcp_2x_close_stream(hdcp); + break; default: break; } @@ -787,6 +953,7 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data) goto unlock; } + INIT_LIST_HEAD(&hdcp->stream_handles); hdcp->client_data = data->client_data; hdcp->client_ops = data->client_ops; diff --git a/drivers/gpu/drm/msm/sde_hdcp_2x.h b/drivers/gpu/drm/msm/sde_hdcp_2x.h index e6c10a745622..cfcd7ce1b543 100644 --- a/drivers/gpu/drm/msm/sde_hdcp_2x.h +++ b/drivers/gpu/drm/msm/sde_hdcp_2x.h @@ -27,6 +27,8 @@ * @HDCP_2X_CMD_QUERY_STREAM_TYPE: start content stream processing * @HDCP_2X_CMD_LINK_FAILED: link failure notification * @HDCP_2X_CMD_MIN_ENC_LEVEL: trigger minimum encryption level change + * @HDCP_2X_CMD_OPEN_STREAMS: open a virtual channel + * @HDCP_2X_CMD_CLOSE_STREAMS: close a virtual channel */ enum sde_hdcp_2x_wakeup_cmd { HDCP_2X_CMD_INVALID, @@ -42,6 +44,8 @@ enum sde_hdcp_2x_wakeup_cmd { HDCP_2X_CMD_QUERY_STREAM_TYPE, HDCP_2X_CMD_LINK_FAILED, HDCP_2X_CMD_MIN_ENC_LEVEL, + HDCP_2X_CMD_OPEN_STREAMS, + HDCP_2X_CMD_CLOSE_STREAMS, }; /** @@ -74,11 +78,13 @@ enum sde_hdcp_2x_device_type { /** * struct sde_hdcp_2x_lib_wakeup_data - command and data send to HDCP driver - * @cmd: command type - * @context: void pointer to the HDCP driver instance - * @buf: message received from the sink - * @buf_len: length of message received from the sink - * @timeout: time out value for timed transactions + * @cmd: command type + * @context: void pointer to the HDCP driver instance + * @buf: message received from the sink + * @buf_len: length of message received from the sink + * @timeout: time out value for timed transactions + * @streams: list indicating which streams need adjustment + * @num_streams: number of entries in streams */ struct sde_hdcp_2x_wakeup_data { enum sde_hdcp_2x_wakeup_cmd cmd; @@ -86,6 +92,8 @@ struct sde_hdcp_2x_wakeup_data { uint32_t total_message_length; uint32_t timeout; u8 min_enc_level; + struct stream_info *streams; + u8 num_streams; }; /** @@ -154,6 +162,10 @@ static inline const char *sde_hdcp_2x_cmd_to_str( return TO_STR(HDCP_2X_CMD_MSG_RECV_TIMEOUT); case HDCP_2X_CMD_QUERY_STREAM_TYPE: return TO_STR(HDCP_2X_CMD_QUERY_STREAM_TYPE); + case HDCP_2X_CMD_OPEN_STREAMS: + return TO_STR(HDCP_2X_CMD_OPEN_STREAMS); + case HDCP_2X_CMD_CLOSE_STREAMS: + return TO_STR(HDCP_2X_CMD_CLOSE_STREAMS); default: return "UNKNOWN"; } From cc843c3ba1a7f730ad602b8184aa84397339705c Mon Sep 17 00:00:00 2001 From: Christopher Braga Date: Thu, 24 Jan 2019 13:08:35 -0500 Subject: [PATCH 21/28] drm/msm/dp: Add MST support for HDCP 2.2 Add MST support for HDCP 2.2 by initializing the HDCP library in MST mode, and adding the support to dynamically add and remove streams. Change-Id: I46ed13f4bc56dca5c2ef08745f435b541265be98 Signed-off-by: Christopher Braga Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_display.c | 62 ++++++++++++++++++++++++++++- drivers/gpu/drm/msm/dp/dp_hdcp2p2.c | 47 ++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 566e1dd36ea0..6949793e5a9e 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -248,6 +248,48 @@ static void dp_display_check_source_hdcp_caps(struct dp_display_private *dp) dp_display_update_hdcp_status(dp, false); } +static void dp_display_hdcp_register_streams(struct dp_display_private *dp) +{ + int rc; + size_t i; + struct sde_hdcp_ops *ops = dp->hdcp.ops; + void *data = dp->hdcp.data; + + if (dp_display_is_ready(dp) && dp->mst.mst_active && ops && + ops->register_streams){ + struct stream_info streams[DP_STREAM_MAX]; + int index = 0; + + pr_debug("Registering all active panel streams with HDCP\n"); + for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) { + if (!dp->active_panels[i]) + continue; + streams[index].stream_id = i; + streams[index].virtual_channel = + dp->active_panels[i]->vcpi; + index++; + } + + if (index > 0) { + rc = ops->register_streams(data, index, streams); + if (rc) + pr_err("failed to register streams. rc = %d\n", + rc); + } + } +} + +static void dp_display_hdcp_deregister_stream(struct dp_display_private *dp, + enum dp_stream_id stream_id) +{ + if (dp->hdcp.ops->deregister_streams) { + struct stream_info stream = {stream_id, 0}; + + pr_debug("Deregistering stream within HDCP library\n"); + dp->hdcp.ops->deregister_streams(dp->hdcp.data, 1, &stream); + } +} + static void dp_display_hdcp_cb_work(struct work_struct *work) { struct dp_display_private *dp; @@ -301,6 +343,7 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) switch (status->hdcp_state) { case HDCP_STATE_AUTHENTICATING: + dp_display_hdcp_register_streams(dp); if (dp->hdcp.ops && dp->hdcp.ops->authenticate) rc = dp->hdcp.ops->authenticate(data); break; @@ -310,6 +353,7 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) dp_display_update_hdcp_status(dp, true); return; } + dp_display_hdcp_register_streams(dp); status->hdcp_state = HDCP_STATE_AUTHENTICATING; if (ops && ops->reauthenticate) { rc = ops->reauthenticate(data); @@ -321,6 +365,7 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) } break; default: + dp_display_hdcp_register_streams(dp); break; } } @@ -1576,6 +1621,7 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) struct dp_panel *dp_panel = panel; struct dp_link_hdcp_status *status; int rc = 0; + size_t i; if (!dp_display || !panel) { pr_err("invalid input\n"); @@ -1595,13 +1641,27 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) if (dp_display_is_hdcp_enabled(dp) && status->hdcp_state != HDCP_STATE_INACTIVE) { - cancel_delayed_work_sync(&dp->hdcp_cb_work); + flush_delayed_work(&dp->hdcp_cb_work); + if (dp->mst.mst_active) { + dp_display_hdcp_deregister_stream(dp, + dp_panel->stream_id); + for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) { + if (i != dp_panel->stream_id) + continue; + if (dp->active_panels[i]) { + pr_debug("Streams are still active. Skip disabling HDCP\n"); + goto stream; + } + } + } + if (dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); dp_display_update_hdcp_status(dp, true); } +stream: if (dp_panel->audio_supported) dp_panel->audio->off(dp_panel->audio); diff --git a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c index 2dabf5fd0589..f71c25e66cb4 100644 --- a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c +++ b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c @@ -748,6 +748,51 @@ static bool dp_hdcp2p2_supported(void *input) return false; } +static int dp_hdcp2p2_change_streams(struct dp_hdcp2p2_ctrl *ctrl, + struct sde_hdcp_2x_wakeup_data *cdata) +{ + if (!ctrl || cdata->num_streams == 0 || !cdata->streams) { + pr_err("invalid input\n"); + return -EINVAL; + } + + if (!ctrl->lib_ctx) { + pr_err("HDCP library needs to be acquired\n"); + return -EINVAL; + } + + if (!ctrl->lib) { + pr_err("invalid lib ops data\n"); + return -EINVAL; + } + + cdata->context = ctrl->lib_ctx; + return ctrl->lib->wakeup(cdata); +} + + +static int dp_hdcp2p2_register_streams(void *input, u8 num_streams, + struct stream_info *streams) +{ + struct dp_hdcp2p2_ctrl *ctrl = input; + struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_OPEN_STREAMS}; + + cdata.streams = streams; + cdata.num_streams = num_streams; + return dp_hdcp2p2_change_streams(ctrl, &cdata); +} + +static int dp_hdcp2p2_deregister_streams(void *input, u8 num_streams, + struct stream_info *streams) +{ + struct dp_hdcp2p2_ctrl *ctrl = input; + struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_CLOSE_STREAMS}; + + cdata.streams = streams; + cdata.num_streams = num_streams; + return dp_hdcp2p2_change_streams(ctrl, &cdata); +} + void sde_dp_hdcp2p2_deinit(void *input) { struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input; @@ -844,6 +889,8 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data) .on = dp_hdcp2p2_on, .off = dp_hdcp2p2_off, .cp_irq = dp_hdcp2p2_cp_irq, + .register_streams = dp_hdcp2p2_register_streams, + .deregister_streams = dp_hdcp2p2_deregister_streams, }; static struct hdcp_transport_ops client_ops = { From 0a3f564d1323df0210f865c74548a931ee257d63 Mon Sep 17 00:00:00 2001 From: Govinda Rajulu Chenna Date: Mon, 21 Jan 2019 12:54:47 -0500 Subject: [PATCH 22/28] drm/dp: export remote dpcd read/write from drm_dp_mst_topology This change exports remote dpcd read/write functions from drm_dp_mst_topology layer in order to read/write dpcd for a select dp mst port. Change-Id: Ib91b71079c2ecbc3702fb89839e46771365c326a Signed-off-by: Govinda Rajulu Chenna Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/drm_dp_mst_topology.c | 18 ++++++------------ include/drm/drm_dp_mst_helper.h | 8 ++++++++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 29f9cb7f8e27..6794d6039639 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -51,10 +51,6 @@ static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, int id, struct drm_dp_payload *payload); -static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int offset, int size, u8 *bytes); - static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb); static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, @@ -1403,7 +1399,6 @@ static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, return false; } -#if 0 static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes) { struct drm_dp_sideband_msg_req_body req; @@ -1416,7 +1411,6 @@ static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 return 0; } -#endif static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr, bool up, u8 *msg, int len) @@ -1982,8 +1976,7 @@ int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr) } EXPORT_SYMBOL(drm_dp_update_payload_part2); -#if 0 /* unused as of yet */ -static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, +int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int offset, int size, u8 *bytes) { @@ -2037,11 +2030,11 @@ static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, drm_dp_put_mst_branch_device(mstb); return ret; } -#endif +EXPORT_SYMBOL(drm_dp_send_dpcd_read); -static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, - int offset, int size, u8 *bytes) +int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int offset, int size, u8 *bytes) { int len; int ret; @@ -2075,6 +2068,7 @@ static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, drm_dp_put_mst_branch_device(mstb); return ret; } +EXPORT_SYMBOL(drm_dp_send_dpcd_write); static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type) { diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 7f78d26a0766..a72efa06aa66 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -634,4 +634,12 @@ int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state, int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, bool power_up); +int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int offset, int size, u8 *bytes); + +int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int offset, int size, u8 *bytes); + #endif From 2ce9abfa6886257e60e316bfd73a459ba0c96258 Mon Sep 17 00:00:00 2001 From: Christopher Braga Date: Tue, 19 Feb 2019 12:15:16 -0500 Subject: [PATCH 23/28] drm/msm/dp: update HDCP worker to wait for sink synchronization Prevent HDCP worker from operating when sink synchronization has not been achieved to prevent HDCP authentication issues with some sinks. Change-Id: Id7948f53c60c5417306f7615c00e8a0ff7cbdeca Signed-off-by: Christopher Braga Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_display.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 6949793e5a9e..73341203d3a6 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -299,12 +299,21 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) void *data; int rc = 0; u32 hdcp_auth_state; + u8 sink_status = 0; dp = container_of(dw, struct dp_display_private, hdcp_cb_work); if (!dp->power_on || !dp->is_connected || atomic_read(&dp->aborted)) return; + drm_dp_dpcd_readb(dp->aux->drm_aux, DP_SINK_STATUS, &sink_status); + sink_status &= (DP_RECEIVE_PORT_0_STATUS | DP_RECEIVE_PORT_1_STATUS); + if (sink_status < 1) { + pr_debug("Sink not synchronized. Queuing again then exiting\n"); + queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ); + return; + } + status = &dp->link->hdcp_status; if (status->hdcp_state == HDCP_STATE_INACTIVE) { From 8f49420abb9ba4a417d6c212ffa5c0ab412c7631 Mon Sep 17 00:00:00 2001 From: Christopher Braga Date: Tue, 19 Feb 2019 12:12:33 -0500 Subject: [PATCH 24/28] drm/msm/dp: update HDCP stream de-registration logic Extend HDCP stream de-registration code to provide virtual channel ID in addition to the stream id. Also fix flaw in de-registration logic that resulted in HDCP off being skipped in valid cases. Change-Id: Iee10193130ca0071b8136e4abade2ba35af20332 Signed-off-by: Christopher Braga Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_display.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 73341203d3a6..9688032d844d 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -283,7 +283,8 @@ static void dp_display_hdcp_deregister_stream(struct dp_display_private *dp, enum dp_stream_id stream_id) { if (dp->hdcp.ops->deregister_streams) { - struct stream_info stream = {stream_id, 0}; + struct stream_info stream = {stream_id, + dp->active_panels[stream_id]->vcpi}; pr_debug("Deregistering stream within HDCP library\n"); dp->hdcp.ops->deregister_streams(dp->hdcp.data, 1, &stream); @@ -1655,9 +1656,8 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) dp_display_hdcp_deregister_stream(dp, dp_panel->stream_id); for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) { - if (i != dp_panel->stream_id) - continue; - if (dp->active_panels[i]) { + if (i != dp_panel->stream_id && + dp->active_panels[i]) { pr_debug("Streams are still active. Skip disabling HDCP\n"); goto stream; } From 0aa44867ab38ee8338534612a83ae45bed85521f Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Tue, 26 Mar 2019 09:32:17 -0700 Subject: [PATCH 25/28] Documentation: add P/N swap lane map for DisplayPort on Kona Adds optional property to specify P/N swap lane map for DisplayPort. Change-Id: Ia625d0efd48f1e0a67df49ea2e060733ace8c3b5 Signed-off-by: Tatenda Chipeperekwa --- Documentation/devicetree/bindings/display/msm/sde-dp.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/sde-dp.txt b/Documentation/devicetree/bindings/display/msm/sde-dp.txt index 370798e91c83..a0b35eb9582e 100644 --- a/Documentation/devicetree/bindings/display/msm/sde-dp.txt +++ b/Documentation/devicetree/bindings/display/msm/sde-dp.txt @@ -100,6 +100,7 @@ Optional properties: - compatible: Must be "qcom,msm-ext-disp" - qcom,dp-low-power-hw-hpd: Low power hardware HPD feature enable control node - qcom,phy-version: Phy version +- qcom,pn-swap-lane-map: P/N swap configuration of each lane - pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node Refer to pinctrl-bindings.txt - pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin From d89e2cf714d739ba78f76ffa1971c088e495f84d Mon Sep 17 00:00:00 2001 From: Xiaowen Wu Date: Wed, 9 Jan 2019 15:52:05 -0500 Subject: [PATCH 26/28] drm/msm/dp: Add P/N swap support for dp phy Add P/N swap support for DP PHY. Change-Id: Ibc8be5aba9b658c52985dcd147cb2acfec9a1cf0 Signed-off-by: Xiaowen Wu Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_catalog.c | 25 ++++++++++++++++++++++++ drivers/gpu/drm/msm/dp/dp_catalog.h | 14 +++++++++++++ drivers/gpu/drm/msm/dp/dp_catalog_v420.c | 25 ++++++++++++++++++++++++ drivers/gpu/drm/msm/dp/dp_ctrl.c | 2 ++ drivers/gpu/drm/msm/dp/dp_parser.c | 6 ++++++ drivers/gpu/drm/msm/dp/dp_parser.h | 2 ++ drivers/gpu/drm/msm/dp/dp_reg.h | 2 ++ 7 files changed, 76 insertions(+) diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c index dc85ccc66783..3d15cd9dc8ba 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c @@ -882,6 +882,30 @@ static void dp_catalog_ctrl_lane_mapping(struct dp_catalog_ctrl *ctrl, 0xe4); } +static void dp_catalog_ctrl_lane_pnswap(struct dp_catalog_ctrl *ctrl, + u8 ln_pnswap) +{ + struct dp_catalog_private *catalog; + struct dp_io_data *io_data; + u32 cfg0, cfg1; + + catalog = dp_catalog_get_priv(ctrl); + + cfg0 = 0x0a; + cfg1 = 0x0a; + + cfg0 |= ((ln_pnswap >> 0) & 0x1) << 0; + cfg0 |= ((ln_pnswap >> 1) & 0x1) << 2; + cfg1 |= ((ln_pnswap >> 2) & 0x1) << 0; + cfg1 |= ((ln_pnswap >> 3) & 0x1) << 2; + + io_data = catalog->io.dp_ln_tx0; + dp_write(catalog->exe_mode, io_data, TXn_TX_POL_INV, cfg0); + + io_data = catalog->io.dp_ln_tx1; + dp_write(catalog->exe_mode, io_data, TXn_TX_POL_INV, cfg1); +} + static void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog_ctrl *ctrl, bool enable) { @@ -2509,6 +2533,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) .state_ctrl = dp_catalog_ctrl_state_ctrl, .config_ctrl = dp_catalog_ctrl_config_ctrl, .lane_mapping = dp_catalog_ctrl_lane_mapping, + .lane_pnswap = dp_catalog_ctrl_lane_pnswap, .mainlink_ctrl = dp_catalog_ctrl_mainlink_ctrl, .set_pattern = dp_catalog_ctrl_set_pattern, .reset = dp_catalog_ctrl_reset, diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h index 78aec7131bff..85ed20977373 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.h +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h @@ -1,6 +1,19 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. +<<<<<<< HEAD +======= + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +>>>>>>> aacf58a... drm/msm/dp: Add P/N swap support for dp phy */ #ifndef _DP_CATALOG_H_ @@ -93,6 +106,7 @@ struct dp_catalog_ctrl { void (*config_ctrl)(struct dp_catalog_ctrl *ctrl, u8 ln_cnt); void (*lane_mapping)(struct dp_catalog_ctrl *ctrl, bool flipped, char *lane_map); + void (*lane_pnswap)(struct dp_catalog_ctrl *ctrl, u8 ln_pnswap); void (*mainlink_ctrl)(struct dp_catalog_ctrl *ctrl, bool enable); void (*set_pattern)(struct dp_catalog_ctrl *ctrl, u32 pattern); void (*reset)(struct dp_catalog_ctrl *ctrl); diff --git a/drivers/gpu/drm/msm/dp/dp_catalog_v420.c b/drivers/gpu/drm/msm/dp/dp_catalog_v420.c index d5eebb40377e..51fa987c81b7 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog_v420.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog_v420.c @@ -266,6 +266,30 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl, } } +static void dp_catalog_ctrl_lane_pnswap_v420(struct dp_catalog_ctrl *ctrl, + u8 ln_pnswap) +{ + struct dp_catalog_private_v420 *catalog; + struct dp_io_data *io_data; + u32 cfg0, cfg1; + + catalog = dp_catalog_get_priv_v420(ctrl); + + cfg0 = 0x0a; + cfg1 = 0x0a; + + cfg0 |= ((ln_pnswap >> 0) & 0x1) << 0; + cfg0 |= ((ln_pnswap >> 1) & 0x1) << 2; + cfg1 |= ((ln_pnswap >> 2) & 0x1) << 0; + cfg1 |= ((ln_pnswap >> 3) & 0x1) << 2; + + io_data = catalog->io->dp_ln_tx0; + dp_write(catalog->exe_mode, io_data, TXn_TX_POL_INV_V420, cfg0); + + io_data = catalog->io->dp_ln_tx1; + dp_write(catalog->exe_mode, io_data, TXn_TX_POL_INV_V420, cfg1); +} + static void dp_catalog_put_v420(struct dp_catalog *catalog) { struct dp_catalog_private_v420 *catalog_priv; @@ -316,6 +340,7 @@ int dp_catalog_get_v420(struct device *dev, struct dp_catalog *catalog, catalog->panel.config_msa = dp_catalog_panel_config_msa_v420; catalog->ctrl.phy_lane_cfg = dp_catalog_ctrl_phy_lane_cfg_v420; catalog->ctrl.update_vx_px = dp_catalog_ctrl_update_vx_px_v420; + catalog->ctrl.lane_pnswap = dp_catalog_ctrl_lane_pnswap_v420; /* Set the default execution mode to hardware mode */ dp_catalog_set_exe_mode_v420(catalog, "hw"); diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 75a2f16a7c2d..d84417e42147 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -161,6 +161,8 @@ static void dp_ctrl_configure_source_link_params(struct dp_ctrl_private *ctrl, if (enable) { ctrl->catalog->lane_mapping(ctrl->catalog, ctrl->orientation, ctrl->parser->l_map); + ctrl->catalog->lane_pnswap(ctrl->catalog, + ctrl->parser->l_pnswap); ctrl->catalog->mst_config(ctrl->catalog, ctrl->mst_mode); ctrl->catalog->config_ctrl(ctrl->catalog, ctrl->link->link_params.lane_count); diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c index 34e795602b41..23ba31ec3b73 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.c +++ b/drivers/gpu/drm/msm/dp/dp_parser.c @@ -151,6 +151,12 @@ static int dp_parser_misc(struct dp_parser *parser) parser->l_map[i] = data[i]; } + data = of_get_property(of_node, "qcom,pn-swap-lane-map", &len); + if (data && (len == DP_MAX_PHY_LN)) { + for (i = 0; i < len; i++) + parser->l_pnswap |= (data[i] & 0x01) << i; + } + rc = of_property_read_u32(of_node, "qcom,max-pclk-frequency-khz", &parser->max_pclk_khz); if (rc) diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h index 9dba175dd94b..7f543c988b7f 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.h +++ b/drivers/gpu/drm/msm/dp/dp_parser.h @@ -182,6 +182,7 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type) * @mp: gpio, regulator and clock related data * @pinctrl: pin-control related data * @disp_data: controller's display related data + * @l_pnswap: P/N swap status on each lane * @max_pclk_khz: maximum pixel clock supported for the platform * @max_lclk_khz: maximum link clock supported for the platform * @hw_cfg: DP HW specific settings @@ -208,6 +209,7 @@ struct dp_parser { struct dp_display_data disp_data; u8 l_map[4]; + u8 l_pnswap; struct dp_aux_cfg aux_cfg[AUX_CFG_LEN]; u32 max_pclk_khz; u32 max_lclk_khz; diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h index 5089f0ce78b0..7f9391dc8f67 100644 --- a/drivers/gpu/drm/msm/dp/dp_reg.h +++ b/drivers/gpu/drm/msm/dp/dp_reg.h @@ -351,12 +351,14 @@ #define TXn_TX_EMP_POST1_LVL (0x000C) #define TXn_TX_DRV_LVL (0x001C) +#define TXn_TX_POL_INV (0x0064) #define DP_PHY_AUX_INTERRUPT_MASK_V420 (0x0054) #define DP_PHY_AUX_INTERRUPT_CLEAR_V420 (0x0058) #define DP_PHY_AUX_INTERRUPT_STATUS_V420 (0x00D8) #define DP_PHY_SPARE0_V420 (0x00C8) #define TXn_TX_DRV_LVL_V420 (0x0014) +#define TXn_TX_POL_INV_V420 (0x005C) #define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x004) From b639470825475f12d38e22d551d719dbd9c2992a Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Tue, 26 Mar 2019 09:46:36 -0700 Subject: [PATCH 27/28] Documentation: add fixed topology for DisplayPort MST on Kona Add optional property to specify fixed topology for DisplayPort MST feature. Change-Id: I20fe53205a25df819762e9d996e7b0adaf312396 Signed-off-by: Tatenda Chipeperekwa --- Documentation/devicetree/bindings/display/msm/sde-dp.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/sde-dp.txt b/Documentation/devicetree/bindings/display/msm/sde-dp.txt index a0b35eb9582e..78812304b887 100644 --- a/Documentation/devicetree/bindings/display/msm/sde-dp.txt +++ b/Documentation/devicetree/bindings/display/msm/sde-dp.txt @@ -107,6 +107,7 @@ Optional properties: controller. These pin configurations are installed in the pinctrl device node. Refer to pinctrl-bindings.txt - qcom,max-lclk-frequency-khz: An integer specifying the max. link clock in KHz supported by Display Port. +- qcom,mst-fixed-topology-ports: u32 values of which MST output port to reserve, start from one [Optional child nodes]: These nodes are for devices which are dependent on msm_ext_disp. If msm_ext_disp is disabled then From a5ca47723ccfde310a532fff5165c98970f42e34 Mon Sep 17 00:00:00 2001 From: Xiaowen Wu Date: Thu, 25 Oct 2018 17:51:13 -0400 Subject: [PATCH 28/28] drm/msm/dp: add encoder and connector reservation dp-mst Currently the dp-mst connector is created and destroyed dynamically, which requires drm client to handle hot-plug and update the whole connector list when device is connected or disconnected. A reservation approach is added to allow connector to be created statically for mst port in devicetree at system startup stage, and drm client can control the connector in the same way as non-mst connectors. This optional devicetree property is useful for system with a built-in mst-hub, and can simplify the drm client design. Change-Id: Ied59a5302b3f4cd1553619e6ee5acd7ce62df9ac Signed-off-by: Xiaowen Wu Signed-off-by: Tatenda Chipeperekwa --- drivers/gpu/drm/msm/dp/dp_display.c | 75 +++++++ drivers/gpu/drm/msm/dp/dp_display.h | 4 + drivers/gpu/drm/msm/dp/dp_mst_drm.c | 327 +++++++++++++++++++++++++++- drivers/gpu/drm/msm/dp/dp_parser.c | 7 + drivers/gpu/drm/msm/dp/dp_parser.h | 3 + 5 files changed, 414 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 9688032d844d..b326a50e3092 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -2346,6 +2346,77 @@ static int dp_display_update_pps(struct dp_display *dp_display, return 0; } +static int dp_display_mst_connector_update_link_info( + struct dp_display *dp_display, + struct drm_connector *connector) +{ + int rc = 0; + struct sde_connector *sde_conn; + struct dp_panel *dp_panel; + struct dp_display_private *dp; + + if (!dp_display || !connector) { + pr_err("invalid input\n"); + return -EINVAL; + } + + dp = container_of(dp_display, struct dp_display_private, dp_display); + + if (!dp->mst.drm_registered) { + pr_debug("drm mst not registered\n"); + return -EPERM; + } + + sde_conn = to_sde_connector(connector); + if (!sde_conn->drv_panel) { + pr_err("invalid panel for connector:%d\n", connector->base.id); + return -EINVAL; + } + + dp_panel = sde_conn->drv_panel; + + memcpy(dp_panel->dpcd, dp->panel->dpcd, + DP_RECEIVER_CAP_SIZE + 1); + memcpy(dp_panel->dsc_dpcd, dp->panel->dsc_dpcd, + DP_RECEIVER_DSC_CAP_SIZE + 1); + memcpy(&dp_panel->link_info, &dp->panel->link_info, + sizeof(dp_panel->link_info)); + + DP_MST_DEBUG("dp mst connector:%d link info updated\n"); + + return rc; +} + +static int dp_display_mst_get_fixed_topology_port( + struct dp_display *dp_display, + u32 strm_id, u32 *port_num) +{ + struct dp_display_private *dp; + u32 port; + + if (!dp_display) { + 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); + + port = dp->parser->mst_fixed_port[strm_id]; + + if (!port || port > 255) + return -ENOENT; + + if (port_num) + *port_num = port; + + return 0; +} + static int dp_display_get_mst_caps(struct dp_display *dp_display, struct dp_mst_caps *mst_caps) { @@ -2429,12 +2500,16 @@ static int dp_display_probe(struct platform_device *pdev) dp_display_mst_connector_uninstall; g_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; 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_mst_get_connector_info; + g_dp_display->mst_get_fixed_topology_port = + dp_display_mst_get_fixed_topology_port; rc = component_add(&pdev->dev, &dp_display_comp_ops); if (rc) { diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h index eb5994b1ed38..fe332aff47f5 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.h +++ b/drivers/gpu/drm/msm/dp/dp_display.h @@ -100,9 +100,13 @@ struct dp_display { int (*mst_connector_update_edid)(struct dp_display *dp_display, struct drm_connector *connector, struct edid *edid); + int (*mst_connector_update_link_info)(struct dp_display *dp_display, + struct drm_connector *connector); int (*mst_get_connector_info)(struct dp_display *dp_display, struct drm_connector *connector, struct dp_mst_connector *mst_conn); + int (*mst_get_fixed_topology_port)(struct dp_display *dp_display, + u32 strm_id, u32 *port_num); int (*get_mst_caps)(struct dp_display *dp_display, struct dp_mst_caps *mst_caps); int (*set_stream_info)(struct dp_display *dp_display, void *panel, diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c index 2f79541e5c40..508c6dccdf7d 100644 --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c @@ -23,7 +23,6 @@ #define DP_MST_DEBUG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) #define DP_MST_INFO_LOG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) -#define MAX_DP_MST_STREAMS 2 #define MAX_DP_MST_DRM_ENCODERS 2 #define MAX_DP_MST_DRM_BRIDGES 2 #define HPD_STRING_SIZE 30 @@ -102,6 +101,10 @@ struct dp_mst_bridge { int pbn; int num_slots; int start_slot; + + u32 fixed_port_num; + bool fixed_port_added; + struct drm_connector *fixed_connector; }; struct dp_mst_private { @@ -902,6 +905,10 @@ static void dp_mst_bridge_mode_set(struct drm_bridge *drm_bridge, /* DP MST Bridge APIs */ +static struct drm_connector * +dp_mst_drm_fixed_connector_init(struct dp_display *dp_display, + struct drm_encoder *encoder); + static const struct drm_bridge_funcs dp_mst_bridge_ops = { .attach = dp_mst_bridge_attach, .mode_fixup = dp_mst_bridge_mode_fixup, @@ -969,6 +976,23 @@ int dp_mst_drm_bridge_init(void *data, struct drm_encoder *encoder) DP_MST_DEBUG("mst drm bridge init. bridge id:%d\n", i); + /* + * If fixed topology port is defined, connector will be created + * immediately. + */ + rc = display->mst_get_fixed_topology_port(display, bridge->id, + &bridge->fixed_port_num); + if (!rc) { + bridge->fixed_connector = + dp_mst_drm_fixed_connector_init(display, + bridge->encoder); + if (bridge->fixed_connector == NULL) { + pr_err("failed to create fixed connector\n"); + rc = -ENOMEM; + goto end; + } + } + return 0; end: @@ -1161,7 +1185,8 @@ dp_mst_atomic_best_encoder(struct drm_connector *connector, } for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { - if (!mst->mst_bridge[i].encoder_active_sts) { + if (!mst->mst_bridge[i].encoder_active_sts && + !mst->mst_bridge[i].fixed_connector) { mst->mst_bridge[i].encoder_active_sts = true; mst->mst_bridge[i].connector = connector; mst->mst_bridge[i].dp_panel = conn->drv_panel; @@ -1368,6 +1393,7 @@ dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr, if (!connector) { pr_err("mst sde_connector_init failed\n"); + drm_modeset_unlock_all(dev); return connector; } @@ -1375,6 +1401,7 @@ dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr, if (rc) { pr_err("mst connector install failed\n"); sde_connector_destroy(connector); + drm_modeset_unlock_all(dev); return NULL; } @@ -1424,6 +1451,291 @@ static void dp_mst_destroy_connector(struct drm_dp_mst_topology_mgr *mgr, drm_connector_put(connector); } +static enum drm_connector_status +dp_mst_fixed_connector_detect(struct drm_connector *connector, bool force, + void *display) +{ + struct dp_display *dp_display = display; + struct dp_mst_private *mst = dp_display->dp_mst_prv_info; + int i; + + for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { + if (mst->mst_bridge[i].fixed_connector != connector) + continue; + + if (!mst->mst_bridge[i].fixed_port_added) + break; + + return dp_mst_connector_detect(connector, force, display); + } + + return connector_status_disconnected; +} + +static struct drm_encoder * +dp_mst_fixed_atomic_best_encoder(struct drm_connector *connector, + void *display, struct drm_connector_state *state) +{ + struct dp_display *dp_display = display; + struct dp_mst_private *mst = dp_display->dp_mst_prv_info; + struct sde_connector *conn = to_sde_connector(connector); + struct drm_encoder *enc = NULL; + u32 i; + + for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { + if (mst->mst_bridge[i].connector == connector) { + enc = mst->mst_bridge[i].encoder; + goto end; + } + } + + for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { + if (mst->mst_bridge[i].fixed_connector == connector) { + mst->mst_bridge[i].encoder_active_sts = true; + mst->mst_bridge[i].connector = connector; + mst->mst_bridge[i].dp_panel = conn->drv_panel; + enc = mst->mst_bridge[i].encoder; + break; + } + } + +end: + if (enc) + DP_MST_DEBUG("mst connector:%d atomic best encoder:%d\n", + connector->base.id, i); + else + DP_MST_DEBUG("mst connector:%d atomic best encoder failed\n", + connector->base.id); + + return enc; +} + +static u32 dp_mst_find_fixed_port_num(struct drm_dp_mst_branch *mstb, + struct drm_dp_mst_port *target) +{ + struct drm_dp_mst_port *port; + u32 port_num = 0; + + /* + * search through reversed order of adding sequence, so the port number + * will be unique once topology is fixed + */ + list_for_each_entry_reverse(port, &mstb->ports, next) { + if (port->mstb) + port_num += dp_mst_find_fixed_port_num(port->mstb, + target); + else if (!port->input) { + ++port_num; + if (port == target) + break; + } + } + + return port_num; +} + +static struct drm_connector * +dp_mst_find_fixed_connector(struct dp_mst_private *dp_mst, + struct drm_dp_mst_port *port) +{ + struct dp_display *dp_display = dp_mst->dp_display; + struct drm_connector *connector = NULL; + struct sde_connector *c_conn; + u32 port_num; + int i; + + mutex_lock(&port->mgr->lock); + port_num = dp_mst_find_fixed_port_num(port->mgr->mst_primary, port); + mutex_unlock(&port->mgr->lock); + + if (!port_num) + return NULL; + + for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { + if (dp_mst->mst_bridge[i].fixed_port_num == port_num) { + connector = dp_mst->mst_bridge[i].fixed_connector; + c_conn = to_sde_connector(connector); + c_conn->mst_port = port; + dp_display->mst_connector_update_link_info(dp_display, + connector); + dp_mst->mst_bridge[i].fixed_port_added = true; + DP_MST_DEBUG("found fixed connector %d\n", + DRMID(connector)); + break; + } + } + + return connector; +} + +static int +dp_mst_find_first_available_encoder_idx(struct dp_mst_private *dp_mst) +{ + int enc_idx = MAX_DP_MST_DRM_BRIDGES; + int i; + + for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { + if (!dp_mst->mst_bridge[i].fixed_connector) { + enc_idx = i; + break; + } + } + + return enc_idx; +} + +static struct drm_connector * +dp_mst_add_fixed_connector(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, const char *pathprop) +{ + struct dp_mst_private *dp_mst; + struct drm_device *dev; + struct dp_display *dp_display; + struct drm_connector *connector; + int i, enc_idx; + + DP_MST_DEBUG("enter\n"); + + dp_mst = container_of(mgr, struct dp_mst_private, mst_mgr); + + dp_display = dp_mst->dp_display; + dev = dp_display->drm_dev; + + if (port->input || port->mstb) + enc_idx = MAX_DP_MST_DRM_BRIDGES; + else { + /* if port is already reserved, return immediately */ + connector = dp_mst_find_fixed_connector(dp_mst, port); + if (connector != NULL) + return connector; + + /* first available bridge index for non-reserved port */ + enc_idx = dp_mst_find_first_available_encoder_idx(dp_mst); + } + + /* add normal connector */ + connector = dp_mst_add_connector(mgr, port, pathprop); + if (!connector) { + DP_MST_DEBUG("failed to add connector\n"); + return NULL; + } + + drm_modeset_lock_all(dev); + + /* clear encoder list */ + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) + connector->encoder_ids[i] = 0; + + /* re-attach encoders from first available encoders */ + for (i = enc_idx; i < MAX_DP_MST_DRM_BRIDGES; i++) + drm_connector_attach_encoder(connector, + dp_mst->mst_bridge[i].encoder); + + drm_modeset_unlock_all(dev); + + DP_MST_DEBUG("add mst connector:%d\n", connector->base.id); + + return connector; +} + +static void dp_mst_register_fixed_connector(struct drm_connector *connector) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct dp_display *dp_display = c_conn->display; + struct dp_mst_private *dp_mst = dp_display->dp_mst_prv_info; + int i; + + DP_MST_DEBUG("enter\n"); + + /* skip connector registered for fixed topology ports */ + for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { + if (dp_mst->mst_bridge[i].fixed_connector == connector) { + DP_MST_DEBUG("found fixed connector %d\n", + DRMID(connector)); + return; + } + } + + dp_mst_register_connector(connector); +} + +static void dp_mst_destroy_fixed_connector(struct drm_dp_mst_topology_mgr *mgr, + struct drm_connector *connector) +{ + struct dp_mst_private *dp_mst; + int i; + + DP_MST_DEBUG("enter\n"); + + dp_mst = container_of(mgr, struct dp_mst_private, mst_mgr); + + /* skip connector destroy for fixed topology ports */ + for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) { + if (dp_mst->mst_bridge[i].fixed_connector == connector) { + dp_mst->mst_bridge[i].fixed_port_added = false; + DP_MST_DEBUG("destroy fixed connector %d\n", + DRMID(connector)); + return; + } + } + + dp_mst_destroy_connector(mgr, connector); +} + +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 = { + .post_init = NULL, + .detect = dp_mst_fixed_connector_detect, + .get_modes = dp_mst_connector_get_modes, + .mode_valid = dp_mst_connector_mode_valid, + .get_info = dp_mst_connector_get_info, + .get_mode_info = dp_mst_connector_get_mode_info, + .atomic_best_encoder = dp_mst_fixed_atomic_best_encoder, + .atomic_check = dp_mst_connector_atomic_check, + .config_hdr = dp_mst_connector_config_hdr, + .pre_destroy = dp_mst_connector_pre_destroy, + }; + struct drm_device *dev; + struct drm_connector *connector; + int rc; + + DP_MST_DEBUG("enter\n"); + + dev = dp_display->drm_dev; + + connector = sde_connector_init(dev, + encoder, + NULL, + dp_display, + &dp_mst_connector_ops, + DRM_CONNECTOR_POLL_HPD, + DRM_MODE_CONNECTOR_DisplayPort); + + if (!connector) { + pr_err("mst sde_connector_init failed\n"); + return NULL; + } + + rc = dp_display->mst_connector_install(dp_display, connector); + if (rc) { + pr_err("mst connector install failed\n"); + sde_connector_destroy(connector); + return NULL; + } + + drm_object_attach_property(&connector->base, + dev->mode_config.path_property, 0); + drm_object_attach_property(&connector->base, + dev->mode_config.tile_property, 0); + + DP_MST_DEBUG("add mst fixed connector:%d\n", connector->base.id); + + return connector; +} + static void dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr) { struct dp_mst_private *mst = container_of(mgr, struct dp_mst_private, @@ -1569,6 +1881,13 @@ static const struct drm_dp_mst_topology_cbs dp_mst_drm_cbs = { .hotplug = dp_mst_hotplug, }; +static const struct drm_dp_mst_topology_cbs dp_mst_fixed_drm_cbs = { + .add_connector = dp_mst_add_fixed_connector, + .register_connector = dp_mst_register_fixed_connector, + .destroy_connector = dp_mst_destroy_fixed_connector, + .hotplug = dp_mst_hotplug, +}; + static void dp_mst_sim_init(struct dp_mst_private *mst) { INIT_WORK(&mst->simulator.probe_work, dp_mst_sim_link_probe_work); @@ -1633,6 +1952,10 @@ int dp_mst_init(struct dp_display *dp_display) } memset(&dp_mst_enc_cache, 0, sizeof(dp_mst_enc_cache)); + /* 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_INFO_LOG("dp drm mst topology manager init completed\n"); return ret; diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c index 23ba31ec3b73..bc4369dc7803 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.c +++ b/drivers/gpu/drm/msm/dp/dp_parser.c @@ -703,6 +703,7 @@ static int dp_parser_catalog(struct dp_parser *parser) static int dp_parser_mst(struct dp_parser *parser) { struct device *dev = &parser->pdev->dev; + int i; parser->has_mst = of_property_read_bool(dev->of_node, "qcom,mst-enable"); @@ -710,6 +711,12 @@ static int dp_parser_mst(struct dp_parser *parser) pr_debug("mst parsing successful. mst:%d\n", parser->has_mst); + for (i = 0; i < MAX_DP_MST_STREAMS; i++) { + of_property_read_u32_index(dev->of_node, + "qcom,mst-fixed-topology-ports", i, + &parser->mst_fixed_port[i]); + } + return 0; } diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h index 7f543c988b7f..9caa1a7ddff7 100644 --- a/drivers/gpu/drm/msm/dp/dp_parser.h +++ b/drivers/gpu/drm/msm/dp/dp_parser.h @@ -12,6 +12,7 @@ #define AUX_CFG_LEN 10 #define DP_MAX_PIXEL_CLK_KHZ 675000 #define DP_MAX_LINK_CLK_KHZ 810000 +#define MAX_DP_MST_STREAMS 2 enum dp_pm_type { DP_CORE_PM, @@ -195,6 +196,7 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type) * @max_dp_dsc_blks: maximum DSC blks for DP interface * @max_dp_dsc_input_width_pixs: Maximum input width for DSC block * @has_widebus: widebus (2PPC) feature eanble status + *@mst_fixed_port: mst port_num reserved for fixed topology * @parse: function to be called by client to parse device tree. * @get_io: function to be called by client to get io data. * @get_io_buf: function to be called by client to get io buffers. @@ -224,6 +226,7 @@ struct dp_parser { u32 max_dp_dsc_blks; u32 max_dp_dsc_input_width_pixs; bool lphw_hpd; + u32 mst_fixed_port[MAX_DP_MST_STREAMS]; int (*parse)(struct dp_parser *parser); struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name);